[PATCH] A new HeapToStack allocation promotion pass

Nick Lewycky nicholas at mxc.ca
Sun Oct 6 23:20:37 PDT 2013


Hal Finkel wrote:
>
> ----- Nick Lewycky<nicholas at mxc.ca>  wrote:
>> Hal Finkel wrote:
>>> ----- Original Message -----
>>>> Hal Finkel wrote:
>>>>> ----- Original Message -----
>>>>>> (I am not on the list)
>>>>>>
>>>>>>> This adds a new optimization pass that can 'promote' malloc'd
>>>>>>> memory to
>>>>>>> stack-allocated memory when the lifetime of the allocation can be
>>>>>>> determined to be bounded by the execution of the function.
>>>>>>>
>>>>>>> To be specific, consider the following three cases:
>>>>>>>
>>>>>>> void bar(int *x);
>>>>>>>
>>>>>>> void foo1() {
>>>>>>> int *x = malloc(16);
>>>>>>> bar(x);
>>>>>>> free(x);
>>>>>>> }
>>>>>>>
>>>>>>> In this case the malloc can be replaced by an alloca, and the
>>>>>>> free
>>>>>>> removed. Note that this is true even though the pointer 'x' is
>>>>>>> definitely
>>>>>>> captured (and may be recorded in global storage, etc.).
>>>>>>
>>>>>> Hello,
>>>>>>
>>>>>> this seems to rely on the fact that 'bar' returns normally, and
>>>>>> thus
>>>>>> that
>>>>>> whenever malloc is executed, free will be as well. However, bar
>>>>>> could
>>>>>> never return, or return abnormally by throwing an exception which
>>>>>> will
>>>>>> skip the call to free.
>>>>>>
>>>>>> void bar(int *x) {
>>>>>> free(x);
>>>>>> throw 42;
>>>>>> }
>>>>>>
>>>>>> will result in calling free on the stack. Now if there was a
>>>>>> destructor
>>>>>> calling free in foo1... Do you actually also consider exceptions
>>>>>> when
>>>>>> you
>>>>>> test that all paths from malloc to the exits contain a call to
>>>>>> free?
>>>>>> That
>>>>>> would only leave noreturn functions.
>>>>>
>>>>> Marc,
>>>>>
>>>>> Thanks!
>>>>>
>>>>> My understanding is that the extra control-flow from exception
>>>>> handling should be accounted for by the basic-block
>>>>> successor/predecessor information (because calling a function that
>>>>> might throw uses invoke, and then you see both the regular
>>>>> predecessor and the cleanup block as predecessors).
>>>>
>>>> No, that's only true if the caller handles the extra control-flow. If
>>>> you call (not invoke) a function, and the callee throws, then the
>>>> exception propagates out of the caller, going up the stack until it
>>>> hits
>>>> the function that did use invoke.
>>>>
>>>> Better is to check "nounwind". However, that is also not sufficient,
>>>> because in llvm nounwind functions may call longjmp.
>>>>
>>>> I'll double-check that's correct. Also, you're right, I should
>>>> check
>>>> the function's does-not-return attribute also.
>>>>
>>>> A function which is marked 'noreturn' is guaranteed to never return.
>>>> A
>>>> function not marked 'noreturn' may also not return -- it may
>>>> terminate
>>>> the program, longjmp, throw an exception, or loop infinitely.
>>>
>>> It looks like I could use your 'halting' attribute. What's the status on that?
>>
>> I'm not working on it.
>>
>> There's an infrastructure problem in LLVM that makes this hard. You want
>> to use the function analyses and the call graph analysis for SCCs.
>> Logically this fits into a CGSCC pass but you can't put it there because
>> those can't depend on function passes. Your options are to either make
>> the whole thing a module pass (bad, doesn't get interwoven as part of
>> the inliner run) or to have the CGSCC pass depend on a really hokey
>> module pass which uses the function pass.
>>
>> This is the same problem Chandler is fixing with his PassManager
>> rewrite, with the goal of letting the inliner use the function passes.
>>
>> Also, the "is there a loop local to this function" detection in my patch
>> is wrong, it was detecting presence of natural loops, not presence of
>> backedges.
>
> Do you know how your loop detection could be fixed? Would it be sufficient to check that all backedges in the function had an associated Loop, and if any did not, return a conservative answer?

Find backedges (J-edges) by looking at the CFG and checking which 
successors aren't strictly dominated according to the domtree. For each 
backedge, look up its Loop. If it doesn't have one, bail. (Note that the 
blocks may both be members of a loop, but that may be an outer loop 
incidentally containing both. You need to check that it's actually 
backedge of this loop, which is true when the branch destination is the 
loop header block.) Query SCEV for the loop trip count, if it doesn't 
know, bail.

(Dan Berlin's answer on the thread is also correct, but forgot to 
address the issue of whether the found loops are finite. For any 
irreducible loop, there's no facility in LLVM that determines whether it 
will terminate.)

Nick



More information about the llvm-commits mailing list