做人呢,最紧要就系开心啦

ARM64基础11:GCC内嵌汇编补充

534次阅读
没有评论

目的:
(1) 优化,对特定代码进行优化;
(2)C 语言需要访问某些特殊指令来实现特殊功能,比如内存屏障指令;

内嵌汇编两种模式:

基础内嵌汇编:不带参数;
扩展的内嵌汇编:C 语言变量参数;

(1) 基础内嵌汇编

格式
asm 关键字 :表明是一个 GNU 扩展;
修饰词 (qualifiers)
volatile: 基础内嵌汇编中,通常不需要;
inline: 内敛,asm 代码会尽可能小;

汇编代码块
GCC 编译器把内嵌汇编当成一个字符串;
GCC 编译器不会去解析和分析内嵌汇编;
多条汇编指令,需要使用“\n\t”换行;
GCC 的优化器,可能乱序汇编指令,如果需要保持汇编指令的顺序,最好使用多个内嵌汇编的方式;
内核 arch/arm64/include/asm/barrier.h 文件
ARM64 基础 11:GCC 内嵌汇编补充

(2) 扩展内嵌汇编

asm asm-qualifiers(
                Assembler Template;
                :outputOperands
                :inputOperands
                [:Clobbers])

格式
asm 关键字:表明是一个 GNU 扩展;
修饰词 (qualifiers)
volatile: 用来关闭 GCC 优化;
inline: 内敛,减小汇编代码尺寸;
goto: 在内嵌汇编里会跳转到 C 语言的 label;
输出部
用于描述在指令部中可以被修改的 C 语言变量以及约束条件;
每个约束通常以 "=" 开头,接着的字母表示对操作类型的说明,然后是关于变量结合的约束;

“=+”+ 约束修饰符 + 变量 

“=”表示被修饰的操作数具有可写属性;
“+”表示被修饰的操作数具有可读可写属性;
输出部可以是空的;

输入部:
用来描述在指令部中,只读的 C 语言变量以及约束条件;
输入部描述的参数,是只读属性,不要修改参数内容,因为 GCC 编译器假定输入部参数,在内嵌汇编前后是一致的;
输入部中不能使用 "=/+" 约束条件,编译器会报错;
输入部可以是空的;

损坏部:
“memory”告诉 GCC 内嵌汇编指令 改变了内存的值,强迫编译器在执行该汇编代码前,存储所有缓存的值,在执行完汇编代码之后重新加载该值,目的是防止编译乱序;
“cc”: 表示内嵌汇编代码影响状态寄存器相关的标志位;

内核里的实例,arch/arm64/include/asm/barrier.h
ARM64 基础 11:GCC 内嵌汇编补充
扩展内嵌汇编指令部中的参数表示:
ARM64 基础 11:GCC 内嵌汇编补充
案例 1,用内嵌汇编实现 memcpy:

  static void my_memcpy_asm_test(unsigned long src, unsigned long dst,unsigned long counter)
  {
      unsigned long tmp;
      unsigned long end = src+counter;

      asm volatile("1: ldr %1, [%2], #8\n"
              "str %1, [%0], #8\n"
              "cmp %2, %3\n"
              "b.cc 1b"
              :"+r"(dst),"+r"(tmp),"+r"(src)   //output list, can write and read
              :"r"(end)                        //input list, readonly
              :"memory"
              );
  }

使用汇编符号名实现内嵌汇编:

  static void my_memcpy_asm_test(unsigned long src, unsigned long dst,unsigned long counter)
  {
      unsigned long tmp;
      unsigned long end = src+counter;

      asm volatile("1: ldr %[tmp], [%[src]], #8\n"
              "str %[tmp], [%[dst]], #8\n"
              "cmp %[src], %[end]\n"
              "b.cc 1b"
              :[dst]"+r"(dst),[tmp]"+r"(tmp),[src]"+r"(src)   //output list, can write and read
              :[end]"r"(end)                        //input list, readonly
              :"memory"
              );  
  }

内嵌汇编与宏结合

 #define MY_OPS(ops,asm_ops) \
  static inline my_asm_##ops(unsigned long mask, void *p) \
  { \
      unsigned long tmp; \
      asm volatile( \
              "ldr %1, [%0]\n" \
              " "#asm_ops" %1, %1, %2\n" \
              "str %1,[%0]\n" \
              : "+r"(p), "+r"(tmp) \
              :"r"(mask) \
              : "memory" \
              ); \
  }

  MY_OPS(and, and)
  MY_OPS(or, orr)
  MY_OPS(andnot, bic)

  static void my_ops_test(void)
  {
      unsigned long p;

      p = 0xf;
      my_asm_and(0x2, &p);
      printk("test and:p=0x%x\n",p);

      p = 0x0;
      my_asm_or(0x3, &p);
      printk("test or:p=0x%x\n",p);

      p = 0x3;
      my_asm_andnot(0x2, &p);
      printk("test andnot:p=0x%x\n",p);
  }

技巧:

(1) 使用了 C 语言宏中的“#”和“##”;
“#”:预处理器把这个参数转换为一个字符串;
“##”:是一种分隔链接方式,它的作用是先分隔,在进行强制链接;
(2) 通过一个宏来实现多个类似的函数,这是 linux 内核常用的技巧;

实现读和写系统寄存器的宏

 //equals    asm volatile("mrs %0,""#reg" :"=r"(_val)); _val;}
  #define read_sysreg(reg) ({ \
      unsigned long _val; \
      asm volatile("mrs %0,"#reg \
                   :"=r"(_val)); \
                   _val; \
  })

  #define write_sysreg(val, reg) ({\
          unsigned long _val=(unsigned long)val; \
          asm volatile("msr "#reg ",%0" \
          ::"rZ"(_val)); \
  })
  static test_sysregs(void)
  {
      unsigned long el; 

      el = read_sysreg(CurrentEL);
      printk("el=%d\n",el>>2);

      write_sysreg(0x10000, vbar_el1);
      printk("read vbar:0x%x\n",read_sysreg(vbar_el1));
  }

实现从汇编到 C 代码的跳转


  static void test_asm_goto(int a)                                                                                                           
  {
      asm goto(
              "cmp %w0, 1\n"
              "b.eq %l[label]\n"
              :        // 必须为空
              :"r"(a)
              :"memory"
              :label
              );  

      return 0;
  label:
      printk("%s: a=%d.\n",__func__,a);
  }

goto 模板的输出部必须为空;

正文完
 
admin
版权声明:本站原创文章,由 admin 2021-11-02发表,共计2776字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)