diff --git a/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.cpp b/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.cpp --- a/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.cpp +++ b/llvm/lib/Target/Sparc/MCTargetDesc/SparcInstPrinter.cpp @@ -178,6 +178,8 @@ default: break; case SP::FBCOND: case SP::FBCONDA: + case SP::FBCOND_V9: + case SP::FBCONDA_V9: case SP::BPFCC: case SP::BPFCCA: case SP::BPFCCNT: diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.h b/llvm/lib/Target/Sparc/SparcInstrInfo.h --- a/llvm/lib/Target/Sparc/SparcInstrInfo.h +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.h @@ -64,6 +64,8 @@ unsigned isStoreToStackSlot(const MachineInstr &MI, int &FrameIndex) const override; + MachineBasicBlock *getBranchDestBlock(const MachineInstr &MI) const override; + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, SmallVectorImpl &Cond, @@ -80,6 +82,9 @@ bool reverseBranchCondition(SmallVectorImpl &Cond) const override; + /// Determine if the branch target is in range. + bool isBranchOffsetInRange(unsigned BranchOpc, int64_t Offset) const override; + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, MCRegister SrcReg, bool KillSrc) const override; @@ -99,6 +104,10 @@ Register getGlobalBaseReg(MachineFunction *MF) const; + /// GetInstSize - Return the number of bytes of code the specified + /// instruction may be. This returns the maximum number of bytes. + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + // Lower pseudo instructions after register allocation. bool expandPostRAPseudo(MachineInstr &MI) const override; }; diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp --- a/llvm/lib/Target/Sparc/SparcInstrInfo.cpp +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.cpp @@ -28,6 +28,10 @@ #define GET_INSTRINFO_CTOR_DTOR #include "SparcGenInstrInfo.inc" +static cl::opt BPccDisplacementBits( + "sparc-bpcc-offset-bits", cl::Hidden, cl::init(19), + cl::desc("Restrict range of BPcc/FBPfcc instructions (DEBUG)")); + // Pin the vtable to this file. void SparcInstrInfo::anchor() {} @@ -73,11 +77,6 @@ return 0; } -static bool IsIntegerCC(unsigned CC) -{ - return (CC <= SPCC::ICC_VC); -} - static SPCC::CondCodes GetOppositeBranchCondition(SPCC::CondCodes CC) { switch(CC) { @@ -155,9 +154,7 @@ llvm_unreachable("Invalid cond code"); } -static bool isUncondBranchOpcode(int Opc) { - return Opc == SP::BA || Opc == SP::BPA; -} +static bool isUncondBranchOpcode(int Opc) { return Opc == SP::BA; } static bool isI32CondBranchOpcode(int Opc) { return Opc == SP::BCOND || Opc == SP::BPICC || Opc == SP::BPICCA || @@ -169,7 +166,10 @@ Opc == SP::BPXCCANT; } -static bool isFCondBranchOpcode(int Opc) { return Opc == SP::FBCOND; } +static bool isFCondBranchOpcode(int Opc) { + return Opc == SP::FBCOND || Opc == SP::FBCONDA || Opc == SP::FBCOND_V9 || + Opc == SP::FBCONDA_V9; +} static bool isCondBranchOpcode(int Opc) { return isI32CondBranchOpcode(Opc) || isI64CondBranchOpcode(Opc) || @@ -193,6 +193,34 @@ Target = LastInst->getOperand(0).getMBB(); } +MachineBasicBlock * +SparcInstrInfo::getBranchDestBlock(const MachineInstr &MI) const { + switch (MI.getOpcode()) { + default: + llvm_unreachable("unexpected opcode!"); + case SP::BA: + case SP::BCOND: + case SP::BCONDA: + case SP::FBCOND: + case SP::FBCONDA: + case SP::BPICC: + case SP::BPICCA: + case SP::BPICCNT: + case SP::BPICCANT: + case SP::BPXCC: + case SP::BPXCCA: + case SP::BPXCCNT: + case SP::BPXCCANT: + case SP::BPFCC: + case SP::BPFCCA: + case SP::BPFCCNT: + case SP::BPFCCANT: + case SP::FBCOND_V9: + case SP::FBCONDA_V9: + return MI.getOperand(0).getMBB(); + } +} + bool SparcInstrInfo::analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, @@ -285,36 +313,37 @@ assert(TBB && "insertBranch must not be told to insert a fallthrough"); assert((Cond.size() <= 2) && "Sparc branch conditions should have at most two components!"); - assert(!BytesAdded && "code size not handled"); if (Cond.empty()) { assert(!FBB && "Unconditional branch with multiple successors!"); - BuildMI(&MBB, DL, get(Subtarget.isV9() ? SP::BPA : SP::BA)).addMBB(TBB); + BuildMI(&MBB, DL, get(SP::BA)).addMBB(TBB); + if (BytesAdded) + *BytesAdded = 8; return 1; } // Conditional branch unsigned Opc = Cond[0].getImm(); unsigned CC = Cond[1].getImm(); + BuildMI(&MBB, DL, get(Opc)).addMBB(TBB).addImm(CC); - if (IsIntegerCC(CC)) { - BuildMI(&MBB, DL, get(Opc)).addMBB(TBB).addImm(CC); - } else { - BuildMI(&MBB, DL, get(SP::FBCOND)).addMBB(TBB).addImm(CC); - } - if (!FBB) + if (!FBB) { + if (BytesAdded) + *BytesAdded = 8; return 1; + } - BuildMI(&MBB, DL, get(Subtarget.isV9() ? SP::BPA : SP::BA)).addMBB(FBB); + BuildMI(&MBB, DL, get(SP::BA)).addMBB(FBB); + if (BytesAdded) + *BytesAdded = 16; return 2; } unsigned SparcInstrInfo::removeBranch(MachineBasicBlock &MBB, int *BytesRemoved) const { - assert(!BytesRemoved && "code size not handled"); - MachineBasicBlock::iterator I = MBB.end(); unsigned Count = 0; + int Removed = 0; while (I != MBB.begin()) { --I; @@ -326,9 +355,13 @@ break; // Not a branch I->eraseFromParent(); + Removed += getInstSizeInBytes(*I); I = MBB.end(); ++Count; } + + if (BytesRemoved) + *BytesRemoved = Removed; return Count; } @@ -340,6 +373,37 @@ return false; } +bool SparcInstrInfo::isBranchOffsetInRange(unsigned BranchOpc, + int64_t Offset) const { + assert((Offset & 0b11) == 0 && "Malformed branch offset"); + switch (BranchOpc) { + case SP::BA: + case SP::BCOND: + case SP::BCONDA: + case SP::FBCOND: + case SP::FBCONDA: + return isIntN(22, Offset >> 2); + + case SP::BPICC: + case SP::BPICCA: + case SP::BPICCNT: + case SP::BPICCANT: + case SP::BPXCC: + case SP::BPXCCA: + case SP::BPXCCNT: + case SP::BPXCCANT: + case SP::BPFCC: + case SP::BPFCCA: + case SP::BPFCCNT: + case SP::BPFCCANT: + case SP::FBCOND_V9: + case SP::FBCONDA_V9: + return isIntN(BPccDisplacementBits, Offset >> 2); + } + + llvm_unreachable("Unknown branch instruction!"); +} + void SparcInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, const DebugLoc &DL, MCRegister DestReg, @@ -530,6 +594,23 @@ return GlobalBaseReg; } +unsigned SparcInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + unsigned Opcode = MI.getOpcode(); + + if (MI.isInlineAsm()) { + const MachineFunction *MF = MI.getParent()->getParent(); + const char *AsmStr = MI.getOperand(0).getSymbolName(); + return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo()); + } + + // If the instruction has a delay slot, be conservative and also include + // it for sizing purposes. This is done so that the BranchRelaxation pass + // will not mistakenly mark out-of-range branches as in-range. + if (MI.hasDelaySlot()) + return get(Opcode).getSize() * 2; + return get(Opcode).getSize(); +} + bool SparcInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { switch (MI.getOpcode()) { case TargetOpcode::LOAD_STACK_GUARD: { diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td --- a/llvm/lib/Target/Sparc/SparcInstrInfo.td +++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td @@ -850,15 +850,8 @@ : F2_3<0b001, 0, 1, (outs), ins, asmstr, pattern>; } -let cond = 8 in { - // If we're compiling for v9, prefer BPA rather than BA - // TODO: Disallow BA emission when FeatureV8Deprecated isn't enabled - let Predicates = [HasV9], cc = 0b00 in - def BPA : BranchPredictAlways<(ins bprtarget:$imm19), - "ba %icc, $imm19", [(br bb:$imm19)]>; - +let cond = 8 in def BA : BranchAlways<(ins brtarget:$imm22), "ba $imm22", [(br bb:$imm22)]>; -} let isBranch = 1, isTerminator = 1, hasDelaySlot = 1 in { diff --git a/llvm/lib/Target/Sparc/SparcTargetMachine.cpp b/llvm/lib/Target/Sparc/SparcTargetMachine.cpp --- a/llvm/lib/Target/Sparc/SparcTargetMachine.cpp +++ b/llvm/lib/Target/Sparc/SparcTargetMachine.cpp @@ -31,6 +31,10 @@ initializeSparcDAGToDAGISelPass(PR); } +static cl::opt + BranchRelaxation("sparc-enable-branch-relax", cl::Hidden, cl::init(true), + cl::desc("Relax out of range conditional branches")); + static std::string computeDataLayout(const Triple &T, bool is64Bit) { // Sparc is typically big endian, but some are little. std::string Ret = T.getArch() == Triple::sparcel ? "e" : "E"; @@ -181,6 +185,9 @@ } void SparcPassConfig::addPreEmitPass(){ + if (BranchRelaxation) + addPass(&BranchRelaxationPassID); + addPass(createSparcDelaySlotFillerPass()); if (this->getSparcTargetMachine().getSubtargetImpl()->insertNOPLoad()) diff --git a/llvm/test/CodeGen/SPARC/branches-relax.ll b/llvm/test/CodeGen/SPARC/branches-relax.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPARC/branches-relax.ll @@ -0,0 +1,113 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=sparc64 -sparc-bpcc-offset-bits=4 | FileCheck --check-prefix=SPARC64 %s + +define i32 @branch_relax_int(i32 %in) { +; SPARC64-LABEL: branch_relax_int: +; SPARC64: .cfi_startproc +; SPARC64-NEXT: ! %bb.0: +; SPARC64-NEXT: save %sp, -128, %sp +; SPARC64-NEXT: .cfi_def_cfa_register %fp +; SPARC64-NEXT: .cfi_window_save +; SPARC64-NEXT: .cfi_register %o7, %i7 +; SPARC64-NEXT: cmp %i0, 0 +; SPARC64-NEXT: bne %icc, .LBB0_1 +; SPARC64-NEXT: nop +; SPARC64-NEXT: ba .LBB0_2 +; SPARC64-NEXT: nop +; SPARC64-NEXT: .LBB0_1: ! %false +; SPARC64-NEXT: !APP +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: !NO_APP +; SPARC64-NEXT: ret +; SPARC64-NEXT: restore %g0, %g0, %o0 +; SPARC64-NEXT: .LBB0_2: ! %true +; SPARC64-NEXT: mov 4, %i0 +; SPARC64-NEXT: !APP +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: !NO_APP +; SPARC64-NEXT: ret +; SPARC64-NEXT: restore + %tst = icmp eq i32 %in, 0 + br i1 %tst, label %true, label %false + +true: + call void asm sideeffect "nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop", ""() + ret i32 4 + +false: + call void asm sideeffect "nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop", ""() + ret i32 0 +} + +define float @branch_relax_float(float %in) { +; SPARC64-LABEL: branch_relax_float: +; SPARC64: .cfi_startproc +; SPARC64-NEXT: ! %bb.0: +; SPARC64-NEXT: save %sp, -128, %sp +; SPARC64-NEXT: .cfi_def_cfa_register %fp +; SPARC64-NEXT: .cfi_window_save +; SPARC64-NEXT: .cfi_register %o7, %i7 +; SPARC64-NEXT: sethi %h44(.LCPI1_0), %i0 +; SPARC64-NEXT: add %i0, %m44(.LCPI1_0), %i0 +; SPARC64-NEXT: sllx %i0, 12, %i0 +; SPARC64-NEXT: ld [%i0+%l44(.LCPI1_0)], %f0 +; SPARC64-NEXT: fcmps %fcc0, %f1, %f0 +; SPARC64-NEXT: fbe %fcc0, .LBB1_1 +; SPARC64-NEXT: nop +; SPARC64-NEXT: ba .LBB1_2 +; SPARC64-NEXT: nop +; SPARC64-NEXT: .LBB1_1: ! %true +; SPARC64-NEXT: sethi %h44(.LCPI1_1), %i0 +; SPARC64-NEXT: add %i0, %m44(.LCPI1_1), %i0 +; SPARC64-NEXT: sllx %i0, 12, %i0 +; SPARC64-NEXT: ld [%i0+%l44(.LCPI1_1)], %f0 +; SPARC64-NEXT: !APP +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: !NO_APP +; SPARC64-NEXT: ret +; SPARC64-NEXT: restore +; SPARC64-NEXT: .LBB1_2: ! %false +; SPARC64-NEXT: !APP +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: nop +; SPARC64-NEXT: !NO_APP +; SPARC64-NEXT: ret +; SPARC64-NEXT: restore + %tst = fcmp oeq float %in, 0.0 + br i1 %tst, label %true, label %false + +true: + call void asm sideeffect "nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop", ""() + ret float 4.0 + +false: + call void asm sideeffect "nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop\0A\09nop", ""() + ret float 0.0 +} diff --git a/llvm/test/CodeGen/SPARC/branches-v9.ll b/llvm/test/CodeGen/SPARC/branches-v9.ll --- a/llvm/test/CodeGen/SPARC/branches-v9.ll +++ b/llvm/test/CodeGen/SPARC/branches-v9.ll @@ -20,7 +20,7 @@ ; CHECK-NEXT: ! %bb.1: ! %fbb ; CHECK-NEXT: call f2 ; CHECK-NEXT: nop -; CHECK-NEXT: ba %icc, .LBB0_3 +; CHECK-NEXT: ba .LBB0_3 ; CHECK-NEXT: nop ; CHECK-NEXT: .LBB0_2: ! %tbb ; CHECK-NEXT: call f1 @@ -61,7 +61,7 @@ ; CHECK-NEXT: ! %bb.1: ! %fbb ; CHECK-NEXT: call f2 ; CHECK-NEXT: nop -; CHECK-NEXT: ba %icc, .LBB1_3 +; CHECK-NEXT: ba .LBB1_3 ; CHECK-NEXT: nop ; CHECK-NEXT: .LBB1_2: ! %tbb ; CHECK-NEXT: call f1 diff --git a/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll b/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll --- a/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll +++ b/llvm/test/CodeGen/SPARC/smulo-128-legalisation-lowering.ll @@ -125,12 +125,12 @@ ; SPARC-NEXT: or %i2, %i4, %i2 ; SPARC-NEXT: or %i2, %i3, %i2 ; SPARC-NEXT: cmp %i2, 0 -; SPARC-NEXT: bne .LBB0_1 +; SPARC-NEXT: bne .LBB0_2 ; SPARC-NEXT: nop -; SPARC-NEXT: ! %bb.2: ! %start +; SPARC-NEXT: ! %bb.1: ! %start ; SPARC-NEXT: ba .LBB0_3 ; SPARC-NEXT: mov %g0, %i4 -; SPARC-NEXT: .LBB0_1: +; SPARC-NEXT: .LBB0_2: ; SPARC-NEXT: mov 1, %i4 ; SPARC-NEXT: .LBB0_3: ! %start ; SPARC-NEXT: ld [%fp+-4], %i2 ! 4-byte Folded Reload diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.generated.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.generated.expected --- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.generated.expected +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.generated.expected @@ -74,12 +74,12 @@ ; CHECK-NEXT: st %g0, [%fp+-4] ; CHECK-NEXT: cmp %g0, 0 ; CHECK-NEXT: st %g0, [%fp+-8] -; CHECK-NEXT: be .LBB0_1 +; CHECK-NEXT: be .LBB0_2 ; CHECK-NEXT: mov 1, %i0 -; CHECK-NEXT: ! %bb.2: +; CHECK-NEXT: ! %bb.1: ; CHECK-NEXT: ba .LBB0_3 ; CHECK-NEXT: st %i0, [%fp+-16] -; CHECK-NEXT: .LBB0_1: +; CHECK-NEXT: .LBB0_2: ; CHECK-NEXT: st %i0, [%fp+-8] ; CHECK-NEXT: mov 2, %i0 ; CHECK-NEXT: st %i0, [%fp+-12] @@ -90,13 +90,13 @@ ; CHECK-NEXT: .LBB0_3: ; CHECK-NEXT: ld [%fp+-8], %i0 ; CHECK-NEXT: cmp %i0, 0 -; CHECK-NEXT: be .LBB0_4 +; CHECK-NEXT: be .LBB0_5 ; CHECK-NEXT: nop -; CHECK-NEXT: ! %bb.5: +; CHECK-NEXT: ! %bb.4: ; CHECK-NEXT: mov 1, %i0 ; CHECK-NEXT: ba .LBB0_6 ; CHECK-NEXT: st %i0, [%fp+-16] -; CHECK-NEXT: .LBB0_4: +; CHECK-NEXT: .LBB0_5: ; CHECK-NEXT: mov 1, %i0 ; CHECK-NEXT: st %i0, [%fp+-8] ; CHECK-NEXT: mov 2, %i0 diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.nogenerated.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.nogenerated.expected --- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.nogenerated.expected +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/sparc_generated_funcs.ll.nogenerated.expected @@ -15,12 +15,12 @@ ; CHECK-NEXT: st %g0, [%fp+-4] ; CHECK-NEXT: cmp %g0, 0 ; CHECK-NEXT: st %g0, [%fp+-8] -; CHECK-NEXT: be .LBB0_1 +; CHECK-NEXT: be .LBB0_2 ; CHECK-NEXT: mov 1, %i0 -; CHECK-NEXT: ! %bb.2: +; CHECK-NEXT: ! %bb.1: ; CHECK-NEXT: ba .LBB0_3 ; CHECK-NEXT: st %i0, [%fp+-16] -; CHECK-NEXT: .LBB0_1: +; CHECK-NEXT: .LBB0_2: ; CHECK-NEXT: st %i0, [%fp+-8] ; CHECK-NEXT: mov 2, %i0 ; CHECK-NEXT: st %i0, [%fp+-12] @@ -31,13 +31,13 @@ ; CHECK-NEXT: .LBB0_3: ; CHECK-NEXT: ld [%fp+-8], %i0 ; CHECK-NEXT: cmp %i0, 0 -; CHECK-NEXT: be .LBB0_4 +; CHECK-NEXT: be .LBB0_5 ; CHECK-NEXT: nop -; CHECK-NEXT: ! %bb.5: +; CHECK-NEXT: ! %bb.4: ; CHECK-NEXT: mov 1, %i0 ; CHECK-NEXT: ba .LBB0_6 ; CHECK-NEXT: st %i0, [%fp+-16] -; CHECK-NEXT: .LBB0_4: +; CHECK-NEXT: .LBB0_5: ; CHECK-NEXT: mov 1, %i0 ; CHECK-NEXT: st %i0, [%fp+-8] ; CHECK-NEXT: mov 2, %i0