ARM isel bug fix for adds/subs operands.
authorAndrew Trick <atrick@apple.com>
Tue, 20 Sep 2011 03:17:40 +0000 (03:17 +0000)
committerAndrew Trick <atrick@apple.com>
Tue, 20 Sep 2011 03:17:40 +0000 (03:17 +0000)
Modified ARMISelLowering::AdjustInstrPostInstrSelection to handle the
full gamut of CPSR defs/uses including instructins whose "optional"
cc_out operand is not really optional. This allowed removal of the
hasPostISelHook to simplify the .td files and make the implementation
more robust.
Fixes rdar://10137436: sqlite3 miscompile

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@140134 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/MC/MCInstrDesc.h
lib/CodeGen/SelectionDAG/InstrEmitter.cpp
lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
lib/Target/ARM/ARMISelLowering.cpp
lib/Target/ARM/ARMInstrInfo.td
lib/Target/ARM/ARMInstrThumb2.td
test/CodeGen/ARM/2011-09-19-cpsr.ll [new file with mode: 0644]
utils/TableGen/CodeGenInstruction.cpp
utils/TableGen/CodeGenInstruction.h
utils/TableGen/InstrInfoEmitter.cpp

index 7061fcb0128404586d94e5a8d493f8e53d126599..0a4ca648e658fc665fe5b1a3e655504471ce4594 100644 (file)
@@ -477,14 +477,6 @@ public:
     return Flags & (1 << MCID::UsesCustomInserter);
   }
 
-  /// hasPostISelHook - Return true if this instruction requires *adjustment*
-  /// after instruction selection by calling a target hook. For example, this
-  /// can be used to fill in ARM 's' optional operand depending on whether
-  /// the conditional flag register is used.
-  bool hasPostISelHook() const {
-    return Flags & (1 << MCID::HasPostISelHook);
-  }
-
   /// isRematerializable - Returns true if this instruction is a candidate for
   /// remat.  This flag is deprecated, please don't use it anymore.  If this
   /// flag is set, the isReallyTriviallyReMaterializable() method is called to
index e2e906afa6a0a689d42237f1b94eb2f81485584c..eebf2b2f691e361ed7fe6dbfae57f2a3fe6081ee 100644 (file)
@@ -763,8 +763,7 @@ EmitMachineNode(SDNode *Node, bool IsClone, bool IsCloned,
     }
 
   // Run post-isel target hook to adjust this instruction if needed.
-  if (II.hasPostISelHook())
-    TLI->AdjustInstrPostInstrSelection(MI, Node);
+  TLI->AdjustInstrPostInstrSelection(MI, Node);
 }
 
 /// EmitSpecialNode - Generate machine code for a target-independent node and
index b684619776f86d58d8ece6c068e9b86ad3e95d9e..9f2369d142d0cff8bbba576adc25592f66aa549f 100644 (file)
@@ -179,12 +179,7 @@ TargetLowering::EmitInstrWithCustomInserter(MachineInstr *MI,
 
 void TargetLowering::AdjustInstrPostInstrSelection(MachineInstr *MI,
                                                    SDNode *Node) const {
-#ifndef NDEBUG
-  dbgs() << "If a target marks an instruction with "
-          "'hasPostISelHook', it must implement "
-          "TargetLowering::AdjustInstrPostInstrSelection!";
-#endif
-  llvm_unreachable(0);
+  // Do nothing unless the target overrides it.
 }
 
 //===----------------------------------------------------------------------===//
index 7a8665858440402a3b6fadeca86d605c7fcd9a23..28860911d66f9fb5806bf62c79304215d99d267f 100644 (file)
@@ -5752,27 +5752,68 @@ ARMTargetLowering::EmitInstrWithCustomInserter(MachineInstr *MI,
   }
 }
 
+/// Generally, ARM instructions may be optionally encoded with a 's'
+/// bit. However, some opcodes have a compact encoding that forces an implicit
+/// 's' bit. List these exceptions here.
+static bool hasForcedCPSRDef(const MCInstrDesc &MCID) {
+  switch (MCID.getOpcode()) {
+  case ARM::t2ADDSri:
+  case ARM::t2ADDSrr:
+  case ARM::t2ADDSrs:
+  case ARM::t2SUBSri:
+  case ARM::t2SUBSrr:
+  case ARM::t2SUBSrs:
+    return true;
+  }
+  return false;
+}
+
 void ARMTargetLowering::AdjustInstrPostInstrSelection(MachineInstr *MI,
                                                       SDNode *Node) const {
-  // Adjust potentially 's' setting instructions after isel, i.e. ADC, SBC,
-  // RSB, RSC. Coming out of isel, they have an implicit CPSR def, but the
-  // optional operand is not filled in. If the carry bit is used, then change
-  // the optional operand to CPSR. Otherwise, remove the CPSR implicit def.
+  // Adjust potentially 's' setting instructions after isel, i.e. ADC, SBC, RSB,
+  // RSC. Coming out of isel, they have an implicit CPSR def, but the optional
+  // operand is still set to noreg. If needed, set the optional operand's
+  // register to CPSR, and remove the redundant implicit def.
+
   const MCInstrDesc &MCID = MI->getDesc();
-  if (Node->hasAnyUseOfValue(1)) {
-    MachineOperand &MO = MI->getOperand(MCID.getNumOperands() - 1);
-    MO.setReg(ARM::CPSR);
-    MO.setIsDef(true);
-  } else {
-    for (unsigned i = MCID.getNumOperands(), e = MI->getNumOperands();
-         i != e; ++i) {
-      const MachineOperand &MO = MI->getOperand(i);
-      if (MO.isReg() && MO.isDef() && MO.getReg() == ARM::CPSR) {
-        MI->RemoveOperand(i);
-        break;
-      }
+  unsigned ccOutIdx = MCID.getNumOperands() - 1;
+  bool forcedCPSR = hasForcedCPSRDef(MCID);
+
+  // Any ARM instruction that sets the 's' bit should specify an optional
+  // "cc_out" operand in the last operand position.
+  if (!MCID.hasOptionalDef() || !MCID.OpInfo[ccOutIdx].isOptionalDef()) {
+    assert(!forcedCPSR && "Optional cc_out operand required");
+    return;
+  }
+  // Look for an implicit def of CPSR added by MachineInstr ctor.
+  bool definesCPSR = false;
+  bool deadCPSR = false;
+  for (unsigned i = MCID.getNumOperands(), e = MI->getNumOperands();
+       i != e; ++i) {
+    const MachineOperand &MO = MI->getOperand(i);
+    if (MO.isReg() && MO.isDef() && MO.getReg() == ARM::CPSR) {
+      definesCPSR = true;
+      if (MO.isDead())
+        deadCPSR = true;
+      MI->RemoveOperand(i);
+      break;
     }
   }
+  if (!definesCPSR) {
+    assert(!forcedCPSR && "Optional cc_out operand required");
+    return;
+  }
+  assert(deadCPSR == !Node->hasAnyUseOfValue(1) && "inconsistent dead flag");
+
+  // If possible, select the encoding that does not set the 's' bit.
+  if (deadCPSR && !forcedCPSR)
+    return;
+
+  MachineOperand &MO = MI->getOperand(ccOutIdx);
+  MO.setReg(ARM::CPSR);
+  MO.setIsDef(true);
+  if (deadCPSR)
+    MO.setIsDead();
 }
 
 //===----------------------------------------------------------------------===//
index 818735561c533b1059aa41543577f16091542455..1ad84f2c166f367c36d4d5919bf436ebb56e79b4 100644 (file)
@@ -1026,7 +1026,7 @@ multiclass AsI1_rbin_irs<bits<4> opcod, string opc,
 }
 
 /// AsI1_rbin_s_is - Same as AsI1_rbin_s_is except it sets 's' bit by default.
-let hasPostISelHook = 1, isCodeGenOnly = 1, Defs = [CPSR] in {
+let isCodeGenOnly = 1, Defs = [CPSR] in {
 multiclass AsI1_rbin_s_is<bits<4> opcod, string opc,
                      InstrItinClass iii, InstrItinClass iir, InstrItinClass iis,
                         PatFrag opnode, bit Commutable = 0> {
@@ -1090,7 +1090,7 @@ multiclass AsI1_rbin_s_is<bits<4> opcod, string opc,
 }
 
 /// AsI1_bin_s_irs - Same as AsI1_bin_irs except it sets the 's' bit by default.
-let hasPostISelHook = 1, isCodeGenOnly = 1, Defs = [CPSR] in {
+let isCodeGenOnly = 1, Defs = [CPSR] in {
 multiclass AsI1_bin_s_irs<bits<4> opcod, string opc,
                      InstrItinClass iii, InstrItinClass iir, InstrItinClass iis,
                          PatFrag opnode, bit Commutable = 0> {
@@ -1278,7 +1278,7 @@ class AI_exta_rrot_np<bits<8> opcod, string opc>
 /// AI1_adde_sube_irs - Define instructions and patterns for adde and sube.
 multiclass AI1_adde_sube_irs<bits<4> opcod, string opc, PatFrag opnode,
                              string baseOpc, bit Commutable = 0> {
-  let hasPostISelHook = 1, Defs = [CPSR], Uses = [CPSR] in {
+  let Defs = [CPSR], Uses = [CPSR] in {
   def ri : AsI1<opcod, (outs GPR:$Rd), (ins GPR:$Rn, so_imm:$imm),
                 DPFrm, IIC_iALUi, opc, "\t$Rd, $Rn, $imm",
                [(set GPR:$Rd, CPSR, (opnode GPR:$Rn, so_imm:$imm, CPSR))]>,
@@ -1366,7 +1366,7 @@ multiclass AI1_adde_sube_irs<bits<4> opcod, string opc, PatFrag opnode,
 /// AI1_rsc_irs - Define instructions and patterns for rsc
 multiclass AI1_rsc_irs<bits<4> opcod, string opc, PatFrag opnode,
                        string baseOpc> {
-  let hasPostISelHook = 1, Defs = [CPSR], Uses = [CPSR] in {
+  let Defs = [CPSR], Uses = [CPSR] in {
   def ri : AsI1<opcod, (outs GPR:$Rd), (ins GPR:$Rn, so_imm:$imm),
                 DPFrm, IIC_iALUi, opc, "\t$Rd, $Rn, $imm",
                [(set GPR:$Rd, CPSR, (opnode so_imm:$imm, GPR:$Rn, CPSR))]>,
index 4ed58a42b720e4601e693c3779f568f6c9d6af4f..ddc4441f039d91b6eaa431bad90dd8b2cc6f8b75 100644 (file)
@@ -592,7 +592,7 @@ multiclass T2I_rbin_irs<bits<4> opcod, string opc, PatFrag opnode> {
 
 /// T2I_bin_s_irs - Similar to T2I_bin_irs except it sets the 's' bit so the
 /// instruction modifies the CPSR register.
-let hasPostISelHook = 1, isCodeGenOnly = 1, Defs = [CPSR] in {
+let isCodeGenOnly = 1, Defs = [CPSR] in {
 multiclass T2I_bin_s_irs<bits<4> opcod, string opc,
                      InstrItinClass iii, InstrItinClass iir, InstrItinClass iis,
                          PatFrag opnode, bit Commutable = 0> {
@@ -738,7 +738,7 @@ multiclass T2I_adde_sube_irs<bits<4> opcod, string opc, PatFrag opnode,
 
 /// T2I_rbin_s_is - Same as T2I_rbin_irs except sets 's' bit and the register
 /// version is not needed since this is only for codegen.
-let hasPostISelHook = 1, isCodeGenOnly = 1, Defs = [CPSR] in {
+let isCodeGenOnly = 1, Defs = [CPSR] in {
 multiclass T2I_rbin_s_is<bits<4> opcod, string opc, PatFrag opnode> {
    // shifted imm
    def ri : T2sTwoRegImm<
@@ -1846,12 +1846,10 @@ defm t2SUBS : T2I_bin_s_irs <0b1101, "sub",
                              IIC_iALUi, IIC_iALUr, IIC_iALUsi,
                              BinOpFrag<(ARMsubc node:$LHS, node:$RHS)>>;
 
-let hasPostISelHook = 1 in {
 defm t2ADC  : T2I_adde_sube_irs<0b1010, "adc",
               BinOpWithFlagFrag<(ARMadde node:$LHS, node:$RHS, node:$FLAG)>, 1>;
 defm t2SBC  : T2I_adde_sube_irs<0b1011, "sbc",
               BinOpWithFlagFrag<(ARMsube node:$LHS, node:$RHS, node:$FLAG)>>;
-}
 
 // RSB
 defm t2RSB  : T2I_rbin_irs  <0b1110, "rsb",
diff --git a/test/CodeGen/ARM/2011-09-19-cpsr.ll b/test/CodeGen/ARM/2011-09-19-cpsr.ll
new file mode 100644 (file)
index 0000000..749a6d2
--- /dev/null
@@ -0,0 +1,54 @@
+; RUN: llc -march=thumb -mcpu=cortex-a8 < %s
+; rdar://problem/10137436: sqlite3 miscompile
+;
+; CHECK: subs
+; CHECK: cmp
+; CHECK: it
+
+target datalayout = "e-p:32:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:32:64-v128:32:128-a0:0:32-n32"
+target triple = "thumbv7-apple-ios4.0.0"
+
+declare i8* @__memset_chk(i8*, i32, i32, i32) nounwind
+
+define hidden fastcc i32 @sqlite3VdbeExec(i32* %p) nounwind {
+entry:
+  br label %sqlite3VarintLen.exit7424
+
+sqlite3VarintLen.exit7424:                        ; preds = %do.body.i7423
+  br label %do.body.i
+
+do.body.i:                                        ; preds = %do.body.i, %sqlite3VarintLen.exit7424
+  br i1 undef, label %do.body.i, label %sqlite3VarintLen.exit
+
+sqlite3VarintLen.exit:                            ; preds = %do.body.i
+  %sub2322 = add i64 undef, undef
+  br i1 undef, label %too_big, label %if.end2327
+
+if.end2327:                                       ; preds = %sqlite3VarintLen.exit
+  br i1 undef, label %if.end2341, label %no_mem
+
+if.end2341:                                       ; preds = %if.end2327
+  br label %for.body2355
+
+for.body2355:                                     ; preds = %for.body2355, %if.end2341
+  %add2366 = add nsw i32 undef, undef
+  br i1 undef, label %for.body2377, label %for.body2355
+
+for.body2377:                                     ; preds = %for.body2355
+  %conv23836154 = zext i32 %add2366 to i64
+  %sub2384 = sub i64 %sub2322, %conv23836154
+  %conv2385 = trunc i64 %sub2384 to i32
+  %len.0.i = select i1 undef, i32 %conv2385, i32 undef
+  %sub.i7384 = sub nsw i32 %len.0.i, 0
+  %call.i.i7385 = call i8* @__memset_chk(i8* undef, i32 0, i32 %sub.i7384, i32 undef) nounwind
+  unreachable
+
+too_big:                                          ; preds = %sqlite3VarintLen.exit
+  unreachable
+
+no_mem:                                           ; preds = %if.end2327, %for.body, %entry.no_mem_crit_edge
+  unreachable
+
+sqlite3ErrStr.exit:                               ; preds = %if.then82
+  unreachable
+}
index 4b252774f002cd300c3ac4a52ce999f0a9f15653..b4f9d150716be48c7346d0305829e024ad952ed7 100644 (file)
@@ -309,7 +309,6 @@ CodeGenInstruction::CodeGenInstruction(Record *R) : TheDef(R), Operands(R) {
   isReMaterializable = R->getValueAsBit("isReMaterializable");
   hasDelaySlot = R->getValueAsBit("hasDelaySlot");
   usesCustomInserter = R->getValueAsBit("usesCustomInserter");
-  hasPostISelHook = R->getValueAsBit("hasPostISelHook");
   hasCtrlDep   = R->getValueAsBit("hasCtrlDep");
   isNotDuplicable = R->getValueAsBit("isNotDuplicable");
   hasSideEffects = R->getValueAsBit("hasSideEffects");
index 468277aa96c2057007bcd780894791c1b1e9024b..8d7669aca98c0730dc2830f50985edb224745fef 100644 (file)
@@ -233,7 +233,6 @@ namespace llvm {
     bool isReMaterializable;
     bool hasDelaySlot;
     bool usesCustomInserter;
-    bool hasPostISelHook;
     bool hasCtrlDep;
     bool isNotDuplicable;
     bool hasSideEffects;
index 1cf7c90496280111aa2a67b5fe0fc5e8aa2ecf96..e4c7ee0146fd6f7e494baa5449be6e363d26131d 100644 (file)
@@ -288,7 +288,6 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
   if (Inst.isNotDuplicable)    OS << "|(1<<MCID::NotDuplicable)";
   if (Inst.Operands.hasOptionalDef) OS << "|(1<<MCID::HasOptionalDef)";
   if (Inst.usesCustomInserter) OS << "|(1<<MCID::UsesCustomInserter)";
-  if (Inst.hasPostISelHook)    OS << "|(1<<MCID::HasPostISelHook)";
   if (Inst.Operands.isVariadic)OS << "|(1<<MCID::Variadic)";
   if (Inst.hasSideEffects)     OS << "|(1<<MCID::UnmodeledSideEffects)";
   if (Inst.isAsCheapAsAMove)   OS << "|(1<<MCID::CheapAsAMove)";
@@ -345,7 +344,7 @@ void InstrInfoEmitter::emitEnums(raw_ostream &OS) {
 
   // We must emit the PHI opcode first...
   std::string Namespace = Target.getInstNamespace();
-  
+
   if (Namespace.empty()) {
     fprintf(stderr, "No instructions defined!\n");
     exit(1);