[llvm] r234663 - [WinEH] Recognize SEH finally block inserted by the frontend

Reid Kleckner reid at kleckner.net
Fri Apr 10 16:12:30 PDT 2015


Author: rnk
Date: Fri Apr 10 18:12:29 2015
New Revision: 234663

URL: http://llvm.org/viewvc/llvm-project?rev=234663&view=rev
Log:
[WinEH] Recognize SEH finally block inserted by the frontend

This allows winehprepare to build sensible llvm.eh.actions calls for SEH
finally blocks.  The pattern matching in this change is brittle and
should be replaced with something more robust soon.  In the meantime,
this will let us write the code that produces __C_specific_handler xdata
tables, which we need regardless of how we decide to get finally blocks
through EH preparation.

Added:
    llvm/trunk/test/CodeGen/WinEH/seh-outlined-finally.ll
Modified:
    llvm/trunk/lib/CodeGen/WinEHPrepare.cpp

Modified: llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/WinEHPrepare.cpp?rev=234663&r1=234662&r2=234663&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/WinEHPrepare.cpp (original)
+++ llvm/trunk/lib/CodeGen/WinEHPrepare.cpp Fri Apr 10 18:12:29 2015
@@ -98,7 +98,8 @@ private:
   void mapLandingPadBlocks(LandingPadInst *LPad, LandingPadActions &Actions);
   CatchHandler *findCatchHandler(BasicBlock *BB, BasicBlock *&NextBB,
                                  VisitedBlockSet &VisitedBlocks);
-  CleanupHandler *findCleanupHandler(BasicBlock *StartBB, BasicBlock *EndBB);
+  void findCleanupHandlers(LandingPadActions &Actions, BasicBlock *StartBB,
+                           BasicBlock *EndBB);
 
   void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB);
 
@@ -402,6 +403,7 @@ bool WinEHPrepare::prepareExceptionHandl
     LandingPadActions Actions;
     mapLandingPadBlocks(LPad, Actions);
 
+    HandlersOutlined |= !Actions.actions().empty();
     for (ActionHandler *Action : Actions) {
       if (Action->hasBeenProcessed())
         continue;
@@ -413,19 +415,12 @@ bool WinEHPrepare::prepareExceptionHandl
       if (isAsynchronousEHPersonality(Personality)) {
         if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
           processSEHCatchHandler(CatchAction, StartBB);
-          HandlersOutlined = true;
           continue;
         }
       }
 
-      if (outlineHandler(Action, &F, LPad, StartBB, FrameVarInfo)) {
-        HandlersOutlined = true;
-      }
-    } // End for each Action
-
-    // FIXME: We need a guard against partially outlined functions.
-    if (!HandlersOutlined)
-      continue;
+      outlineHandler(Action, &F, LPad, StartBB, FrameVarInfo);
+    }
 
     // Replace the landing pad with a new llvm.eh.action based landing pad.
     BasicBlock *NewLPadBB = BasicBlock::Create(Context, "lpad", &F, LPadBB);
@@ -1340,13 +1335,7 @@ void WinEHPrepare::mapLandingPadBlocks(L
   DEBUG(dbgs() << "Mapping landing pad: " << BB->getName() << "\n");
 
   if (NumClauses == 0) {
-    // This landing pad contains only cleanup code.
-    CleanupHandler *Action = new CleanupHandler(BB);
-    CleanupHandlerMap[BB] = Action;
-    Actions.insertCleanupHandler(Action);
-    DEBUG(dbgs() << "  Assuming cleanup code in block " << BB->getName()
-                 << "\n");
-    assert(LPad->isCleanup());
+    findCleanupHandlers(Actions, BB, nullptr);
     return;
   }
 
@@ -1366,14 +1355,8 @@ void WinEHPrepare::mapLandingPadBlocks(L
       // exceptions but code called from catches can. For SEH, it isn't
       // important if some finally code before a catch-all is executed out of
       // line or after recovering from the exception.
-      if (Personality == EHPersonality::MSVC_CXX) {
-        if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
-          //   Add a cleanup entry to the list
-          Actions.insertCleanupHandler(CleanupAction);
-          DEBUG(dbgs() << "  Found cleanup code in block "
-                       << CleanupAction->getStartBlock()->getName() << "\n");
-        }
-      }
+      if (Personality == EHPersonality::MSVC_CXX)
+        findCleanupHandlers(Actions, BB, BB);
 
       // Add the catch handler to the action list.
       CatchHandler *Action =
@@ -1390,13 +1373,7 @@ void WinEHPrepare::mapLandingPadBlocks(L
 
     CatchHandler *CatchAction = findCatchHandler(BB, NextBB, VisitedBlocks);
     // See if there is any interesting code executed before the dispatch.
-    if (auto *CleanupAction =
-            findCleanupHandler(BB, CatchAction->getStartBlock())) {
-      //   Add a cleanup entry to the list
-      Actions.insertCleanupHandler(CleanupAction);
-      DEBUG(dbgs() << "  Found cleanup code in block "
-                   << CleanupAction->getStartBlock()->getName() << "\n");
-    }
+    findCleanupHandlers(Actions, BB, CatchAction->getStartBlock());
 
     assert(CatchAction);
     ++HandlersFound;
@@ -1412,12 +1389,7 @@ void WinEHPrepare::mapLandingPadBlocks(L
 
   // If we didn't wind up in a catch-all, see if there is any interesting code
   // executed before the resume.
-  if (auto *CleanupAction = findCleanupHandler(BB, BB)) {
-    //   Add a cleanup entry to the list
-    Actions.insertCleanupHandler(CleanupAction);
-    DEBUG(dbgs() << "  Found cleanup code in block "
-                 << CleanupAction->getStartBlock()->getName() << "\n");
-  }
+  findCleanupHandlers(Actions, BB, BB);
 
   // It's possible that some optimization moved code into a landingpad that
   // wasn't
@@ -1477,20 +1449,56 @@ CatchHandler *WinEHPrepare::findCatchHan
   return nullptr;
 }
 
-// These are helper functions to combine repeated code from findCleanupHandler.
-static CleanupHandler *
-createCleanupHandler(CleanupHandlerMapTy &CleanupHandlerMap, BasicBlock *BB) {
+// These are helper functions to combine repeated code from findCleanupHandlers.
+static void createCleanupHandler(LandingPadActions &Actions,
+                                 CleanupHandlerMapTy &CleanupHandlerMap,
+                                 BasicBlock *BB) {
   CleanupHandler *Action = new CleanupHandler(BB);
   CleanupHandlerMap[BB] = Action;
-  return Action;
+  Actions.insertCleanupHandler(Action);
+  DEBUG(dbgs() << "  Found cleanup code in block "
+               << Action->getStartBlock()->getName() << "\n");
+}
+
+static bool isFrameAddressCall(Value *V) {
+  return match(V, m_Intrinsic<Intrinsic::frameaddress>(m_SpecificInt(0)));
+}
+
+static CallSite matchOutlinedFinallyCall(BasicBlock *BB,
+                                         Instruction *MaybeCall) {
+  // Look for finally blocks that Clang has already outlined for us.
+  //   %fp = call i8* @llvm.frameaddress(i32 0)
+  //   call void @"fin$parent"(iN 1, i8* %fp)
+  if (isFrameAddressCall(MaybeCall) && MaybeCall != BB->getTerminator())
+    MaybeCall = MaybeCall->getNextNode();
+  CallSite FinallyCall(MaybeCall);
+  if (!FinallyCall || FinallyCall.arg_size() != 2)
+    return CallSite();
+  if (!match(FinallyCall.getArgument(0), m_SpecificInt(1)))
+    return CallSite();
+  if (!isFrameAddressCall(FinallyCall.getArgument(1)))
+    return CallSite();
+  return FinallyCall;
+}
+
+static BasicBlock *followSingleUnconditionalBranches(BasicBlock *BB) {
+  // Skip single ubr blocks.
+  while (BB->getFirstNonPHIOrDbg() == BB->getTerminator()) {
+    auto *Br = dyn_cast<BranchInst>(BB->getTerminator());
+    if (Br && Br->isUnconditional())
+      BB = Br->getSuccessor(0);
+    else
+      return BB;
+  }
+  return BB;
 }
 
 // This function searches starting with the input block for the next block that
 // contains code that is not part of a catch handler and would not be eliminated
 // during handler outlining.
 //
-CleanupHandler *WinEHPrepare::findCleanupHandler(BasicBlock *StartBB,
-                                                 BasicBlock *EndBB) {
+void WinEHPrepare::findCleanupHandlers(LandingPadActions &Actions,
+                                       BasicBlock *StartBB, BasicBlock *EndBB) {
   // Here we will skip over the following:
   //
   // landing pad prolog:
@@ -1507,6 +1515,7 @@ CleanupHandler *WinEHPrepare::findCleanu
   // Anything other than an unconditional branch will kick us out of this loop
   // one way or another.
   while (BB) {
+    BB = followSingleUnconditionalBranches(BB);
     // If we've already scanned this block, don't scan it again.  If it is
     // a cleanup block, there will be an action in the CleanupHandlerMap.
     // If we've scanned it and it is not a cleanup block, there will be a
@@ -1515,7 +1524,12 @@ CleanupHandler *WinEHPrepare::findCleanu
     // avoid creating a null entry for blocks we haven't scanned.
     if (CleanupHandlerMap.count(BB)) {
       if (auto *Action = CleanupHandlerMap[BB]) {
-        return cast<CleanupHandler>(Action);
+        Actions.insertCleanupHandler(Action);
+        DEBUG(dbgs() << "  Found cleanup code in block "
+              << Action->getStartBlock()->getName() << "\n");
+        // FIXME: This cleanup might chain into another, and we need to discover
+        // that.
+        return;
       } else {
         // Here we handle the case where the cleanup handler map contains a
         // value for this block but the value is a nullptr.  This means that
@@ -1527,11 +1541,9 @@ CleanupHandler *WinEHPrepare::findCleanu
         // would terminate the search for cleanup code, so the unconditional
         // branch is the only case for which we might need to continue
         // searching.
-        if (BB == EndBB)
-          return nullptr;
-        BasicBlock *SuccBB;
-        if (!match(BB->getTerminator(), m_UnconditionalBr(SuccBB)))
-          return nullptr;
+        BasicBlock *SuccBB = followSingleUnconditionalBranches(BB);
+        if (SuccBB == BB || SuccBB == EndBB)
+          return;
         BB = SuccBB;
         continue;
       }
@@ -1564,14 +1576,14 @@ CleanupHandler *WinEHPrepare::findCleanu
       // If there is only one landingpad, we may use the lpad directly with no
       // insertions.
       if (isa<LandingPadInst>(ResumeVal))
-        return nullptr;
+        return;
       if (!isa<PHINode>(ResumeVal)) {
         Insert2 = dyn_cast<InsertValueInst>(ResumeVal);
         if (!Insert2)
-          return createCleanupHandler(CleanupHandlerMap, BB);
+          return createCleanupHandler(Actions, CleanupHandlerMap, BB);
         Insert1 = dyn_cast<InsertValueInst>(Insert2->getAggregateOperand());
         if (!Insert1)
-          return createCleanupHandler(CleanupHandlerMap, BB);
+          return createCleanupHandler(Actions, CleanupHandlerMap, BB);
       }
       for (BasicBlock::iterator II = BB->getFirstNonPHIOrDbg(), IE = BB->end();
            II != IE; ++II) {
@@ -1582,10 +1594,10 @@ CleanupHandler *WinEHPrepare::findCleanu
           continue;
         if (!Inst->hasOneUse() ||
             (Inst->user_back() != Insert1 && Inst->user_back() != Insert2)) {
-          return createCleanupHandler(CleanupHandlerMap, BB);
+          return createCleanupHandler(Actions, CleanupHandlerMap, BB);
         }
       }
-      return nullptr;
+      return;
     }
 
     BranchInst *Branch = dyn_cast<BranchInst>(Terminator);
@@ -1596,7 +1608,7 @@ CleanupHandler *WinEHPrepare::findCleanu
       //   br i1 %matches, label %catch14, label %eh.resume
       CmpInst *Compare = dyn_cast<CmpInst>(Branch->getCondition());
       if (!Compare || !Compare->isEquality())
-        return createCleanupHandler(CleanupHandlerMap, BB);
+        return createCleanupHandler(Actions, CleanupHandlerMap, BB);
       for (BasicBlock::iterator II = BB->getFirstNonPHIOrDbg(), IE = BB->end();
            II != IE; ++II) {
         Instruction *Inst = II;
@@ -1606,11 +1618,54 @@ CleanupHandler *WinEHPrepare::findCleanu
           continue;
         if (match(Inst, m_Intrinsic<Intrinsic::eh_typeid_for>()))
           continue;
-        return createCleanupHandler(CleanupHandlerMap, BB);
+        return createCleanupHandler(Actions, CleanupHandlerMap, BB);
       }
       // The selector dispatch block should always terminate our search.
       assert(BB == EndBB);
-      return nullptr;
+      return;
+    }
+
+    if (isAsynchronousEHPersonality(Personality)) {
+      // If this is a landingpad block, split the block at the first non-landing
+      // pad instruction.
+      Instruction *MaybeCall = BB->getFirstNonPHIOrDbg();
+      if (LPadMap) {
+        while (MaybeCall != BB->getTerminator() &&
+               LPadMap->isLandingPadSpecificInst(MaybeCall))
+          MaybeCall = MaybeCall->getNextNode();
+      }
+
+      // Look for outlined finally calls.
+      if (CallSite FinallyCall = matchOutlinedFinallyCall(BB, MaybeCall)) {
+        Function *Fin = FinallyCall.getCalledFunction();
+        assert(Fin && "outlined finally call should be direct");
+        auto *Action = new CleanupHandler(BB);
+        Action->setHandlerBlockOrFunc(Fin);
+        Actions.insertCleanupHandler(Action);
+        CleanupHandlerMap[BB] = Action;
+        DEBUG(dbgs() << "  Found frontend-outlined finally call to "
+                     << Fin->getName() << " in block "
+                     << Action->getStartBlock()->getName() << "\n");
+
+        // Split the block if there were more interesting instructions and look
+        // for finally calls in the normal successor block.
+        BasicBlock *SuccBB = BB;
+        if (FinallyCall.getInstruction() != BB->getTerminator() &&
+            FinallyCall.getInstruction()->getNextNode() != BB->getTerminator()) {
+          SuccBB = BB->splitBasicBlock(FinallyCall.getInstruction()->getNextNode());
+        } else {
+          if (FinallyCall.isInvoke()) {
+            SuccBB = cast<InvokeInst>(FinallyCall.getInstruction())->getNormalDest();
+          } else {
+            SuccBB = BB->getUniqueSuccessor();
+            assert(SuccBB && "splitOutlinedFinallyCalls didn't insert a branch");
+          }
+        }
+        BB = SuccBB;
+        if (BB == EndBB)
+          return;
+        continue;
+      }
     }
 
     // Anything else is either a catch block or interesting cleanup code.
@@ -1624,21 +1679,21 @@ CleanupHandler *WinEHPrepare::findCleanu
         continue;
       // If this is a catch block, there is no cleanup code to be found.
       if (match(Inst, m_Intrinsic<Intrinsic::eh_begincatch>()))
-        return nullptr;
+        return;
       // If this a nested landing pad, it may contain an endcatch call.
       if (match(Inst, m_Intrinsic<Intrinsic::eh_endcatch>()))
-        return nullptr;
+        return;
       // Anything else makes this interesting cleanup code.
-      return createCleanupHandler(CleanupHandlerMap, BB);
+      return createCleanupHandler(Actions, CleanupHandlerMap, BB);
     }
 
     // Only unconditional branches in empty blocks should get this far.
     assert(Branch && Branch->isUnconditional());
     if (BB == EndBB)
-      return nullptr;
+      return;
     BB = Branch->getSuccessor(0);
   }
-  return nullptr;
+  return;
 }
 
 // This is a public function, declared in WinEHFuncInfo.h and is also

Added: llvm/trunk/test/CodeGen/WinEH/seh-outlined-finally.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/seh-outlined-finally.ll?rev=234663&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/seh-outlined-finally.ll (added)
+++ llvm/trunk/test/CodeGen/WinEH/seh-outlined-finally.ll Fri Apr 10 18:12:29 2015
@@ -0,0 +1,155 @@
+; RUN: opt -S -winehprepare -sehprepare -mtriple=x86_64-windows-msvc < %s | FileCheck %s
+
+; Test case based on this code:
+;
+; extern "C" int _abnormal_termination();
+; #pragma intrinsic(_abnormal_termination)
+; extern "C" int printf(const char *, ...);
+; extern "C" void may_crash() {
+;   *(volatile int *)0 = 42;
+; }
+; int main() {
+;   int myres = 0;
+;   __try {
+;     __try {
+;       may_crash();
+;     } __finally {
+;       printf("inner finally %d\n", _abnormal_termination());
+;       may_crash();
+;     }
+;   } __finally {
+;     printf("outer finally %d\n", _abnormal_termination());
+;   }
+; }
+;
+; Note that if the inner finally crashes, the outer finally still runs. There
+; is nothing like a std::terminate call in this situation.
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+ at str_outer_finally = linkonce_odr unnamed_addr constant [18 x i8] c"outer finally %d\0A\00", align 1
+ at str_inner_finally = linkonce_odr unnamed_addr constant [18 x i8] c"inner finally %d\0A\00", align 1
+
+; Function Attrs: nounwind uwtable
+define void @may_crash() #0 {
+entry:
+  store volatile i32 42, i32* null, align 4
+  ret void
+}
+
+; Function Attrs: uwtable
+define i32 @main() #1 {
+entry:
+  %myres = alloca i32, align 4
+  %exn.slot = alloca i8*
+  %ehselector.slot = alloca i32
+  store i32 0, i32* %myres, align 4
+  invoke void @may_crash() #4
+          to label %invoke.cont unwind label %lpad
+
+invoke.cont:                                      ; preds = %entry
+  %0 = call i8* @llvm.frameaddress(i32 0)
+  invoke void @"\01?fin$1 at 0@main@@"(i1 zeroext false, i8* %0) #4
+          to label %invoke.cont2 unwind label %lpad1
+
+invoke.cont2:                                     ; preds = %invoke.cont
+  %1 = call i8* @llvm.frameaddress(i32 0)
+  call void @"\01?fin$0 at 0@main@@"(i1 zeroext false, i8* %1)
+  ret i32 0
+
+lpad:                                             ; preds = %entry
+  %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+          cleanup
+  %3 = extractvalue { i8*, i32 } %2, 0
+  store i8* %3, i8** %exn.slot
+  %4 = extractvalue { i8*, i32 } %2, 1
+  store i32 %4, i32* %ehselector.slot
+  %5 = call i8* @llvm.frameaddress(i32 0)
+  invoke void @"\01?fin$1 at 0@main@@"(i1 zeroext true, i8* %5) #4
+          to label %invoke.cont3 unwind label %lpad1
+
+lpad1:                                            ; preds = %lpad, %invoke.cont
+  %6 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+          cleanup
+  %7 = extractvalue { i8*, i32 } %6, 0
+  store i8* %7, i8** %exn.slot
+  %8 = extractvalue { i8*, i32 } %6, 1
+  store i32 %8, i32* %ehselector.slot
+  br label %ehcleanup
+
+invoke.cont3:                                     ; preds = %lpad
+  br label %ehcleanup
+
+ehcleanup:                                        ; preds = %invoke.cont3, %lpad1
+  %9 = call i8* @llvm.frameaddress(i32 0)
+  call void @"\01?fin$0 at 0@main@@"(i1 zeroext true, i8* %9)
+  br label %eh.resume
+
+eh.resume:                                        ; preds = %ehcleanup
+  %exn = load i8*, i8** %exn.slot
+  %sel = load i32, i32* %ehselector.slot
+  %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0
+  %lpad.val4 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1
+  resume { i8*, i32 } %lpad.val4
+}
+
+; CHECK-NOT: define internal void @
+
+; CHECK-LABEL: define i32 @main()
+; CHECK: invoke void @may_crash()
+;
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: cleanup
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions(i32 0, void (i1, i8*)* @"\01?fin$1 at 0@main@@", i32 0, void (i1, i8*)* @"\01?fin$0 at 0@main@@")
+; CHECK-NEXT: indirectbr
+;
+; CHECK: landingpad { i8*, i32 }
+; CHECK-NEXT: cleanup
+; CHECK-NEXT: call i8* (...)* @llvm.eh.actions(i32 0, void (i1, i8*)* @"\01?fin$0 at 0@main@@")
+; CHECK-NEXT: indirectbr
+
+; There should not be any *new* cleanup helpers, just the existing ones.
+; CHECK-NOT: define internal void @
+; CHECK: define internal void @"\01?fin$0 at 0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
+; CHECK-NOT: define internal void @
+; CHECK: define internal void @"\01?fin$1 at 0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer)
+; CHECK-NOT: define internal void @
+
+define internal void @"\01?fin$0 at 0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) #2 {
+entry:
+  %frame_pointer.addr = alloca i8*, align 8
+  %abnormal_termination.addr = alloca i8, align 1
+  store i8* %frame_pointer, i8** %frame_pointer.addr, align 8
+  %frombool = zext i1 %abnormal_termination to i8
+  store i8 %frombool, i8* %abnormal_termination.addr, align 1
+  %0 = zext i1 %abnormal_termination to i32
+  %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @str_outer_finally, i32 0, i32 0), i32 %0)
+  ret void
+}
+
+declare i32 @printf(i8*, ...) #2
+
+define internal void @"\01?fin$1 at 0@main@@"(i1 zeroext %abnormal_termination, i8* %frame_pointer) #2 {
+entry:
+  %frame_pointer.addr = alloca i8*, align 8
+  %abnormal_termination.addr = alloca i8, align 1
+  store i8* %frame_pointer, i8** %frame_pointer.addr, align 8
+  %frombool = zext i1 %abnormal_termination to i8
+  store i8 %frombool, i8* %abnormal_termination.addr, align 1
+  %0 = zext i1 %abnormal_termination to i32
+  %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @str_inner_finally, i32 0, i32 0), i32 %0)
+  call void @may_crash()
+  ret void
+}
+
+declare i32 @__C_specific_handler(...)
+
+; Function Attrs: nounwind readnone
+declare i8* @llvm.frameaddress(i32) #3
+
+attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #3 = { nounwind readnone }
+attributes #4 = { noinline }





More information about the llvm-commits mailing list