diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td index 19197f50d9dff..c7eef211ae342 100644 --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -1374,7 +1374,7 @@ def G_BRCOND : GenericInstruction { // Generic indirect branch. def G_BRINDIRECT : GenericInstruction { let OutOperandList = (outs); - let InOperandList = (ins type0:$src1); + let InOperandList = (ins ptype0:$src1); let hasSideEffects = false; let isBranch = true; let isTerminator = true; diff --git a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td index b1f3c500a1b6c..0349cc2be7371 100644 --- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -131,6 +131,7 @@ let IfConvergent = G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS in { def : GINodeEquiv; def : GINodeEquiv; +def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; diff --git a/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp b/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp index 26932ba2c8e24..255385c85a7b9 100644 --- a/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp +++ b/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp @@ -107,6 +107,8 @@ class X86InstructionSelector : public InstructionSelector { MachineFunction &MF) const; bool selectCondBranch(MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF) const; + bool selectJumpTable(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; bool selectTurnIntoCOPY(MachineInstr &I, MachineRegisterInfo &MRI, const unsigned DstReg, const TargetRegisterClass *DstRC, @@ -421,6 +423,8 @@ bool X86InstructionSelector::select(MachineInstr &I) { return selectInsert(I, MRI, MF); case TargetOpcode::G_BRCOND: return selectCondBranch(I, MRI, MF); + case TargetOpcode::G_JUMP_TABLE: + return selectJumpTable(I, MRI, MF); case TargetOpcode::G_IMPLICIT_DEF: case TargetOpcode::G_PHI: return selectImplicitDefOrPHI(I, MRI); @@ -1472,6 +1476,33 @@ bool X86InstructionSelector::selectCondBranch(MachineInstr &I, return true; } +bool X86InstructionSelector::selectJumpTable(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + auto Dst = I.getOperand(0).getReg(); + auto JTI = I.getOperand(1).getIndex(); + auto OpCode = STI.is64Bit() ? X86::LEA64r : X86::LEA32r; + + auto *TLI = STI.getTargetLowering(); + + MachineInstr &Lea = + *BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(OpCode), Dst) + .addReg(TLI->isJumpTableRelative() + ? (STI.is64Bit() ? X86::RIP : X86::EIP) + : 0) + .addImm(1) + .addReg(0) + .addJumpTableIndex(JTI) + .addReg(0) + .getInstr(); + + constrainSelectedInstRegOperands(Lea, TII, TRI, RBI); + + I.removeFromParent(); + + return true; +} + bool X86InstructionSelector::materializeFP(MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF) const { diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp index 27381dff338e2..0b8f89f2262e2 100644 --- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp +++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp @@ -14,10 +14,16 @@ #include "X86Subtarget.h" #include "X86TargetMachine.h" #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/CodeGen/ValueTypes.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Type.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" using namespace llvm; using namespace TargetOpcode; @@ -326,6 +332,10 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI, getActionDefinitionsBuilder(G_BRCOND).legalFor({s1}); + getActionDefinitionsBuilder(G_BRINDIRECT).legalFor({p0}); + getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0}); + getActionDefinitionsBuilder(G_BRJT).custom(); + // pointer handling const std::initializer_list PtrTypes32 = {s1, s8, s16, s32}; const std::initializer_list PtrTypes64 = {s1, s8, s16, s32, s64}; @@ -549,6 +559,64 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI, verify(*STI.getInstrInfo()); } +bool X86LegalizerInfo::legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, + LostDebugLocObserver &LocObserver) const { + switch (MI.getOpcode()) { + case G_BRJT: + return legalizeBrJT(Helper, MI); + default: + llvm_unreachable("instruction is not in switch"); + } +} + +bool X86LegalizerInfo::legalizeBrJT(LegalizerHelper &Helper, + MachineInstr &MI) const { + MachineIRBuilder &MIB = Helper.MIRBuilder; + MachineFunction &MF = *MI.getMF(); + MachineRegisterInfo &MRI = *MIB.getMRI(); + const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); + + unsigned EntrySize = MJTI->getEntrySize(MF.getDataLayout()); + + auto PtrReg = MI.getOperand(0).getReg(); + auto PtrTy = MRI.getType(PtrReg); + auto IdxReg = MI.getOperand(2).getReg(); + auto IdxTy = MRI.getType(IdxReg); + + MachineMemOperand *MMO = MF.getMachineMemOperand( + MachinePointerInfo::getJumpTable(MF), MachineMemOperand::MOLoad, + PtrTy.getSizeInBytes(), Align(EntrySize)); + + auto ShiftAmt = + MIB.buildConstant(IdxTy, Log2_32(MJTI->getEntrySize(MF.getDataLayout()))); + auto Shift = MIB.buildShl(IdxTy, IdxReg, ShiftAmt); + auto Target = MIB.buildPtrAdd(PtrTy, PtrReg, Shift); + + switch (MJTI->getEntryKind()) { + default: + return false; + case MachineJumpTableInfo::EK_BlockAddress: { + Target = MIB.buildLoad(PtrTy, Target, *MMO); + break; + } + case MachineJumpTableInfo::EK_LabelDifference64: + assert(Subtarget.is64Bit()); + [[fallthrough]]; + case MachineJumpTableInfo::EK_LabelDifference32: + auto Load = MIB.buildLoadInstr( + TargetOpcode::G_LOAD, LLT::scalar(PtrTy.getSizeInBits()), Target, *MMO); + Load = MIB.buildSExtOrTrunc(IdxTy, Load); + Target = MIB.buildPtrAdd(PtrTy, PtrReg, Load); + break; + } + + MIB.buildBrIndirect(Target.getReg(0)); + + MI.removeFromParent(); + + return true; +} + bool X86LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper, MachineInstr &MI) const { return true; diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h index 12134f7b00f1c..d6ec06ff0c9fc 100644 --- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h +++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.h @@ -30,6 +30,10 @@ class X86LegalizerInfo : public LegalizerInfo { public: X86LegalizerInfo(const X86Subtarget &STI, const X86TargetMachine &TM); + bool legalizeBrJT(LegalizerHelper &Helper, MachineInstr &MI) const; + + bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, + LostDebugLocObserver &LocObserver) const override; bool legalizeIntrinsic(LegalizerHelper &Helper, MachineInstr &MI) const override; }; diff --git a/llvm/test/CodeGen/X86/GlobalISel/legalize-brindirect.mir b/llvm/test/CodeGen/X86/GlobalISel/legalize-brindirect.mir new file mode 100644 index 0000000000000..c023124dab429 --- /dev/null +++ b/llvm/test/CodeGen/X86/GlobalISel/legalize-brindirect.mir @@ -0,0 +1,55 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4 +# RUN: split-file %s %t +# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=legalizer %t/x64.mir -o - | FileCheck %t/x64.mir +# RUN: llc -mtriple=i386-linux-gnu -run-pass=legalizer %t/x32.mir -o - | FileCheck %t/x32.mir + +#--- x64.mir +--- +name: test64 +alignment: 16 +legalized: false +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: _, preferred-register: '' } +body: | + bb.1.entry: + successors: + liveins: $rdi + + ; CHECK-LABEL: name: test64 + ; CHECK: liveins: $rdi + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $rdi + ; CHECK-NEXT: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[COPY]](s64) + ; CHECK-NEXT: G_BRINDIRECT [[INTTOPTR]](p0) + %0(s64) = COPY $rdi + %1(p0) = G_INTTOPTR %0(s64) + G_BRINDIRECT %1(p0) + +... + +#--- x32.mir +--- +name: test32 +alignment: 16 +legalized: false +registers: + - { id: 0, class: _, preferred-register: '' } + - { id: 1, class: _, preferred-register: '' } +body: | + bb.1.entry: + successors: + liveins: $edi + + ; CHECK-LABEL: name: test32 + ; CHECK: liveins: $edi + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $edi + ; CHECK-NEXT: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[COPY]](s32) + ; CHECK-NEXT: G_BRINDIRECT [[INTTOPTR]](p0) + %0(s32) = COPY $edi + %1(p0) = G_INTTOPTR %0(s32) + G_BRINDIRECT %1(p0) + +... + diff --git a/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir b/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir new file mode 100644 index 0000000000000..faea0fe1b1441 --- /dev/null +++ b/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir @@ -0,0 +1,257 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4 +# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=X64 +# RUN: llc -mtriple=i386-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=X32 + +--- +name: test_block_address +alignment: 16 +legalized: false +jumpTable: + kind: block-address + entries: + - id: 0 + blocks: [ '%bb.2', '%bb.3', ] +body: | + ; X64-LABEL: name: test_block_address + ; X64: bb.0.entry: + ; X64-NEXT: successors: %bb.1(0x40000000), %bb.4(0x40000000) + ; X64-NEXT: liveins: $edi + ; X64-NEXT: {{ $}} + ; X64-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $edi + ; X64-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4 + ; X64-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0 + ; X64-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[C1]] + ; X64-NEXT: [[ICMP:%[0-9]+]]:_(s8) = G_ICMP intpred(ugt), [[SUB]](s32), [[C]] + ; X64-NEXT: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ICMP]](s8) + ; X64-NEXT: G_BRCOND [[TRUNC]](s1), %bb.4 + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.1.entry: + ; X64-NEXT: successors: %bb.2(0x40000000), %bb.3(0x40000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: [[JUMP_TABLE:%[0-9]+]]:_(p0) = G_JUMP_TABLE %jump-table.0 + ; X64-NEXT: [[C2:%[0-9]+]]:_(s8) = G_CONSTANT i8 3 + ; X64-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SUB]], [[C2]](s8) + ; X64-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[JUMP_TABLE]], [[SHL]](s32) + ; X64-NEXT: [[LOAD:%[0-9]+]]:_(p0) = G_LOAD [[PTR_ADD]](p0) :: (load (s64) from jump-table) + ; X64-NEXT: G_BRINDIRECT [[LOAD]](p0) + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.2: + ; X64-NEXT: successors: %bb.5(0x80000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: G_BR %bb.5 + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.3: + ; X64-NEXT: successors: %bb.5(0x80000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: G_BR %bb.5 + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.4: + ; X64-NEXT: successors: %bb.5(0x80000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.5: + ; X64-NEXT: $eax = COPY [[COPY]](s32) + ; X64-NEXT: RET 0, implicit $eax + ; + ; X32-LABEL: name: test_block_address + ; X32: bb.0.entry: + ; X32-NEXT: successors: %bb.1(0x40000000), %bb.4(0x40000000) + ; X32-NEXT: liveins: $edi + ; X32-NEXT: {{ $}} + ; X32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $edi + ; X32-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4 + ; X32-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0 + ; X32-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[C1]] + ; X32-NEXT: [[ICMP:%[0-9]+]]:_(s8) = G_ICMP intpred(ugt), [[SUB]](s32), [[C]] + ; X32-NEXT: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ICMP]](s8) + ; X32-NEXT: G_BRCOND [[TRUNC]](s1), %bb.4 + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.1.entry: + ; X32-NEXT: successors: %bb.2(0x40000000), %bb.3(0x40000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: [[JUMP_TABLE:%[0-9]+]]:_(p0) = G_JUMP_TABLE %jump-table.0 + ; X32-NEXT: [[C2:%[0-9]+]]:_(s8) = G_CONSTANT i8 2 + ; X32-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SUB]], [[C2]](s8) + ; X32-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[JUMP_TABLE]], [[SHL]](s32) + ; X32-NEXT: [[LOAD:%[0-9]+]]:_(p0) = G_LOAD [[PTR_ADD]](p0) :: (load (s32) from jump-table) + ; X32-NEXT: G_BRINDIRECT [[LOAD]](p0) + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.2: + ; X32-NEXT: successors: %bb.5(0x80000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: G_BR %bb.5 + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.3: + ; X32-NEXT: successors: %bb.5(0x80000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: G_BR %bb.5 + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.4: + ; X32-NEXT: successors: %bb.5(0x80000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.5: + ; X32-NEXT: $eax = COPY [[COPY]](s32) + ; X32-NEXT: RET 0, implicit $eax + bb.0.entry: + successors: %bb.1(0x40000000), %bb.4(0x40000000) + liveins: $edi + + %0:_(s32) = COPY $edi + %4:_(s32) = G_CONSTANT i32 4 + %1:_(s32) = G_CONSTANT i32 0 + %2:_(s32) = G_SUB %0, %1 + %5:_(s1) = G_ICMP intpred(ugt), %2(s32), %4 + G_BRCOND %5(s1), %bb.4 + + bb.1.entry: + successors: %bb.2(0x1999999a), %bb.3(0x1999999a) + + %6:_(p0) = G_JUMP_TABLE %jump-table.0 + G_BRJT %6(p0), %jump-table.0, %2(s32) + + bb.2: + successors: %bb.5(0x80000000) + + G_BR %bb.5 + + bb.3: + successors: %bb.5(0x80000000) + + G_BR %bb.5 + + bb.4: + successors: %bb.5(0x80000000) + + bb.5: + $eax = COPY %0(s32) + RET 0, implicit $eax + +... + +--- +name: test_label_diff32 +alignment: 16 +legalized: false +jumpTable: + kind: label-difference32 + entries: + - id: 0 + blocks: [ '%bb.2', '%bb.3', ] +body: | + ; X64-LABEL: name: test_label_diff32 + ; X64: bb.0.entry: + ; X64-NEXT: successors: %bb.1(0x40000000), %bb.4(0x40000000) + ; X64-NEXT: liveins: $edi + ; X64-NEXT: {{ $}} + ; X64-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $edi + ; X64-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4 + ; X64-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0 + ; X64-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[C1]] + ; X64-NEXT: [[ICMP:%[0-9]+]]:_(s8) = G_ICMP intpred(ugt), [[SUB]](s32), [[C]] + ; X64-NEXT: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ICMP]](s8) + ; X64-NEXT: G_BRCOND [[TRUNC]](s1), %bb.4 + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.1.entry: + ; X64-NEXT: successors: %bb.2(0x40000000), %bb.3(0x40000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: [[JUMP_TABLE:%[0-9]+]]:_(p0) = G_JUMP_TABLE %jump-table.0 + ; X64-NEXT: [[C2:%[0-9]+]]:_(s8) = G_CONSTANT i8 3 + ; X64-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SUB]], [[C2]](s8) + ; X64-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[JUMP_TABLE]], [[SHL]](s32) + ; X64-NEXT: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[PTR_ADD]](p0) :: (load (s64) from jump-table, align 4) + ; X64-NEXT: [[TRUNC1:%[0-9]+]]:_(s32) = G_TRUNC [[LOAD]](s64) + ; X64-NEXT: [[PTR_ADD1:%[0-9]+]]:_(p0) = G_PTR_ADD [[JUMP_TABLE]], [[TRUNC1]](s32) + ; X64-NEXT: G_BRINDIRECT [[PTR_ADD1]](p0) + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.2: + ; X64-NEXT: successors: %bb.5(0x80000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: G_BR %bb.5 + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.3: + ; X64-NEXT: successors: %bb.5(0x80000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: G_BR %bb.5 + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.4: + ; X64-NEXT: successors: %bb.5(0x80000000) + ; X64-NEXT: {{ $}} + ; X64-NEXT: bb.5: + ; X64-NEXT: $eax = COPY [[COPY]](s32) + ; X64-NEXT: RET 0, implicit $eax + ; + ; X32-LABEL: name: test_label_diff32 + ; X32: bb.0.entry: + ; X32-NEXT: successors: %bb.1(0x40000000), %bb.4(0x40000000) + ; X32-NEXT: liveins: $edi + ; X32-NEXT: {{ $}} + ; X32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $edi + ; X32-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4 + ; X32-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0 + ; X32-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[C1]] + ; X32-NEXT: [[ICMP:%[0-9]+]]:_(s8) = G_ICMP intpred(ugt), [[SUB]](s32), [[C]] + ; X32-NEXT: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ICMP]](s8) + ; X32-NEXT: G_BRCOND [[TRUNC]](s1), %bb.4 + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.1.entry: + ; X32-NEXT: successors: %bb.2(0x40000000), %bb.3(0x40000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: [[JUMP_TABLE:%[0-9]+]]:_(p0) = G_JUMP_TABLE %jump-table.0 + ; X32-NEXT: [[C2:%[0-9]+]]:_(s8) = G_CONSTANT i8 2 + ; X32-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[SUB]], [[C2]](s8) + ; X32-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[JUMP_TABLE]], [[SHL]](s32) + ; X32-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[PTR_ADD]](p0) :: (load (s32) from jump-table) + ; X32-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY [[LOAD]](s32) + ; X32-NEXT: [[PTR_ADD1:%[0-9]+]]:_(p0) = G_PTR_ADD [[JUMP_TABLE]], [[COPY1]](s32) + ; X32-NEXT: G_BRINDIRECT [[PTR_ADD1]](p0) + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.2: + ; X32-NEXT: successors: %bb.5(0x80000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: G_BR %bb.5 + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.3: + ; X32-NEXT: successors: %bb.5(0x80000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: G_BR %bb.5 + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.4: + ; X32-NEXT: successors: %bb.5(0x80000000) + ; X32-NEXT: {{ $}} + ; X32-NEXT: bb.5: + ; X32-NEXT: $eax = COPY [[COPY]](s32) + ; X32-NEXT: RET 0, implicit $eax + bb.0.entry: + successors: %bb.1(0x40000000), %bb.4(0x40000000) + liveins: $edi + + %0:_(s32) = COPY $edi + %4:_(s32) = G_CONSTANT i32 4 + %1:_(s32) = G_CONSTANT i32 0 + %2:_(s32) = G_SUB %0, %1 + %5:_(s1) = G_ICMP intpred(ugt), %2(s32), %4 + G_BRCOND %5(s1), %bb.4 + + bb.1.entry: + successors: %bb.2(0x1999999a), %bb.3(0x1999999a) + + %6:_(p0) = G_JUMP_TABLE %jump-table.0 + G_BRJT %6(p0), %jump-table.0, %2(s32) + + bb.2: + successors: %bb.5(0x80000000) + + G_BR %bb.5 + + bb.3: + successors: %bb.5(0x80000000) + + G_BR %bb.5 + + bb.4: + successors: %bb.5(0x80000000) + + bb.5: + $eax = COPY %0(s32) + RET 0, implicit $eax + +... diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-jumptable.mir b/llvm/test/CodeGen/X86/GlobalISel/select-jumptable.mir new file mode 100644 index 0000000000000..53057c2d6c5b9 --- /dev/null +++ b/llvm/test/CodeGen/X86/GlobalISel/select-jumptable.mir @@ -0,0 +1,114 @@ +# RUN: split-file %s %t +# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=instruction-select --relocation-model=static %t/x64.mir -o - | \ +# RUN: FileCheck %t/x64.mir --check-prefix=X64-STATIC +# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=instruction-select --relocation-model=pic %t/x64.mir -o - | \ +# RUN: FileCheck %t/x64.mir --check-prefix=X64-PIC +# RUN: llc -mtriple=i386-linux-gnu -run-pass=instruction-select --relocation-model=static %t/x32.mir -o - | \ +# RUN: FileCheck %t/x32.mir --check-prefix=X32-STATIC +# RUN: llc -mtriple=i386-linux-gnu -run-pass=instruction-select --relocation-model=pic %t/x32.mir -o - | \ +# RUN: FileCheck %t/x32.mir --check-prefix=X32-PIC + +#--- x64.mir +--- +name: test64 +alignment: 16 +exposesReturnsTwice: false +legalized: true +regBankSelected: true +selected: false +registers: + - { id: 0, class: gpr, preferred-register: '' } +jumpTable: + kind: block-address + entries: + - id: 0 + blocks: [ '%bb.1', '%bb.2' ] +body: | + ; X64-STATIC-LABEL: name: test64 + ; X64-STATIC: bb.0.entry: + ; X64-STATIC-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; X64-STATIC-NEXT: {{ $}} + ; X64-STATIC-NEXT: [[LEA64r:%[0-9]+]]:gr64 = LEA64r $noreg, 1, $noreg, %jump-table.0, $noreg + ; X64-STATIC-NEXT: $rax = COPY [[LEA64r]] + ; X64-STATIC-NEXT: RET 0, implicit $rax + ; + ; X64-PIC-LABEL: name: test64 + ; X64-PIC: bb.0.entry: + ; X64-PIC-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; X64-PIC-NEXT: {{ $}} + ; X64-PIC-NEXT: [[LEA64r:%[0-9]+]]:gr64 = LEA64r $rip, 1, $noreg, %jump-table.0, $noreg + ; X64-PIC-NEXT: $rax = COPY [[LEA64r]] + ; X64-PIC-NEXT: RET 0, implicit $rax + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + + %0:gpr(p0) = G_JUMP_TABLE %jump-table.0 + $rax = COPY %0(p0) + RET 0, implicit $rax + + bb.1: + successors: %bb.4(0x80000000) + + G_BR %bb.4 + + bb.2: + successors: %bb.4(0x80000000) + + G_BR %bb.4 + + bb.4: + RET 0, implicit $rax + +... +#--- x32.mir +--- +name: test32 +alignment: 16 +exposesReturnsTwice: false +legalized: true +regBankSelected: true +selected: false +registers: + - { id: 0, class: gpr, preferred-register: '' } +jumpTable: + kind: block-address + entries: + - id: 0 + blocks: [ '%bb.1', '%bb.2' ] +body: | + ; X32-STATIC-LABEL: name: test32 + ; X32-STATIC: bb.0.entry: + ; X32-STATIC-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; X32-STATIC-NEXT: {{ $}} + ; X32-STATIC-NEXT: [[LEA32r:%[0-9]+]]:gr32 = LEA32r $noreg, 1, $noreg, %jump-table.0, $noreg + ; X32-STATIC-NEXT: $eax = COPY [[LEA32r]] + ; X32-STATIC-NEXT: RET 0, implicit $eax + ; + ; X32-PIC-LABEL: name: test32 + ; X32-PIC: bb.0.entry: + ; X32-PIC-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000) + ; X32-PIC-NEXT: {{ $}} + ; X32-PIC-NEXT: [[LEA32r:%[0-9]+]]:gr32 = LEA32r $eip, 1, $noreg, %jump-table.0, $noreg + ; X32-PIC-NEXT: $eax = COPY [[LEA32r]] + ; X32-PIC-NEXT: RET 0, implicit $eax + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + + %0:gpr(p0) = G_JUMP_TABLE %jump-table.0 + $eax = COPY %0(p0) + RET 0, implicit $eax + + bb.1: + successors: %bb.4(0x80000000) + + G_BR %bb.4 + + bb.2: + successors: %bb.4(0x80000000) + + G_BR %bb.4 + + bb.4: + RET 0, implicit $eax + +... diff --git a/llvm/test/CodeGen/X86/isel-brjt.ll b/llvm/test/CodeGen/X86/isel-brjt.ll new file mode 100644 index 0000000000000..1671f9cd32bc7 --- /dev/null +++ b/llvm/test/CodeGen/X86/isel-brjt.ll @@ -0,0 +1,206 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 +; RUN: llc < %s -O0 -mtriple=i686-linux-gnu -global-isel=0 -verify-machineinstrs | FileCheck %s --check-prefix=DAG86 +; RUN: llc < %s -O0 -mtriple=i686-linux-gnu -fast-isel -fast-isel-abort=1 | FileCheck %s --check-prefix=DAGF86 +; RUN: llc < %s -O0 -mtriple=i686-linux-gnu -global-isel -global-isel-abort=1 -verify-machineinstrs | FileCheck %s --check-prefix=GISEL86 +; RUN: llc < %s -O0 -mtriple=x86_64-linux-gnu -global-isel=0 | FileCheck %s --check-prefix=DAG64 +; RUN: llc < %s -O0 -mtriple=x86_64-linux-gnu -fast-isel -fast-isel-abort=1 | FileCheck %s --check-prefix=DAGF64 +; RUN: llc < %s -O0 -mtriple=x86_64-linux-gnu -global-isel -global-isel-abort=1 | FileCheck %s --check-prefix=GISEL64 + +define i32 @brjt(i32 %num, i32 %x) { +; DAG86-LABEL: brjt: +; DAG86: # %bb.0: # %entry +; DAG86-NEXT: subl $8, %esp +; DAG86-NEXT: .cfi_def_cfa_offset 12 +; DAG86-NEXT: movl {{[0-9]+}}(%esp), %eax +; DAG86-NEXT: movl %eax, (%esp) # 4-byte Spill +; DAG86-NEXT: decl %eax +; DAG86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; DAG86-NEXT: subl $3, %eax +; DAG86-NEXT: ja .LBB0_4 +; DAG86-NEXT: # %bb.6: # %entry +; DAG86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload +; DAG86-NEXT: movl .LJTI0_0(,%eax,4), %eax +; DAG86-NEXT: jmpl *%eax +; DAG86-NEXT: .LBB0_1: # %sw.bb +; DAG86-NEXT: jmp .LBB0_5 +; DAG86-NEXT: .LBB0_2: # %sw.bb1 +; DAG86-NEXT: jmp .LBB0_5 +; DAG86-NEXT: .LBB0_3: # %sw.bb3 +; DAG86-NEXT: jmp .LBB0_5 +; DAG86-NEXT: .LBB0_4: # %sw.epilog +; DAG86-NEXT: jmp .LBB0_5 +; DAG86-NEXT: .LBB0_5: # %return +; DAG86-NEXT: movl (%esp), %eax # 4-byte Reload +; DAG86-NEXT: addl $8, %esp +; DAG86-NEXT: .cfi_def_cfa_offset 4 +; DAG86-NEXT: retl +; +; DAGF86-LABEL: brjt: +; DAGF86: # %bb.0: # %entry +; DAGF86-NEXT: subl $8, %esp +; DAGF86-NEXT: .cfi_def_cfa_offset 12 +; DAGF86-NEXT: movl {{[0-9]+}}(%esp), %eax +; DAGF86-NEXT: movl %eax, (%esp) # 4-byte Spill +; DAGF86-NEXT: decl %eax +; DAGF86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; DAGF86-NEXT: subl $3, %eax +; DAGF86-NEXT: ja .LBB0_4 +; DAGF86-NEXT: # %bb.6: # %entry +; DAGF86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %eax # 4-byte Reload +; DAGF86-NEXT: movl .LJTI0_0(,%eax,4), %eax +; DAGF86-NEXT: jmpl *%eax +; DAGF86-NEXT: .LBB0_1: # %sw.bb +; DAGF86-NEXT: jmp .LBB0_5 +; DAGF86-NEXT: .LBB0_2: # %sw.bb1 +; DAGF86-NEXT: jmp .LBB0_5 +; DAGF86-NEXT: .LBB0_3: # %sw.bb3 +; DAGF86-NEXT: jmp .LBB0_5 +; DAGF86-NEXT: .LBB0_4: # %sw.epilog +; DAGF86-NEXT: jmp .LBB0_5 +; DAGF86-NEXT: .LBB0_5: # %return +; DAGF86-NEXT: movl (%esp), %eax # 4-byte Reload +; DAGF86-NEXT: addl $8, %esp +; DAGF86-NEXT: .cfi_def_cfa_offset 4 +; DAGF86-NEXT: retl +; +; GISEL86-LABEL: brjt: +; GISEL86: # %bb.1: # %entry +; GISEL86-NEXT: subl $8, %esp +; GISEL86-NEXT: .cfi_def_cfa_offset 12 +; GISEL86-NEXT: movl {{[0-9]+}}(%esp), %eax +; GISEL86-NEXT: movl %eax, (%esp) # 4-byte Spill +; GISEL86-NEXT: movl $3, %ecx +; GISEL86-NEXT: subl $1, %eax +; GISEL86-NEXT: movl %eax, {{[-0-9]+}}(%e{{[sb]}}p) # 4-byte Spill +; GISEL86-NEXT: cmpl %ecx, %eax +; GISEL86-NEXT: seta %al +; GISEL86-NEXT: testb $1, %al +; GISEL86-NEXT: jne .LBB0_5 +; GISEL86-NEXT: # %bb.7: # %entry +; GISEL86-NEXT: movl {{[-0-9]+}}(%e{{[sb]}}p), %ecx # 4-byte Reload +; GISEL86-NEXT: leal .LJTI0_0, %eax +; GISEL86-NEXT: shll $2, %ecx +; GISEL86-NEXT: leal (%eax,%ecx), %eax +; GISEL86-NEXT: movl (%eax), %eax +; GISEL86-NEXT: jmpl *%eax +; GISEL86-NEXT: .LBB0_2: # %sw.bb +; GISEL86-NEXT: jmp .LBB0_6 +; GISEL86-NEXT: .LBB0_3: # %sw.bb1 +; GISEL86-NEXT: jmp .LBB0_6 +; GISEL86-NEXT: .LBB0_4: # %sw.bb3 +; GISEL86-NEXT: jmp .LBB0_6 +; GISEL86-NEXT: .LBB0_5: # %sw.epilog +; GISEL86-NEXT: jmp .LBB0_6 +; GISEL86-NEXT: .LBB0_6: # %return +; GISEL86-NEXT: movl (%esp), %eax # 4-byte Reload +; GISEL86-NEXT: addl $8, %esp +; GISEL86-NEXT: .cfi_def_cfa_offset 4 +; GISEL86-NEXT: retl +; +; DAG64-LABEL: brjt: +; DAG64: # %bb.0: # %entry +; DAG64-NEXT: movl %edi, %eax +; DAG64-NEXT: movl %eax, {{[-0-9]+}}(%r{{[sb]}}p) # 4-byte Spill +; DAG64-NEXT: decl %eax +; DAG64-NEXT: movl %eax, %ecx +; DAG64-NEXT: movq %rcx, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill +; DAG64-NEXT: subl $3, %eax +; DAG64-NEXT: ja .LBB0_4 +; DAG64-NEXT: # %bb.6: # %entry +; DAG64-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rax # 8-byte Reload +; DAG64-NEXT: movq .LJTI0_0(,%rax,8), %rax +; DAG64-NEXT: jmpq *%rax +; DAG64-NEXT: .LBB0_1: # %sw.bb +; DAG64-NEXT: jmp .LBB0_5 +; DAG64-NEXT: .LBB0_2: # %sw.bb1 +; DAG64-NEXT: jmp .LBB0_5 +; DAG64-NEXT: .LBB0_3: # %sw.bb3 +; DAG64-NEXT: jmp .LBB0_5 +; DAG64-NEXT: .LBB0_4: # %sw.epilog +; DAG64-NEXT: jmp .LBB0_5 +; DAG64-NEXT: .LBB0_5: # %return +; DAG64-NEXT: movl {{[-0-9]+}}(%r{{[sb]}}p), %eax # 4-byte Reload +; DAG64-NEXT: retq +; +; DAGF64-LABEL: brjt: +; DAGF64: # %bb.0: # %entry +; DAGF64-NEXT: movl %edi, %eax +; DAGF64-NEXT: movl %eax, {{[-0-9]+}}(%r{{[sb]}}p) # 4-byte Spill +; DAGF64-NEXT: decl %eax +; DAGF64-NEXT: movl %eax, %ecx +; DAGF64-NEXT: movq %rcx, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill +; DAGF64-NEXT: subl $3, %eax +; DAGF64-NEXT: ja .LBB0_4 +; DAGF64-NEXT: # %bb.6: # %entry +; DAGF64-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rax # 8-byte Reload +; DAGF64-NEXT: movq .LJTI0_0(,%rax,8), %rax +; DAGF64-NEXT: jmpq *%rax +; DAGF64-NEXT: .LBB0_1: # %sw.bb +; DAGF64-NEXT: jmp .LBB0_5 +; DAGF64-NEXT: .LBB0_2: # %sw.bb1 +; DAGF64-NEXT: jmp .LBB0_5 +; DAGF64-NEXT: .LBB0_3: # %sw.bb3 +; DAGF64-NEXT: jmp .LBB0_5 +; DAGF64-NEXT: .LBB0_4: # %sw.epilog +; DAGF64-NEXT: jmp .LBB0_5 +; DAGF64-NEXT: .LBB0_5: # %return +; DAGF64-NEXT: movl {{[-0-9]+}}(%r{{[sb]}}p), %eax # 4-byte Reload +; DAGF64-NEXT: retq +; +; GISEL64-LABEL: brjt: +; GISEL64: # %bb.1: # %entry +; GISEL64-NEXT: movl %edi, {{[-0-9]+}}(%r{{[sb]}}p) # 4-byte Spill +; GISEL64-NEXT: subl $1, %edi +; GISEL64-NEXT: movl %edi, %eax +; GISEL64-NEXT: # kill: def $rax killed $eax +; GISEL64-NEXT: movq %rax, {{[-0-9]+}}(%r{{[sb]}}p) # 8-byte Spill +; GISEL64-NEXT: movl $3, %ecx +; GISEL64-NEXT: cmpq %rcx, %rax +; GISEL64-NEXT: seta %al +; GISEL64-NEXT: testb $1, %al +; GISEL64-NEXT: jne .LBB0_5 +; GISEL64-NEXT: # %bb.7: # %entry +; GISEL64-NEXT: movq {{[-0-9]+}}(%r{{[sb]}}p), %rcx # 8-byte Reload +; GISEL64-NEXT: leaq .LJTI0_0, %rax +; GISEL64-NEXT: shlq $3, %rcx +; GISEL64-NEXT: leaq (%rax,%rcx), %rax +; GISEL64-NEXT: movq (%rax), %rax +; GISEL64-NEXT: jmpq *%rax +; GISEL64-NEXT: .LBB0_2: # %sw.bb +; GISEL64-NEXT: jmp .LBB0_6 +; GISEL64-NEXT: .LBB0_3: # %sw.bb1 +; GISEL64-NEXT: jmp .LBB0_6 +; GISEL64-NEXT: .LBB0_4: # %sw.bb3 +; GISEL64-NEXT: jmp .LBB0_6 +; GISEL64-NEXT: .LBB0_5: # %sw.epilog +; GISEL64-NEXT: jmp .LBB0_6 +; GISEL64-NEXT: .LBB0_6: # %return +; GISEL64-NEXT: movl {{[-0-9]+}}(%r{{[sb]}}p), %eax # 4-byte Reload +; GISEL64-NEXT: retq +entry: + switch i32 %num, label %sw.epilog [ + i32 1, label %sw.bb + i32 2, label %sw.bb1 + i32 3, label %return + i32 4, label %sw.bb3 + ] + +sw.bb: ; preds = %entry + br label %return + +sw.bb1: ; preds = %entry + br label %return + +sw.bb3: ; preds = %entry + br label %return + +sw.epilog: ; preds = %entry + br label %return + +return: ; preds = %entry, %sw.epilog, %sw.bb3, %sw.bb1, %sw.bb + ret i32 %num +} + +!llvm.module.flags = !{!0} + +!0 = !{i32 8, !"PIC Level", i32 2}