Using Inline Assembly With gcc

来源:互联网 发布:php成绩管理系统代码 编辑:程序博客网 时间:2024/04/29 23:51

All following are from http://www.cs.dartmouth.edu/~sergey/cs108/2009/gcc-inline-asm.pdf

 

Assembler Instructions with C Expression Operands

C表达式作为操作数的汇编器指令

 

In an assembler instruction using ‘asm’, you can now specify the operands of the instruction using C expressions.  This means no more guessing which registers or memory locations will contain the data you want to use.

在使用'asm'的汇编器指令中,可以使用C表达式定义指令的操作数。这意味着对于哪些寄存器或者内存位置会保存你想要使用的数据没有进一步的猜测。

 

 

You must specify an assembler instruction template much like what appears in a machine description, plus an operand constraint string for each operand.

你必须定义一个汇编器指令模板,就像出现在机器描述中的一样,加上每一个操作数的操作数受限字符串。

 

 

 

For example, here is how to use the 68881’s ‘fsinx’ instruction:


asm (“fsinx %1,%0” : “=f” (result) : “f” (angle));

 

 


Here ‘angle’ is the C expression for the input operand while ‘result’ is that of the output

operand.  Each has ‘”f”’ as its operand constraint, saying that a floating point register is

required.  The ‘=’ in ‘=f’ indicates that the operand is an output; all output operands’ constraints must use ‘=’.  The constraints use the same language used in the machine description (*note Constraints::.).

 

例如,这儿有怎样使用68881的'fsinx'指令:

asm("fsinx %1, %0":"=f"(result):"f"(angle));

 

 

在这个例子里,‘angle’ 是输入操作数的C表达式,而‘result’是输出操作数的C表达式。每一个将“f”作为它的操作数限制符的操作数,说明需要浮点寄存器。(也就是,angle的值需要保存在浮点寄存器里)。'=f'里的'='说明,该操作数是输出操作数,所有输出操作数的限制符必须使用'='。限制符使用的语言与用在机器描述中的语言一样。

 

 

Each operand is described by an operand-constraint string followed by the C expression

in parentheses.  A colon separates the assembler template from the first output operand,

and another separates the last output operand from the first input, if any. Commas separate

output operands and separate inputs.  The total number of operands is limited to ten or to

the maximum number of operands in any instruction pattern in the machine description,

whichever is greater.

 

每一个操作数是以操作数限制符字符串后边跟带括号的C表达式来描述的。冒号(:)会将汇编器模板与第一个输出操作数分隔开,另外的冒号会将最后一个输出操作数与第一个输入操作数分割开,如果有的话。逗号分隔输出操作数和输入操作数。总的操作数数目限制到10或者机器描述中指令格式的最大操作数数目,取两者中的较大者即为限制的最大数目。

 

 

 

If there are no output operands, and there are input operands, then there must be two

consecutive colons surrounding the place where the output operands would go.

如果没有输出操作数,有输入操作数,那么,必须有2个连续的冒号围绕输出操作数所在的位置。

 

 

 

Output operand expressions must be lvalues; the compiler can check this.  The input

operands need not be lvalues. The compiler cannot check whether the operands have data

types that are reasonable for the instruction being executed.  It does not parse the assembler instruction template and does not know what it means, or whether it is valid assembler

input.  The extended ‘asm’ feature is most often used for machine instructions that the

compiler itself does not know exist. If the output expression cannot be directly addressed

(for example, it is a bit field), your constraint must allow a register. In that case, GNU CC

will use the register as the output of the ‘asm’, and then store that register into the output.


输出操作数表达式必须是lvalues;编译器会做该项检查。输入操作数不必是lvalues。编译器不能检查操作数是否具有对于正在执行的指令来说合理的数据类型。编译器不会分析汇编器指令模板,也不知道它意味着什么,或者它是否是有效的汇编器输入。扩展的'asm'特性经常用于编译器本身不知道的机器指令。如果输出表达式不能直接寻址(例如,输出表达式是位域),你的限制符必须允许寄存器表示。在这种情况下,GNU CC 会使用寄存器作为'asm'输出,之后,将寄存器保存到输出。

 

 

 

The output operands must be write-only; GNU CC will assume that the values in these

operands before the instruction are dead and need not be generated.  Extended asm does

not support input-output or read-write operands.  For this reason, the constraint character

‘+’, which indicates such an operand, may not be used.


输出操作数必须可写;GNU CC 会假设该指令之前的这些操作数中的值是dead,不必生成。扩展的asm不支持输入-输出或者读-写操作数。基于这个原因,限制字符'+',指出了这种操作数,或许不会用到。

 

 

 

When the assembler instruction has a read-write operand, or an operand in which only

some of the bits are to be changed, you must logically split its function into two separate

operands, one input operand and one write-only output operand. The connection between

them is expressed by constraints which say they need to be in the same location when the

instruction executes.  You can use the same C expression for both operands, or different

expressions.  For example, here we write the (fictitious) ‘combine’ instruction with ‘bar’

as its read-only source operand and ‘foo’ as its read-write destination:


     asm (“combine %2,%0”

          : “=r” (foo)

          : “0” (foo), “g” (bar));

 

The constraint ‘”0”’ for operand 1 says that it must occupy the same location as operand 0.

A digit in constraint is allowed only in an input operand, and it must refer to an output

operand.

 

 

当汇编器的指令中有既要读又要写的操作数,或者只会改变某个操作数中的某些bits的时候,必须在逻辑上将该功能分成

2个独立的操作数,一个输入操作数,一个只写的输出操作数。

它们间的连接可以通过限制符来表达,限制符就说明了,当指令执行的时候,这2个操作数在相同的位置。

你可以为两个操作数使用相同的或者不同的C表达式。

例如,我们写一个'combine'指令,'bar'作为该指令的只读的源操作数,'foo'作为它的既读又写的目的操作数:

    asm( "combine %2,%0"

             :"=r"(foo)

             :"0"(foo),"g"(bar));

 

操作数1的限制符"0",说明了,操作数1占据的位置与操作数0的位置相同。限制符中的一个数位,如"0",仅允许出现在输入操作数中,并且该数位关联的是一个输出操作数。

 

 

 

Only a digit in the constraint can guarantee that one operand will be in the same place as

another. The mere fact that ‘foo’ is the value of both operands is not enough to guarantee

that they will be in the same place in the generated assembler code. The following would

not work:

     asm (“combine %2,%0”

          : “=r” (foo)

          : “r” (foo), “g” (bar));

Various optimizations or reloading could cause operands 0 and 1 to be in different registers; GNU CC knows no reason not to do so. For example, the compiler might find a copy

of the value of ‘foo’ in one register and use it for operand 1, but generate the output operand 0 in a different register (copying it afterward to ‘foo’’s own address). Of course, since

the register for operand 1 is not even mentioned in the assembler code, the result will not

work, but GNU CC can’t tell that.

 

限制符中的仅仅一个数位就可以保证一个操作数和另一个操作数在相同的位置。只凭'foo'是两个操作数的值的事实并不足以保证两个操作数在生成的汇编代码中占据相同的位置。

下面这个例子就不能奏效:

 asm("comine %2,%0"

         :"=r"(foo)

         :"r"(foo),"g"(bar));

各种优化或者重载会使得操作数0和1在不同的寄存器中;GNU CC并没有理由不去这么做。

例如,编译器或许会在一个寄存器中发现'foo'的值的一份拷贝,并将其用作操作数1(也就是一个输入操作数),但是在一个不同的寄存器中生成输出操作数0(将其拷贝回'foo'自己的地址)。当然,因为操作数1所在的寄存器甚至没有在汇编代码中提到,结果不会运行,但是,GNU CC并不能辨识这个情况。

 

 

Some instructions clobber specific hard registers.  To describe this, write a third colon

after the input operands, followed by the names of the clobbered hard registers (given as

strings).  Here is a realistic example for the Vax:

     asm volatile (“movc3 %0,%1,%2”

                   : /* no outputs */

                   : “g” (from), “g” (to), “g” (count)

: “r0”, “r1”, “r2”, “r3”, “r4”, “r5”);

   If you refer to a particular hardware register from the assembler code, then you will

probably have to list the register after the third colon to tell the compiler that the register’s

value is modified. In many assemblers, the register names begin with ‘%’; to produce one

‘%’ in the assembler code, you must write ‘%%’ in the input.

 

一些指令会占据特定的硬件寄存器。

为了描述这一点,在输入参数之后写第三个冒号,冒号后面跟随着被占用的硬件寄存器的名字(以字符串的形式)。下面是Vax上一个实际的例子:

  asm volatile("movc3 %0,%1,%2"

                      :/*no outpus*/

                      :“g”(from),"g"(to),"g"(count)

                      :"r0","r1","r2","r3","r4","r5");

如果你在汇编代码中有涉及到硬件寄存器,那么,你可能必须在第三个冒号之后列出相关的寄存器,以告知编译器寄存器的值呗修改了。

在很多汇编器中,寄存器名字以'%‘开始;为了在汇编代码中生成一个'%',你必须在输入的时候写成'%%'.

 

 

 

 If your assembler instruction can alter the condition code register, add ‘cc’ to the list of

clobbered registers.  GNU CC on some machines represents the condition codes as a specific hardware register; ‘cc’ serves to name this register. On other machines, the condition

code is handled differently, and specifying ‘cc’ has no effect.  But it is valid no matter

what the machine.

 

如果你的汇编指令能够改动condition code寄存器,那么, 将'cc'也加入到占据的寄存器列表中。某些机器上的GNU CC将condition codes表示为特定的硬件寄存器;'cc'用来命名这个寄存器。在其他的机器上,condition code的处理就不同了,定义'cc'也不会起作用。但是,无论什么机器,'cc'都是有效的。

 

 

 

If your assembler instruction modifies memory in an unpredictable fashion, add ‘memory’ to the list of clobbered registers. This will cause GNU CC to not keep memory values

cached in registers across the assembler instruction.

 

如果汇编指令会在非预期的情况下修改内存,那么,将'memory'加入到占据的寄存器的列表中。

这会使得GNU CC不会在汇编指令中将内存的值缓存在寄存器里。

 

 

 

You can put multiple assembler instructions together in a single ‘asm’ template, separated either with newlines (written as ‘/n’) or with semicolons if the assembler allows such

semicolons.  The GNU assembler allows semicolons and all Unix assemblers seem to do

so.  The input operands are guaranteed not to use any of the clobbered registers, and neither will the output operands’ addresses, so you can read and write the clobbered registers

 

as many times as you like.  Here is an example of multiple instructions in a template; it

assumes that the subroutine ‘_foo’ accepts arguments in registers 9 and 10:

asm (“movl %0,r9;movl %1,r10;call _foo”

              : /* no outputs */

              : “g” (from), “g” (to)

              : “r9”, “r10”);

Unless an output operand has the ‘&’ constraint modifier, GNU CC may allocate it in the

same register as an unrelated input operand, on the assumption that the inputs are consumed before the outputs are produced. This assumption may be false if the assembler

code actually consists of more than one instruction.  In such a case, use ‘&’ for each output operand that may not overlap an input.  *Note Modifiers::.

 

 

你可以将多个汇编指令放在单一的'asm'模板中,通过换行符'/n'或者分号来分隔,如果汇编器允许分号的话。

GNU 汇编器允许分号,并且所有的Unix汇编器看起来也是这样。输入操作数保证不要使用任何clobbered寄存器,输出操作数的地址也是一样,因此,你可以不止一次的读写clobbered寄存器。下面就是一个例子,多个指令在一个模板中;该例子假设子例程'_foo'以寄存器9和10来接收参数:

    asm("movl %0,r9;movl %1,r10; call _foo"

            :/*no outputs*/

            :"g"(from),"g"(to)

            :"r9","r10");

除非输出操作数有'&'限制修改符,假定输入在输出被生成之前使用了,GNU CC或许会将其分配在一个不相干的输入操作数所在的同一个寄存器中。如果汇编代码由多个指令组成,该假设或许是不成立的。在这样的情况下,为每一个输出操作数使用'&'应该不回覆盖掉输入操作数。注意修改符::。

 

 

If you want to test the condition code produced by an assembler instruction, you must

include a branch and a label in the ‘asm’ construct, as follows:

     asm (“clr %0;frob %1;beq 0f;mov #1,%0;0:”

              : “g” (result)

              : “g” (input));

This assumes your assembler supports local labels, as the GNU assembler and most Unix

assemblers do.

 

如果你想要测试由汇编指令产生的condition code,那么必须在'asm'构建中包含一个branch和一个label,如下:

  asm("clr %0;frob %1;beq 0f;mov #1,%0;0:"

           :"g"(result)

           :"g"(input));

该假定基于你的汇编器支持本地labels,正如GNU汇编器和大多数的Unix汇编器所作的那样。

 

 

 

Speaking of labels, jumps from one ‘asm’ to another are not supported.  The compiler’s

optimizers do not know about these jumps, and therefore they cannot take account of them

when deciding how to optimize.

 

说到labels,从一个'asm'跳到另一个是不支持的。编译器的优化器对于这些跳转没有认识,因此,在决定该怎么样优化的时候,它们不会考虑这些跳转。

 

 

 

Usually the most convenient way to use these ‘asm’ instructions is to encapsulate them

in macros that look like functions.  For example,

     #define sin(x)       /

      ({ double __value, __arg = (x);   /

         asm (“fsinx %1,%0” /

              : “=f” (__value) /

              : “f” (__arg));  /

         __value; })

Here the variable ‘__arg’ is used to make sure that the instruction operates on a proper

‘double’ value, and to accept only those arguments ‘x’ which can convert automatically to

a ‘double’.

 

通常,使用这些'asm'指令最方便的方法是将它们封装在看起来像函数的宏里,例如

    #define sin(x)           /

     ({double __value, __arg = (x);  /

         asm ("fsinx %1, %0" /

 

 

                  :"=f"(__value)   /

                  :"f"(__arg));  /

                __value; })

这里,变量'__arg'用来确保指令操作于适当的'double'的值上,并且仅接受这些参数'x','x'能自动转换为'double'。

 

 

Another way to make sure the instruction operates on the correct data type is to use a

cast in the ‘asm’.  This is different from using a variable ‘__arg’ in that it converts more

different types.  For example, if the desired type were ‘int’, casting the argument to ‘int’

would accept a pointer with no complaint, while assigning the argument to an ‘int’ variable named ‘__arg’ would warn about using a pointer unless the caller explicitly casts it.

 

另一个可以确保指令操作在正确的数据类型上的方法是,在'asm'里,使用cast(强制转换)。这与在'asm'里使用变量'__arg'转换不同类型不同。例如,如果期望的类型是'int',将参数转换为'int'会无抱怨的接受一个指针,而将参数分配给一个名为'__arg'的变量会有关于使用指针的告警,除非调用者显示地转换它。

 

 

 

If an ‘asm’ has output operands, GNU CC assumes for optimization purposes that the

instruction has no side effects except to change the output operands.  This does not mean

that instructions with a side effect cannot be used, but you must be careful, because the

compiler may eliminate them if the output operands aren’t used, or move them out of

loops, or replace two with one if they constitute a common subexpression.  Also, if your

 

instruction does have a side effect on a variable that otherwise appears not to change, the

old value of the variable may be reused later if it happens to be found in a register.

 

 

如果'asm'有输出操作数,为了优化目的考虑,GNU CC会假定,指令除了改变输出操作数之外,没有副作用。

这并不是说,带有副作用的指令不能使用,但是必须小心,如果没有使用输出操作数,或者将它们移动到了循环之外,

或者如果它们组成了一个通用的字表达式因而将2个替换成了一个的话,编译器会消除副作用。

还有,如果你的指令对一个变量确实有副作用,然而该变量看起来却没有改变,该变量之前的值,如果碰巧在一个寄存器中被发现,它或许会被重用的。

 

You can prevent an ‘asm’ instruction from being deleted, moved significantly, or combined, by writing the keyword ‘volatile’ after the ‘asm’.  For example:

     #define set_priority(x)  /

      asm volatile (“set_priority %0”   /

                    : /* no outputs */  /

                    : “g” (x))

An instruction without output operands will not be deleted or moved significantly, regardless, unless it is unreachable.

 

可以通过在'asm'之后写上关键字'volatile'来防止'asm'指令被删除,被移动,或者被合并。

例如:

  #define set_priority(x) /

    asm volatile("set_priority %0"  /

                         :/*no outputs*/   /

                         :"g"(x))

没有输出操作数的指令不会被删除或者移动,除非,该指令不会得到执行。

 

Note that even a volatile ‘asm’ instruction can be moved in ways that appear insignifi-

cant to the compiler, such as across jump instructions. You can’t expect a sequence of volatile ‘asm’ instructions to remain perfectly consecutive.  If you want consecutive output,

use a single ‘asm’.

 

注意,即使volatile 'asm'指令也可能以对编译器而言无足轻重的方式进行移动,例如,通过jump指令。你并不能期望volatile 'asm'指令的序列保持完美的连续性。如果你想要连续的输出,使用单个的'asm'。

 

   It is a natural idea to look for a way to give access to the condition code left by the

assembler instruction. However, when we attempted to implement this, we found no way

to make it work reliably. The problem is that output operands might need reloading, which

would result in additional following “store” instructions.  On most machines, these

instructions would alter the condition code before there was time to test it.  This problem

doesn’t arise for ordinary “test” and “compare” instructions because they don’t have any

output operands.

 

寻找一个方法访问汇编器指令留下的condition code是很自然的想法。然而,当我们尝试将其实现的时候,我们发现并不能找到让它稳定工作的方法。问题就在于,输出操作数可能需要重载,而这会引起另外的随后的'store'指令。在大多数的机器上,在能够有时间测试condition code之前,这些指令就已经改变了它。对于通常的'test'和'compare'指令,该问题就不会出现,因为,这些指令没有输出操作数。

 

 

   If you are writing a header file that should be includable in ANSI C programs, write ‘__asm__’ instead of ‘asm’.  *Note Alternate Keywords::.

 

如果你正在写需要被包含在ANSI C程序里的头文件的话,使用'__asm__'而不是'asm'。

 

这篇翻译就结束了,由于个人的理解也不是很深,有些地方可能是写了表面的意思,不过还好,有英文原文对照,希望没有污了各位的眼。

 

 

原创粉丝点击