[llvm-dev] Altering the return address , for a function with multiple return paths

John McCall via llvm-dev llvm-dev at lists.llvm.org
Tue Jul 23 20:42:53 PDT 2019



On 21 Jul 2019, at 12:29, James Y Knight via llvm-dev wrote:

> Yes, indeed!
>
> The SBCL lisp compiler (not llvm based) used to emit functions which would
> return either via ret to the usual instruction after the call, or else load
> the return-address from the stack, then jump 2 bytes later (which would
> skip over either a nop or a short jmp at original target location). Which
> one it used depended upon whether the function was doing a multi-valued
> return (in which case it used ret) or a single-valued return (in which case
> it did the jmp retpc+2).
>
> While this seems like a clever and efficient hack, it actually has an
> absolutely awful effect on performance, due to the unpaired call vs return,
> and the unexpected return address.
>
> SBCL stopped doing this in 2006, a decade later than it should've -- the
> Pentium1 MMX from 1997 already had a hardware return stack which made this
> a really bad idea!
>
> What it does now is have the called function set or clear the carry flag
> (using STC and CLC) immediately before the return. If the caller cares,
> then the caller emits JNC as the first instruction after the call. (but
> callers typically do not care -- most calls only consume a single value,
> and any extra return-values are silently ignored).

On Swift, we've occasionally considered whether it would be useful to be
able to return values in flags.  For example, you could imagine returning
a trinary comparison result on x86_64 based on whether ZF and CF are set.
A function which compares two pairs of unsigned numbers could be compiled
to something like:

```
  cmpq %rdi, %rdx
  jz end
  cmpq %rsi, %rcx
end:
  ret
```

And the caller can switch over the values just by testing the flags.

The main problem is that this is really elegant if you have an
instruction that sets the flags exactly right and really terrible
if you don't.  For example, if we want this function to compare two
pairs of *signed* numbers, we need to move OF to CF without disturbing
ZF, which I don't think is possible without some really ugly
instruction sequences.  (Or we could add 0x8000_0000_0000_0000 to both
operands before the comparison, but that's terrible in its own right.)

That problem isn't as bad if it's just a single boolean in ZF or CF, but
it's still not great, at least on x86.

Now, specialized purposes like SBCL's can definitely still benefit from
being able to return in a flag.  If LLVM had had the ability to return
values in flags, we might've used it in Swift's coroutines ABI, where
(similar to SBCL) any particular return site does know exactly which
value it wants to return.  So it'd be nice if someone was interested in
adding it.

But we did ultimately decide that it wasn't even worth prototyping it
for the generic Swift CC.

John.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20190723/c4fbc9b6/attachment.html>


More information about the llvm-dev mailing list