[ARM64] Split tbz/tbnz into W/X register variant

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@209134 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Bradley Smith 2014-05-19 15:58:15 +00:00
parent 3f19ce848d
commit 07df0ca6c9
9 changed files with 146 additions and 66 deletions

View File

@ -275,8 +275,10 @@ static bool isConditionalBranch(unsigned Opc) {
switch (Opc) { switch (Opc) {
default: default:
return false; return false;
case ARM64::TBZ: case ARM64::TBZW:
case ARM64::TBNZ: case ARM64::TBNZW:
case ARM64::TBZX:
case ARM64::TBNZX:
case ARM64::CBZW: case ARM64::CBZW:
case ARM64::CBNZW: case ARM64::CBNZW:
case ARM64::CBZX: case ARM64::CBZX:
@ -290,8 +292,10 @@ static MachineBasicBlock *getDestBlock(MachineInstr *MI) {
switch (MI->getOpcode()) { switch (MI->getOpcode()) {
default: default:
assert(0 && "unexpected opcode!"); assert(0 && "unexpected opcode!");
case ARM64::TBZ: case ARM64::TBZW:
case ARM64::TBNZ: case ARM64::TBNZW:
case ARM64::TBZX:
case ARM64::TBNZX:
return MI->getOperand(2).getMBB(); return MI->getOperand(2).getMBB();
case ARM64::CBZW: case ARM64::CBZW:
case ARM64::CBNZW: case ARM64::CBNZW:
@ -306,8 +310,10 @@ static unsigned getOppositeConditionOpcode(unsigned Opc) {
switch (Opc) { switch (Opc) {
default: default:
assert(0 && "unexpected opcode!"); assert(0 && "unexpected opcode!");
case ARM64::TBNZ: return ARM64::TBZ; case ARM64::TBNZW: return ARM64::TBZW;
case ARM64::TBZ: return ARM64::TBNZ; case ARM64::TBNZX: return ARM64::TBZX;
case ARM64::TBZW: return ARM64::TBNZW;
case ARM64::TBZX: return ARM64::TBNZX;
case ARM64::CBNZW: return ARM64::CBZW; case ARM64::CBNZW: return ARM64::CBZW;
case ARM64::CBNZX: return ARM64::CBZX; case ARM64::CBNZX: return ARM64::CBZX;
case ARM64::CBZW: return ARM64::CBNZW; case ARM64::CBZW: return ARM64::CBNZW;
@ -320,8 +326,10 @@ static unsigned getBranchDisplacementBits(unsigned Opc) {
switch (Opc) { switch (Opc) {
default: default:
assert(0 && "unexpected opcode!"); assert(0 && "unexpected opcode!");
case ARM64::TBNZ: case ARM64::TBNZW:
case ARM64::TBZ: case ARM64::TBZW:
case ARM64::TBNZX:
case ARM64::TBZX:
return TBZDisplacementBits; return TBZDisplacementBits;
case ARM64::CBNZW: case ARM64::CBNZW:
case ARM64::CBZW: case ARM64::CBZW:
@ -379,7 +387,8 @@ bool ARM64BranchRelaxation::fixupConditionalBranch(MachineInstr *MI) {
<< *BMI); << *BMI);
BMI->getOperand(0).setMBB(DestBB); BMI->getOperand(0).setMBB(DestBB);
unsigned OpNum = unsigned OpNum =
(MI->getOpcode() == ARM64::TBZ || MI->getOpcode() == ARM64::TBNZ) (MI->getOpcode() == ARM64::TBZW || MI->getOpcode() == ARM64::TBNZW ||
MI->getOpcode() == ARM64::TBZX || MI->getOpcode() == ARM64::TBNZX)
? 2 ? 2
: 1; : 1;
MI->getOperand(OpNum).setMBB(NewDest); MI->getOperand(OpNum).setMBB(NewDest);
@ -420,7 +429,8 @@ bool ARM64BranchRelaxation::fixupConditionalBranch(MachineInstr *MI) {
MachineInstrBuilder MIB = BuildMI( MachineInstrBuilder MIB = BuildMI(
MBB, DebugLoc(), TII->get(getOppositeConditionOpcode(MI->getOpcode()))) MBB, DebugLoc(), TII->get(getOppositeConditionOpcode(MI->getOpcode())))
.addOperand(MI->getOperand(0)); .addOperand(MI->getOperand(0));
if (MI->getOpcode() == ARM64::TBZ || MI->getOpcode() == ARM64::TBNZ) if (MI->getOpcode() == ARM64::TBZW || MI->getOpcode() == ARM64::TBNZW ||
MI->getOpcode() == ARM64::TBZX || MI->getOpcode() == ARM64::TBNZX)
MIB.addOperand(MI->getOperand(1)); MIB.addOperand(MI->getOperand(1));
if (MI->getOpcode() == ARM64::Bcc) if (MI->getOpcode() == ARM64::Bcc)
invertBccCondition(MIB); invertBccCondition(MIB);

View File

@ -173,6 +173,14 @@ def CondCode : AsmOperandClass {
let DiagnosticType = "InvalidCondCode"; let DiagnosticType = "InvalidCondCode";
} }
// A 32-bit register pasrsed as 64-bit
def GPR32as64Operand : AsmOperandClass {
let Name = "GPR32as64";
}
def GPR32as64 : RegisterOperand<GPR32> {
let ParserMatchClass = GPR32as64Operand;
}
// 8-bit immediate for AdvSIMD where 64-bit values of the form: // 8-bit immediate for AdvSIMD where 64-bit values of the form:
// aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh // aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh
// are encoded as the eight bit value 'abcdefgh'. // are encoded as the eight bit value 'abcdefgh'.
@ -1037,10 +1045,37 @@ def am_tbrcond : Operand<OtherVT> {
let ParserMatchClass = BranchTarget14Operand; let ParserMatchClass = BranchTarget14Operand;
} }
class TestBranch<bit op, string asm, SDNode node> // AsmOperand classes to emit (or not) special diagnostics
: I<(outs), (ins GPR64:$Rt, imm0_63:$bit_off, am_tbrcond:$target), def TBZImm0_31Operand : AsmOperandClass {
let Name = "TBZImm0_31";
let PredicateMethod = "isImm0_31";
let RenderMethod = "addImm0_31Operands";
}
def TBZImm32_63Operand : AsmOperandClass {
let Name = "Imm32_63";
let DiagnosticType = "InvalidImm0_63";
}
class tbz_imm0_31<AsmOperandClass matcher> : Operand<i64>, ImmLeaf<i64, [{
return (((uint32_t)Imm) < 32);
}]> {
let ParserMatchClass = matcher;
}
def tbz_imm0_31_diag : tbz_imm0_31<Imm0_31Operand>;
def tbz_imm0_31_nodiag : tbz_imm0_31<TBZImm0_31Operand>;
def tbz_imm32_63 : Operand<i64>, ImmLeaf<i64, [{
return (((uint32_t)Imm) > 31) && (((uint32_t)Imm) < 64);
}]> {
let ParserMatchClass = TBZImm32_63Operand;
}
class BaseTestBranch<RegisterClass regtype, Operand immtype,
bit op, string asm, SDNode node>
: I<(outs), (ins regtype:$Rt, immtype:$bit_off, am_tbrcond:$target),
asm, "\t$Rt, $bit_off, $target", "", asm, "\t$Rt, $bit_off, $target", "",
[(node GPR64:$Rt, imm0_63:$bit_off, bb:$target)]>, [(node regtype:$Rt, immtype:$bit_off, bb:$target)]>,
Sched<[WriteBr]> { Sched<[WriteBr]> {
let isBranch = 1; let isBranch = 1;
let isTerminator = 1; let isTerminator = 1;
@ -1049,7 +1084,6 @@ class TestBranch<bit op, string asm, SDNode node>
bits<6> bit_off; bits<6> bit_off;
bits<14> target; bits<14> target;
let Inst{31} = bit_off{5};
let Inst{30-25} = 0b011011; let Inst{30-25} = 0b011011;
let Inst{24} = op; let Inst{24} = op;
let Inst{23-19} = bit_off{4-0}; let Inst{23-19} = bit_off{4-0};
@ -1059,6 +1093,24 @@ class TestBranch<bit op, string asm, SDNode node>
let DecoderMethod = "DecodeTestAndBranch"; let DecoderMethod = "DecodeTestAndBranch";
} }
multiclass TestBranch<bit op, string asm, SDNode node> {
def W : BaseTestBranch<GPR32, tbz_imm0_31_diag, op, asm, node> {
let Inst{31} = 0;
}
def X : BaseTestBranch<GPR64, tbz_imm32_63, op, asm, node> {
let Inst{31} = 1;
}
// Alias X-reg with 0-31 imm to W-Reg.
def : InstAlias<asm # "\t$Rd, $imm, $target",
(!cast<Instruction>(NAME#"W") GPR32as64:$Rd,
tbz_imm0_31_nodiag:$imm, am_tbrcond:$target), 0>;
def : Pat<(node GPR64:$Rn, tbz_imm0_31_diag:$imm, bb:$target),
(!cast<Instruction>(NAME#"W") (EXTRACT_SUBREG GPR64:$Rn, sub_32),
tbz_imm0_31_diag:$imm, bb:$target)>;
}
//--- //---
// Unconditional branch (immediate) instructions. // Unconditional branch (immediate) instructions.
//--- //---

View File

@ -70,8 +70,10 @@ static void parseCondBranch(MachineInstr *LastInst, MachineBasicBlock *&Target,
Cond.push_back(MachineOperand::CreateImm(LastInst->getOpcode())); Cond.push_back(MachineOperand::CreateImm(LastInst->getOpcode()));
Cond.push_back(LastInst->getOperand(0)); Cond.push_back(LastInst->getOperand(0));
break; break;
case ARM64::TBZ: case ARM64::TBZW:
case ARM64::TBNZ: case ARM64::TBZX:
case ARM64::TBNZW:
case ARM64::TBNZX:
Target = LastInst->getOperand(2).getMBB(); Target = LastInst->getOperand(2).getMBB();
Cond.push_back(MachineOperand::CreateImm(-1)); Cond.push_back(MachineOperand::CreateImm(-1));
Cond.push_back(MachineOperand::CreateImm(LastInst->getOpcode())); Cond.push_back(MachineOperand::CreateImm(LastInst->getOpcode()));
@ -196,11 +198,17 @@ bool ARM64InstrInfo::ReverseBranchCondition(
case ARM64::CBNZX: case ARM64::CBNZX:
Cond[1].setImm(ARM64::CBZX); Cond[1].setImm(ARM64::CBZX);
break; break;
case ARM64::TBZ: case ARM64::TBZW:
Cond[1].setImm(ARM64::TBNZ); Cond[1].setImm(ARM64::TBNZW);
break; break;
case ARM64::TBNZ: case ARM64::TBNZW:
Cond[1].setImm(ARM64::TBZ); Cond[1].setImm(ARM64::TBZW);
break;
case ARM64::TBZX:
Cond[1].setImm(ARM64::TBNZX);
break;
case ARM64::TBNZX:
Cond[1].setImm(ARM64::TBZX);
break; break;
} }
} }
@ -453,17 +461,24 @@ void ARM64InstrInfo::insertSelect(MachineBasicBlock &MBB,
switch (Cond[1].getImm()) { switch (Cond[1].getImm()) {
default: default:
llvm_unreachable("Unknown branch opcode in Cond"); llvm_unreachable("Unknown branch opcode in Cond");
case ARM64::TBZ: case ARM64::TBZW:
case ARM64::TBZX:
CC = ARM64CC::EQ; CC = ARM64CC::EQ;
break; break;
case ARM64::TBNZ: case ARM64::TBNZW:
case ARM64::TBNZX:
CC = ARM64CC::NE; CC = ARM64CC::NE;
break; break;
} }
// cmp reg, #foo is actually ands xzr, reg, #1<<foo. // cmp reg, #foo is actually ands xzr, reg, #1<<foo.
BuildMI(MBB, I, DL, get(ARM64::ANDSXri), ARM64::XZR) if (Cond[1].getImm() == ARM64::TBZW || Cond[1].getImm() == ARM64::TBNZW)
.addReg(Cond[2].getReg()) BuildMI(MBB, I, DL, get(ARM64::ANDSWri), ARM64::WZR)
.addImm(ARM64_AM::encodeLogicalImmediate(1ull << Cond[3].getImm(), 64)); .addReg(Cond[2].getReg())
.addImm(ARM64_AM::encodeLogicalImmediate(1ull << Cond[3].getImm(), 32));
else
BuildMI(MBB, I, DL, get(ARM64::ANDSXri), ARM64::XZR)
.addReg(Cond[2].getReg())
.addImm(ARM64_AM::encodeLogicalImmediate(1ull << Cond[3].getImm(), 64));
break; break;
} }
} }

View File

@ -209,8 +209,10 @@ static inline bool isCondBranchOpcode(int Opc) {
case ARM64::CBZX: case ARM64::CBZX:
case ARM64::CBNZW: case ARM64::CBNZW:
case ARM64::CBNZX: case ARM64::CBNZX:
case ARM64::TBZ: case ARM64::TBZW:
case ARM64::TBNZ: case ARM64::TBZX:
case ARM64::TBNZW:
case ARM64::TBNZX:
return true; return true;
default: default:
return false; return false;

View File

@ -54,7 +54,7 @@ def SDT_ARM64Brcond : SDTypeProfile<0, 3,
[SDTCisVT<0, OtherVT>, SDTCisVT<1, i32>, [SDTCisVT<0, OtherVT>, SDTCisVT<1, i32>,
SDTCisVT<2, i32>]>; SDTCisVT<2, i32>]>;
def SDT_ARM64cbz : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisVT<1, OtherVT>]>; def SDT_ARM64cbz : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisVT<1, OtherVT>]>;
def SDT_ARM64tbz : SDTypeProfile<0, 3, [SDTCisVT<0, i64>, SDTCisVT<1, i64>, def SDT_ARM64tbz : SDTypeProfile<0, 3, [SDTCisInt<0>, SDTCisInt<1>,
SDTCisVT<2, OtherVT>]>; SDTCisVT<2, OtherVT>]>;
@ -1045,8 +1045,8 @@ defm CBNZ : CmpBranch<1, "cbnz", ARM64cbnz>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Test-bit-and-branch instructions. // Test-bit-and-branch instructions.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
def TBZ : TestBranch<0, "tbz", ARM64tbz>; defm TBZ : TestBranch<0, "tbz", ARM64tbz>;
def TBNZ : TestBranch<1, "tbnz", ARM64tbnz>; defm TBNZ : TestBranch<1, "tbnz", ARM64tbnz>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Unconditional branch (immediate) instructions. // Unconditional branch (immediate) instructions.

View File

@ -563,6 +563,15 @@ public:
int64_t Val = MCE->getValue(); int64_t Val = MCE->getValue();
return (Val >= 0 && Val < 65536); return (Val >= 0 && Val < 65536);
} }
bool isImm32_63() const {
if (!isImm())
return false;
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(getImm());
if (!MCE)
return false;
int64_t Val = MCE->getValue();
return (Val >= 32 && Val < 64);
}
bool isLogicalImm32() const { bool isLogicalImm32() const {
if (!isImm()) if (!isImm())
return false; return false;
@ -812,6 +821,10 @@ public:
return Kind == k_Register && Reg.isVector && return Kind == k_Register && Reg.isVector &&
ARM64MCRegisterClasses[ARM64::FPR128_loRegClassID].contains(Reg.RegNum); ARM64MCRegisterClasses[ARM64::FPR128_loRegClassID].contains(Reg.RegNum);
} }
bool isGPR32as64() const {
return Kind == k_Register && !Reg.isVector &&
ARM64MCRegisterClasses[ARM64::GPR64RegClassID].contains(Reg.RegNum);
}
/// Is this a vector list with the type implicit (presumably attached to the /// Is this a vector list with the type implicit (presumably attached to the
/// instruction itself)? /// instruction itself)?
@ -1188,6 +1201,17 @@ public:
Inst.addOperand(MCOperand::CreateReg(getReg())); Inst.addOperand(MCOperand::CreateReg(getReg()));
} }
void addGPR32as64Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
assert(ARM64MCRegisterClasses[ARM64::GPR64RegClassID].contains(getReg()));
const MCRegisterInfo *RI = Ctx.getRegisterInfo();
uint32_t Reg = RI->getRegClass(ARM64::GPR32RegClassID).getRegister(
RI->getEncodingValue(getReg()));
Inst.addOperand(MCOperand::CreateReg(Reg));
}
void addVectorReg64Operands(MCInst &Inst, unsigned N) const { void addVectorReg64Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!"); assert(N == 1 && "Invalid number of operands!");
assert(ARM64MCRegisterClasses[ARM64::FPR128RegClassID].contains(getReg())); assert(ARM64MCRegisterClasses[ARM64::FPR128RegClassID].contains(getReg()));
@ -1408,6 +1432,13 @@ public:
Inst.addOperand(MCOperand::CreateImm(MCE->getValue())); Inst.addOperand(MCOperand::CreateImm(MCE->getValue()));
} }
void addImm32_63Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(getImm());
assert(MCE && "Invalid constant immediate operand!");
Inst.addOperand(MCOperand::CreateImm(MCE->getValue()));
}
void addLogicalImm32Operands(MCInst &Inst, unsigned N) const { void addLogicalImm32Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!"); assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(getImm()); const MCConstantExpr *MCE = dyn_cast<MCConstantExpr>(getImm());
@ -3952,27 +3983,6 @@ bool ARM64AsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
} }
} }
} }
// FIXME: Horrible hack for tbz and tbnz with Wn register operand.
// InstAlias can't quite handle this since the reg classes aren't
// subclasses.
if (NumOperands == 4 && (Tok == "tbz" || Tok == "tbnz")) {
ARM64Operand *Op = static_cast<ARM64Operand *>(Operands[2]);
if (Op->isImm()) {
if (const MCConstantExpr *OpCE = dyn_cast<MCConstantExpr>(Op->getImm())) {
if (OpCE->getValue() < 32) {
// The source register can be Wn here, but the matcher expects a
// GPR64. Twiddle it here if necessary.
ARM64Operand *Op = static_cast<ARM64Operand *>(Operands[1]);
if (Op->isReg()) {
unsigned Reg = getXRegFromWReg(Op->getReg());
Operands[1] = ARM64Operand::CreateReg(
Reg, false, Op->getStartLoc(), Op->getEndLoc(), getContext());
delete Op;
}
}
}
}
}
// FIXME: Horrible hack for sxtw and uxtw with Wn src and Xd dst operands. // FIXME: Horrible hack for sxtw and uxtw with Wn src and Xd dst operands.
// InstAlias can't quite handle this since the reg classes aren't // InstAlias can't quite handle this since the reg classes aren't
// subclasses. // subclasses.

View File

@ -1512,7 +1512,10 @@ static DecodeStatus DecodeTestAndBranch(llvm::MCInst &Inst, uint32_t insn,
if (dst & (1 << (14 - 1))) if (dst & (1 << (14 - 1)))
dst |= ~((1LL << 14) - 1); dst |= ~((1LL << 14) - 1);
DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder); if (fieldFromInstruction(insn, 31, 1) == 0)
DecodeGPR32RegisterClass(Inst, Rt, Addr, Decoder);
else
DecodeGPR64RegisterClass(Inst, Rt, Addr, Decoder);
Inst.addOperand(MCOperand::CreateImm(bit)); Inst.addOperand(MCOperand::CreateImm(bit));
if (!Dis->tryAddingSymbolicOperand(Inst, dst << 2, Addr, true, 0, 4)) if (!Dis->tryAddingSymbolicOperand(Inst, dst << 2, Addr, true, 0, 4))
Inst.addOperand(MCOperand::CreateImm(dst)); Inst.addOperand(MCOperand::CreateImm(dst));

View File

@ -63,18 +63,6 @@ void ARM64InstPrinter::printInst(const MCInst *MI, raw_ostream &O,
return; return;
} }
// TBZ/TBNZ should print the register operand as a Wreg if the bit
// number is < 32.
if ((Opcode == ARM64::TBNZ || Opcode == ARM64::TBZ) &&
MI->getOperand(1).getImm() < 32) {
MCInst newMI = *MI;
unsigned Reg = MI->getOperand(0).getReg();
newMI.getOperand(0).setReg(getWRegFromXReg(Reg));
printInstruction(&newMI, O);
printAnnotation(O, Annot);
return;
}
// SBFM/UBFM should print to a nicer aliased form if possible. // SBFM/UBFM should print to a nicer aliased form if possible.
if (Opcode == ARM64::SBFMXri || Opcode == ARM64::SBFMWri || if (Opcode == ARM64::SBFMXri || Opcode == ARM64::SBFMWri ||
Opcode == ARM64::UBFMXri || Opcode == ARM64::UBFMWri) { Opcode == ARM64::UBFMXri || Opcode == ARM64::UBFMWri) {

View File

@ -322,7 +322,7 @@ done:
} }
; CHECK: tbnz_32 ; CHECK: tbnz_32
; CHECK: {{ands.*xzr,|tst}} x2, #0x80 ; CHECK: {{ands.*xzr,|tst}} w2, #0x80
; CHECK-NEXT: csel w0, w1, w0, ne ; CHECK-NEXT: csel w0, w1, w0, ne
; CHECK-NEXT: ret ; CHECK-NEXT: ret
define i32 @tbnz_32(i32 %x, i32 %y, i32 %c) nounwind ssp { define i32 @tbnz_32(i32 %x, i32 %y, i32 %c) nounwind ssp {
@ -358,7 +358,7 @@ done:
} }
; CHECK: tbz_32 ; CHECK: tbz_32
; CHECK: {{ands.*xzr,|tst}} x2, #0x80 ; CHECK: {{ands.*xzr,|tst}} w2, #0x80
; CHECK-NEXT: csel w0, w1, w0, eq ; CHECK-NEXT: csel w0, w1, w0, eq
; CHECK-NEXT: ret ; CHECK-NEXT: ret
define i32 @tbz_32(i32 %x, i32 %y, i32 %c) nounwind ssp { define i32 @tbz_32(i32 %x, i32 %y, i32 %c) nounwind ssp {