<div dir="ltr"><div dir="ltr">On Thu, Jun 24, 2021 at 9:59 AM Nicolai Hähnle <<a href="mailto:nhaehnle@gmail.com">nhaehnle@gmail.com</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>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:</div><div><br></div><div>1. All ABI-relevant information, including calling convention and byval, is part of the function type.</div></div></blockquote><div><br></div><div>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`.</div><div><br></div><div>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.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>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.</div></div></blockquote><div><br></div><div>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).</div><div><br></div><div>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.</div><div><br></div><div>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.</div><div><br></div><div>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.</div></div></div>