为了在每次|=
或&=
时使GCC不生成加载 - 修改 - 存储操作,我已经定义了以下宏:
#define bset(base, offset, mask) bmanip(set, base, offset, mask)
#define bclr(base, offset, mask) bmanip(clr, base, offset, mask)
#define bmanip(op, base, offset, mask) \
asm("pshx");\
asm("ldx " #base);\
asm("b" #op " " #offset ",x " #mask);\
asm("pulx")
他们工作得很好;反汇编的二进制文件是完美的。
当我按顺序使用多个时,问题出现了:
inline void spi_init()
{
bset(_io_ports, M6811_DDRD, 0x38);
bset(_io_ports, M6811_PORTD, 0x20);
bset(_io_ports, M6811_SPCR, (M6811_SPE | M6811_DWOM | M6811_MSTR));
}
这导致:
00002227 <spi_init>:
2227: 3c pshx
2228: fe 10 00 ldx 0x1000 <_io_ports>
222b: 1c 09 38 bset 0x9,x, #0x38
222e: 38 pulx
222f: 3c pshx
2230: fe 10 00 ldx 0x1000 <_io_ports>
2233: 1c 08 20 bset 0x8,x, #0x20
2236: 38 pulx
2237: 3c pshx
2238: fe 10 00 ldx 0x1000 <_io_ports>
223b: 1c 28 70 bset 0x28,x, #0x70
223e: 38 pulx
223f: 39 rts
有没有办法让GCC(3.3.6-m68hc1x-20060122)自动优化冗余堆栈操作?
答案 0 :(得分:9)
gcc将始终发出您告诉它发出的汇编指令。因此,您不是明确地编写代码来加载具有您想要操作的值的寄存器,而是希望告诉gcc代表您执行此操作。您可以使用寄存器约束来执行此操作。
不幸的是6811代码生成器似乎不是gcc的标准部分---我没有发现手册中的文档。所以我不能指出你在文档的平台特定位。但是你需要阅读的通用位是:http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm
语法很怪异,但摘要是:
asm("instructions" : outputs : inputs);
...其中inputs
和outputs
是约束列表,它告诉gcc要放在哪里的值。典型的例子是:
asm("fsinx %1,%0" : "=f" (result) : "f" (angle));
f
表示命名值需要进入浮点寄存器; =
表示它是输出;然后将寄存器的名称替换为指令。
所以,你可能想要这样的东西:
asm("b" #op " " #offset ",%0 " #mask : "=Z" (i) : "0" (i));
...其中i
是包含您要修改的值的变量。 Z
你需要在6811 gcc docs中查找---这是一个约束,它表示一个对正在生成的asm指令有效的寄存器。 0
表示输入与输出0共享一个寄存器,用于读/写值。
因为你告诉gcc你想要i
是什么注册,它可以将这些知识集成到它的寄存器分配器中,并找到最便宜的方法来获取所需的i
。最少量的代码。 (有时没有额外的代码。)
gcc内联汇编是非常扭曲和怪异的,但非常强大。值得花一些时间彻底了解约束系统,以便最好地利用它。
(顺便说一下,我不知道6811代码,但是你忘了将op的结果放在某处吗?我希望看到stx
与ldx
匹配。)< / p>
更新哦,我看到bset
正在做什么 - 它正在将结果写回内存位置,对吧?这仍然可行,但有点痛苦。您需要告诉gcc您正在修改该内存位置,以便它知道不依赖于任何缓存的值。您需要具有约束m
的输出参数,该参数表示该位置。检查文档。