Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/IRTranslator.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/IRTranslator.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/IRTranslator.h @@ -190,6 +190,8 @@ bool translateSwitch(const User &U, MachineIRBuilder &MIRBuilder); + bool translateIndirectBr(const User &U, MachineIRBuilder &MIRBuilder); + bool translateExtractValue(const User &U, MachineIRBuilder &MIRBuilder); bool translateInsertValue(const User &U, MachineIRBuilder &MIRBuilder); @@ -304,9 +306,6 @@ // Stubs to keep the compiler happy while we implement the rest of the // translation. - bool translateIndirectBr(const User &U, MachineIRBuilder &MIRBuilder) { - return false; - } bool translateResume(const User &U, MachineIRBuilder &MIRBuilder) { return false; } Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -322,6 +322,16 @@ /// \return The newly created instruction. MachineInstrBuilder buildBrCond(unsigned Tst, MachineBasicBlock &BB); + /// Build and insert G_BRINDIRECT \p Tgt + /// + /// G_BRINDIRECT is an indirect branch to \p Tgt. + /// + /// \pre setBasicBlock or setMI must have been called. + /// \pre \p Tgt must be a generic virtual register with pointer type. + /// + /// \return a MachineInstrBuilder for the newly created instruction. + MachineInstrBuilder buildBrIndirect(unsigned Tgt); + /// Build and insert \p Res = G_CONSTANT \p Val /// /// G_CONSTANT is an integer constant with the specified size and value. \p Index: llvm/trunk/include/llvm/Target/GenericOpcodes.td =================================================================== --- llvm/trunk/include/llvm/Target/GenericOpcodes.td +++ llvm/trunk/include/llvm/Target/GenericOpcodes.td @@ -445,4 +445,13 @@ let isTerminator = 1; } +// Generic indirect branch. +def G_BRINDIRECT : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins type0:$src1); + let hasSideEffects = 0; + let isBranch = 1; + let isTerminator = 1; +} + // TODO: Add the other generic opcodes. Index: llvm/trunk/include/llvm/Target/TargetOpcodes.def =================================================================== --- llvm/trunk/include/llvm/Target/TargetOpcodes.def +++ llvm/trunk/include/llvm/Target/TargetOpcodes.def @@ -251,6 +251,9 @@ /// Generic conditional branch instruction. HANDLE_TARGET_OPCODE(G_BRCOND) +/// Generic indirect branch instruction. +HANDLE_TARGET_OPCODE(G_BRINDIRECT) + /// Generic intrinsic use (without side effects). HANDLE_TARGET_OPCODE(G_INTRINSIC) Index: llvm/trunk/lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -252,6 +252,21 @@ return true; } +bool IRTranslator::translateIndirectBr(const User &U, + MachineIRBuilder &MIRBuilder) { + const IndirectBrInst &BrInst = cast(U); + + const unsigned Tgt = getOrCreateVReg(*BrInst.getAddress()); + MIRBuilder.buildBrIndirect(Tgt); + + // Link successors. + MachineBasicBlock &CurBB = MIRBuilder.getMBB(); + for (const BasicBlock *Succ : BrInst.successors()) + CurBB.addSuccessor(&getOrCreateBB(*Succ)); + + return true; +} + bool IRTranslator::translateLoad(const User &U, MachineIRBuilder &MIRBuilder) { const LoadInst &LI = cast(U); Index: llvm/trunk/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -217,6 +217,10 @@ return buildInstr(TargetOpcode::G_BR).addMBB(&Dest); } +MachineInstrBuilder MachineIRBuilder::buildBrIndirect(unsigned Tgt) { + return buildInstr(TargetOpcode::G_BRINDIRECT).addUse(Tgt); +} + MachineInstrBuilder MachineIRBuilder::buildCopy(unsigned Res, unsigned Op) { return buildInstr(TargetOpcode::COPY).addDef(Res).addUse(Op); } Index: llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp +++ llvm/trunk/lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -525,6 +525,11 @@ return constrainSelectedInstRegOperands(*MIB.getInstr(), TII, TRI, RBI); } + case TargetOpcode::G_BRINDIRECT: { + I.setDesc(TII.get(AArch64::BR)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + case TargetOpcode::G_FCONSTANT: case TargetOpcode::G_CONSTANT: { const bool isFP = Opcode == TargetOpcode::G_FCONSTANT; Index: llvm/trunk/lib/Target/AArch64/AArch64LegalizerInfo.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ llvm/trunk/lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -167,6 +167,7 @@ // Control-flow for (auto Ty : {s1, s8, s16, s32}) setAction({G_BRCOND, Ty}, Legal); + setAction({G_BRINDIRECT, p0}, Legal); // Select for (auto Ty : {s1, s8, s16, s32, s64, p0}) Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir @@ -79,6 +79,7 @@ define void @unconditional_br() { ret void } define void @conditional_br() { ret void } + define void @indirect_br() { ret void } define void @load_s64_gpr(i64* %addr) { ret void } define void @load_s32_gpr(i32* %addr) { ret void } @@ -1511,6 +1512,28 @@ ... --- +# CHECK-LABEL: name: indirect_br +name: indirect_br +legalized: true +regBankSelected: true + +registers: + - { id: 0, class: gpr } + +# CHECK: body: +# CHECK: bb.0: +# CHECK: %0 = COPY %x0 +# CHECK: BR %0 +body: | + bb.0: + successors: %bb.0, %bb.1 + %0(p0) = COPY %x0 + G_BRINDIRECT %0(p0) + + bb.1: +... + +--- # CHECK-LABEL: name: load_s64_gpr name: load_s64_gpr legalized: true Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-irtranslator.ll =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-irtranslator.ll +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/arm64-irtranslator.ll @@ -217,6 +217,42 @@ ret i32 12 } +; Tests for indirect br. +; CHECK-LABEL: name: indirectbr +; CHECK: body: +; +; ABI/constant lowering and IR-level entry basic block. +; CHECK: {{bb.[0-9]+.entry}}: +; Make sure we have one successor +; CHECK-NEXT: successors: %[[BB_L1:bb.[0-9]+.L1]](0x80000000) +; CHECK: G_BR %[[BB_L1]] +; +; Check basic block L1 has 2 successors: BBL1 and BBL2 +; CHECK: [[BB_L1]] (address-taken): +; CHECK-NEXT: successors: %[[BB_L1]](0x40000000), +; CHECK: %[[BB_L2:bb.[0-9]+.L2]](0x40000000) +; CHECK: G_BRINDIRECT %{{[0-9]+}}(p0) +; +; Check basic block L2 is the return basic block +; CHECK: [[BB_L2]] (address-taken): +; CHECK-NEXT: RET_ReallyLR + +@indirectbr.L = internal unnamed_addr constant [3 x i8*] [i8* blockaddress(@indirectbr, %L1), i8* blockaddress(@indirectbr, %L2), i8* null], align 8 + +define void @indirectbr() { +entry: + br label %L1 +L1: ; preds = %entry, %L1 + %i = phi i32 [ 0, %entry ], [ %inc, %L1 ] + %inc = add i32 %i, 1 + %idxprom = zext i32 %i to i64 + %arrayidx = getelementptr inbounds [3 x i8*], [3 x i8*]* @indirectbr.L, i64 0, i64 %idxprom + %brtarget = load i8*, i8** %arrayidx, align 8 + indirectbr i8* %brtarget, [label %L1, label %L2] +L2: ; preds = %L1 + ret void +} + ; Tests for or. ; CHECK-LABEL: name: ori64 ; CHECK: [[ARG1:%[0-9]+]](s64) = COPY %x0