让 GCC 优化手工组装

2024-03-05

试图让 GCC 不会每次生成加载-修改-存储操作|= or &=,我定义了以下宏:

#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)自动优化冗余堆栈操作?


gcc 将始终发出您告诉它发出的汇编指令。因此,您不需要显式编写代码来加载要操作的值的寄存器,而是希望告诉 gcc 代表您执行此操作。您可以通过寄存器限制来做到这一点。

不幸的是,6811 代码生成器似乎不是 gcc 的标准部分 --- 我没有在手册中找到文档。因此,我无法向您指出文档中特定于平台的部分。但您需要阅读的通用内容在这里:http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm

语法很奇怪,但摘要是:

asm("instructions" : outputs : inputs);

...在哪里inputs and 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 文档 --- 它是一个约束,表示对正在生成的 asm 指令有效的寄存器。这0表示输入与输出0共享一个寄存器,用于读/写值。

因为你已经告诉 gcc 你想要什么寄存器i也就是说,它可以将这些知识集成到其寄存器分配器中,并找到成本最低的方法来获得i在您需要的地方使用最少的代码。 (有时不需要额外的代码。)

gcc 内联汇编非常扭曲和奇怪,但非常强大。值得花一些时间来彻底了解约束系统,以充分利用它。

(顺便说一句,我不知道 6811 代码,但是你是否忘记将操作的结果放在某处?我希望看到一个stx来匹配ldx.)

Update:哦,我明白了什么bset现在正在做的事情——它将结果写回内存位置,对吗?这仍然是可行的,但会更痛苦一些。您需要告诉 gcc 您正在修改该内存位置,以便它知道不依赖任何缓存的值。您需要有一个带有约束的输出参数m代表该位置。检查文档。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

让 GCC 优化手工组装 的相关文章

  • 从 DX:AX 寄存器转移到单个 32 位寄存器

    我在添加 16 位乘法的乘积时遇到问题 我想将一年 例如 2015 年 乘以 365 为此 我 mov dx 0 to clear the register mov ax cx cx holds the year such as 2015
  • ICC 中的 -O3 会扰乱内在函数,使用 -O1 或 -O2 或相应的手动汇编即可

    这是后续这个问题 http stackoverflow com questions 49791664 o2 in icc messes up assembler fine with o1 in icc and all optimizatio
  • 具有自定义值类型的 map::emplace()

    我使用时遇到问题map emplace 谁能帮我找出正确的语法来使用 我实际上正在尝试做同样的事情这个例子 http www cplusplus com reference map map emplace 这是我的版本 include
  • 比“add esp, 4”更小的指令

    又是我 我的程序中有很多 add esp 4 我正在尝试减小它的大小 是否有任何更小的指令可以替代 add esp 4 pop edx 或者您不介意破坏的任何其他整数寄存器 这就是现代编译器实际上所做的 https stackoverflo
  • 使用 ACPI 在 MS-DOS 中关闭计算机

    我在基于 Pentium 的计算机上运行 MS DOS 6 22 主板支持 ACPI 并且想知道是否有一个可以用来关闭计算机的汇编语言例程 或者它是否比那个更难 即主板 具体的 基本上 我想创建一个小程序来从命令行关闭计算机 这是专门为此编
  • 为什么 g++ 在编译的二进制文件中存储类名?

    我注意到如果我跑strings在我编译的程序上g 输出包含它使用的各种类的名称 该程序是用 O3并且没有 g or p 并且当我剥离二进制文件时 类名仍然存在 我想知道为什么有必要g 将此信息存储在二进制文件中 出现的类名似乎都是使用虚函数
  • intfmt: db "%d", 10, 0 在汇编中的含义

    我最近在我的一个汇编文件的顶部看到了这个 并意识到我在打印整数的过程中花了很长时间使用它 而没有真正意识到它最初来自哪里 在我的基本汇编模板中使用 或 10 0 是什么结尾的意思是 section data intfmt db d 10 0
  • 嵌入式系统:使用汇编语言时的内存布局

    根据我的理解 嵌入式系统运行机器代码 有多种方法可以生成此代码 一种是用 C 等高级语言编写程序 然后使用编译器获得这样的代码 另一种方法是用汇编语言为该嵌入式系统编写指令 并使用汇编器将其转换为机器代码 现在我们得到了加载到系统并执行的机
  • x86 asm 图形设置的分辨率高于 640x480?

    我刚刚开始使用汇编语言 感觉像学习新东西 并且遇到了一些问题 到目前为止 我一直在浏览的所有教程都没有回答 或者太旧而无法知道 1 我尝试了一些搜索 也许我只是不知道正确的关键字 但我找不到用于更改屏幕分辨率等的图形模式的更新列表 我发现的
  • 为什么 LED 保持亮起而不是闪烁?

    这是使用 pic16f676 中的 TIMER0 中断使 LED 闪烁的 MPASM 代码 端口 A 的引脚 0 RA0 未切换至关闭位置 请帮忙 我是图片组装的新手 我想掌握图片 有没有高手帮我学习一下 我需要以 1 秒的间隔眨眼 代码是
  • 将以下机器语言代码(0x2237FFF1)翻译成MIPS汇编

    到目前为止我已经翻译了这段代码 但我不明白的是如何计算 计算 16 位立即地址的数量 0x2237FFF1 转为二进制 0010 0010 0011 0111 1111 1111 1111 0001 现在我正在读取操作码 001000 并知
  • 测试 xmm/ymm 寄存器是否为零的更快方法?

    It s fortunate that PTEST does not affect the carry flag but only sets the rather awkward ZF also affects both CF and ZF
  • INT 13h 无法读取超出特定扇区的数据

    我正在为我的操作系统编写内核 在将磁盘扇区加载到内存时遇到问题 以下是从磁盘加载扇区的函数代码部分 mov ax 0x3000 mov es ax mov ax 0x0201 mov bx word ptr bp 6 bx 0x000 0x
  • 处理器在操作码​​和数据之间有何不同? [复制]

    这个问题在这里已经有答案了 我正在尝试编写一个反汇编程序 我想知道处理器如何区分操作码和数据字节 例如 这是 Hello World 的字节表示 0x48 0x65 0x6c 0x6c 0x6f 0x20 0x57 0x6f 0x72 0x
  • cpp 和 gcc -E 的区别

    我以为两者cpp foo c and gcc E foo c以相同的方式预处理源文件 但是对于同一文件 我得到的输出有所不同 cat foo c define VARIABLE 3 define PASTER x y x y define
  • 为什么我的代码在编译用于分析 (-pg) 时在多线程下运行比在单线程下运行慢?

    我正在写一个光线追踪器 最近 我在程序中添加了线程 以利用 i5 四核上的附加内核 奇怪的是 应用程序的调试版本现在运行速度变慢 但优化后的构建运行速度比添加线程之前更快 我将 g pg 标志传递给 gcc 以进行调试构建 并将 O3 标志
  • 具有重复符号的 C++ 插件库上的段错误

    我有一个跨平台 C 应用程序 它分为多个共享库 并从插件共享库加载附加功能 插件库应该是自包含的并自行运行 无需了解或依赖于调用应用程序 其中一个插件包含从主应用程序复制的代码 因此包含与引擎中的符号名称重复的符号名称 是的 我知道这通常是
  • 将代码保存在 L1 缓存中

    我一直在阅读维基百科关于 K 编程语言的文章 http en wikipedia org wiki K programming language Performance characteristics这就是我所看到的 解释器的小尺寸和语言的
  • 为什么这个函数在额外读取内存时运行速度如此之快?

    我目前正在尝试了解 x86 64 上某些循环的性能属性 特别是我的 Intel R Core TM i3 8145U CPU 2 10GHz 处理器 具体来说 在循环体内添加一条额外的指令来读取内存几乎可以使性能提高一倍 而细节并不是特别重
  • 跳转目的地太远:3 个字节

    我的循环有问题 其中包含的代码很长并且给了我错误jump destination too far by 3 byte s 当我删除时 mov edx offset str1 call writestring 这部分位于主过程下方 它不会给出

随机推荐