简体   繁体   English

了解 GCC 内联汇编 function

[英]understanding GCC inline asm function

i will write my assumptions (based on my researches) in the question below i assume that there are mistakes in my assemptions outside the question it self:我将在下面的问题中写下我的假设(基于我的研究)我假设我的假设在它本身的问题之外存在错误:

i'm looking into some code written for ARM:我正在研究为 ARM 编写的一些代码:

this function (taken from FreeRTOS port code):这个 function(取自 FreeRTOS 端口代码):

portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI(void)
{
    uint32_t ulOriginalBASEPRI, ulNewBASEPRI;

    __asm volatile("    mrs %0, basepri                                         \n"
                   "    mov %1, %2                                              \n"
                   "    msr basepri, %1                                         \n"
                   "    isb                                                     \n"
                   "    dsb                                                     \n"
                   : "=r"(ulOriginalBASEPRI), "=r"(ulNewBASEPRI)
                   : "i"(configMAX_SYSCALL_INTERRUPT_PRIORITY));

    /* This return will not be reached but is necessary to prevent compiler
    warnings. */
    return ulOriginalBASEPRI;
}

i understand in gcc "=r" is output operand.我了解 gcc “=r” 是 output 操作数。 so we save values from asm to C variable所以我们将值从 asm 保存到 C 变量

now the code in my understanding is equivalent to:现在我理解的代码相当于:

ulOriginalBASEPRI = basepri
ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY
basepri = ulNewBASEPRI

i understand we are returning the original value of BASEPRI so thats the first line.我知道我们正在返回 BASEPRI 的原始值,所以这是第一行。 however, i didn't understand why we assign variable ulNewBASEPRI then we use it in MSR instruction..但是,我不明白为什么我们分配变量 ulNewBASEPRI 然后我们在 MSR 指令中使用它..

so I've looked in the ARMV7 instruction set and i saw this:所以我查看了ARMV7 指令集,我看到了这个: 在此处输入图像描述

i assume there is no (MSR immediate) in thumb instruction and "Encoding A1" means its only in Arm instruction mode.我假设拇指指令中没有(MSR 立即数),并且“编码 A1”意味着它仅在 Arm 指令模式下。 so we have to use =r output operand to let asembler to auto select a register for our variable am i correct?所以我们必须使用 =r output 操作数让汇编器自动 select 为我们的变量注册一个寄存器,我正确吗?


EDIT: ignore this section because i miscounted colons编辑:忽略此部分,因为我算错了冒号

: "i"(configMAX_SYSCALL_INTERRUPT_PRIORITY));

from my understanding for assembly template :根据我对装配模板的理解:

   asm ( assembler template 
       : output operands                  /* optional */
       : input operands                   /* optional */
       : list of clobbered registers      /* optional */
       );

isn't "i" just means (immediate) or constant in the assembly? “i”在程序集中不只是意味着(立即)或常量吗?
does this mean the third colon is not only for clobber list?这是否意味着第三个冒号不仅用于clobber列表?
if that so, isn't it more appropriate to find the constraint "i" in the input operands?如果是这样,在输入操作数中找到约束“i”不是更合适吗?
EDIT: ignore this section because i miscounted colons编辑:忽略此部分,因为我算错了冒号


i understand isb, dsb are memory barrier stuff but i really dont understand the discription of them.我了解 isb,dsb 是 memory 屏障的东西,但我真的不明白它们的描述。 what they really do?他们到底在做什么? what happen if we remove dsb or isb instruction for example.?例如,如果我们删除 dsb 或 isb 指令会发生什么。?

Concerning ARM/Thumb instruction set differences on msr : you should be able to answer this yourself from the documentation.关于msr上的 ARM/Thumb 指令集差异:您应该能够从文档中自己回答这个问题。 ;-) It is just 2 pages later. ;-) 它只是 2 页之后。 Edit: Chapter A8.1.3 of the linked manual clearly states how encodings are documented on instructions.编辑:链接手册的第 A8.1.3 章清楚地说明了如何在说明中记录编码。

dsb (data synchronization barrier) makes sure that all memory accesses are finished before the next instruction is executed. dsb (数据同步屏障)确保在执行下一条指令之前完成所有 memory 访问。 This is really shortly written, for the full details you need to read the documentation.这真的写得很短,有关您需要阅读文档的完整详细信息。 If you have further specific questions about this operation, please post another question.如果您对此操作还有其他具体问题,请发布另一个问题。

isb (instruction synchronization barrier) purges the instruction pipeline. isb (指令同步屏障)清除指令流水线。 This pipeline buffers instructions which are already fetched from memory but are not yet executed.此管道缓冲已从 memory 获取但尚未执行的指令。 So the next instruction will be fetched with possibly changed memory access, and this is what a programmer expects.因此,下一条指令将通过可能更改的 memory 访问来获取,这正是程序员所期望的。 The note above applies here, too.上面的注释在这里也适用。

so we have to use =r output operand to let assembler to auto select a register for our variable am i correct?所以我们必须使用 =r output 操作数让汇编程序自动 select 为我们的变量注册一个寄存器,我正确吗?

Yes, but it's the compiler that does register allocation.是的,但它是编译器进行寄存器分配。 It just fills in the %[operand] in the asm template string as a text substitution and feeds that to the assembler.它只是将 asm 模板字符串中的%[operand]填充为文本替换,并将提供给汇编程序。

Alternatively, you could hard-code a specific register in the asm template string, and use a register-asm local variable to make sure an "=r" constraint picked it.或者,您可以硬编码 asm 模板字符串中的特定寄存器,并使用 register-asm 局部变量来确保"=r"约束选择了它。 Or use an "=m" memory output operand and str a result into it, and declare a clobber on any registers you used.或者使用"=m" memory str操作数并将结果写入其中,并在您使用的任何寄存器上声明一个clobber。 But those alternatives are obviously terrible compared to just telling the compiler about how your block of asm can produce an output.但与仅仅告诉编译器你的 asm 块如何生成 output 相比,这些替代方案显然很糟糕。


I don't understand why the comment says the return statement doesn't run:我不明白为什么评论说 return 语句不运行:

   /* This return will not be reached but is necessary to prevent compiler
   warnings. */
   return ulOriginalBASEPRI;

Raising the basepri ( ARM docs ) to a higher number might allow an interrupt handler to run right away, before later instructions, but if that exception ever returns, execution will eventually reach the C outside the asm statement.basepri ( ARM 文档) 提高到更高的数字可能允许中断处理程序在后面的指令之前立即运行,但如果该异常再次返回,执行最终将到达 asm 语句之外的 C。 That's the whole point of saving the old basepri into a register and having an output operand for it, I assume.我认为,这就是将旧的basepri保存到寄存器并为其使用 output 操作数的全部意义所在。

(I had been assuming that "raise" meant higher number = more interrupts allowed. But Ross comments that it will never allow more interrupts; they're "raising the bar" = lower number = fewer interrupts allowed.) (我一直假设“提高”意味着更高的数量 = 允许更多的中断。但罗斯评论说它永远不会允许更多的中断;他们正在“提高标准”= 更低的数量 = 允许的中断更少。)

If execution really never comes out the end of your asm, you should tell the compiler about it .如果执行真的永远不会出现在你的 asm 的末尾,你应该告诉编译器它 There isasm goto , but that needs a list of possible branch targets.asm goto ,但这需要一个可能的分支目标列表。 The GCC manual says: GCC 手册说:

GCC assumes that asm execution falls through to the next statement (if this is not the case, consider using the __builtin_unreachable() intrinsic after the asm statement). GCC 假定 asm 执行落到下一条语句(如果不是这种情况,请考虑在 asm 语句之后使用__builtin_unreachable()内部函数)。

Failing to do this might lead to the compiler planning to do something after the asm, and then it never happening even though in the source it's before the asm.不这样做可能会导致编译器计划在 asm 之后执行某些操作,然后即使在源代码中它位于 asm 之前,它也永远不会发生。


It might be a good idea to use a "memory" clobber to make sure the compiler has memory contents in sync with the C abstract machine .使用"memory"破坏器来确保编译器具有与 C 抽象机同步的 memory 内容可能是个好主意 (At least for variables other than locals, which an interrupt handler might access). (至少对于中断处理程序可能访问的本地变量以外的变量)。 This is usually desirable around asm barrier instructions like dsb , but it seems here we maybe don't care about being an SMP memory barrier, just about consistent execution after changing basepri ?对于像dsb这样的 asm 屏障指令,这通常是可取的,但似乎在这里我们可能并不关心成为 SMP memory 屏障,只是在更改basepri后保持一致执行? I don't understand why that's necessary, but if you do then worth considering one way or another whether compile-time reordering of memory access around the asm statement is or isn't a problem.我不明白为什么这是必要的,但如果你这样做,那么值得考虑一种或另一种方式,是否围绕asm语句对 memory 访问进行编译时重新排序是否存在问题。

You'd use a third colon-separated section in the asm statement (after the inputs) : "memory"您将在 asm 语句中使用第三个以冒号分隔的部分(在输入之后) : "memory"

Without that, compilers might decide to do an assignment after this asm instead of before, leaving a value just in registers.没有它,编译器可能会决定在这个 asm之后而不是之前进行赋值,只在寄存器中留下一个值。

// actual C source
  global_var = 1;
  uint32_t oldpri = ulPortRaiseBASEPRI();
  global_var = 2;

could optimize (via dead-store elimination) into asm that worked like this可以优化(通过死店消除)成这样工作的 asm

// possible asm
  global_var = 2;
  uint32_t oldpri = ulPortRaiseBASEPRI();
  // or global_var = 2; here *instead* of before the asm

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM