[llvm-commits] [llvm] r38506 - in /llvm/trunk: lib/VMCore/Verifier.cpp test/Verifier/byval-1.ll test/Verifier/byval-2.ll test/Verifier/byval-3.ll test/Verifier/byval-4.ll

Chris Lattner clattner at apple.com
Mon Jul 16 23:21:42 PDT 2007


> Here's my take on this whole area.  Perhaps I'm just confused :)
> On some targets the ABI specifies that C functions that take a by-copy
> struct argument that is sufficiently small should pass it in  
> registers,
> or maybe on the stack.

Right.

> Likewise, on some targets the ABI specifies
> that a function that returns a struct should return it in certain
> registers or on the stack (StructReturn attribute).  On other targets,
> by-copy struct arguments are passed by passing pointer to a copy, or
> by passing a pointer to the original struct with a copy being made in
> the callee, I don't know which.  Likewise, on targets with nothing
> specified for StructReturn in the ABI, the value is returned by  
> writing
> through a pointer into a struct supplied by the caller.
>
> [ByVal and StructReturn are closely related, so maybe we should change
> the names: ByVal -> CopyIn, StructReturn -> CopyOut].
>
> Note that the representation in the IR is "by reference": a pointer to
> the struct is passed:
> 	declare void @h(%struct.foo* byval %num)

Right.

> I think ByVal should mean: if the struct is compatible with the  
> ABI's way
> of passing structs by-copy, then pass using this method, otherwise  
> pass by
> reference.  At the level of the target independent IR, it would be  
> *undefined*
> as to whether a ByVal parameter is in fact passed by reference or  
> by copy.
> My understanding is that this is exactly how StructReturn works:  
> if, before
> calling the function, you write to the struct that is passed in as the
> StructReturn parameter, and then you try reading from the struct  
> inside the
> function you may (returned by reference) or may not (returned by copy)
> see the values you previously wrote.

This won't work.  Unlike struct return, the callee actually can  
notice if this happens.  For example, consider this:

void a() {
   struct Foo A;
   b(A, &A);
}
void b(struct foo B, struct foo *C) {
    B.whatever = 1;
    use C->whatever
}

the call in "a" should just pass the address of A twice to "b", the  
first with the byval flag set.  However, in "b", the store into "B"  
can't modify "*C".


If it were really undefined, this would require the callee to make a  
copy of the struct before passing it in.  In order to get decent  
codegen, this copy would have to be eliminated somehow in the codegen  
stages (because it would be to the wrong location).  Thus, we'd get  
bad/ugly codegen.

> Advantages of this scheme:
> (1) it doesn't *require* any changes to the target independent  
> optimizers, eg
> to alias analysis, the inliner and who knows what else: they can  
> just ignore
> the ByVal attribute, which amounts to considering the struct to  
> have been
> passed by reference (which is how it is represented in the IR).   
> Now, you
> might want to enhance the optimizers so they can exploit the  
> undefinedness
> of whether the struct is passed by reference or by copy, but this  
> is optional
> can be implemented incrementally.  It seems to me that this is much  
> much better
> than having to say in various places: ok, this thing looks like a  
> pointer but
> it's not really a pointer!  That way lies madness.  If I dare say  
> so, that way
> lies... gcc!

These are advantages, but despite the hyperbole, I don't think the  
advantages are that significant.  The inliner needs to be updated,  
the generic code needs to default to lowering byval arguments as a  
copy to a temporary, and passing its address, but otherwise it is  
pretty straight-forward.  It isn't even clear that alias analysis  
needs to change.

> (2) targets that don't have a special way of passing structs by- 
> copy don't
> have to do anything.

This is handled by target-independent code, so it only needs to be  
done once.

> (3) if a function is known not to write to a struct parameter, then  
> that
> parameter could be marked ByVal, meaning that it will (hopefully)  
> get passed
> by the most efficient method for the target (in registers).  I say  
> hopefully,
> because I suppose some ABIs may specify that by-copy structs of any  
> size must
> be passed on the stack, which would mean that promoting a parameter  
> to ByVal
> could result in a slowdown rather than a speedup.

Cute, but can be addressed other ways.

> (4) it conceptually unifies the treatment of ByVal and StructReturn  
> parameters.

It would be nice to do this, but it's not worth sacrificing codegen  
quality to get it.

-Chris



More information about the llvm-commits mailing list