From 7eaa42190fafffaaeaf24f2c30677cf7eb332ea0 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 13 Jun 2019 10:01:52 +0000 Subject: [PATCH] [ARM] Refactor handling of IT mask operands. During assembly, the mask operand to an IT instruction (storing the sequence of T/E for 'Then' and 'Else') is parsed out of the mnemonic into a representation that encodes 'Then' and 'Else' in the same way regardless of the condition code. At some point during encoding it has to be converted into the instruction encoding used in the architecture, in which the mask encodes a sequence of replacement low-order bits for the condition code, so that which bit value means 'then' and which 'else' depends on whether the original condition code had its low bit set. Previously, that transformation was done by processInstruction(), half way through assembly. So an MCOperand storing an IT mask would sometimes store it in one format, and sometimes in the other, depending on where in the assembly pipeline you were. You can see this in diagnostics from `llvm-mc -debug -triple=thumbv8a -show-inst`, for example: if you give it an instruction such as `itete eq`, you'd see an `` in a diagnostic become `` in the final output. Having the same data structure store values with time-dependent semantics is confusing already, and it will get more confusing when we introduce the MVE VPT instruction which reuses the Then/Else bitmask idea in a different context. So I'm refactoring: now, all `ARMOperand` and `MCOperand` representations of an IT mask work exactly the same way, namely, 0 means 'Then' and 1 means 'Else', regardless of what original predicate is being referred to. The architectural encoding of IT that depends on the original condition is now constructed at the point when we turn the `MCOperand` into the final instruction bit pattern, and decoded similarly in the disassembler. The previous condition-independent parse-time format used 0 for Else and 1 for Then. I've taken the opportunity to flip the sense of it while I'm changing all of this anyway, because it seems to me more natural to use 0 for 'leave the starting condition unchanged' and 1 for 'invert it', as if those bits were an XOR mask. Reviewers: ostannard Subscribers: javed.absar, kristof.beyls, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63219 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@363244 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMInstrThumb2.td | 1 + lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 83 +++++++++---------- .../ARM/Disassembler/ARMDisassembler.cpp | 25 ++++-- .../ARM/MCTargetDesc/ARMInstPrinter.cpp | 9 +- .../ARM/MCTargetDesc/ARMMCCodeEmitter.cpp | 30 +++++++ lib/Target/ARM/Thumb2ITBlockPass.cpp | 4 +- 6 files changed, 88 insertions(+), 64 deletions(-) diff --git a/lib/Target/ARM/ARMInstrThumb2.td b/lib/Target/ARM/ARMInstrThumb2.td index 1f7d28a4c8b..61e157ba986 100644 --- a/lib/Target/ARM/ARMInstrThumb2.td +++ b/lib/Target/ARM/ARMInstrThumb2.td @@ -25,6 +25,7 @@ def it_mask_asmoperand : AsmOperandClass { let Name = "ITMask"; } def it_mask : Operand { let PrintMethod = "printThumbITMask"; let ParserMatchClass = it_mask_asmoperand; + let EncoderMethod = "getITMaskOpValue"; } // t2_shift_imm: An integer that encodes a shift amount and the type of shift diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index f8dc24a4267..1e9341182f3 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -90,6 +90,16 @@ static cl::opt AddBuildAttributes("arm-add-build-attributes", enum VectorLaneTy { NoLanes, AllLanes, IndexedLane }; +static inline unsigned extractITMaskBit(unsigned Mask, unsigned Position) { + // Position==0 means we're not in an IT block at all. Position==1 + // means we want the first state bit, which is always 0 (Then). + // Position==2 means we want the second state bit, stored at bit 3 + // of Mask, and so on downwards. So (5 - Position) will shift the + // right bit down to bit 0, including the always-0 bit at bit 4 for + // the mandatory initial Then. + return (Mask >> (5 - Position) & 1); +} + class UnwindContext { using Locs = SmallVector; @@ -226,11 +236,10 @@ class ARMAsmParser : public MCTargetAsmParser { } // Emit the IT instruction - unsigned Mask = getITMaskEncoding(); MCInst ITInst; ITInst.setOpcode(ARM::t2IT); ITInst.addOperand(MCOperand::createImm(ITState.Cond)); - ITInst.addOperand(MCOperand::createImm(Mask)); + ITInst.addOperand(MCOperand::createImm(ITState.Mask)); Out.EmitInstruction(ITInst, getSTI()); // Emit the conditonal instructions @@ -288,27 +297,10 @@ class ARMAsmParser : public MCTargetAsmParser { return MRI->getSubReg(QReg, ARM::dsub_0); } - // Get the encoding of the IT mask, as it will appear in an IT instruction. - unsigned getITMaskEncoding() { - assert(inITBlock()); - unsigned Mask = ITState.Mask; - unsigned TZ = countTrailingZeros(Mask); - if ((ITState.Cond & 1) == 0) { - assert(Mask && TZ <= 3 && "illegal IT mask value!"); - Mask ^= (0xE << TZ) & 0xF; - } - return Mask; - } - // Get the condition code corresponding to the current IT block slot. ARMCC::CondCodes currentITCond() { - unsigned MaskBit; - if (ITState.CurPosition == 1) - MaskBit = 1; - else - MaskBit = (ITState.Mask >> (5 - ITState.CurPosition)) & 1; - - return MaskBit ? ITState.Cond : ARMCC::getOppositeCondition(ITState.Cond); + unsigned MaskBit = extractITMaskBit(ITState.Mask, ITState.CurPosition); + return MaskBit ? ARMCC::getOppositeCondition(ITState.Cond) : ITState.Cond; } // Invert the condition of the current IT block slot without changing any @@ -338,7 +330,7 @@ class ARMAsmParser : public MCTargetAsmParser { // Keep any existing condition bits. NewMask |= ITState.Mask & (0xE << TZ); // Insert the new condition bit. - NewMask |= (Cond == ITState.Cond) << TZ; + NewMask |= (Cond != ITState.Cond) << TZ; // Move the trailing 1 down one bit. NewMask |= 1 << (TZ - 1); ITState.Mask = NewMask; @@ -353,9 +345,10 @@ class ARMAsmParser : public MCTargetAsmParser { ITState.IsExplicit = false; } - // Create a new explicit IT block with the given condition and mask. The mask - // should be in the parsed format, with a 1 implying 't', regardless of the - // low bit of the condition. + // Create a new explicit IT block with the given condition and mask. + // The mask should be in the format used in ARMOperand and + // MCOperand, with a 1 implying 'e', regardless of the low bit of + // the condition. void startExplicitITBlock(ARMCC::CondCodes Cond, unsigned Mask) { assert(!inITBlock()); ITState.Cond = Cond; @@ -3355,10 +3348,10 @@ void ARMOperand::print(raw_ostream &OS) const { break; case k_ITCondMask: { static const char *const MaskStr[] = { - "(invalid)", "(teee)", "(tee)", "(teet)", - "(te)", "(tete)", "(tet)", "(tett)", - "(t)", "(ttee)", "(tte)", "(ttet)", - "(tt)", "(ttte)", "(ttt)", "(tttt)" + "(invalid)", "(tttt)", "(ttt)", "(ttte)", + "(tt)", "(ttet)", "(tte)", "(ttee)", + "(t)", "(tett)", "(tet)", "(tete)", + "(te)", "(teet)", "(tee)", "(teee)", }; assert((ITMask.Mask & 0xf) == ITMask.Mask); OS << ""; @@ -6272,11 +6265,14 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, Operands.push_back(ARMOperand::CreateToken(Mnemonic, NameLoc)); - // Handle the IT instruction ITMask. Convert it to a bitmask. This - // is the mask as it will be for the IT encoding if the conditional - // encoding has a '1' as it's bit0 (i.e. 't' ==> '1'). In the case - // where the conditional bit0 is zero, the instruction post-processing - // will adjust the mask accordingly. + // Handle the mask for IT instructions. In ARMOperand and MCOperand, + // this is stored in a format independent of the condition code: the + // lowest set bit indicates the end of the encoding, and above that, + // a 1 bit indicates 'else', and an 0 indicates 'then'. E.g. + // IT -> 1000 + // ITx -> x100 (ITT -> 0100, ITE -> 1100) + // ITxy -> xy10 (e.g. ITET -> 1010) + // ITxyz -> xyz1 (e.g. ITEET -> 1101) if (Mnemonic == "it") { SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + 2); if (ITMask.size() > 3) { @@ -6289,7 +6285,7 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, return Error(Loc, "illegal IT block condition mask '" + ITMask + "'"); } Mask >>= 1; - if (ITMask[i - 1] == 't') + if (ITMask[i - 1] == 'e') Mask |= 8; } Operands.push_back(ARMOperand::CreateITMask(Mask, Loc)); @@ -6681,11 +6677,10 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst, unsigned Cond = Inst.getOperand(0).getImm(); unsigned Mask = Inst.getOperand(1).getImm(); - // Mask hasn't been modified to the IT instruction encoding yet so - // conditions only allowing a 't' are a block of 1s starting at bit 3 - // followed by all 0s. Easiest way is to just list the 4 possibilities. - if (Cond == ARMCC::AL && Mask != 8 && Mask != 12 && Mask != 14 && - Mask != 15) + // Conditions only allowing a 't' are those with no set bit except + // the lowest-order one that indicates the end of the sequence. In + // other words, powers of 2. + if (Cond == ARMCC::AL && countPopulation(Mask) != 1) return Error(Loc, "unpredictable IT predicate sequence"); break; } @@ -9260,15 +9255,11 @@ bool ARMAsmParser::processInstruction(MCInst &Inst, } case ARM::ITasm: case ARM::t2IT: { - MCOperand &MO = Inst.getOperand(1); - unsigned Mask = MO.getImm(); - ARMCC::CondCodes Cond = ARMCC::CondCodes(Inst.getOperand(0).getImm()); - // Set up the IT block state according to the IT instruction we just // matched. assert(!inITBlock() && "nested IT blocks?!"); - startExplicitITBlock(Cond, Mask); - MO.setImm(getITMaskEncoding()); + startExplicitITBlock(ARMCC::CondCodes(Inst.getOperand(0).getImm()), + Inst.getOperand(1).getImm()); break; } case ARM::t2LSLrr: diff --git a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index ef71cacfa92..bc9d5a0913f 100644 --- a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -63,22 +63,19 @@ namespace { return ITStates.size() == 1; } - // Called when decoding an IT instruction. Sets the IT state for the following - // instructions that for the IT block. Firstcond and Mask correspond to the - // fields in the IT instruction encoding. + // Called when decoding an IT instruction. Sets the IT state for + // the following instructions that for the IT block. Firstcond + // corresponds to the field in the IT instruction encoding; Mask + // is in the MCOperand format in which 1 means 'else' and 0 'then'. void setITState(char Firstcond, char Mask) { // (3 - the number of trailing zeros) is the number of then / else. - unsigned CondBit0 = Firstcond & 1; unsigned NumTZ = countTrailingZeros(Mask); unsigned char CCBits = static_cast(Firstcond & 0xf); assert(NumTZ <= 3 && "Invalid IT mask!"); // push condition codes onto the stack the correct order for the pops for (unsigned Pos = NumTZ+1; Pos <= 3; ++Pos) { - bool T = ((Mask >> Pos) & 1) == CondBit0; - if (T) - ITStates.push_back(CCBits); - else - ITStates.push_back(CCBits ^ 1); + unsigned Else = (Mask >> Pos) & 1; + ITStates.push_back(CCBits ^ Else); } ITStates.push_back(CCBits); } @@ -5176,6 +5173,16 @@ static DecodeStatus DecodeIT(MCInst &Inst, unsigned Insn, if (mask == 0x0) return MCDisassembler::Fail; + // IT masks are encoded as a sequence of replacement low-order bits + // for the condition code. So if the low bit of the starting + // condition code is 1, then we have to flip all the bits above the + // terminating bit (which is the lowest 1 bit). + if (pred & 1) { + unsigned LowBit = mask & -mask; + unsigned BitsAboveLowBit = 0xF & (-LowBit << 1); + mask ^= BitsAboveLowBit; + } + Inst.addOperand(MCOperand::createImm(pred)); Inst.addOperand(MCOperand::createImm(mask)); return S; diff --git a/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp b/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp index 271959c38de..52576840a5f 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp @@ -1039,16 +1039,13 @@ void ARMInstPrinter::printThumbITMask(const MCInst *MI, unsigned OpNum, raw_ostream &O) { // (3 - the number of trailing zeros) is the number of then / else. unsigned Mask = MI->getOperand(OpNum).getImm(); - unsigned Firstcond = MI->getOperand(OpNum - 1).getImm(); - unsigned CondBit0 = Firstcond & 1; unsigned NumTZ = countTrailingZeros(Mask); assert(NumTZ <= 3 && "Invalid IT mask!"); for (unsigned Pos = 3, e = NumTZ; Pos > e; --Pos) { - bool T = ((Mask >> Pos) & 1) == CondBit0; - if (T) - O << 't'; - else + if ((Mask >> Pos) & 1) O << 'e'; + else + O << 't'; } } diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp index e84fe35ee20..13dca6dc1de 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -162,6 +162,9 @@ public: SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + uint32_t getITMaskOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; /// getAddrModeImm12OpValue - Return encoding info for 'reg +/- imm12' /// operand. @@ -829,6 +832,33 @@ getT2AdrLabelOpValue(const MCInst &MI, unsigned OpIdx, return Val; } +/// getITMaskOpValue - Return the architectural encoding of an IT +/// predication mask, given the MCOperand format. +uint32_t ARMMCCodeEmitter:: +getITMaskOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand MaskMO = MI.getOperand(OpIdx); + assert(MaskMO.isImm() && "Unexpected operand type!"); + + unsigned Mask = MaskMO.getImm(); + + // IT masks are encoded as a sequence of replacement low-order bits + // for the condition code. So if the low bit of the starting + // condition code is 1, then we have to flip all the bits above the + // terminating bit (which is the lowest 1 bit). + assert(OpIdx > 0 && "IT mask appears first!"); + const MCOperand CondMO = MI.getOperand(OpIdx-1); + assert(CondMO.isImm() && "Unexpected operand type!"); + if (CondMO.getImm() & 1) { + unsigned LowBit = Mask & -Mask; + unsigned BitsAboveLowBit = 0xF & (-LowBit << 1); + Mask ^= BitsAboveLowBit; + } + + return Mask; +} + /// getThumbAdrLabelOpValue - Return encoding info for 8-bit immediate ADR label /// target. uint32_t ARMMCCodeEmitter:: diff --git a/lib/Target/ARM/Thumb2ITBlockPass.cpp b/lib/Target/ARM/Thumb2ITBlockPass.cpp index 70c07f50b03..715ad743ab5 100644 --- a/lib/Target/ARM/Thumb2ITBlockPass.cpp +++ b/lib/Target/ARM/Thumb2ITBlockPass.cpp @@ -245,7 +245,7 @@ bool Thumb2ITBlockPass::InsertITInstructions(MachineBasicBlock &MBB) { unsigned NPredReg = 0; ARMCC::CondCodes NCC = getITInstrPredicate(*NMI, NPredReg); if (NCC == CC || NCC == OCC) { - Mask |= (NCC & 1) << Pos; + Mask |= ((NCC ^ CC) & 1) << Pos; // Add implicit use of ITSTATE. NMI->addOperand(MachineOperand::CreateReg(ARM::ITSTATE, false/*ifDef*/, true/*isImp*/, false/*isKill*/)); @@ -269,8 +269,6 @@ bool Thumb2ITBlockPass::InsertITInstructions(MachineBasicBlock &MBB) { // Finalize IT mask. Mask |= (1 << Pos); - // Tag along (firstcond[0] << 4) with the mask. - Mask |= (CC & 1) << 4; MIB.addImm(Mask); // Last instruction in IT block kills ITSTATE. -- 2.50.1