Thumb2 parsing and encoding for IT blocks.
authorJim Grosbach <grosbach@apple.com>
Mon, 29 Aug 2011 22:24:09 +0000 (22:24 +0000)
committerJim Grosbach <grosbach@apple.com>
Mon, 29 Aug 2011 22:24:09 +0000 (22:24 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@138773 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/MC/MCInstrDesc.h
lib/CodeGen/MachineInstr.cpp
lib/Target/ARM/AsmParser/ARMAsmParser.cpp
test/MC/ARM/basic-thumb2-instructions.s [new file with mode: 0644]
test/MC/ARM/thumb2-diagnostics.s [new file with mode: 0644]

index c49e02e6892e1ac5b7ac93766f364504b20d6236..5f5fa4c2f3a8a7fef79762e1ec493f14e031ac64 100644 (file)
@@ -289,6 +289,18 @@ public:
     return Flags & (1 << MCID::Barrier);
   }
 
+  /// findFirstPredOperandIdx() - Find the index of the first operand in the
+  /// operand list that is used to represent the predicate. It returns -1 if
+  /// none is found.
+  int findFirstPredOperandIdx() const {
+    if (isPredicable()) {
+      for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
+        if (OpInfo[i].isPredicate())
+          return i;
+    }
+    return -1;
+  }
+
   /// isTerminator - Returns true if this instruction part of the terminator for
   /// a basic block.  Typically this is things like return and branch
   /// instructions.
index 03104f0aed601e2144f1ffb9f03938b0631628c8..dd8cbe4e216eac60d28420f0b88a0ae0642d8c68 100644 (file)
@@ -942,6 +942,10 @@ MachineInstr::findRegisterDefOperandIdx(unsigned Reg, bool isDead, bool Overlap,
 /// operand list that is used to represent the predicate. It returns -1 if
 /// none is found.
 int MachineInstr::findFirstPredOperandIdx() const {
+  // Don't call MCID.findFirstPredOperandIdx() because this variant
+  // is sometimes called on an instruction that's not yet complete, and
+  // so the number of operands is less than the MCID indicates. In
+  // particular, the PTX target does this.
   const MCInstrDesc &MCID = getDesc();
   if (MCID.isPredicable()) {
     for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
index 443e54481a02ff5487e0feec2a15da0c253f5ee6..ab0ff87eaea0627d8969cad750f284c3724df612 100644 (file)
@@ -44,6 +44,28 @@ class ARMAsmParser : public MCTargetAsmParser {
   MCSubtargetInfo &STI;
   MCAsmParser &Parser;
 
+  struct {
+    ARMCC::CondCodes Cond;    // Condition for IT block.
+    unsigned Mask:4;          // Condition mask for instructions.
+                              // Starting at first 1 (from lsb).
+                              //   '1'  condition as indicated in IT.
+                              //   '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.
+
+    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.
+  } ITState;
+  bool inITBlock() { return ITState.CurPosition != ~0U;}
+
+
   MCAsmParser &getParser() const { return Parser; }
   MCAsmLexer &getLexer() const { return Parser.getLexer(); }
 
@@ -165,6 +187,7 @@ class ARMAsmParser : public MCTargetAsmParser {
 public:
   enum ARMMatchResultTy {
     Match_RequiresITBlock = FIRST_TARGET_MATCH_RESULT_TY,
+    Match_RequiresNotITBlock,
     Match_RequiresV6,
     Match_RequiresThumb2
   };
@@ -175,6 +198,9 @@ public:
 
     // Initialize the set of available features.
     setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
+
+    // Not in an ITBlock to start with.
+    ITState.CurPosition = ~0U;
   }
 
   // Implementation of the MCTargetAsmParser interface:
@@ -3085,18 +3111,23 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
   // where the conditional bit0 is zero, the instruction post-processing
   // will adjust the mask accordingly.
   if (Mnemonic == "it") {
+    SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + 2);
+    if (ITMask.size() > 3) {
+      Parser.EatToEndOfStatement();
+      return Error(Loc, "too many conditions on IT instruction");
+    }
     unsigned Mask = 8;
     for (unsigned i = ITMask.size(); i != 0; --i) {
       char pos = ITMask[i - 1];
       if (pos != 't' && pos != 'e') {
         Parser.EatToEndOfStatement();
-        return Error(NameLoc, "illegal IT instruction mask '" + ITMask + "'");
+        return Error(Loc, "illegal IT block condition mask '" + ITMask + "'");
       }
       Mask >>= 1;
       if (ITMask[i - 1] == 't')
         Mask |= 8;
     }
-    Operands.push_back(ARMOperand::CreateITMask(Mask, NameLoc));
+    Operands.push_back(ARMOperand::CreateITMask(Mask, Loc));
   }
 
   // FIXME: This is all a pretty gross hack. We should automatically handle
@@ -3128,18 +3159,18 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
   }
 
   // Add the carry setting operand, if necessary.
-  //
-  // FIXME: It would be awesome if we could somehow invent a location such that
-  // match errors on this operand would print a nice diagnostic about how the
-  // 's' character in the mnemonic resulted in a CCOut operand.
-  if (CanAcceptCarrySet)
+  if (CanAcceptCarrySet) {
+    SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size());
     Operands.push_back(ARMOperand::CreateCCOut(CarrySetting ? ARM::CPSR : 0,
-                                               NameLoc));
+                                               Loc));
+  }
 
   // Add the predication code operand, if necessary.
   if (CanAcceptPredicationCode) {
+    SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size() +
+                                      CarrySetting);
     Operands.push_back(ARMOperand::CreateCondCode(
-                         ARMCC::CondCodes(PredicationCode), NameLoc));
+                         ARMCC::CondCodes(PredicationCode), Loc));
   }
 
   // Add the processor imod operand, if necessary.
@@ -3261,10 +3292,57 @@ static bool checkLowRegisterList(MCInst Inst, unsigned OpNo, unsigned Reg,
   return false;
 }
 
+// FIXME: We would really prefer to have MCInstrInfo (the wrapper around
+// the ARMInsts array) instead. Getting that here requires awkward
+// API changes, though. Better way?
+namespace llvm {
+extern MCInstrDesc ARMInsts[];
+}
+static MCInstrDesc &getInstDesc(unsigned Opcode) {
+  return ARMInsts[Opcode];
+}
+
 // FIXME: We would really like to be able to tablegen'erate this.
 bool ARMAsmParser::
 validateInstruction(MCInst &Inst,
                     const SmallVectorImpl<MCParsedAsmOperand*> &Operands) {
+  MCInstrDesc &MCID = getInstDesc(Inst.getOpcode());
+  SMLoc Loc = Operands[0]->getStartLoc();
+  // Check the IT block state first.
+  if (inITBlock()) {
+    unsigned bit = 1;
+    if (ITState.FirstCond)
+      ITState.FirstCond = false;
+    else
+      bit = (ITState.Mask >> (4 - ITState.CurPosition)) & 1;
+    // Increment our position in the IT block first thing, as we want to
+    // move forward even if we find an error in the IT block.
+    unsigned TZ = CountTrailingZeros_32(ITState.Mask);
+    if (++ITState.CurPosition == 4 - TZ)
+      ITState.CurPosition = ~0U; // Done with the IT block after this.
+    // 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) {
+      // Find the condition code Operand to get its SMLoc information.
+      SMLoc CondLoc;
+      for (unsigned i = 1; i < Operands.size(); ++i)
+        if (static_cast<ARMOperand*>(Operands[i])->isCondCode())
+          CondLoc = Operands[i]->getStartLoc();
+      return Error(CondLoc, "incorrect condition in IT block; got '" +
+                   StringRef(ARMCondCodeToString(ARMCC::CondCodes(Cond))) +
+                   "', but expected '" +
+                   ARMCondCodeToString(ARMCC::CondCodes(ITCond)) + "'");
+    }
+    // Check for non-'al' condition codes outside of the IT block.
+  } else if (isThumbTwo() && MCID.isPredicable() &&
+             Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() !=
+             ARMCC::AL)
+    return Error(Loc, "predicated instructions must be in IT block");
+
   switch (Inst.getOpcode()) {
   case ARM::LDRD:
   case ARM::LDRD_PRE:
@@ -3413,29 +3491,28 @@ processInstruction(MCInst &Inst,
     // so mask that in if needed
     MCOperand &MO = Inst.getOperand(1);
     unsigned Mask = MO.getImm();
+    unsigned OrigMask = Mask;
+    unsigned TZ = CountTrailingZeros_32(Mask);
     if ((Inst.getOperand(0).getImm() & 1) == 0) {
-      unsigned TZ = CountTrailingZeros_32(Mask);
       assert(Mask && TZ <= 3 && "illegal IT mask value!");
       for (unsigned i = 3; i != TZ; --i)
         Mask ^= 1 << i;
     } else
       Mask |= 0x10;
     MO.setImm(Mask);
+
+    // 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;
     break;
   }
   }
 }
 
-// FIXME: We would really prefer to have MCInstrInfo (the wrapper around
-// the ARMInsts array) instead. Getting that here requires awkward
-// API changes, though. Better way?
-namespace llvm {
-extern MCInstrDesc ARMInsts[];
-}
-static MCInstrDesc &getInstDesc(unsigned Opcode) {
-  return ARMInsts[Opcode];
-}
-
 unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
   // 16-bit thumb arithmetic instructions either require or preclude the 'S'
   // suffix depending on whether they're in an IT block or not.
@@ -3457,10 +3534,12 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
       return Match_MnemonicFail;
     // If we're parsing Thumb2, which form is legal depends on whether we're
     // in an IT block.
-    // FIXME: We don't yet do IT blocks, so just always consider it to be
-    // that we aren't in one until we do.
-    if (isThumbTwo() && Inst.getOperand(OpNo).getReg() != ARM::CPSR)
+    if (isThumbTwo() && Inst.getOperand(OpNo).getReg() != ARM::CPSR &&
+        !inITBlock())
       return Match_RequiresITBlock;
+    if (isThumbTwo() && Inst.getOperand(OpNo).getReg() == ARM::CPSR &&
+        inITBlock())
+      return Match_RequiresNotITBlock;
   }
   // Some high-register supporting Thumb1 encodings only allow both registers
   // to be from r0-r7 when in Thumb2.
@@ -3518,6 +3597,8 @@ MatchAndEmitInstruction(SMLoc IDLoc,
   case Match_ConversionFail:
     // The converter function will have already emited a diagnostic.
     return true;
+  case Match_RequiresNotITBlock:
+    return Error(IDLoc, "flag setting instruction only valid outside IT block");
   case Match_RequiresITBlock:
     return Error(IDLoc, "instruction only valid inside IT block");
   case Match_RequiresV6:
diff --git a/test/MC/ARM/basic-thumb2-instructions.s b/test/MC/ARM/basic-thumb2-instructions.s
new file mode 100644 (file)
index 0000000..0469802
--- /dev/null
@@ -0,0 +1,32 @@
+@ RUN: llvm-mc -triple=thumbv7-apple-darwin -show-encoding < %s | FileCheck %s
+  .syntax unified
+  .globl _func
+
+@ Check that the assembler can handle the documented syntax from the ARM ARM.
+@ For complex constructs like shifter operands, check more thoroughly for them
+@ once then spot check that following instructions accept the form generally.
+@ This gives us good coverage while keeping the overall size of the test
+@ more reasonable.
+
+
+@ FIXME: Some 3-operand instructions have a 2-operand assembly syntax.
+
+_func:
+@ CHECK: _func
+
+@------------------------------------------------------------------------------
+@ IT
+@------------------------------------------------------------------------------
+@ Test encodings of a few full IT blocks, not just the IT instruction
+
+        iteet eq
+        addeq r0, r1, r2
+        nopne
+        subne r5, r6, r7
+        addeq r1, r2, #4
+
+@ CHECK: iteet eq                      @ encoding: [0x0d,0xbf]
+@ CHECK: addeq r0, r1, r2              @ encoding: [0x88,0x18]
+@ CHECK: nopne                          @ encoding: [0x00,0xbf]
+@ CHECK: subne r5, r6, r7              @ encoding: [0xf5,0x1b]
+@ CHECK: addeq r1, r2, #4              @ encoding: [0x11,0x1d]
diff --git a/test/MC/ARM/thumb2-diagnostics.s b/test/MC/ARM/thumb2-diagnostics.s
new file mode 100644 (file)
index 0000000..93e2e98
--- /dev/null
@@ -0,0 +1,30 @@
+@ RUN: not llvm-mc -triple=thumbv7-apple-darwin < %s 2> %t
+@ RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
+
+@ Ill-formed IT block instructions.
+        itet eq
+        addle r0, r1, r2
+        nop
+        it le
+        iteeee gt
+        ittfe le
+        nopeq
+
+@ CHECK-ERRORS: error: incorrect condition in IT block; got 'le', but expected 'eq'
+@ CHECK-ERRORS:         addle r0, r1, r2
+@ CHECK-ERRORS:            ^
+@ CHECK-ERRORS: error: incorrect condition in IT block; got 'al', but expected 'ne'
+@ CHECK-ERRORS:         nop
+@ CHECK-ERRORS:            ^
+@ CHECK-ERRORS: error: instructions in IT block must be predicable
+@ CHECK-ERRORS:         it le
+@ CHECK-ERRORS:         ^
+@ CHECK-ERRORS: error: too many conditions on IT instruction
+@ CHECK-ERRORS:         iteeee gt
+@ CHECK-ERRORS:           ^
+@ CHECK-ERRORS: error: illegal IT block condition mask 'tfe'
+@ CHECK-ERRORS:         ittfe le
+@ CHECK-ERRORS:           ^
+@ CHECK-ERRORS: error: predicated instructions must be in IT block
+@ CHECK-ERRORS:         nopeq
+@ CHECK-ERRORS:         ^