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:
#define GET_INSTRINFO_CTOR_DTOR
#include "SparcGenInstrInfo.inc"
+static cl::opt<unsigned> 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() {}
return 0;
}
-static bool IsIntegerCC(unsigned CC)
-{
- return (CC <= SPCC::ICC_VC);
-}
-
static SPCC::CondCodes GetOppositeBranchCondition(SPCC::CondCodes CC)
{
switch(CC) {
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 ||
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) ||
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,
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;
break; // Not a branch
I->eraseFromParent();
+ Removed += getInstSizeInBytes(*I);
I = MBB.end();
++Count;
}
+
+ if (BytesRemoved)
+ *BytesRemoved = Removed;
return Count;
}
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,
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: {
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<MachineOperand> &Cond,
bool
reverseBranchCondition(SmallVectorImpl<MachineOperand> &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;
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;
};
: 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 {
initializeSparcDAGToDAGISelPass(PR);
}
+static cl::opt<bool>
+ 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";
}
void SparcPassConfig::addPreEmitPass(){
+ if (BranchRelaxation)
+ addPass(&BranchRelaxationPassID);
+
addPass(createSparcDelaySlotFillerPass());
if (this->getSparcTargetMachine().getSubtargetImpl()->insertNOPLoad())
--- /dev/null
+; 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
+}
; 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
; 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
; 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
; 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]
; 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
; 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]
; 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