声明:我只有X86机器代码的经验。其他指令集可能具有不同的寻址能力,因此我的建议的部分内容可能不适用。对不起,我目前没有时间研究指令集。
那么首先,大多数编译器不会生成汇编文本,因为它有点低效的序列化的代码转换成汇编只有把它解析马上由汇编程序,你可能已经实现。它是合理有单独的汇编和汇编阶段,但不是必需的。
在编译阶段,这两种策略,我会考虑是:
我猜想的(a)是通过优化像GCC编译器所使用的方法,同时(b)中是通过高速的编译器等TCC使用的方法。
让我们再次通过检查现有的编译器生成一个简单的if/else
分支代码考虑if
例如:
注意重叠在拆卸跳跃 - 一个跳过“采取'块和一个跳过'未采取'块。
这些是相对跳转,所以为了组装它们,我们需要知道在跳转指令和目标之间有多少字节的指令。
这里是什么样的编辑功能,可以像使用策略(一)一个例子:
Instruction[] compile_if(IfNode n) {
Instruction[] code;
code ~= compile_condition(n.condition);
Instruction skip_taken = new JumpInstruction(`jz`);
code ~= skip_taken;
code ~= compile_block(n.taken_block);
Instruction skip_nottaken = new JumpInstruction(`jmp`);
code ~= skip_nottaken;
Instruction[] nottaken_code = compile_block(n.nottaken_block);
skip_taken.destination = nottaken_code[0];
code ~= nottaken_code;
Instruction end = new NopInstruction();
skip_nottaken.destination = end;
code ~= end;
return code;
};
这应该是不言自明。
请注意指令如何以符号方式相互引用(skip_taken.destination = nottaken_code[0]
),而不是像序列化机器码中的字节偏移。我们离开汇编器的偏移计算。
另请注意,我们如何设置JumpInstruction
的目的地才可用。
NopInstruction
最后只是给skip_nottaken
跳了一些东西来引用。
现在,我们如何实际将这些跳转组合成真正的机器码/字节码?这里有一个可能性(一个非常简单的例子):
byte[2] assemble_jz(Instruction[] code, int idx) {
// assemble the jz instruction at code[idx]
JumpInstruction jump = code[idx];
++idx;
byte jump_offset = 0;
while (code[idx] != jump.destination) {
jump_offset += size_of_instruction(code[idx]);
++idx;
};
byte[2] machinecode = [
0x74, // jz short
jump_offset
];
return machinecode;
};
因为汇编器访问所有指令的对象,它可以通过提前扫描,直到找到目标指令计算相对跳跃的实际偏移。
我希望这个简短的介绍可以帮助您开始设计自己的编译器后端。很明显,我并不是说你编写的编译器和我的例子完全一样,但是它应该给你一些关于如何处理编译和汇编非线性指令块的常见问题的想法。
您可能还想看看一些现有的汇编器API,如https://github.com/asmjit/asmjit。
祝你好运。 “
”然后最后一个字符串通过汇编程序运行。“ - 它可能是也可能不是“大多数编译器”所做的,但这是一个完全有效的方法。尽管如此,我没有看到这与C++有什么关系,所以我删除了标签。 –
你在问什么? – harold
模板字符串 – Accumulator