[llvm-dev] Reserved/Unallocatable Registers

Matthias Braun via llvm-dev llvm-dev at lists.llvm.org
Fri Feb 26 13:54:28 PST 2016


Let's try this again after some longer offline discussions:

= Reserved Registers =
The primary use of reserved registers is to hold values required by runtime conventions. Typical examples are the stack pointer, frame pointer maybe TLS base address, GOT address ...
Zero registers and program counters are an odd special case for which we may be able to provide looser rules.

== Rules ==
1) Reserved registers are always life: They are live-in and live-out of functions. There are no dead-defs, they are still alive after being clobbered by a regmask.
2) It is not legal to add or remove definitions of a reserved register; This implies that we cannot replace it with a different register or temporarily spill/reload it.
3) Calls are considered uses of reserved registers. That means you cannot reorder a write to a reserved register over a call, even if there is no explicit use operand on the call
4) The value of the reserved register can only change for instructions with a Def operand or regmask clobbering the register. This rule is just for clarification, all registers behave like this. See [1] for a note on program counter/time stamp registers.

== Implications ==
- We skip Liveness analysis because we know a reserved register is live anyway.
- Register allocators cannot use a reserved registers: It is never free and therefore considered unallocatable.
- Scheduling has to consider the implicit use on calls
- No special considerations necessary for copy propagation
- Writes to a reserved register are not dead code, because the value is always live out!

== Examples ==
Assume r0 is a normal register r1 is a reserved register:

- We can remove the 2nd COPY here:
  r0 = COPY r1
      ... use v0
  r0 = COPY r1
      ... use v0
- We can remove this COPY because r0 is unused:
    r0 = COPY r1
    return
- We cannot remove this COPY because r1 is live-out:
    r1 = COPY r0
    return
- We cannot reorder the add before the call. The call reads r1 so it has an anti dependency on the add.
    call foobar
    r1 = add r1, 10

== [1] Constant Registers ==
The rules above are designed for the case of normal registers which are reserved for runtime conventions. We also have the case of zero register. We have the concept of a constant register for them which allows us to ignore any reordering constraints and assume all uses read the same value.

We should even be able to fit the program counter into the class of constant registers: The only practical use of reading the program counter is to find relative positions in position independent code (PIC), it is always used in combination with a relocation, which is adjusted to the actual position of the instruction. The value after adding this relocation is constant in the function!

= Unallocatable Registers =

A reserved register is not allocatable, however there are also registers which are unallocatable just because they are explicitely excluded from allocation orders, they are not reserved registers. This can be confusing so I added this section talking about those!

== Rules ==
They behave like normal registers, the only difference is that:
1) The register allocator will never assign an unallocatable register to a virtual register.

== Motivation ==
Typical examples of unallocatable but no reserved registers are:
- A CPUs flag register: The scheduler has to respect the order! We are interested in liveness, but we do not necessarily want to spill/reload them or perform register allocation on a single register.
- X87 floating point stack registers need special handling on top of the generic register allocation

== Impliciations ==
Except for the register allocator not using them they behave like normal registers:
- We track the liveness of unallocatable registers
- The scheduler respects data/output dependencies for unallocatable registers

== Examples ==
Assume r0 is a normal register, r1 is an unallocatable register (but not a reserved one):

- We can remove the 2nd COPY here:
  r0 = COPY r1
      ... use v0
  r0 = COPY r1
      ... use v0

- We can remove remove the following two COPYs because the r0/r1 are not used afterwards:
  r0 = COPY r1
  r1 = COPY r0
  return 

- We can replace r1 with a different (normal register) here (provided we replace all following uses)
 r1 = ...
     // ...
     = use r1

> On Feb 26, 2016, at 11:41 AM, Matt Arsenault via llvm-dev <llvm-dev at lists.llvm.org> wrote:
> 
> On 02/25/2016 06:14 PM, Matthias Braun via llvm-dev wrote:
>> 1) The value read from a reserved register cannot be predicted. Reading a reserved register twice may each time produce a different result.
> This seems broken to me that treating another copy should be assumed to produce a different result. This seems like it should be optimized, and have a special volatile_copy instruction for the special cases where the reserved register may randomly change.
> 
> -Matt
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev



More information about the llvm-dev mailing list