制御フロー生成
いよいよLLVMバックエンドの醍醐味、制御構文の生成に入る。
まずは、以下のような簡単なCプログラムをClangに入力し、LLVM IRを生成してみる。どのような制御IRが必要になるのかを確認する。
if_ctrl.cpp
int test_ifctrl() { unsigned int a = 0; if (a == 0) { a++; // a = 1 } return a; }
./bin/clang --target=riscv32-unknown-elf if_ctrl.cpp -c -emit-llvm ./bin/llvm-dis if_ctrl.bc -o -
; Function Attrs: noinline nounwind optnone define dso_local i32 @_Z11test_ifctrlv() #0 { entry: %a = alloca i32, align 4 store i32 0, i32* %a, align 4 %0 = load i32, i32* %a, align 4 %cmp = icmp eq i32 %0, 0 br i1 %cmp, label %if.then, label %if.end if.then: ; preds = %entry %1 = load i32, i32* %a, align 4 %inc = add i32 %1, 1 store i32 %inc, i32* %a, align 4 br label %if.end if.end: ; preds = %if.then, %entry %2 = load i32, i32* %a, align 4 ret i32 %2 }
まずは、無条件でジャンプする命令から追加する。これはif.then
節が有効である場合、最後にbr label %if.end
によりif.end
によりジャンプする場合に必要な命令だ(実際には不要なジャンプだが、これはPassを追加することで後で削除する)。
commit:3cda3e3d750 Implement Jump and Branch instructions
llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
// Jump and Link (Call) let isCall = 1 in class JumpLink<bits<7> opcode, string opstr, DAGOperand opnd> : MYRISCVX_J<opcode, (outs GPR:$rd), (ins opnd:$imm20), !strconcat(opstr, "\t$rd, $imm20"), [], IIAlu> { let DecoderMethod = "DecodeJumpTarget"; }
// JAL def brtarget20 : Operand<OtherVT> { let EncoderMethod = "getBranch20TargetOpValue"; let OperandType = "OPERAND_PCREL"; let DecoderMethod = "DecodeBranch20Target"; }
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCCodeEmitter.cpp
/// getBranch20TargetOpValue - Return binary encoding of the branch /// target operand. If the machine operand requires relocation, /// record the relocation and return zero. unsigned MYRISCVXMCCodeEmitter:: getBranch20TargetOpValue(const MCInst &MI, unsigned OpNo, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { const MCOperand &MO = MI.getOperand(OpNo); if (MO.isImm()) { return static_cast<unsigned>(MO.getImm()); } llvm_unreachable("getBranch12TargetOpValue should be imm."); }
JumpLink
は即値のジャンプで、書き込みレジスタrd
と、ジャンプ先のオペランド$imm
を取ることができる。
RISC-VのJ形式のフォーマットで、通常は書き込み先の汎用レジスタを取るのだが、J命令の場合は書き込みレジスタをZEROに設定する。
つまり、エイリアスとして、jal
(書き込みレジスタ省略版)とj
(書き込みレジスタ省略かつZERO)を作成する。
def JAL : JumpLink<0b1101111, "jal", brtarget20>; def : InstAlias<"j $offset", (JAL ZERO, brtarget20:$offset)>; def : InstAlias<"jal $offset", (JAL RA, brtarget20:$offset)>;
これを用いて、ブロックへのジャンプのためのパタンを作成する。
let isBarrier = 1, isBranch = 1, isTerminator = 1 in def PseudoBR : MYRISCVXPseudo<(outs), (ins brtarget20:$simm20), "", [(br bb:$simm20)]>, PseudoInstExpansion<(JAL ZERO, brtarget20:$simm20)>;
Pseudo命令というものを使用した。これは疑似命令を使用してその命令を展開する。
このパタン(br bb:$simm20)
が現れると、この疑似命令を(JAL ZERO, brtarget20:$simm20)
に展開する。
このPseudoInstExpansion
で定義したパタンは、コンパイル時にMYRISCVXGenMCPseudoLowering.inc
に生成パタンが登録される。
build-myriscvx80/lib/Target/MYRISCVX/MYRISCVXGenMCPseudoLowering.inc
bool MYRISCVXAsmPrinter:: emitPseudoExpansionLowering(MCStreamer &OutStreamer, const MachineInstr *MI) { switch (MI->getOpcode()) { default: return false; case MYRISCVX::PseudoBR: { MCInst TmpInst; MCOperand MCOp; TmpInst.setOpcode(MYRISCVX::JAL); // Operand: rd TmpInst.addOperand(MCOperand::createReg(MYRISCVX::ZERO)); // Operand: imm20 lowerOperand(MI->getOperand(0), MCOp); TmpInst.addOperand(MCOp); EmitToStreamer(OutStreamer, TmpInst); break; } } return true; }
つまりPseudoBR
のノードが来たときは、JAL ZERO ジャンプ先オフセット
に置き換える訳だ。
このemitPseudoExpansionLowering()
は、MYRISCVXAsmPrinter.cpp
のMYRISCVXAsmPrinter::EmitInstruction()
内に追記し、命令を生成する前に疑似命令を取り除くために使用する。
llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXAsmPrinter.cpp
bool MYRISCVXAsmPrinter::lowerOperand(const MachineOperand &MO, MCOperand &MCOp) { MCOp = MCInstLowering.LowerOperand(MO); return MCOp.isValid(); } // Simple pseudo-instructions have their lowering (with expansion to real // instructions) auto-generated. #include "MYRISCVXGenMCPseudoLowering.inc" //@EmitInstruction { //- EmitInstruction() must exists or will have run time error. void MYRISCVXAsmPrinter::EmitInstruction(const MachineInstr *MI) { // Do any auto-generated pseudo lowerings. if (emitPseudoExpansionLowering(*OutStreamer, MI)) return; ...