OSDN Git Service

When inlining through an 'nounwind' call, mark inlined
authorDuncan Sands <baldrick@free.fr>
Wed, 19 Dec 2007 21:13:37 +0000 (21:13 +0000)
committerDuncan Sands <baldrick@free.fr>
Wed, 19 Dec 2007 21:13:37 +0000 (21:13 +0000)
calls 'nounwind'.  It is important for correct C++
exception handling that nounwind markings do not get
lost, so this transformation is actually needed for
correctness.

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

include/llvm/Instructions.h
include/llvm/ParameterAttributes.h
include/llvm/Support/CallSite.h
lib/Transforms/IPO/PruneEH.cpp
lib/Transforms/Scalar/InstructionCombining.cpp
lib/Transforms/Utils/InlineFunction.cpp
lib/VMCore/Function.cpp
lib/VMCore/Instructions.cpp
test/Transforms/Inline/2007-12-19-InlineNoUnwind.ll [new file with mode: 0644]

index cdce9ec..635a8d6 100644 (file)
@@ -948,6 +948,7 @@ public:
   bool doesNotThrow() const {
     return paramHasAttr(0, ParamAttr::NoUnwind);
   }
+  void setDoesNotThrow(bool doesNotThrow = true);
 
   /// @brief Determine if the call returns a structure.
   bool isStructReturn() const {
@@ -1752,6 +1753,7 @@ public:
   bool doesNotThrow() const {
     return paramHasAttr(0, ParamAttr::NoUnwind);
   }
+  void setDoesNotThrow(bool doesNotThrow = true);
 
   /// @brief Determine if the call returns a structure.
   bool isStructReturn() const {
index 29bbd23..e582f74 100644 (file)
@@ -149,6 +149,14 @@ class ParamAttrsList : public FoldingSetNode {
     static const ParamAttrsList *getModified(const ParamAttrsList *PAL,
                                              const ParamAttrsVector &modVec);
 
+    /// @brief Add the specified attributes to those in PAL at index idx.
+    static const ParamAttrsList *includeAttrs(const ParamAttrsList *PAL,
+                                              uint16_t idx, uint16_t attrs);
+
+    /// @brief Remove the specified attributes from those in PAL at index idx.
+    static const ParamAttrsList *excludeAttrs(const ParamAttrsList *PAL,
+                                              uint16_t idx, uint16_t attrs);
+
     /// Returns whether each of the specified lists of attributes can be safely
     /// replaced with the other in a function or a function call.
     /// @brief Whether one attribute list can safely replace the other.
index ce8e9ff..3c60daf 100644 (file)
@@ -75,6 +75,7 @@ public:
 
   /// @brief Determine if the call cannot unwind.
   bool doesNotThrow() const;
+  void setDoesNotThrow(bool doesNotThrow = true);
 
   /// getType - Return the type of the instruction that generated this call site
   ///
index 69bb1f6..529f98e 100644 (file)
@@ -122,17 +122,15 @@ bool PruneEH::runOnSCC(const std::vector<CallGraphNode *> &SCC) {
   // If the SCC doesn't unwind or doesn't throw, note this fact.
   if (!SCCMightUnwind || !SCCMightReturn)
     for (unsigned i = 0, e = SCC.size(); i != e; ++i) {
-      const ParamAttrsList *PAL = SCC[i]->getFunction()->getParamAttrs();
-      uint16_t RAttributes = PAL ? PAL->getParamAttrs(0) : 0;
+      uint16_t NewAttributes = ParamAttr::None;
 
       if (!SCCMightUnwind)
-        RAttributes |= ParamAttr::NoUnwind;
+        NewAttributes |= ParamAttr::NoUnwind;
       if (!SCCMightReturn)
-        RAttributes |= ParamAttr::NoReturn;
+        NewAttributes |= ParamAttr::NoReturn;
 
-      ParamAttrsVector modVec;
-      modVec.push_back(ParamAttrsWithIndex::get(0, RAttributes));
-      PAL = ParamAttrsList::getModified(PAL, modVec);
+      const ParamAttrsList *PAL = SCC[i]->getFunction()->getParamAttrs();
+      PAL = ParamAttrsList::includeAttrs(PAL, 0, NewAttributes);
       SCC[i]->getFunction()->setParamAttrs(PAL);
     }
 
index 166b484..d7c4923 100644 (file)
@@ -8026,16 +8026,9 @@ Instruction *InstCombiner::visitCallSite(CallSite CS) {
       }
   }
 
-  if (isa<InlineAsm>(Callee) && !CS.paramHasAttr(0, ParamAttr::NoUnwind)) {
+  if (isa<InlineAsm>(Callee) && !CS.doesNotThrow()) {
     // Inline asm calls cannot throw - mark them 'nounwind'.
-    const ParamAttrsList *PAL = CS.getParamAttrs();
-    uint16_t RAttributes = PAL ? PAL->getParamAttrs(0) : 0;
-    RAttributes |= ParamAttr::NoUnwind;
-
-    ParamAttrsVector modVec;
-    modVec.push_back(ParamAttrsWithIndex::get(0, RAttributes));
-    PAL = ParamAttrsList::getModified(PAL, modVec);
-    CS.setParamAttrs(PAL);
+    CS.setDoesNotThrow();
     Changed = true;
   }
 
index a2b834b..dba0e69 100644 (file)
@@ -194,6 +194,10 @@ bool llvm::InlineFunction(CallSite CS, CallGraph *CG, const TargetData *TD) {
   bool MustClearTailCallFlags =
     isa<CallInst>(TheCall) && !cast<CallInst>(TheCall)->isTailCall();
 
+  // If the call to the callee cannot throw, set the 'nounwind' flag on any
+  // calls that we inline.
+  bool MarkNoUnwind = CS.doesNotThrow();
+
   BasicBlock *OrigBB = TheCall->getParent();
   Function *Caller = OrigBB->getParent();
 
@@ -207,7 +211,7 @@ bool llvm::InlineFunction(CallSite CS, CallGraph *CG, const TargetData *TD) {
   std::vector<ReturnInst*> Returns;
   ClonedCodeInfo InlinedFunctionInfo;
   Function::iterator FirstNewBlock;
-  
+
   { // Scope to destroy ValueMap after cloning.
     DenseMap<const Value*, Value*> ValueMap;
 
@@ -323,15 +327,33 @@ bool llvm::InlineFunction(CallSite CS, CallGraph *CG, const TargetData *TD) {
 
   // If we are inlining tail call instruction through a call site that isn't 
   // marked 'tail', we must remove the tail marker for any calls in the inlined
-  // code.
-  if (MustClearTailCallFlags && InlinedFunctionInfo.ContainsCalls) {
+  // code.  Also, calls inlined through a 'nounwind' call site should be marked
+  // 'nounwind'.
+  if (InlinedFunctionInfo.ContainsCalls &&
+      (MustClearTailCallFlags || MarkNoUnwind)) {
     for (Function::iterator BB = FirstNewBlock, E = Caller->end();
          BB != E; ++BB)
       for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I)
-        if (CallInst *CI = dyn_cast<CallInst>(I))
-          CI->setTailCall(false);
+        if (CallInst *CI = dyn_cast<CallInst>(I)) {
+          if (MustClearTailCallFlags)
+            CI->setTailCall(false);
+          if (MarkNoUnwind)
+            CI->setDoesNotThrow();
+        }
   }
 
+  // If we are inlining through a 'nounwind' call site then any inlined 'unwind'
+  // instructions are unreachable.
+  if (InlinedFunctionInfo.ContainsUnwinds && MarkNoUnwind)
+    for (Function::iterator BB = FirstNewBlock, E = Caller->end();
+         BB != E; ++BB) {
+      TerminatorInst *Term = BB->getTerminator();
+      if (isa<UnwindInst>(Term)) {
+        new UnreachableInst(Term);
+        BB->getInstList().erase(Term);
+      }
+    }
+
   // If we are inlining for an invoke instruction, we must make sure to rewrite
   // any inlined 'unwind' instructions into branches to the invoke exception
   // destination, and call instructions into invoke instructions.
index f985899..37e25ad 100644 (file)
@@ -261,6 +261,32 @@ ParamAttrsList::getModified(const ParamAttrsList *PAL,
   return get(newVec);
 }
 
+const ParamAttrsList *
+ParamAttrsList::includeAttrs(const ParamAttrsList *PAL,
+                             uint16_t idx, uint16_t attrs) {
+  uint16_t OldAttrs = PAL ? PAL->getParamAttrs(idx) : 0;
+  uint16_t NewAttrs = OldAttrs | attrs;
+  if (NewAttrs == OldAttrs)
+    return PAL;
+
+  ParamAttrsVector modVec;
+  modVec.push_back(ParamAttrsWithIndex::get(idx, NewAttrs));
+  return getModified(PAL, modVec);
+}
+
+const ParamAttrsList *
+ParamAttrsList::excludeAttrs(const ParamAttrsList *PAL,
+                             uint16_t idx, uint16_t attrs) {
+  uint16_t OldAttrs = PAL ? PAL->getParamAttrs(idx) : 0;
+  uint16_t NewAttrs = OldAttrs & ~attrs;
+  if (NewAttrs == OldAttrs)
+    return PAL;
+
+  ParamAttrsVector modVec;
+  modVec.push_back(ParamAttrsWithIndex::get(idx, NewAttrs));
+  return getModified(PAL, modVec);
+}
+
 ParamAttrsList::~ParamAttrsList() {
   ParamAttrsLists->RemoveNode(this);
 }
index 3531bad..b76b11d 100644 (file)
@@ -71,6 +71,12 @@ bool CallSite::doesNotThrow() const {
   else
     return cast<InvokeInst>(I)->doesNotThrow();
 }
+void CallSite::setDoesNotThrow(bool doesNotThrow) {
+  if (CallInst *CI = dyn_cast<CallInst>(I))
+    CI->setDoesNotThrow(doesNotThrow);
+  else
+    cast<InvokeInst>(I)->setDoesNotThrow(doesNotThrow);
+}
 
 //===----------------------------------------------------------------------===//
 //                            TerminatorInst Class
@@ -405,6 +411,15 @@ bool CallInst::paramHasAttr(uint16_t i, ParameterAttributes attr) const {
   return false;
 }
 
+void CallInst::setDoesNotThrow(bool doesNotThrow) {
+  const ParamAttrsList *PAL = getParamAttrs();
+  if (doesNotThrow)
+    PAL = ParamAttrsList::includeAttrs(PAL, 0, ParamAttr::NoUnwind);
+  else
+    PAL = ParamAttrsList::excludeAttrs(PAL, 0, ParamAttr::NoUnwind);
+  setParamAttrs(PAL);
+}
+
 
 //===----------------------------------------------------------------------===//
 //                        InvokeInst Implementation
@@ -483,6 +498,15 @@ bool InvokeInst::paramHasAttr(uint16_t i, ParameterAttributes attr) const {
   return false;
 }
 
+void InvokeInst::setDoesNotThrow(bool doesNotThrow) {
+  const ParamAttrsList *PAL = getParamAttrs();
+  if (doesNotThrow)
+    PAL = ParamAttrsList::includeAttrs(PAL, 0, ParamAttr::NoUnwind);
+  else
+    PAL = ParamAttrsList::excludeAttrs(PAL, 0, ParamAttr::NoUnwind);
+  setParamAttrs(PAL);
+}
+
 
 //===----------------------------------------------------------------------===//
 //                        ReturnInst Implementation
diff --git a/test/Transforms/Inline/2007-12-19-InlineNoUnwind.ll b/test/Transforms/Inline/2007-12-19-InlineNoUnwind.ll
new file mode 100644 (file)
index 0000000..6264f8f
--- /dev/null
@@ -0,0 +1,19 @@
+; RUN: llvm-as < %s -o - | opt -inline | llvm-dis | grep nounwind
+; RUN: llvm-as < %s -o - | opt -inline | llvm-dis | grep unreachable
+
+declare i1 @extern()
+
+define internal i32 @test() {
+entry:
+       %n = call i1 @extern( )
+       br i1 %n, label %r, label %u
+r:
+       ret i32 0;
+u:
+       unwind
+}
+
+define i32 @caller() {
+       %X = call i32 @test( ) nounwind
+       ret i32 %X
+}