[LLVMdev] Passing and returning aggregates (who is responsible for the ABI?)

Christophe de Dinechin christophe at dinechin.org
Mon Nov 5 23:07:59 PST 2007


On 6 nov. 07, at 06:17, Chris Lattner wrote:

>> But then, why refuse aggregates as input or output of a call? What is
>> the rationale?
>
> Because LLVM has no notion of aggregates as "values" that can be
> passed around as atomic units.  This is a very important design point,
> and has many useful values.

I see. You explained one of them in a message on the XL mailing list,  
which I think is worth repeating here:

> This doesn't fit naturally with the way that LLVM does things:  In
> LLVM, each instruction can produce at most one value.  This means that
> a pointer to the instruction is as good as a pointer to the value,
> which dramatically simplifies the IR and everything that consumes or
> produces it.

An additional constraint you did not mention is that all the values  
must be first-class. But what is "first class" actually depends on  
the hardware and ABI. An i64, for instance, is first class on 64-bit  
CPUs, but not on 32-bit CPUs. Is the following legal on a 32-bit target?

	declare i64 @foo(i128, i256)

>   The "getaggregatevalue" is a localized hack to work
> around this for the few cases that return multiple values.

As a matter of fact, what annoys me the most with the  
getaggregatevalue proposal is precisely that it does not seem too  
localized to me. What about:

    %Agg = call {int, float} %foo()
    %intpart = getaggregatevalue {int, float} %Agg, uint 0
    [insert 200 instructions here]
    %floatpart = getaggregatevalue {int, float} %Agg, uint 1

What about a downstream IR manipulation turning that into:

    %Agg = call {int, float} %foo()
    %intpart = getaggregatevalue {int, float} %Agg, uint 0
    br label somewhere
somewhere:
    %floatpart = getaggregatevalue {int, float} %Agg, uint 1

I am afraid that the hack would not remain localized for too long ;-)  
i.e. you probably will need to have stuff to keep the call and  
getaggregatevalue close together.


>>
> Unfortunately, this wouldn't solve the problem that you think it
> does.  For example, lets assume that LLVM allowed you to pass and
> return structs by value.  Even with this, LLVM would not be able to
> directly implement all ABIs "naturally".  For example, some ABIs
> specify that a _Complex double should be returned in two FP registers,
> but that a struct with two doubles in it should be returned in memory.

Even today, that must be special cased, i.e. the IR needs to be  
distinct between the two cases. As I understand it, the following is  
already legal, since vectors are first class:

	declare <2 x double> @builtin_complex_add (<2 x double>, <2 x double>)

That would be the built-in complex type. The user-defined complex-in- 
struct type could be one of the following depending on the ABI:

	declare void @user_complex_add (double, double, double, double,  
{double, double} *)
	declare void @user_complex_add ({double, double} *, double, double,  
double, double)
	declare void @user_complex_add ({double, double} *, {double, double}  
*, {double, double} *)

My proposal would not invalidate any of these, but allow the  
following, which would immediately be expanded to the appropriate  
choice of the above depending on the target calling conventions:

	declare {double, double} @user_complex_add({double, double},  
{double, double})

It's possible that you want to allow some parameter attributes, i.e.  
be able to distinguish:

	declare sret {double, double} @user_complex_add({double, double},  
{double, double})
	declare inreg {double, double} @user_complex_add({double, double},  
{double, double})


> By the time you lower to LLVM, all you have is {double,double}.  In
> fact, there is no way, in general, to retain all the high level
> information in LLVM without flavoring the LLVM IR with target info

Agreed.

Anyway, for the moment, I will generate what LLVM accepts as input.


Thanks
Christophe




More information about the llvm-dev mailing list