[llvm-commits] [llvm] r159385 - in /llvm/trunk: lib/Transforms/Scalar/SimplifyCFGPass.cpp test/Transforms/SimplifyCFG/invoke.ll

Nuno Lopes nunoplopes at sapo.pt
Fri Jun 29 09:07:57 PDT 2012


Quoting Duncan Sands <baldrick at free.fr>:

> Hi Nuno,
>>>> make simplifyCFG erase invokes to readonly/readnone functions
>>>
>>> this is wrong, please don't do this!  Being readonly/readnone is  
>>> orthogonal to
>>> being nounwind.
>>
>> Sorry, I guess my commit log was not explicit enough.
>> I'm just removing invokes to functions that are both readnone and  
>> nounwind. The
>> code doesn't touch functions that may unwind.
>
> oof, glad to hear that!  However I'm surprised that this changes anything,
> since the invoke will be turned into a call (because of nounwind), then the
> call will be deleted (because calls of readonly functions are removed if the
> result is not used).  Or are you trying to get the same effect more  
> efficiently?

:)
Yes, the idea was to make it more efficient. And really make sure the  
calls are deleted (I spotted a case where it wasn't, possibly due to  
pass ordering issues).

Nuno


>> My reasoning was a bit simpler than yours (maybe naive, though),  
>> which was that
>> if a function can throw an exception, then we cannot delete a call  
>> to it, since
>> then we'll never possibly see that exception, meaning that we changed the
>> semantics of the program.
>
> Correct.  For calls, they are removed if !mayHaveSideEffects, where
>
>   bool mayHaveSideEffects() const {
>     return mayWriteToMemory() || mayThrow();
>   }
>
> mayThrow means !nounwind, while mayWriteToMemory for a call instruction means
> !readonly.
>
> Ciao, Duncan.
>
>> BTW, thanks for your explanation! Always learning :)
>>
>> Nuno
>>
>>
>>> To convince you of this, consider the following simple way that
>>> some basic exception handling could be implemented: every function  
>>> returns an
>>> extra boolean value.  If "true" is returned in it (indicating that  
>>> an exception
>>> was thrown) then the caller branches to the appropriate code:  
>>> either a landing
>>> pad if called from an invoke, or, if an ordinary call was used, to  
>>> code that
>>> returns "true" in the caller's boolean (i.e. continues to unwind).
>>>
>>> Notice how in this scheme it is perfectly possible for a function  
>>> to be readnone
>>> but throw an exception (i.e. return "true").
>>>
>>> Note that these extra return values don't have to be present at  
>>> the IR level,
>>> codegen can add them as its way of implementing exception  
>>> handling.  In fact
>>> it might be nice if codegen offered this scheme as an option, for languages
>>> that don't need all the dwarf bells and whistles.  In fact vmkit does this
>>> (I don't know if it does it at the IR level or via codegen) because it is
>>> faster for java (which throws exceptions a lot) than using dwarf.
>>>
>>> It is true that the dwarf implementation of exception handling  
>>> writes to memory
>>> all over the place, and thus is incompatible with  
>>> readonly/readnone.  But that's
>>> just due to how the dwarf implementation works, and thus shouldn't  
>>> be baked into
>>> the IR as your patch does.
>>>
>>> In short, at the IR level it would be wrong to do this, because it is
>>> presupposing how codegen will implement exception handling.  Maybe  
>>> it is OK to
>>> do this optimization at the codegen level once you know that dwarf  
>>> exception
>>> handling has been selected?
>>>
>>> Ciao, Duncan.
>>>
>>> PS: a front-end can always output nounwind on functions it knows are
>>> readnone/readonly if it wants those semantics.
>>> PPS: this was endlessly debated on the GCC mailing list, and the  
>>> conclusion for
>>> LLVM was to decouple readnone/readonly and nounwind, which lets  
>>> front-ends make
>>> their own decision.
>>>
>>>>
>>>> Modified:
>>>>     llvm/trunk/lib/Transforms/Scalar/SimplifyCFGPass.cpp
>>>>     llvm/trunk/test/Transforms/SimplifyCFG/invoke.ll
>>>>
>>>> Modified: llvm/trunk/lib/Transforms/Scalar/SimplifyCFGPass.cpp
>>>> URL:
>>>> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/SimplifyCFGPass.cpp?rev=159385&r1=159384&r2=159385&view=diff
>>>>
>>>> ==============================================================================
>>>> --- llvm/trunk/lib/Transforms/Scalar/SimplifyCFGPass.cpp (original)
>>>> +++ llvm/trunk/lib/Transforms/Scalar/SimplifyCFGPass.cpp Thu Jun  
>>>> 28 17:32:27
>>>> 2012
>>>> @@ -89,7 +89,6 @@
>>>>
>>>>  /// ChangeToCall - Convert the specified invoke into a normal call.
>>>>  static void ChangeToCall(InvokeInst *II) {
>>>> -  BasicBlock *BB = II->getParent();
>>>>    SmallVector<Value*, 8> Args(II->op_begin(), II->op_end() - 3);
>>>>    CallInst *NewCall = CallInst::Create(II->getCalledValue(),  
>>>> Args, "", II);
>>>>    NewCall->takeName(II);
>>>> @@ -100,10 +99,7 @@
>>>>
>>>>    // Follow the call by a branch to the normal destination.
>>>>    BranchInst::Create(II->getNormalDest(), II);
>>>> -
>>>> -  // Update PHI nodes in the unwind destination
>>>> -  II->getUnwindDest()->removePredecessor(BB);
>>>> -  BB->getInstList().erase(II);
>>>> +  II->eraseFromParent();
>>>>  }
>>>>
>>>>  static bool MarkAliveBlocks(BasicBlock *BB,
>>>> @@ -163,7 +159,12 @@
>>>>          ChangeToUnreachable(II, true);
>>>>          Changed = true;
>>>>        } else if (II->doesNotThrow()) {
>>>> -        ChangeToCall(II);
>>>> +        if (II->use_empty() && II->onlyReadsMemory()) {
>>>> +          // jump to the normal destination branch.
>>>> +          BranchInst::Create(II->getNormalDest(), II);
>>>> +          II->eraseFromParent();
>>>> +        } else
>>>> +          ChangeToCall(II);
>>>>          Changed = true;
>>>>        }
>>>>      }
>>>>
>>>> Modified: llvm/trunk/test/Transforms/SimplifyCFG/invoke.ll
>>>> URL:
>>>> http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/SimplifyCFG/invoke.ll?rev=159385&r1=159384&r2=159385&view=diff
>>>>
>>>> ==============================================================================
>>>> --- llvm/trunk/test/Transforms/SimplifyCFG/invoke.ll (original)
>>>> +++ llvm/trunk/test/Transforms/SimplifyCFG/invoke.ll Thu Jun 28  
>>>> 17:32:27 2012
>>>> @@ -3,7 +3,7 @@
>>>>
>>>>  declare i32 @__gxx_personality_v0(...)
>>>>  declare void @__cxa_call_unexpected(i8*)
>>>> -declare i64 @llvm.objectsize.i64(i8*, i1) nounwind readonly
>>>> +declare i32 @read_only() nounwind readonly
>>>>
>>>>
>>>>  ; CHECK: @f1
>>>> @@ -43,3 +43,42 @@
>>>>    tail call void @__cxa_call_unexpected(i8* %1) noreturn nounwind
>>>>    unreachable
>>>>  }
>>>> +
>>>> +; CHECK: @f3
>>>> +define i32 @f3() nounwind uwtable ssp {
>>>> +; CHECK-NEXT: entry
>>>> +entry:
>>>> +; CHECK-NEXT: ret i32 3
>>>> +  %call = invoke i32 @read_only()
>>>> +          to label %invoke.cont unwind label %lpad
>>>> +
>>>> +invoke.cont:
>>>> +  ret i32 3
>>>> +
>>>> +lpad:
>>>> +  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
>>>> @__gxx_personality_v0 to i8*)
>>>> +          filter [0 x i8*] zeroinitializer
>>>> +  %1 = extractvalue { i8*, i32 } %0, 0
>>>> +  tail call void @__cxa_call_unexpected(i8* %1) noreturn nounwind
>>>> +  unreachable
>>>> +}
>>>> +
>>>> +; CHECK: @f4
>>>> +define i32 @f4() nounwind uwtable ssp {
>>>> +; CHECK-NEXT: entry
>>>> +entry:
>>>> +; CHECK-NEXT: call i32 @read_only()
>>>> +  %call = invoke i32 @read_only()
>>>> +          to label %invoke.cont unwind label %lpad
>>>> +
>>>> +invoke.cont:
>>>> +; CHECK-NEXT: ret i32 %call
>>>> +  ret i32 %call
>>>> +
>>>> +lpad:
>>>> +  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)*
>>>> @__gxx_personality_v0 to i8*)
>>>> +          filter [0 x i8*] zeroinitializer
>>>> +  %1 = extractvalue { i8*, i32 } %0, 0
>>>> +  tail call void @__cxa_call_unexpected(i8* %1) noreturn nounwind
>>>> +  unreachable
>>>> +}



More information about the llvm-commits mailing list