[LLVMdev] Meaning of byval?

Alon Zakai
Sat, 07 Jan 2012 03:11:17 -0000

The docs say "[byval] indicates that the pointer parameter should
really be passed by value to the function. The attribute implies
that a hidden copy of the pointee is made between the caller and
the callee, so the callee is unable to modify the value in the
callee."

I am not sure what this means though - when I generate code
from the LLVM assembly, do I need to do anything with byval?
Either in the calling location or in the called function? I've
been trying to figure this out from generated LLVM assembly
and it puzzles me.

For example, this code

          struct point
          {
            int x, y;
          };

          void dump(struct point p) {
            p.x++; // should not modify
            p.y++; // anything in the caller!
            printf("dump: %d,%d\n", p.x, p.y);
          }

          int main( int argc, const char *argv[] ) {
            point p = { 54, 2 };
            printf("pre:  %d,%d\n", p.x, p.y);
            dump(p);
            return 0;
          }

when compiled with some LLVM optimization passes (not all),
looks like this in main():

  %p = alloca %struct.point, align 4              ; [#uses=5
type=%struct.point*]
  %agg.tmp = alloca %struct.point, align 4        ; [#uses=1
type=%struct.point*]
  [..]
  %3 = bitcast %struct.point* %agg.tmp to i8*, !dbg !39 ; [#uses=1 type=i8*]
[debug line = 18:13]
  %4 = bitcast %struct.point* %p to i8*, !dbg !39 ; [#uses=1 type=i8*] [debug
line = 18:13]
  call void @llvm.memcpy.p0i8.p0i8.i32(i8* %3, i8* %4, i32 8, i32 4, i1 false),
!dbg !39 ; [debug line = 18:13]
  call void @_Z4dump5point(%struct.point* byval align 4 %p), !dbg !39 ; [debug
line = 18:13]

It looks like a copy is made in the caller, but the copy
isn't used - instead the original is passed to the function.
So this will not work properly unless the called function
creates a copy. There is no explicit code created for this,
but since it is tagged 'byval' an implementation can create
a copy in the called function. Is that what I should do?

Or, is it the responsibility of the caller to create a
copy, and this will appear explicitly in the LLVM assembly?
A copy does appear to be made in the code above, it just
isn't used - is that a bug?

I don't see this happen without optimizations - then
the temporary copy is actually used. Is the problem then
that I am running some, but not all LLVM optimization
passes? In other words, is it possible for the opt tool
to output code that doesn't work (without other passes
being run later)? Or is there some meaning to byval that
makes it valid - I guess it would have to mean that
implementations need to create a copy in the called
function?

Best,
  Alon Zakai