所以我试图将一个函数从c重写为汇编,这更像是在C语言中编写程序集而不是提高效率。
我遇到的问题是我在三个asm()块中有工作代码,但我似乎无法将它们组合起来。我认为在组合时我必须要有一些东西。
目前这是有效的代码:
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;
上述两段代码都按预期工作。但是,我的问题是将三个装配块组合成一个。例如,以下代码由于某种原因无法正常工作。
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 );
我做的唯一改变是将第2和第3个块组合到第一个块中,将变量'hi_bit_set'和'b'更改为读和写。根据我的理解,这对我来说似乎很好。然而,这并没有产生正确的结果所以我猜我错过了什么。
提前感谢您的帮助。
答案 0 :(得分:2)
你看过'early clobber'了吗?编译器将为输入和输出分配相同的寄存器,您需要保留一些更长的时间并且需要将它们分开。
此外,您不会告诉编辑您使用的&#39; R0&#39;,&#39; R1&#39;和&#39; R2&#39;作为显式寄存器。你应该创建一个&#39; tmp1&#39;变量并将其作为输入传递;称之为“R0&#39;你可以使用寄存器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寄存器,编译器会将&#39; R0&#39;等用于其他目的。编译器的主要工作是管理寄存器。在堆栈上推送/弹出是不好的(溢出),但是不需要MOV
指令也是如此。
在提问时,提供编译的完整功能总是很好。我试着制作一个,你可以看到GCC&#39;翻译你的功能。您可以使用&#39;进位标记&#39;而不是常量来提取设置位信息。
#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位拇指输出,
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用于&#39; .L2&#39;和&#39; .L3&#39;上面的标签。