【高级C】GNU C/C++ 内联汇编——实例参考
本人就职于国际知名终端厂商,负责modem芯片研发。在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。博客内容主要围绕: 5G协议讲解 算力网络讲解(云计算,边缘计算,端计算)
本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。
博客内容主要围绕:
5G协议讲解
算力网络讲解(云计算,边缘计算,端计算)
高级C语言讲解
Rust语言讲解
文章目录
GNU C/C++ 内联汇编——实例参考
下面的示例以 IBM 的 Power PC 为例。
Example 1
下面的示例说明了 volatile 关键字的用法:
#include <stdio.h>
inline bool acquireLock(int *lock){
bool returnvalue = false;
int lockval;
asm volatile(
/*--------a fence here-----*/
" 0: lwarx %0,0,%2 \n" // Loads the word and reserves
// a memory location for the subsequent
// stwcx. instruction.
" cmpwi %0,0 \n" // Compares the lock value to 0.
" bne- 1f \n" // If it is 0, you can acquire the
// lock. Otherwise, you did not get the
// lock and must try again later.
" ori %0,%0,1 \n" // Sets the lock to 1.
" stwcx. %0,0,%2 \n" // Tries to conditionally store 1
// into the lock word to acquire
// the lock.
" bne- 0b \n" // Reservation was lost. Try again.
" isync \n" // Lock acquired. The isync instruction
// implements an import barrier to
// ensure that the instructions that
// access the shared region guarded by
// this lock are executed only after
// they acquire the lock.
" ori %1,%1,1 \n" // Sets the return value for the
// function acquireLock to true.
" 1: \n" // Did not get the lock.
// Will return false.
/*------a fence here------*/
: "+r" (lockval),
"+r" (returnvalue)
: "r" (lock) // "lock" is the address of the lock in
// memory.
: "cr0" // cr0 is clobbered by cmpwi and stwcx.
);
return returnvalue;
}
int main()
{
int myLock;
if(acquireLock(&myLock)){
printf("got it!\n");
}else{
printf("someone else got it\n");
}
return 0;
}
在这个例子中,%0指的是第一个操作数“+r”(lockval), %1指的是第二个操作数“+r”(returnvalue), %2指的是第三个操作数“r”(lock)。
程序使用一个锁来控制对共享存储的访问; 在获取锁之前,没有指令可以访问共享存储。
volatile关键字意味着在汇编指令组周围设置栅栏,因此任何汇编指令都不能移出汇编代码块。 如果没有volatile关键字,编译器可以移动指令进行优化。 这可能会导致一些指令在不获取锁的情况下访问共享存储。
在这个汇编语句中,没有必要使用 memory clobber,因为这些指令不会以意想不到的方式修改内存。 如果您使用 memory clobber,程序在功能上仍然是正确的。 但是, memory clobber 会导致许多不必要的重新加载,从而造成性能损失。
Example 2
下面的示例演示了输入和输出操作数的符号名称的使用:
int a ;
int b = 1, c = 2, d = 3 ;
__asm(" addc %[result], %[first], %[second]"
: [result] "=r" (a)
: [first] "r" (b),
[second] "r" (d)
);
在这个例子中,%[result]指的是输出操作数变量a, %[first]指的是输入操作数变量b, %[second]指的是输入操作数变量d。
Example 3
下面的例子展示了条件寄存器在 clobber 中的典型用法:
asm (" add. %0,%1,%2 \n"
: "=r" (c)
: "r" (a),
"r" (b)
: "cc"
);
在本例中,除了程序的输入和输出中列出的寄存器外,add.指令还影响条件寄存器。 因此,您必须通过将 “cc” 添加到 clobbers 来通知编译器。
Example 4
下面的示例显示了 memory clobber 的使用情况:
asm volatile (" dcbz 0, %0 \n"
: "=r"(b)
:
: "memory"
);
在这个例子中,指令 “dcbz” 清除了一个缓存块(cache block),并且可能改变了内存位置中的变量。 编译器无法知道哪些变量被更改了。
因此,所有需要的东西都必须在程序执行完成后从内存中重新加载。memory clobber 以程序性能为代价来确保程序的正确性,因为编译器可能会重新加载与这段程序无关的数据。
Example 5
下面的例子展示了 ‘=’ 约束修饰符和 ‘r’ 约束的用法 :
int a ;
int b = 100 ;
int c = 200 ;
asm(" add %0, %1, %2"
: "=r" (a)
: "r" (b),
"r" (c)
);
加法指令将两个通用寄存器的内容相加。 %0、%1和%2操作数被输出/输入操作数字段中的C表达式替换。
输出操作数使用 ‘=’ 修饰符表示需要一个可修改的操作数。它使用 ‘r’ 约束来表示需要一个通用寄存器。 同样,输入操作数中的 ‘r’ 约束表明需要通用寄存器。 在这些限制中,编译器可以自由选择任何寄存器来替换%0、%1和%2。
注意:如果编译器选择 ‘r0’ 作为第二个操作数,则 add 指令使用立即数 0 并产生一个错误的结果。 因此,为了防止编译器选择 ‘r0’ 作为第二个操作数,可以为第二个操作数使用 ‘b’ 约束。
Example 6
下面的例子展示了 ‘+’ 修饰符和 ‘K’ 约束的用法:
asm (" addi %0,%0,%2"
: "+r" (a)
: "r" (a),
"K" (15)
);
此程序将操作数%0和操作数%2相加,并将结果写入操作数%0。 输出操作数使用 ‘+’ 修饰符表示操作数%0可以被指令读取和写入。 ‘K’ 约束指出,加载到操作数%2中的值必须是一个无符号的16位常量值。
Example 7
下面的示例显示了 ‘%’ 修饰符和 ‘f’ 约束的用法:
asm(" fadd %0, %1, %2"
: "=f" (c)
: "%f" (a),
"f" (b)
);
这个程序将操作数a和b相加,并将结果写入操作数c。’%’ 修饰符表示,如果编译器可以通过交换操作数a和b以生成更好的代码,那么可以交换操作数a和b。 每个操作数都有 ‘f’ 约束,表示需要一个浮点寄存器。
Example 8
下面的例子展示了 ‘b’ 约束的用法:
char res[8]={'a','b','c','d','e','f','g','h'};
char a='y';
int index=7;
asm (" stbx %0,%1,%2 \n" \
: \
: "r" (a),
"b" (index),
"r" (res)
);
在这个例子中,‘b’ 约束指示编译器为输入操作数%1选择一个普通寄存器,而不是 ‘r0’ 。 这个程序的输出结果是 “abcdefgy” 。 但是,如果使用 ‘r’ 约束,并且编译器为%1选择 ‘r0’ ,则该指令将生成一个不正确的结果字符串 “ybcdefgh” 。 因此,对于特殊处理 ‘r0’ 的指令,需要用 ‘b’ 来约束输入操作数。
Example 9
下面的例子展示了 ‘m’ 约束的用法:
asm (" stb %1,%0 \n" \
: "=m" (res) \
: "r" (a)
);
在这个例子中,stb指令的语法是stb RS,D(RA),其中D是一个偏移,R是一个寄存器,D+RA为有效地址。 通过使用约束 ‘m’ ,你不需要通过分别指定寄存器和位移来手动构造有效地址。
您可以使用一个约束 ‘m’ 或 ‘o’ 来引用指令中的两个操作数,而不管正确的偏移量应该是多少,也不管它是堆栈的偏移量还是ToC(Table of Contents)的偏移量。 这允许编译器选择正确的寄存器(例如,r1是一个自动变量),并自动应用正确的位移。
这里是从善若水的博客,感谢您的阅读⌨🖥🖱
文章链接
《GNU C/C++ 内联汇编编程指南全集》
《GNU C/C++ 内联汇编——入门级》
《GNU C/C++ 内联汇编——进阶——语法详解》
《GNU C/C++ 内联汇编——进阶——约束详解》
《GNU C/C++ 内联汇编——补充介绍》
《GNU C/C++ 内联汇编——实例参考》
《GNU C/C++ 内联汇编——Intel与ATT汇编语法对比》
更多推荐
所有评论(0)