在 C 代码中使用原始二进制指令块

Use raw binary blob of instructions in C code

我正在使用 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 位数字。)

GNU as manual

GNU as 支持两种处理原始指令的好方法。有 .byte.short.longsimilar directives 可用于直接在输出中发出原始数字。

另一种方式(仅针对少数架构实现)是 .insn 指令,它允许普通汇编指令和上述原始数字之间的中间方式。例如,参见 RISC-V .insn formats.

的文档

要在 C 中使用它们中的任何一个,您可以使用 (non-standard) asm 语句(或其变体)。以下示例使用 GCC 的 naked 函数属性(也仅在某些体系结构上可用)来防止编译器发出任何其他指令(例如,函数 prologue/epilogue),这很容易破坏寄存器。

此示例适用于 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 函数中使用它,请使用 GNU C Extended asm with constraints / clobbers to tell the compiler what your asm does and where the inputs/outputs are. And of course don't use your own ret. At least with gcc/clang; if you're actually using TCC, it doesn't optimize at all between C statements so it should be safe to us Basic asm 语句。