c - 在 C 中组合 ASM block 时出现问题

标签 c gcc arm inline-assembly thumb

所以我正在尝试将一个函数从 c 重写为汇编,这更像是一种用 C 编写汇编的练习,而不是提高它的效率。

我遇到的问题是我在三个 asm() block 中有工作代码,但我似乎无法将它们组合起来。我认为在组合它们时一定有一些我遗漏的东西。

这是目前有效的代码:

137         __asm__ __volatile__ (
138             "mov R0, #1\n\t"
139             "mov R1, %[bb]\n\t"
140             "and R0, R1\n\t"
141             "cmp R0, #1\n\t"    // (b & 1) == 1
142             "bne aftereor\n\t"
143             "eor %[pp], %[pp], %[aa]\n\t"
144             "aftereor:\n\t"
145             "mov %[hbs], %[aa]\n\t"
146             "mov R0, #128 \n\t"
147             "and %[hbs], R0 \n\t"
148             "lsl %[aa], %[aa], #1\n\t"
149             : [pp]"+l" (p),[aa]"+l" (a),[hbs]"=l" (hi_bit_set)
150             : [bb]"l" (b)
151             :
152         );
153         __asm__ __volatile__ (
154             "cmp %[hbs], #128 \n\t"
155             "bne brancha \n\t"
156             "mov R2, #0x1b\n\t"
157             "eor %[aa], %[aa], R2\n\t"
158             "brancha:\n\t"
159             : [aa]"+l" (a)
160             : [hbs]"l" (hi_bit_set)
161             :
162          );
163         __asm__ __volatile__ (
164             "lsr %[bb], %[bb], #1"
165             : [bb]"+l" (b)
166             :
167             :
168         );

这是我试图用汇编重写的 C 代码:

if((b & 1) == 1) {
    p ^= a;
}
hi_bit_set = (a & 0x80);
a <<= 1;
if(hi_bit_set == 0x80) {
    a ^= 0x1b;
}
b >>= 1;

以上两段代码都按预期工作。但是,我的问题是将三个组装 block 合二为一。例如,由于某些原因,以下代码无法按预期工作。

137         __asm__ __volatile__ (
138             "mov R0, #1\n\t"
139             "mov R1, %[bb]\n\t"
140             "and R0, R1\n\t"
141             "cmp R0, #1\n\t"    // (b & 1) == 1
142             "bne aftereor\n\t"
143             "eor %[pp], %[pp], %[aa]\n\t"
144             "aftereor:\n\t"
145             "mov %[hbs], %[aa]\n\t"
146             "mov R0, #128 \n\t"
147             "and %[hbs], R0 \n\t"
148             "lsl %[aa], %[aa], #1\n\t"
149             "cmp %[hbs], #128 \n\t"
150             "bne brancha \n\t"
151             "mov R2, #0x1b\n\t"
152             "eor %[aa], %[aa], R2\n\t"
153             "brancha:\n\t"
154             "lsr %[bb], %[bb], #1"
155             : [pp]"+l" (p),[aa]"+l" (a),[hbs]"+l" (hi_bit_set),[bb]"+l" (b)
156             :
157             :
158         );

我所做的唯一更改是将第二个和第三个 block 合并到第一个 block 中,将变量“hi_bit_set”和“b”更改为可读写。据我了解,这对我来说似乎很好。然而,这并没有产生正确的结果,所以我猜我错过了一些东西。

预先感谢您的帮助。

最佳答案

你看过'early clobber'了吗? ?编译器将为输入和输出分配相同的寄存器,您需要将一些寄存器保留更长时间并且需要将它们分开。

此外,您没有告诉编译器您使用“R0”、“R1”和“R2”作为显式寄存器。您应该创建一个“tmp1”变量并将其作为输入传递;称它为“R0”,您可以使用寄存器 asm 实际分配它(或将它们列为 clobbers)。

该代码充满了许多潜在的优化,可能只有 50% 的大小。但是,我会忠实于您的原件,但会指定寄存器以使其正常工作。

void foo(uint32_t a, uint32_t b, uint32_t p)
{
  register uint32_t tmp1 asm ("r0");
  register uint32_t tmp2 asm ("r1");
  register uint32_t tmp3 asm ("r2");
  uint32_t hi_bit_set;

    __asm__ __volatile__ (
         "mov R0, #1\n\t"
         "mov R1, %[bb]\n\t"
         "and R0, R1\n\t"
         "cmp R0, #1\n\t"    // (b & 1) == 1
         "bne aftereor\n\t"
         "eor %[pp], %[pp], %[aa]\n\t"
         "aftereor:\n\t"
         "mov %[hbs], %[aa]\n\t"
         "mov R0, #128 \n\t"
         "and %[hbs], R0 \n\t"
         "lsl %[aa], %[aa], #1\n\t"
         "cmp %[hbs], #128 \n\t"
         "bne brancha \n\t"
         "mov R2, #0x1b\n\t"
         "eor %[aa], %[aa], R2\n\t"
         "brancha:\n\t"
         "lsr %[bb], %[bb], #1"
         : [pp]"+l" (p),[aa]"+l" (a),[hbs]"+l" (hi_bit_set),[bb]"+l" (b)
         :
         : "r0", "r1", "r2"  /* THIS IS IMPORTANT! */
     );
}

output appears good而如果我不包括 clobber 寄存器,编译器会使用“R0”等用于其他目的。编译器的主要工作是管理寄存器。在堆栈上压入/弹出是不好的(溢出),但不需要的 MOV 指令也是如此。

提出问题时,提供可编译的完整函数总是好的。我试着做了一个,你可以看到“GCC”是如何翻译你的功能的。您可以使用“进位标志”而不是常量来提取设置位信息。

#include <stdint.h>

uint32_t foo(uint32_t *A, uint32_t *B, uint32_t p)
{
  uint32_t a = *A;
  uint32_t b = *B;

  /* codes starts here... */

  if((b & 1) == 1) {
    p ^= a;
  }
  a <<= 1;
  if(a & 0x100) {
     a ^= 0x1b;
  }
  b >>= 1;

  /* codes is done. */


  *A = a;
  *B = b;
  return p;
}

这是 gcc 的 16 位 thumb 输出,

foo(unsigned long*, unsigned long*, unsigned long):
        push    {r4, r5, lr}  ; save callee reg
        ldr     r4, [r1]      ; get 'B' pointer, your [bb] is r4.
        ldr     r3, [r0]      ; get 'A' pointer, your [aa] is r3.

       ; codes starts here...

        lsls    r5, r4, #31
        bpl     .L2
        eors    r2, r3
.L2:
        lsls    r3, r3, #1
        lsls    r5, r3, #23
        bpl     .L3
        movs    r5, #27
        eors    r3, r5
.L3:
        lsrs    r4, r4, #1

       ; code is done

        str     r3, [r0]      ; saving [aa]
        movs    r0, r2        ; r0 is 'pp'
        str     r4, [r1]      ; save [bb] value.
        pop     {r4, r5, pc}  ; restore to callers value.

汇编编码器可能更喜欢 local labels对于上面的“.L2”和“.L3”标签。

关于c - 在 C 中组合 ASM block 时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41894703/

相关文章:

objective-c - 使用 LLVM GCC 4.2 不会让我将 CFStringRef 桥接到 NSString

C 绩效衡量

c - 使用 memcpy 会导致内存覆盖为 NULL

linux - 内核页面是否被换出?

c++ - 编译麻烦c转c++反

c - 浮点值与另一个翻译单元中使用的函数不同

C#/Mono - 内存映射文件问题

c - 快速查找某个值是否存在于 C 数组中?

c - eclipse cygwin printf 在调试时没有输出

c - 什么时候写 "0 - x"而不是 "-x"有用?