Index: llvm/trunk/include/llvm/MC/MCParser/MCTargetAsmParser.h =================================================================== --- llvm/trunk/include/llvm/MC/MCParser/MCTargetAsmParser.h +++ llvm/trunk/include/llvm/MC/MCParser/MCTargetAsmParser.h @@ -217,6 +217,10 @@ } virtual void onLabelParsed(MCSymbol *Symbol) { } + + /// Ensure that all previously parsed instructions have been emitted to the + /// output streamer, if the target does not emit them immediately. + virtual void flushPendingInstructions(MCStreamer &Out) { } }; } // End llvm namespace Index: llvm/trunk/lib/MC/MCParser/AsmParser.cpp =================================================================== --- llvm/trunk/lib/MC/MCParser/AsmParser.cpp +++ llvm/trunk/lib/MC/MCParser/AsmParser.cpp @@ -737,6 +737,8 @@ eatToEndOfStatement(); } + getTargetParser().flushPendingInstructions(getStreamer()); + if (TheCondState.TheCond != StartingCondState.TheCond || TheCondState.Ignore != StartingCondState.Ignore) return TokError("unmatched .ifs or .elses"); @@ -1590,6 +1592,8 @@ // manner, or at least have a default behavior that's shared between // all targets and platforms. + getTargetParser().flushPendingInstructions(getStreamer()); + // First query the target-specific parser. It will return 'true' if it // isn't interested in this directive. if (!getTargetParser().ParseDirective(ID)) Index: llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td =================================================================== --- llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td +++ llvm/trunk/lib/Target/ARM/ARMInstrThumb2.td @@ -3456,6 +3456,7 @@ // Branch and Exchange Jazelle -- for disassembly only // Rm = Inst{19-16} +let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in def t2BXJ : T2I<(outs), (ins GPRnopc:$func), NoItinerary, "bxj", "\t$func", []>, Sched<[WriteBr]>, Requires<[IsThumb2, IsNotMClass]> { bits<4> func; @@ -3564,6 +3565,7 @@ // Secure Monitor Call is a system instruction. // Option = Inst{19-16} +let isCall = 1, Uses = [SP] in def t2SMC : T2I<(outs), (ins imm0_15:$opt), NoItinerary, "smc", "\t$opt", []>, Requires<[IsThumb2, HasTrustZone]> { let Inst{31-27} = 0b11110; @@ -3620,6 +3622,7 @@ def : t2InstAlias<"srsia${p} $mode!", (t2SRSIA_UPD imm0_31:$mode, pred:$p)>; // Return From Exception is a system instruction. +let isReturn = 1, isBarrier = 1, isTerminator = 1, Defs = [PC] in class T2RFE op31_20, dag oops, dag iops, InstrItinClass itin, string opc, string asm, list pattern> : T2I, Index: llvm/trunk/lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- llvm/trunk/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ llvm/trunk/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -40,6 +40,7 @@ #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/ARMEHABI.h" #include "llvm/Support/COFF.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ELF.h" #include "llvm/Support/MathExtras.h" @@ -52,6 +53,21 @@ namespace { +enum class ImplicitItModeTy { Always, Never, ARMOnly, ThumbOnly }; + +static cl::opt ImplicitItMode( + "arm-implicit-it", cl::init(ImplicitItModeTy::ARMOnly), + cl::desc("Allow conditional instructions outdside of an IT block"), + cl::values(clEnumValN(ImplicitItModeTy::Always, "always", + "Accept in both ISAs, emit implicit ITs in Thumb"), + clEnumValN(ImplicitItModeTy::Never, "never", + "Warn in ARM, reject in Thumb"), + clEnumValN(ImplicitItModeTy::ARMOnly, "arm", + "Accept in ARM, reject in Thumb"), + clEnumValN(ImplicitItModeTy::ThumbOnly, "thumb", + "Warn in ARM, emit implicit ITs in Thumb"), + clEnumValEnd)); + class ARMOperand; enum VectorLaneTy { NoLanes, AllLanes, IndexedLane }; @@ -145,6 +161,16 @@ bool NextSymbolIsThumb; + bool useImplicitITThumb() const { + return ImplicitItMode == ImplicitItModeTy::Always || + ImplicitItMode == ImplicitItModeTy::ThumbOnly; + } + + bool useImplicitITARM() const { + return ImplicitItMode == ImplicitItModeTy::Always || + ImplicitItMode == ImplicitItModeTy::ARMOnly; + } + struct { ARMCC::CondCodes Cond; // Condition for IT block. unsigned Mask:4; // Condition mask for instructions. @@ -153,30 +179,166 @@ // '0' inverse of condition (else). // Count of instructions in IT block is // 4 - trailingzeroes(mask) - - bool FirstCond; // Explicit flag for when we're parsing the - // First instruction in the IT block. It's - // implied in the mask, so needs special - // handling. + // Note that this does not have the same encoding + // as in the IT instruction, which also depends + // on the low bit of the condition code. unsigned CurPosition; // Current position in parsing of IT - // block. In range [0,3]. Initialized - // according to count of instructions in block. - // ~0U if no active IT block. + // block. In range [0,4], with 0 being the IT + // instruction itself. Initialized according to + // count of instructions in block. ~0U if no + // active IT block. + + bool IsExplicit; // true - The IT instruction was present in the + // input, we should not modify it. + // false - The IT instruction was added + // implicitly, we can extend it if that + // would be legal. } ITState; + + llvm::SmallVector PendingConditionalInsts; + + void flushPendingInstructions(MCStreamer &Out) override { + if (!inImplicitITBlock()) { + assert(PendingConditionalInsts.size() == 0); + return; + } + + // Emit the IT instruction + unsigned Mask = getITMaskEncoding(); + MCInst ITInst; + ITInst.setOpcode(ARM::t2IT); + ITInst.addOperand(MCOperand::createImm(ITState.Cond)); + ITInst.addOperand(MCOperand::createImm(Mask)); + Out.EmitInstruction(ITInst, getSTI()); + + // Emit the conditonal instructions + assert(PendingConditionalInsts.size() <= 4); + for (MCInst Inst : PendingConditionalInsts) { + Out.EmitInstruction(Inst, getSTI()); + } + PendingConditionalInsts.clear(); + + // Clear the IT state + ITState.Mask = 0; + ITState.CurPosition = ~0U; + } + bool inITBlock() { return ITState.CurPosition != ~0U; } + bool inExplicitITBlock() { return inITBlock() && ITState.IsExplicit; } + bool inImplicitITBlock() { return inITBlock() && !ITState.IsExplicit; } bool lastInITBlock() { return ITState.CurPosition == 4 - countTrailingZeros(ITState.Mask); } void forwardITPosition() { if (!inITBlock()) return; // Move to the next instruction in the IT block, if there is one. If not, - // mark the block as done. + // mark the block as done, except for implicit IT blocks, which we leave + // open until we find an instruction that can't be added to it. unsigned TZ = countTrailingZeros(ITState.Mask); - if (++ITState.CurPosition == 5 - TZ) + if (++ITState.CurPosition == 5 - TZ && ITState.IsExplicit) ITState.CurPosition = ~0U; // Done with the IT block after this. } + // Rewind the state of the current IT block, removing the last slot from it. + void rewindImplicitITPosition() { + assert(inImplicitITBlock()); + assert(ITState.CurPosition > 1); + ITState.CurPosition--; + unsigned TZ = countTrailingZeros(ITState.Mask); + unsigned NewMask = 0; + NewMask |= ITState.Mask & (0xC << TZ); + NewMask |= 0x2 << TZ; + ITState.Mask = NewMask; + } + + // Rewind the state of the current IT block, removing the last slot from it. + // If we were at the first slot, this closes the IT block. + void discardImplicitITBlock() { + assert(inImplicitITBlock()); + assert(ITState.CurPosition == 1); + ITState.CurPosition = ~0U; + return; + } + + // 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); + } + + // Invert the condition of the current IT block slot without changing any + // other slots in the same block. + void invertCurrentITCondition() { + if (ITState.CurPosition == 1) { + ITState.Cond = ARMCC::getOppositeCondition(ITState.Cond); + } else { + ITState.Mask ^= 1 << (5 - ITState.CurPosition); + } + } + + // Returns true if the current IT block is full (all 4 slots used). + bool isITBlockFull() { + return inITBlock() && (ITState.Mask & 1); + } + + // Extend the current implicit IT block to have one more slot with the given + // condition code. + void extendImplicitITBlock(ARMCC::CondCodes Cond) { + assert(inImplicitITBlock()); + assert(!isITBlockFull()); + assert(Cond == ITState.Cond || + Cond == ARMCC::getOppositeCondition(ITState.Cond)); + unsigned TZ = countTrailingZeros(ITState.Mask); + unsigned NewMask = 0; + // Keep any existing condition bits. + NewMask |= ITState.Mask & (0xE << TZ); + // Insert the new condition bit. + NewMask |= (Cond == ITState.Cond) << TZ; + // Move the trailing 1 down one bit. + NewMask |= 1 << (TZ - 1); + ITState.Mask = NewMask; + } + + // Create a new implicit IT block with a dummy condition code. + void startImplicitITBlock() { + assert(!inITBlock()); + ITState.Cond = ARMCC::AL; + ITState.Mask = 8; + ITState.CurPosition = 1; + ITState.IsExplicit = false; + return; + } + + // 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. + void startExplicitITBlock(ARMCC::CondCodes Cond, unsigned Mask) { + assert(!inITBlock()); + ITState.Cond = Cond; + ITState.Mask = Mask; + ITState.CurPosition = 0; + ITState.IsExplicit = true; + return; + } + void Note(SMLoc L, const Twine &Msg, ArrayRef Ranges = None) { return getParser().Note(L, Msg, Ranges); } @@ -355,6 +517,7 @@ bool processInstruction(MCInst &Inst, const OperandVector &Ops, MCStreamer &Out); bool shouldOmitCCOutOperand(StringRef Mnemonic, OperandVector &Operands); bool shouldOmitPredicateOperand(StringRef Mnemonic, OperandVector &Operands); + bool isITBlockTerminator(MCInst &Inst) const; public: enum ARMMatchResultTy { @@ -399,6 +562,9 @@ OperandVector &Operands, MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) override; + unsigned MatchInstruction(OperandVector &Operands, MCInst &Inst, + uint64_t &ErrorInfo, bool MatchingInlineAsm, + bool &EmitInITBlock, MCStreamer &Out); void onLabelParsed(MCSymbol *Symbol) override; }; } // end anonymous namespace @@ -6188,18 +6354,11 @@ // NOTE: BKPT and HLT instructions have the interesting property of being // allowed in IT blocks, but not being predicable. They just always execute. if (inITBlock() && !instIsBreakpoint(Inst)) { - unsigned Bit = 1; - if (ITState.FirstCond) - ITState.FirstCond = false; - else - Bit = (ITState.Mask >> (5 - ITState.CurPosition)) & 1; // The instruction must be predicable. if (!MCID.isPredicable()) return Error(Loc, "instructions in IT block must be predicable"); unsigned Cond = Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm(); - unsigned ITCond = Bit ? ITState.Cond : - ARMCC::getOppositeCondition(ITState.Cond); - if (Cond != ITCond) { + if (Cond != currentITCond()) { // Find the condition code Operand to get its SMLoc information. SMLoc CondLoc; for (unsigned I = 1; I < Operands.size(); ++I) @@ -6208,14 +6367,19 @@ return Error(CondLoc, "incorrect condition in IT block; got '" + StringRef(ARMCondCodeToString(ARMCC::CondCodes(Cond))) + "', but expected '" + - ARMCondCodeToString(ARMCC::CondCodes(ITCond)) + "'"); + ARMCondCodeToString(ARMCC::CondCodes(currentITCond())) + "'"); } // Check for non-'al' condition codes outside of the IT block. } else if (isThumbTwo() && MCID.isPredicable() && Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() != ARMCC::AL && Inst.getOpcode() != ARM::tBcc && - Inst.getOpcode() != ARM::t2Bcc) + Inst.getOpcode() != ARM::t2Bcc) { return Error(Loc, "predicated instructions must be in IT block"); + } else if (!isThumb() && !useImplicitITARM() && MCID.isPredicable() && + Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() != + ARMCC::AL) { + return Warning(Loc, "predicated instructions should be in IT block"); + } const unsigned Opcode = Inst.getOpcode(); switch (Opcode) { @@ -8636,27 +8800,15 @@ } case ARM::ITasm: case ARM::t2IT: { - // The mask bits for all but the first condition are represented as - // the low bit of the condition code value implies 't'. We currently - // always have 1 implies 't', so XOR toggle the bits if the low bit - // of the condition code is zero. MCOperand &MO = Inst.getOperand(1); unsigned Mask = MO.getImm(); - unsigned OrigMask = Mask; - unsigned TZ = countTrailingZeros(Mask); - if ((Inst.getOperand(0).getImm() & 1) == 0) { - assert(Mask && TZ <= 3 && "illegal IT mask value!"); - Mask ^= (0xE << TZ) & 0xF; - } - MO.setImm(Mask); + 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?!"); - ITState.Cond = ARMCC::CondCodes(Inst.getOperand(0).getImm()); - ITState.Mask = OrigMask; // Use the original mask, not the updated one. - ITState.CurPosition = 0; - ITState.FirstCond = true; + startExplicitITBlock(Cond, Mask); + MO.setImm(getITMaskEncoding()); break; } case ARM::t2LSLrr: @@ -8804,6 +8956,132 @@ } } +// Returns true if Inst is unpredictable if it is in and IT block, but is not +// the last instruction in the block. +bool ARMAsmParser::isITBlockTerminator(MCInst &Inst) const { + const MCInstrDesc &MCID = MII.get(Inst.getOpcode()); + + // All branch & call instructions terminate IT blocks. + if (MCID.isTerminator() || MCID.isCall() || MCID.isReturn() || + MCID.isBranch() || MCID.isIndirectBranch()) + return true; + + // Any arithmetic instruction which writes to the PC also terminates the IT + // block. + for (unsigned OpIdx = 0; OpIdx < MCID.getNumDefs(); ++OpIdx) { + MCOperand &Op = Inst.getOperand(OpIdx); + if (Op.isReg() && Op.getReg() == ARM::PC) + return true; + } + + if (MCID.hasImplicitDefOfPhysReg(ARM::PC, MRI)) + return true; + + // Instructions with variable operand lists, which write to the variable + // operands. We only care about Thumb instructions here, as ARM instructions + // obviously can't be in an IT block. + switch (Inst.getOpcode()) { + case ARM::t2LDMIA: + case ARM::t2LDMIA_UPD: + case ARM::t2LDMDB: + case ARM::t2LDMDB_UPD: + if (listContainsReg(Inst, 3, ARM::PC)) + return true; + break; + case ARM::tPOP: + if (listContainsReg(Inst, 2, ARM::PC)) + return true; + break; + } + + return false; +} + +unsigned ARMAsmParser::MatchInstruction(OperandVector &Operands, MCInst &Inst, + uint64_t &ErrorInfo, + bool MatchingInlineAsm, + bool &EmitInITBlock, + MCStreamer &Out) { + // If we can't use an implicit IT block here, just match as normal. + if (inExplicitITBlock() || !isThumbTwo() || !useImplicitITThumb()) + return MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); + + // Try to match the instruction in an extension of the current IT block (if + // there is one). + if (inImplicitITBlock()) { + extendImplicitITBlock(ITState.Cond); + if (MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm) == + Match_Success) { + // The match succeded, but we still have to check that the instruction is + // valid in this implicit IT block. + const MCInstrDesc &MCID = MII.get(Inst.getOpcode()); + if (MCID.isPredicable()) { + ARMCC::CondCodes InstCond = + (ARMCC::CondCodes)Inst.getOperand(MCID.findFirstPredOperandIdx()) + .getImm(); + ARMCC::CondCodes ITCond = currentITCond(); + if (InstCond == ITCond) { + EmitInITBlock = true; + return Match_Success; + } else if (InstCond == ARMCC::getOppositeCondition(ITCond)) { + invertCurrentITCondition(); + EmitInITBlock = true; + return Match_Success; + } + } + } + rewindImplicitITPosition(); + } + + // Finish the current IT block, and try to match outside any IT block. + flushPendingInstructions(Out); + unsigned PlainMatchResult = + MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); + if (PlainMatchResult == Match_Success) { + const MCInstrDesc &MCID = MII.get(Inst.getOpcode()); + if (MCID.isPredicable()) { + ARMCC::CondCodes InstCond = + (ARMCC::CondCodes)Inst.getOperand(MCID.findFirstPredOperandIdx()) + .getImm(); + // Some forms of the branch instruction have their own condition code + // fields, so can be conditionally executed without an IT block. + if (Inst.getOpcode() == ARM::tBcc || Inst.getOpcode() == ARM::t2Bcc) { + EmitInITBlock = false; + return Match_Success; + } + if (InstCond == ARMCC::AL) { + EmitInITBlock = false; + return Match_Success; + } + } else { + EmitInITBlock = false; + return Match_Success; + } + } + + // Try to match in a new IT block. The matcher doesn't check the actual + // condition, so we create an IT block with a dummy condition, and fix it up + // once we know the actual condition. + startImplicitITBlock(); + if (MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm) == + Match_Success) { + const MCInstrDesc &MCID = MII.get(Inst.getOpcode()); + if (MCID.isPredicable()) { + ITState.Cond = + (ARMCC::CondCodes)Inst.getOperand(MCID.findFirstPredOperandIdx()) + .getImm(); + EmitInITBlock = true; + return Match_Success; + } + } + discardImplicitITBlock(); + + // If none of these succeed, return the error we got when trying to match + // outside any IT blocks. + EmitInITBlock = false; + return PlainMatchResult; +} + static const char *getSubtargetFeatureName(uint64_t Val); bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, @@ -8811,9 +9089,11 @@ bool MatchingInlineAsm) { MCInst Inst; unsigned MatchResult; + bool PendConditionalInstruction = false; + + MatchResult = MatchInstruction(Operands, Inst, ErrorInfo, MatchingInlineAsm, + PendConditionalInstruction, Out); - MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo, - MatchingInlineAsm); switch (MatchResult) { case Match_Success: // Context sensitive operand constraints aren't handled by the matcher, @@ -8853,7 +9133,13 @@ return false; Inst.setLoc(IDLoc); - Out.EmitInstruction(Inst, getSTI()); + if (PendConditionalInstruction) { + PendingConditionalInsts.push_back(Inst); + if (isITBlockFull() || isITBlockTerminator(Inst)) + flushPendingInstructions(Out); + } else { + Out.EmitInstruction(Inst, getSTI()); + } return false; case Match_MissingFeature: { assert(ErrorInfo && "Unknown missing feature!"); @@ -9106,6 +9392,9 @@ } void ARMAsmParser::onLabelParsed(MCSymbol *Symbol) { + // We need to flush the current implicit IT block on a label, because it is + // not legal to branch into an IT block. + flushPendingInstructions(getStreamer()); if (NextSymbolIsThumb) { getParser().getStreamer().EmitThumbFunc(Symbol); NextSymbolIsThumb = false; Index: llvm/trunk/test/MC/ARM/implicit-it-generation.s =================================================================== --- llvm/trunk/test/MC/ARM/implicit-it-generation.s +++ llvm/trunk/test/MC/ARM/implicit-it-generation.s @@ -0,0 +1,409 @@ +@ RUN: llvm-mc -triple thumbv7a--none-eabi -arm-implicit-it=always < %s -show-encoding | FileCheck %s + +@ Single instruction + .section test1 +@ CHECK-LABEL: test1 + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq + +@ Multiple instructions, same condition + .section test2 +@ CHECK-LABEL: test2 + addeq r0, #1 + addeq r0, #1 + addeq r0, #1 + addeq r0, #1 +@ CHECK: itttt eq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: addeq + +@ Multiple instructions, equal but opposite conditions + .section test3 +@ CHECK-LABEL: test3 + addeq r0, #1 + addne r0, #1 + addeq r0, #1 + addne r0, #1 +@ CHECK: itete eq +@ CHECK: addeq +@ CHECK: addne +@ CHECK: addeq +@ CHECK: addne + +@ Multiple instructions, unrelated conditions + .section test4 +@ CHECK-LABEL: test4 + addeq r0, #1 + addlt r0, #1 + addeq r0, #1 + addge r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it lt +@ CHECK: addlt +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it ge +@ CHECK: addge + +@ More than 4 instructions eligible for a block + .section test5 +@ CHECK-LABEL: test5 + addeq r0, #1 + addeq r0, #1 + addeq r0, #1 + addeq r0, #1 + addeq r0, #1 + addeq r0, #1 +@ CHECK: itttt eq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: itt eq +@ CHECK: addeq +@ CHECK: addeq + +@ Flush on a label + .section test6 +@ CHECK-LABEL: test6 + addeq r0, #1 +label: + addeq r0, #1 +5: + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ Flush on a section-change directive + .section test7a +@ CHECK-LABEL: test7a + addeq r0, #1 + .section test7b + addeq r0, #1 + .previous + addeq r0, #1 + .pushsection test7c + addeq r0, #1 + .popsection + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ Flush on an ISA change (even to the same ISA) + .section test8 +@ CHECK-LABEL: test8 + addeq r0, #1 + .thumb + addeq r0, #1 + .arm + addeq r0, #1 + .thumb + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ Flush on an arch, cpu or fpu change + .section test9 +@ CHECK-LABEL: test9 + addeq r0, #1 + .arch armv7-a + addeq r0, #1 + .cpu cortex-a15 + addeq r0, #1 + .fpu vfpv3 + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ Flush on an unpredicable instruction + .section test10 +@ CHECK-LABEL: test10 + addeq r0, #1 + setend le + addeq r0, #1 + hvc #0 + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: setend le +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: hvc.w #0 +@ CHECK: it eq +@ CHECK: addeq + +@ Flush when reaching an explicit IT instruction + .section test11 +@ CHECK-LABEL: test11 + addeq r0, #1 + it eq + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ Don't extend an explicit IT instruction + .section test12 +@ CHECK-LABEL: test12 + it eq + addeq r0, #1 + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ Branch-like instructions can only be used at the end of an IT block, so +@ terminate it. + .section test13 +@ CHECK-LABEL: test13 + .cpu cortex-a15 + addeq pc, r0 + addeq pc, sp, pc + ldreq pc, [r0, #4] + ldreq pc, [r0, #-4] + ldreq pc, [r0, r1] + ldreq pc, [pc, #-0] + moveq pc, r0 + bleq #4 + blxeq #4 + blxeq r0 + bxeq r0 + bxjeq r0 + tbbeq [r0, r1] + tbheq [r0, r1, lsl #1] + ereteq + rfeiaeq r0 + rfeiaeq r0! + rfedbeq r0 + rfedbeq r0! + smceq #0 + ldmiaeq r0, {pc} + ldmiaeq r0!, {r1, pc} + ldmdbeq r0, {pc} + ldmdbeq r0!, {r1, pc} + popeq {pc} + .arch armv8-m.main + bxnseq r0 + blxnseq r0 +@ CHECK: it eq +@ CHECK: addeq pc, r0 +@ CHECK: it eq +@ CHECK: addeq pc, sp, pc +@ CHECK: it eq +@ CHECK: ldreq.w pc, [r0, #4] +@ CHECK: it eq +@ CHECK: ldreq pc, [r0, #-4] +@ CHECK: it eq +@ CHECK: ldreq.w pc, [r0, r1] +@ CHECK: it eq +@ CHECK: ldreq.w pc, [pc, #-0] +@ CHECK: it eq +@ CHECK: moveq pc, r0 +@ CHECK: it eq +@ CHECK: bleq #4 +@ CHECK: it eq +@ CHECK: blxeq #4 +@ CHECK: it eq +@ CHECK: blxeq r0 +@ CHECK: it eq +@ CHECK: bxeq r0 +@ CHECK: it eq +@ CHECK: bxjeq r0 +@ CHECK: it eq +@ CHECK: tbbeq [r0, r1] +@ CHECK: it eq +@ CHECK: tbheq [r0, r1, lsl #1] +@ CHECK: it eq +@ CHECK: ereteq +@ CHECK: it eq +@ CHECK: rfeiaeq r0 +@ CHECK: it eq +@ CHECK: rfeiaeq r0! +@ CHECK: it eq +@ CHECK: rfedbeq r0 +@ CHECK: it eq +@ CHECK: rfedbeq r0! +@ CHECK: it eq +@ CHECK: smceq #0 +@ CHECK: it eq +@ CHECK: ldmeq.w r0, {pc} +@ CHECK: it eq +@ CHECK: ldmeq.w r0!, {r1, pc} +@ CHECK: it eq +@ CHECK: ldmdbeq r0, {pc} +@ CHECK: it eq +@ CHECK: ldmdbeq r0!, {r1, pc} +@ CHECK: it eq +@ CHECK: popeq {pc} +@ CHECK: it eq +@ CHECK: bxnseq r0 +@ CHECK: it eq +@ CHECK: blxnseq r0 + +@ Thumb 16-bit ALU instructions set the flags iff they are not in an IT block, +@ so instruction matching must change when generating an implicit IT block. + .section test14 +@ CHECK-LABEL: test14 +@ Outside an IT block, the 16-bit encoding must set flags + add r0, #1 +@ CHECK:add.w r0, r0, #1 @ encoding: [0x00,0xf1,0x01,0x00] + adds r0, #1 +@ CHECK: adds r0, #1 @ encoding: [0x01,0x30] +@ Inside an IT block, the 16-bit encoding can not set flags + addeq r0, #1 +@ CHECK: itt eq +@ CHECK: addeq r0, #1 @ encoding: [0x01,0x30] + addseq r0, #1 +@ CHECK: addseq.w r0, r0, #1 @ encoding: [0x10,0xf1,0x01,0x00] + +@ Some variants of the B instruction have their own condition code field, and +@ are not valid in IT blocks. + .section test15 +@ CHECK-LABEL: test15 +@ Outside of an IT block, the 4 variants (narrow/wide, +@ predicated/non-predicated) are selected as normal, and the predicated +@ encodings are used instead of opening a new IT block: + b #0x100 +@ CHECK: b #256 @ encoding: [0x80,0xe0] + b #0x800 +@ CHECK: b.w #2048 @ encoding: [0x00,0xf0,0x00,0xbc] + beq #0x4 +@ CHECK-NOT: it +@ CHECK: beq #4 @ encoding: [0x02,0xd0] + beq #0x100 +@ CHECK-NOT: it +@ CHECK: beq.w #256 @ encoding: [0x00,0xf0,0x80,0x80] + +@ We could support "beq #0x100000" to "beq #0x1fffffc" by using t2Bcc in +@ an IT block (these currently fail as the target is out of range). However, long +@ ranges like this are rarely assembly-time constants, so this probably isn't +@ worth doing. + +@ If we already have an open IT block, we can use the non-predicated encodings, +@ which have a greater range: + addeq r0, r1 + beq #0x4 +@ CHECK: itt eq +@ CHECK: addeq r0, r1 +@ CHECK: beq #4 @ encoding: [0x02,0xe0] + addeq r0, r1 + beq #0x100 +@ CHECK: itt eq +@ CHECK: addeq r0, r1 +@ CHECK: beq #256 @ encoding: [0x80,0xe0] + addeq r0, r1 + beq #0x800 +@ CHECK: itt eq +@ CHECK: addeq r0, r1 +@ CHECK: beq.w #2048 @ encoding: [0x00,0xf0,0x00,0xbc] + +@ If we have an open but incompatible IT block, we close it and use the +@ self-predicated encodings, without an IT block: + addeq r0, r1 + bgt #0x4 +@ CHECK: it eq +@ CHECK: addeq r0, r1 +@ CHECK: bgt #4 @ encoding: [0x02,0xdc] + addeq r0, r1 + bgt #0x100 +@ CHECK: it eq +@ CHECK: addeq r0, r1 +@ CHECK: bgt.w #256 @ encoding: [0x00,0xf3,0x80,0x80] + +@ Breakpoint instructions are allowed in IT blocks, but are always executed +@ regardless of the condition flags. We could continue an IT block through +@ them, but currently do not. + .section test16 +@ CHECK-LABEL: test16 + addeq r0, r1 + bkpt #0 + addeq r0, r1 +@ CHECK: it eq +@ CHECK: addeq r0, r1 +@ CHECK: bkpt #0 +@ CHECK: it eq +@ CHECK: addeq r0, r1 + +@ The .if directive causes entire assembly statments to be dropped before they +@ reach the IT block generation code. This happens to be exactly what we want, +@ and allows IT blocks to extend into and out of .if blocks. Only one arm of the +@ .if will be seen by the IT state tracking code, so the subeq shouldn't have +@ any effect here. + .section test17 +@ CHECK-LABEL: test17 + addeq r0, r1 + .if 1 + addeq r0, r1 + .else + subeq r0, r1 + .endif + addeq r0, r1 +@ CHECK: ittt eq +@ CHECK: addeq +@ CHECK: addeq +@ CHECK: addeq + +@ TODO: There are some other directives which we could continue through, such +@ as .set and .global, but we currently conservatively flush the IT block before +@ every directive (except for .if and friends, which are handled separately). + .section test18 +@ CHECK-LABEL: test18 + addeq r0, r1 + .set s, 1 + addeq r0, r1 +@ CHECK: it eq +@ CHECK: addeq +@ CHECK: it eq +@ CHECK: addeq + +@ The .rept directive can be used to create long IT blocks. + .section test19 +@ CHECK-LABEL: test19 + .rept 3 + addeq r0, r1 + subne r0, r1 + .endr +@ CHECK: itete eq +@ CHECK: addeq r0, r1 +@ CHECK: subne.w r0, r0, r1 +@ CHECK: addeq r0, r1 +@ CHECK: subne.w r0, r0, r1 +@ CHECK: ite eq +@ CHECK: addeq r0, r1 +@ CHECK: subne.w r0, r0, r1 + +@ Flush at end of file + .section test99 +@ CHECK-LABEL: test99 + addeq r0, #1 +@ CHECK: it eq +@ CHECK: addeq Index: llvm/trunk/test/MC/ARM/implicit-it.s =================================================================== --- llvm/trunk/test/MC/ARM/implicit-it.s +++ llvm/trunk/test/MC/ARM/implicit-it.s @@ -0,0 +1,73 @@ +@ RUN: not llvm-mc -triple thumbv7a--none-eabi -arm-implicit-it=never < %s 2>%t | FileCheck %s --check-prefix=CHECK +@ RUN: FileCheck %s < %t --check-prefix=THUMB-STDERR +@ RUN: not llvm-mc -triple armv7a--none-eabi -arm-implicit-it=never < %s 2>%t | FileCheck %s --check-prefix=CHECK --check-prefix=ARM +@ RUN: FileCheck %s < %t --check-prefix=ARM-STDERR + +@ RUN: not llvm-mc -triple thumbv7a--none-eabi -arm-implicit-it=always < %s | FileCheck %s --check-prefix=CHECK --check-prefix=THUMB +@ RUN: not llvm-mc -triple armv7a--none-eabi -arm-implicit-it=always < %s | FileCheck %s --check-prefix=CHECK --check-prefix=ARM + +@ RUN: not llvm-mc -triple thumbv7a--none-eabi -arm-implicit-it=arm < %s 2>%t | FileCheck %s --check-prefix=CHECK +@ RUN: FileCheck %s < %t --check-prefix=THUMB-STDERR +@ RUN: not llvm-mc -triple armv7a--none-eabi -arm-implicit-it=arm < %s | FileCheck %s --check-prefix=CHECK --check-prefix=ARM + +@ RUN: not llvm-mc -triple thumbv7a--none-eabi < %s 2>%t | FileCheck %s --check-prefix=CHECK +@ RUN: FileCheck %s < %t --check-prefix=THUMB-STDERR +@ RUN: not llvm-mc -triple armv7a--none-eabi < %s | FileCheck %s --check-prefix=CHECK --check-prefix=ARM + +@ RUN: not llvm-mc -triple thumbv7a--none-eabi -arm-implicit-it=thumb < %s | FileCheck %s --check-prefix=CHECK --check-prefix=THUMB +@ RUN: not llvm-mc -triple armv7a--none-eabi -arm-implicit-it=thumb < %s 2>%t | FileCheck %s --check-prefix=CHECK --check-prefix=ARM +@ RUN: FileCheck %s < %t --check-prefix=ARM-STDERR + +@ A single conditional instruction without IT block + .section test1 +@ CHECK-LABEL: test1 + addeq r0, r0, #1 +@ THUMB: it eq +@ THUMB: addeq r0, r0, #1 +@ ARM: addeq r0, r0, #1 +@ THUMB-STDERR: error: predicated instructions must be in IT block +@ ARM-STDERR: warning: predicated instructions should be in IT block + +@ A single conditional instruction with IT block + .section test2 +@ CHECK-LABEL: test2 + it eq + addeq r0, r0, #1 +@ THUMB: it eq +@ THUMB: addeq r0, r0, #1 +@ ARM: addeq r0, r0, #1 +@ THUMB-STDERR-NOT: error: +@ ARM-STDERR-NOT: warning: + +@ A single conditional instruction with IT block, but incorrect condition + .section test3 +@ CHECK-LABEL: test3 + it eq + addgt r0, r0, #1 +@ THUMB-STDERR: error: incorrect condition in IT block +@ ARM-STDERR: error: incorrect condition in IT block + +@ Multiple conditional instructions in an IT block, inverted and non-inverted conditions + .section test4 +@ CHECK-LABEL: test4 + itete gt + addgt r0, r0, #1 + addle r0, r0, #1 + addgt r0, r0, #1 + addle r0, r0, #1 +@ THUMB: itete gt +@ CHECK: addgt r0, r0, #1 +@ CHECK: addle r0, r0, #1 +@ CHECK: addgt r0, r0, #1 +@ CHECK: addle r0, r0, #1 +@ THUMB-STDERR-NOT: error: +@ ARM-STDERR-NOT: warning: + +@ Incorrectly inverted condition on the second slot of an IT block + .section test5 +@ CHECK-LABEL: test5 + itt eq + addeq r0, r0, #1 + addne r0, r0, #1 +@ THUMB-STDERR: error: incorrect condition in IT block +@ ARM-STDERR: error: incorrect condition in IT block