[LLVMdev] Plans considering first class structs and multiple return values

Dan Gohman gohman at apple.com
Mon Jun 2 11:48:30 PDT 2008

On Jun 2, 2008, at 8:45 AM, Matthijs Kooijman wrote:

> Hi Dan,
>> Yes, the intention is that getresult will be removed once first-class
>> aggregates are a ready replacement. This won't leave LLVM missing the
>> concept of returning multiple values; a struct can be thought of as
>> a container for multiple values.
> I'm not saying we don't have some way of modeling multiple return  
> values, I'm
> sayin the explicit concept disappears. We can use a struct return  
> type to
> create a function that effectively returns multiple values, but that  
> is still
> a function returning a single value: A struct. In particular, it  
> will be
> impossible to distinguish between a function returning a single  
> struct and a
> function returning multiple values.
> I'm not sure this is a big problem, but it makes adding a return  
> value to a
> function harder. I'm not sure this is really a problem though. Would  
> adding a
> function attribute returns_multiple or something like that be useful?
> returns_multiple would mean to interpret the returned struct as  
> multiple
> return values and in particular forbids to use the resulting value  
> in any way
> but as an operand to extractvalue. The main goal of this is to make
> adding/removing an argument easier, because you only need to modify  
> the
> extractvalues. On the other hand, this limitation sounds a lot like  
> the
> current getresult approach and might not be all to useful.

The requirement to update all callers' call instructions when a callee
gets a new return value is also present in the current MRV-mechanism
with getresult. It's not been a problem we've worried about so far.
Can you give some background about what kinds of things you're thinking
about for this?

>>> Additionally, the current form of the ret instruction is still
>>> useful, for
>>> making multiple return values readable. In particular, writing
>>> 	ret i32 1, i32 2
>>> is a lot more readable than the (functionally identical) three line
>>> return
>>> statement above. However, to make the first class aggregrates even
>>> more
>>> usable, it might be better to remove the multi operand return
>>> instruction and
>>> add support for literal aggregrates. Currently, I think these are  
>>> only
>>> supported as global constants. It would be useful if the following
>>> was valid:
>>> 	ret { i32, i32 } { i32 1, i32 2 }
>> I think this form should be valid once first-class struct support  
>> is  more
>> complete. If it's not accepted today it may be a conflict with the   
>> current
>> multiple-return-value syntax, or it may be a bug.
> It doesn't seem to be accepted by llvm-as. I think you might be able  
> to build
> this in memory, since a ConstantStruct is just a Value*.

Ok. I'll look into this after some more of the basics of
first-class aggregates are in place.

>>> Even more, one would also like to be able to build non constant   
>>> structs
>>> in a similar manner. i.e., writing
>>> 	ret { i32, i32 } { i32 %a, i32 %b }
>>> would be a lot more useful than the current
>>> 	ret i32 %a, i32 %b
>>> form, since in the first form the ret instruction still has a single
>>> operand that is easy to work with.
>> The current design will have it looking like this:
>>        %t0 = insertvalue { i32, i32 } undef, i32 %a, 0
>>        %t1 = insertvalue { i32, i32 } %t0,   i32 %b, 1
>>        ret { i32, i32 } %t1
>> once first-class structs take over from the current multiple- 
>> return- value
>> support. It's significantly more syntax, but it's a significantly   
>> simpler
>> IR schema.
> On the other hand, anyone looking to support multiple return values  
> but other
> (potentially complicated) uses for first class structs would have a  
> harder
> time trying to find out what these nested insertvalues actually do.  
> The main
> difference here is that using insertvalue you can do something like:
> 	%a = phi { i32, i32 } [ %a.0, %foo ], [ %a.1, %bar ]
> 	%b = insertvalue { i32, i32 } %a, i32 0, 0
> which you can't do directly using a literal { } or buildagg kind of
> instruction. OTOH, you can still do things like this using nested  
> structs
> then, so having a builddag will probably not improve things much.
> Anyhow, so much for my blabbering of incoherent thoughts. I think  
> that simply
> using insertvalue for now and not having an explicit multiple return  
> function
> attribute should work fine.

Ok. And as I mentioned before, we can add buildagg (maybe with a
different name ;-)) later if we find it would be of significant
use or convenience. In any case, I'm glad to have someone with a
different perspective thinking about this feature :-).

> Whenever I want to add a function argument, I will just let it  
> return a struct
> of two elements (current value and the new value).

> I'll also add a feature to the sretpromotion pass that flattens out  
> nested
> struct return types as much as possible, without having to  
> reconstruct structs
> at the caller end (ie, preserver struct types that are used in any  
> way other
> than extractvalue in any caller and flatten out all other elements).  
> Would
> this be the right place for that? Or should sretpromotion really  
> only take
> care of promotion sret pointer arguments to multiple return values,  
> and have
> second pass for flattening them out? A problem of that seems to be  
> that it is
> hard for sretpromotion to simply add return values, since it doesn't  
> know
> whether to add to the existing struct return type (if any) or create  
> a new
> struct return type. I guess integrating these two passes makes the  
> most sense,
> then.

I'm not sure what you're saying here. What do you mean by flattening out
nested structs? If you mean moving all the members in nested structs to
be members of a single non-nested struct, that doesn't really buy
anything, because extractvalue and insertvalue can index directly into
nested structs.

> I guess the argumentpromotion pass also needs to be adapted to  
> promote first
> class struct arguments (probably handle them identical to how byval
> pointer-to-struct are handled now), but I don't currently have need  
> of that.
> Lastly, I'll modify IPConstProp and DeadArgElim to properly handle  
> multiple
> return values and do constprop/removal on each of them individually,  
> instead
> of only working when all of them are constant/dead as is done  
> currently.
> I'm also thinking of adding a transformation that makes return  
> values dead if
> they only return an unmodified argument, this can probably go into
> DeadArgElim.

Sounds interesting. One thing to keep in mind here is the tradeoff
between teaching existing optimizations special things about
aggregate values, versus having a separate pass that just promotes
first-class aggregate arguments to a bunch of individual
non-aggregate arguments. The latter would let many existing passes
achieve many of the same results as the former without having to be
burdened with special aggregate knowledge, which would be nice.
But it might be less aggressive in some cases.


More information about the llvm-dev mailing list