[PATCH] D19995: Optimize access to global variable references in PIE mode when linker supports copy relocations for PIE

Rafael Ávila de Espíndola via llvm-commits llvm-commits at lists.llvm.org
Fri May 6 07:30:24 PDT 2016


rafael added a comment.

Sorry for the long reply, but I think it is critical we get this right.

When a variable/function is defined in a shared library, either the library or the main program has to use a got. The other one can then use direct accesses.

In every non-elf system, access from inside the library are direct (lea foo(%rip)) and from the main binary indirect (mov foo at GOTPCREL(%rip)). The rationale being that access from within the library is more common and normally the library wants the guarantee that its own functions/variables are not preempted. I think it is critical to support something like this in ELF.

On ELF, quite unfortunately IMHO, the default is to allow preemption. If something can be preempted, you need to use a got, if you need to use a got the main executable may as well create a copy relocation and at least it will be able to use a lea.

So the first question is: In your testcase/benchmark, is preemption required or is the variable/function more frequently accessed from the main binary than from the library that defines it? If not, you would probably get even better performance by disallowing preemption and have the library access its own symbols directly and main binary uses a got.

If you do have a case where the main binary is the one with the most frequent accesses (or preemption is required), I think that is fine. With ELF we should be able to support both cases, we just have to be really careful.

Right now the llvm support for preemption is also really bad. We will pretend the symbol cannot be preempted and then at the last minute use a got.

So lets start from the IR up and see how we can represent all cases. It would be particularly desirable that:

- The access type can be decided from looking just at the GV, not the module or codegen flags. The codegen flag would be just for "mov $foo or lea foo(%rip)", not for using a GOT or not.
- The IR linker just works.
- The same IR has the same semantics in COFF, MachO and ELF.
- Simpler IR is used for the common cases.
- Keep the property that one only has to look at the linkage to decide if a function can be inlined or not (thanks to John McCall for suggesting this one).
- A visibility attribute in C ends up with that visibility in the .o.

Lets first consider a how to design a IR that provides that and then see how to map C and command line options to it.  We need IR to cover 4 cases

- Direct access to definition.
- GOT access to definition.
- Direct access to declaration.
- GOT access to declaration.

I propose that the IR we use is:

For

  @a = global i32 42
  define i32* @f() { ret i32* @a }

we know @a cannot be preempted, so we produce

  leaq a(%rip), %rax

For

  @a = external global i32
  define i32* @f() { ret i32* @a }

we don't know where @a is, so we produce

  movq a at GOTPCREL(%rip), %rax

For

  @a = preemptable global i32 42
  define i32* @f() { ret i32* @a }

we have a new linkage type to mark a definition as preemptable.  GVs with preemptable linkage
must have default visibility. We produce:

  movq a at GOTPCREL(%rip), %rax

For

  @a = external_local global i32
  define i32* @f() { ret i32* @a }

We have a new linkage for assuming a declaration is in the current dso.  We produce

  leaq a(%rip), %rax

For

  @a = protected external_local global i32
  define i32* @f() {  ret i32* @a }

We produce

  leaq a(%rip), %rax
  .protected a

Declarations with non-default visibility are required to be external_local.

New, lets see how the various file formats would map from C to the above:

ELF:

- If there is a non-default visibility attribute:
  - Declarations become extern_local if the visibility is not default
  - Use the visibility

- -fPIC. Symbols can be preemepted, so use the preemptable linkage.

- No option or -fPIE: Symbols cannot be preempted:
  - A dllimport declartion is external, others are external_local.

MachO:

- -fPIC: Just like today.
- static (is that supported?): declarations are external_local.

COFF:

- A dllimport declaration is external, others are external_local.

Sorry, I know that this is a lot more work, but I am willing to help since we have to fix this representation deficiency once and for all. I think the steps would be

- Introduce the preeptible and extern_local linkages and verifier checks for them.
- Codegen them as defined above, regardless of -relocation-model.
- Change clang to use the new translation.
- Upgrade declarations with non default visibility to extern_local.
- Verify that all non default visibility declarations are extern_local.
- Consolidate all of the pic/pie into a single option that says if the code is position independent or not. The only difference is using "mov $foo" or "lea foo(%rip)"


http://reviews.llvm.org/D19995





More information about the llvm-commits mailing list