我正在使用 TCC(Tiny C 编译器)进行 jit 编译,但它对汇编的支持有限,我经常被这个卡住......我想知道是否有某种技巧可以将原始指令插入内联部件?如:
mov -0x18(%rbp), %rax
finit
flds (%rax)
/* Custom unsupported binary instructions here */
flds (%rcx)
我知道这不是一件容易维护的事情,但我想让 TCC 保持不变。
我正在使用 TCC(Tiny C 编译器)进行 jit 编译,但它对汇编的支持有限,我经常被这个卡住......我想知道是否有某种技巧可以将原始指令插入内联部件?如:
mov -0x18(%rbp), %rax
finit
flds (%rax)
/* Custom unsupported binary instructions here */
flds (%rcx)
我知道这不是一件容易维护的事情,但我想让 TCC 保持不变。
如果它支持标准 GAS / unix-assembler 指令.byte 0x00, 0x12
,你可以发出任何你想要的字节序列。(.word
或者.long
,如果您想使用将 16 位或 32 位立即数写入单个 32 位数字。)
GNUas
支持两种处理原始指令的好方法。有.byte
、.short
和类似的指令.long
可用于直接在输出中发出原始数字。
另一种方式(仅针对少数架构实现)是.insn
允许在普通汇编指令和上述原始数字之间的中间方式的指令。例如,参见RISC-V.insn
格式的文档。
要在 C 中使用它们中的任何一个,您可以使用(非标准)asm
语句(或其变体)。以下示例使用 GCC 的naked
函数属性(也仅在某些体系结构上可用)来防止编译器发出任何附加指令(例如,函数序言/结尾),这很容易破坏寄存器。
此示例适用于 RISC-V,显示标准 32 位指令以及压缩的 16 位指令:
void __attribute__ ((naked)) rawinst (void) {
__asm__ volatile ("li t5,1");
__asm__ volatile (".short 0x4f05"); // li t5,1
__asm__ volatile ("lui t5, 0x1c000");
__asm__ volatile (".word 0x1c000f37"); // lui t5, 0x1c000
__asm__ volatile (".insn r 0x33, 0, 0, a0, a1, a2"); // add a0, a1, a2
__asm__ volatile ("ret");
}
如果您想在非naked
函数中使用它,请使用带有约束/clobbers 的 GNU C Extended asm来告诉编译器您的 asm 做什么以及输入/输出在哪里。当然不要使用你自己的ret
. 至少使用 gcc/clang;如果您实际上使用的是 TCC,它根本不会在 C 语句之间进行优化,因此它对我们基本 asm语句应该是安全的。