[llvm-dev] ABI attributes on arguments vs parameters

Reid Kleckner via llvm-dev llvm-dev at lists.llvm.org
Thu Jun 24 10:50:26 PDT 2021

On Thu, Jun 24, 2021 at 9:59 AM Nicolai Hähnle <nhaehnle at gmail.com> wrote:

> This may be a silly question, but... why aren't all ABI-relevant facts
> about functions encoded in the function type? If you zoom back out from the
> problem sufficiently far, it seems that today, some ABI-relevant facts are
> part of the pointer type (argument & return types) while others aren't. It
> seems to me that a saner design would have everything in the same place.
> Which means one of:
> 1. All ABI-relevant information, including calling convention and byval,
> is part of the function type.

When I started working on LLVM, I was also surprised that the calling
convention is not part of the function type. The rationale at the time was
that LLVM value types are immutable: they can only be changed by creating a
new value and doing RAUW. Keeping these ABI details out of the type allows
passes to update them. See the way GlobalOpt applies fastcc to all
non-address-taken internal functions. It just calls `setCallingConvention`.

However, we have many other passes that change FunctionTypes with the
complex RAUW operation, and keeping a parallel list of attributes is
actually a challenge for them. See the way DAE and ArgPromotion build new
function prototypes, create a new Function, and update all call sites.

> 2. Function types become "opaque" (with opaque pointers, perhaps
> FunctionType is eliminated entirely?) and all relevant information is
> stored with the call instruction, independent of direct vs. indirect calls.

I believe that, today, call instructions carry the FunctionType that will
be used to lower the call. If you use IRBuilder to build a call with a
Function*, the type is implicitly taken from the Function. The
FunctionCallee is implicitly constructed, and that overload is used. If you
pass in a Value* (so, maybe an indirect call), the caller must supply a
FunctionType. I'm proposing that we build on that distinction and require
callers that pass a Value* to also pass the other prototype information (CC
and attrs).

I think your idea makes sense, but for practical reasons, I think it makes
sense to introduce something new, tentatively named a FunctionPrototype,
that does not inherit from llvm::Type. Not being a Type allows it to be
mutable. It should carry everything needed for argument lowering: CC, ABI
attrs, and return and argument types. Functions and Calls would carry a
FunctionPrototype, and they should match.

The goal is to make FunctionPrototype mismatch become immediate UB (->
unreachable), just like mismatched CCs are today. However, we are not ready
for that, and we may always want to tolerate things like incompatible
`zeroext/signext` prototypes to permit minor C prototype mismatches between
`int` and `unsigned int` when only positive integer arguments are passed.

Once we have opaque pointers, I think that will greatly simplify argument
promotion: changing the argument list will not require changing the type of
the function. The passes still have to update all callers, but it should no
longer require allocating a new Function.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20210624/a365732b/attachment.html>

More information about the llvm-dev mailing list