diff --git a/lib/Target/ARM64/ARM64BranchRelaxation.cpp b/lib/Target/ARM64/ARM64BranchRelaxation.cpp index cbe97edde27..73be3504790 100644 --- a/lib/Target/ARM64/ARM64BranchRelaxation.cpp +++ b/lib/Target/ARM64/ARM64BranchRelaxation.cpp @@ -275,8 +275,10 @@ static bool isConditionalBranch(unsigned Opc) { switch (Opc) { default: return false; - case ARM64::TBZ: - case ARM64::TBNZ: + case ARM64::TBZW: + case ARM64::TBNZW: + case ARM64::TBZX: + case ARM64::TBNZX: case ARM64::CBZW: case ARM64::CBNZW: case ARM64::CBZX: @@ -290,8 +292,10 @@ static MachineBasicBlock *getDestBlock(MachineInstr *MI) { switch (MI->getOpcode()) { default: assert(0 && "unexpected opcode!"); - case ARM64::TBZ: - case ARM64::TBNZ: + case ARM64::TBZW: + case ARM64::TBNZW: + case ARM64::TBZX: + case ARM64::TBNZX: return MI->getOperand(2).getMBB(); case ARM64::CBZW: case ARM64::CBNZW: @@ -306,8 +310,10 @@ static unsigned getOppositeConditionOpcode(unsigned Opc) { switch (Opc) { default: assert(0 && "unexpected opcode!"); - case ARM64::TBNZ: return ARM64::TBZ; - case ARM64::TBZ: return ARM64::TBNZ; + case ARM64::TBNZW: return ARM64::TBZW; + 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::CBNZX: return ARM64::CBZX; case ARM64::CBZW: return ARM64::CBNZW; @@ -320,8 +326,10 @@ static unsigned getBranchDisplacementBits(unsigned Opc) { switch (Opc) { default: assert(0 && "unexpected opcode!"); - case ARM64::TBNZ: - case ARM64::TBZ: + case ARM64::TBNZW: + case ARM64::TBZW: + case ARM64::TBNZX: + case ARM64::TBZX: return TBZDisplacementBits; case ARM64::CBNZW: case ARM64::CBZW: @@ -379,7 +387,8 @@ bool ARM64BranchRelaxation::fixupConditionalBranch(MachineInstr *MI) { << *BMI); BMI->getOperand(0).setMBB(DestBB); 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 : 1; MI->getOperand(OpNum).setMBB(NewDest); @@ -420,7 +429,8 @@ bool ARM64BranchRelaxation::fixupConditionalBranch(MachineInstr *MI) { MachineInstrBuilder MIB = BuildMI( MBB, DebugLoc(), TII->get(getOppositeConditionOpcode(MI->getOpcode()))) .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)); if (MI->getOpcode() == ARM64::Bcc) invertBccCondition(MIB); diff --git a/lib/Target/ARM64/ARM64InstrFormats.td b/lib/Target/ARM64/ARM64InstrFormats.td index a9bad10b31e..3f9104deaf1 100644 --- a/lib/Target/ARM64/ARM64InstrFormats.td +++ b/lib/Target/ARM64/ARM64InstrFormats.td @@ -173,6 +173,14 @@ def CondCode : AsmOperandClass { let DiagnosticType = "InvalidCondCode"; } +// A 32-bit register pasrsed as 64-bit +def GPR32as64Operand : AsmOperandClass { + let Name = "GPR32as64"; +} +def GPR32as64 : RegisterOperand { + let ParserMatchClass = GPR32as64Operand; +} + // 8-bit immediate for AdvSIMD where 64-bit values of the form: // aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh // are encoded as the eight bit value 'abcdefgh'. @@ -1037,10 +1045,37 @@ def am_tbrcond : Operand { let ParserMatchClass = BranchTarget14Operand; } -class TestBranch - : I<(outs), (ins GPR64:$Rt, imm0_63:$bit_off, am_tbrcond:$target), +// AsmOperand classes to emit (or not) special diagnostics +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 : Operand, ImmLeaf { + let ParserMatchClass = matcher; +} + +def tbz_imm0_31_diag : tbz_imm0_31; +def tbz_imm0_31_nodiag : tbz_imm0_31; + +def tbz_imm32_63 : Operand, ImmLeaf 31) && (((uint32_t)Imm) < 64); +}]> { + let ParserMatchClass = TBZImm32_63Operand; +} + +class BaseTestBranch + : I<(outs), (ins regtype:$Rt, immtype:$bit_off, am_tbrcond:$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]> { let isBranch = 1; let isTerminator = 1; @@ -1049,7 +1084,6 @@ class TestBranch bits<6> bit_off; bits<14> target; - let Inst{31} = bit_off{5}; let Inst{30-25} = 0b011011; let Inst{24} = op; let Inst{23-19} = bit_off{4-0}; @@ -1059,6 +1093,24 @@ class TestBranch let DecoderMethod = "DecodeTestAndBranch"; } +multiclass TestBranch { + def W : BaseTestBranch { + let Inst{31} = 0; + } + + def X : BaseTestBranch { + let Inst{31} = 1; + } + + // Alias X-reg with 0-31 imm to W-Reg. + def : InstAlias(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(NAME#"W") (EXTRACT_SUBREG GPR64:$Rn, sub_32), + tbz_imm0_31_diag:$imm, bb:$target)>; +} + //--- // Unconditional branch (immediate) instructions. //--- diff --git a/lib/Target/ARM64/ARM64InstrInfo.cpp b/lib/Target/ARM64/ARM64InstrInfo.cpp index e4112655f8d..97194b1d479 100644 --- a/lib/Target/ARM64/ARM64InstrInfo.cpp +++ b/lib/Target/ARM64/ARM64InstrInfo.cpp @@ -70,8 +70,10 @@ static void parseCondBranch(MachineInstr *LastInst, MachineBasicBlock *&Target, Cond.push_back(MachineOperand::CreateImm(LastInst->getOpcode())); Cond.push_back(LastInst->getOperand(0)); break; - case ARM64::TBZ: - case ARM64::TBNZ: + case ARM64::TBZW: + case ARM64::TBZX: + case ARM64::TBNZW: + case ARM64::TBNZX: Target = LastInst->getOperand(2).getMBB(); Cond.push_back(MachineOperand::CreateImm(-1)); Cond.push_back(MachineOperand::CreateImm(LastInst->getOpcode())); @@ -196,11 +198,17 @@ bool ARM64InstrInfo::ReverseBranchCondition( case ARM64::CBNZX: Cond[1].setImm(ARM64::CBZX); break; - case ARM64::TBZ: - Cond[1].setImm(ARM64::TBNZ); + case ARM64::TBZW: + Cond[1].setImm(ARM64::TBNZW); break; - case ARM64::TBNZ: - Cond[1].setImm(ARM64::TBZ); + case ARM64::TBNZW: + Cond[1].setImm(ARM64::TBZW); + break; + case ARM64::TBZX: + Cond[1].setImm(ARM64::TBNZX); + break; + case ARM64::TBNZX: + Cond[1].setImm(ARM64::TBZX); break; } } @@ -453,17 +461,24 @@ void ARM64InstrInfo::insertSelect(MachineBasicBlock &MBB, switch (Cond[1].getImm()) { default: llvm_unreachable("Unknown branch opcode in Cond"); - case ARM64::TBZ: + case ARM64::TBZW: + case ARM64::TBZX: CC = ARM64CC::EQ; break; - case ARM64::TBNZ: + case ARM64::TBNZW: + case ARM64::TBNZX: CC = ARM64CC::NE; break; } // cmp reg, #foo is actually ands xzr, reg, #1<, SDTCisVT<1, i32>, SDTCisVT<2, i32>]>; 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>]>; @@ -1045,8 +1045,8 @@ defm CBNZ : CmpBranch<1, "cbnz", ARM64cbnz>; //===----------------------------------------------------------------------===// // Test-bit-and-branch instructions. //===----------------------------------------------------------------------===// -def TBZ : TestBranch<0, "tbz", ARM64tbz>; -def TBNZ : TestBranch<1, "tbnz", ARM64tbnz>; +defm TBZ : TestBranch<0, "tbz", ARM64tbz>; +defm TBNZ : TestBranch<1, "tbnz", ARM64tbnz>; //===----------------------------------------------------------------------===// // Unconditional branch (immediate) instructions. diff --git a/lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp b/lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp index ad9c7562adb..cc301f60ede 100644 --- a/lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp +++ b/lib/Target/ARM64/AsmParser/ARM64AsmParser.cpp @@ -563,6 +563,15 @@ public: int64_t Val = MCE->getValue(); return (Val >= 0 && Val < 65536); } + bool isImm32_63() const { + if (!isImm()) + return false; + const MCConstantExpr *MCE = dyn_cast(getImm()); + if (!MCE) + return false; + int64_t Val = MCE->getValue(); + return (Val >= 32 && Val < 64); + } bool isLogicalImm32() const { if (!isImm()) return false; @@ -812,6 +821,10 @@ public: return Kind == k_Register && Reg.isVector && 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 /// instruction itself)? @@ -1188,6 +1201,17 @@ public: 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 { assert(N == 1 && "Invalid number of operands!"); assert(ARM64MCRegisterClasses[ARM64::FPR128RegClassID].contains(getReg())); @@ -1408,6 +1432,13 @@ public: 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(getImm()); + assert(MCE && "Invalid constant immediate operand!"); + Inst.addOperand(MCOperand::CreateImm(MCE->getValue())); + } + void addLogicalImm32Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *MCE = dyn_cast(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(Operands[2]); - if (Op->isImm()) { - if (const MCConstantExpr *OpCE = dyn_cast(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(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. // InstAlias can't quite handle this since the reg classes aren't // subclasses. diff --git a/lib/Target/ARM64/Disassembler/ARM64Disassembler.cpp b/lib/Target/ARM64/Disassembler/ARM64Disassembler.cpp index d14e3a86f8f..92eabcf2b4e 100644 --- a/lib/Target/ARM64/Disassembler/ARM64Disassembler.cpp +++ b/lib/Target/ARM64/Disassembler/ARM64Disassembler.cpp @@ -1512,7 +1512,10 @@ static DecodeStatus DecodeTestAndBranch(llvm::MCInst &Inst, uint32_t insn, if (dst & (1 << (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)); if (!Dis->tryAddingSymbolicOperand(Inst, dst << 2, Addr, true, 0, 4)) Inst.addOperand(MCOperand::CreateImm(dst)); diff --git a/lib/Target/ARM64/InstPrinter/ARM64InstPrinter.cpp b/lib/Target/ARM64/InstPrinter/ARM64InstPrinter.cpp index ebdbd13596a..adfcb46ac40 100644 --- a/lib/Target/ARM64/InstPrinter/ARM64InstPrinter.cpp +++ b/lib/Target/ARM64/InstPrinter/ARM64InstPrinter.cpp @@ -63,18 +63,6 @@ void ARM64InstPrinter::printInst(const MCInst *MI, raw_ostream &O, 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. if (Opcode == ARM64::SBFMXri || Opcode == ARM64::SBFMWri || Opcode == ARM64::UBFMXri || Opcode == ARM64::UBFMWri) { diff --git a/test/CodeGen/ARM64/early-ifcvt.ll b/test/CodeGen/ARM64/early-ifcvt.ll index a5c1e26c619..17d783a488f 100644 --- a/test/CodeGen/ARM64/early-ifcvt.ll +++ b/test/CodeGen/ARM64/early-ifcvt.ll @@ -322,7 +322,7 @@ done: } ; CHECK: tbnz_32 -; CHECK: {{ands.*xzr,|tst}} x2, #0x80 +; CHECK: {{ands.*xzr,|tst}} w2, #0x80 ; CHECK-NEXT: csel w0, w1, w0, ne ; CHECK-NEXT: ret define i32 @tbnz_32(i32 %x, i32 %y, i32 %c) nounwind ssp { @@ -358,7 +358,7 @@ done: } ; CHECK: tbz_32 -; CHECK: {{ands.*xzr,|tst}} x2, #0x80 +; CHECK: {{ands.*xzr,|tst}} w2, #0x80 ; CHECK-NEXT: csel w0, w1, w0, eq ; CHECK-NEXT: ret define i32 @tbz_32(i32 %x, i32 %y, i32 %c) nounwind ssp {