[llvm-commits] patch: teach deadargelim to work on externally visible functions!

Dan Gohman gohman at apple.com
Sat Jan 9 10:40:08 PST 2010



On Jan 9, 2010, at 1:56 AM, Török Edwin <edwintorok at gmail.com> wrote:

> On 2010-01-09 11:50, Török Edwin wrote:
>> On 2010-01-09 03:48, Chris Lattner wrote:
>>
>>> On Jan 5, 2010, at 2:58 AM, Török Edwin wrote:
>>>
>>>>>> then why not do further optimizations based on this assumption,  
>>>>>> and
>>>>>> really call the function we see
>>>>>> (instead of going through the .plt and calling whatever that
>>>>>> resolves to
>>>>>> at runtime).
>>>>>>
>>>>>>
>>>>> Can you elaborate?
>>>>>
>>>>>
>>>> Currently an externally visible function is called like this (with
>>>> -fPIC), even when we know
>>>> the function is in the current module:
>>>> callq   foo at PLT
>>>>
>>>> Which does allow for an override at runtime (see example at  
>>>> bottom), but
>>>> that is not what the optimizer assumed.
>>>>
>>> Does GCC do this?  This seems "bad".  Neither gcc or llvm does the
>>> equivalent on darwin.
>>>
>>
>> Yes, try compiling my example on Linux, you'll see that you *can*
>> override those functions.
>> Its useful if you do hooking via LD_PRELOAD, and you want to  
>> intercept
>> calls from a shared lib to itself,
>> but not for much else.
>>
>>
>>>> My suggestion is:
>>>>  - why go through all the trouble of calling it via PLT, and  
>>>> allowing
>>>> runtime override, when what we really want
>>>> is call the function we see? We could call it directly, i.e.:
>>>>       callq foo
>>>>
>>> This makes sense to me, this is what I thought we did already. :)
>>>
>>>
>>>>  - from an ELF symbol visibility point of view this would mean the
>>>> symbols should get protected visibility, instead of default  
>>>> visibility
>>>>
>>> How does this affect visibility?
>>>
>>
>> From the gcc manual:
>> "On ELF, default visibility means that the declaration is  visible to
>> other modules and, in shared libraries,
>> means that the declared entity may be overridden."
>> "Protected visibility is like default visibility except that it
>> indicates that references within the defining module will
>> bind to the definition in that module.  That is, the declared entity
>> cannot be overridden by another module."
>>
>> I don't know if "default" is something needed to respect an ABI, or  
>> its
>> default because gcc chose it to be.
>>
>> So I had a look here:
>> http://people.redhat.com/drepper/dsohowto.pdf
>>
>> It says (refering to -fvisibility):
>> "The default is unsurprisingly 'default' since that is the behavior  
>> of
>> the compiler before the introduction of this option"
>>
>> Also "protected" visibility (i.e. not going through the PLT) doesn't
>> seem like a good idea:
>> "The generic ELF ABI defines another visibility mode:
>> protected [...] This sounds like an ideal mechanism to optimize DSO  
>> by
>> avoiding the
>> use of exported symbols but it isn’t.
>>
>> Processing references to protected symbols is even more
>> expensive than normal lookup. The problem is a require-
>> ment in the ISO C standard. The standard requires that
>> function pointers, pointing to the same function, can be
>> compared for equality. This rule would be violated with a
>> fast and simple-minded implementation of the protected
>> visibility. Assume an application which references a pro-
>> tected function in a DSO. Also in the DSO is another
>> function which references said function. The pointer in
>> the application points to the PLT entry for the function
>> in the application’s PLT. If a protected symbol lookup
>> would simply return the address of the function inside
>> the DSO the addresses would differ."
>>
>> Since Darwin already does the optimization I was talking about, how  
>> does
>> it solve the above issue?
>> (functions from outside DSO referencing function inside DSO via  
>> PLT, and
>> those inside the DSO
>> referencing it directly, thus yielding different addresses).
>>
>>
>
> On a second thought, this could be solved by going through the PLT
> when taking the address of a function, but when calling the function
> directly
> we skip the PLT.
>
>>>>  - then we would get consistent behavior from optimized and
>>>> non-optimized code
>>>>  - you get same behavior regardless which file you place your  
>>>> function
>>>> into, it will always get called directly, instead of indirectly via
>>>> the PLT
>>>>
>>>> Here is an example where the link order of shared libs causes a
>>>> different function to be called:
>>>>
>>> ...
>>>
>>> This example has undefined behavior, so it doesn't matter what we
>>> produce.  We should just always generate the faster code.  This  
>>> sounds
>>> like a pretty serious performance bug on ELF systems.
>>>
>>
>> Agreed, thats why I brought this up.
>>
>>
>
> On 2010-01-09 04:34, Dan Gohman wrote:
>> It may be as simple as this:
>>
>> --- lib/Target/X86/X86ISelLowering.cpp    (revision 93032)
>> +++ lib/Target/X86/X86ISelLowering.cpp    (working copy)
>> @@ -1984,6 +1984,7 @@
>>       // we don't need to use the PLT - we can directly call it.
>>       if (Subtarget->isTargetELF() &&
>>           getTargetMachine().getRelocationModel() == Reloc::PIC_ &&
>> +          (GV->isDeclaration() || GV->isWeakForLinker()) &&
>>           GV->hasDefaultVisibility() && !GV->hasLocalLinkage()) {
>>         OpFlags = X86II::MO_PLT;
>>       } else if (Subtarget->isPICStyleStubAny() &&
>>
>> At least, this does the right thing on the most trivial testcase,
>> and it's the same logic that the Darwin code uses, 5 lines later. Can
>> someone familiar with this check this?
>>
>
> Do function pointers still get the address foo at PLT?
> If yes then it should address my concern above.

Yes (it's not actually @PLT for addresses, but it is what it should  
be). The above patch is in code that only handles actual calls.

Dan



More information about the llvm-commits mailing list