How to access llvm Types from the codegen?

In order to the code generators to lower functions arguments that have
the "byval" attribute, they would have to access the original argument
Type. For example, on linux x86_64 {i64, i64} should be passed on
registers and {i64, i64, i64} goes on the stack.

The problem is that when looking at (for example) FORMAL_ARGUMENTS,
the only thing that is present is the type of the pointer itself (i64
for example).

I can see three options to solve the problem:

1) Make FORMAL_ARGUMENTS have one SrcValueSDNode per argument in
addition to the flags and make CALL use SrcValueSDNode for the
arguments. The calling convention functions (CC_X86_64_C) can then be
modified to take a Type as an additional argument.

2) Implement part of the ABI on the DAG construction. In this approach
a struct that should be passed on registers would result on a series
of arguments at the DAG level, similar to what is done by llvm-gcc
today, but one lever lower (llvm -> DAG instead of C -> llvm).
Arguments that should go on the stack result in an argument tagged as
a implicit pointer that the code generators can compute as an offset
from the frame pointer. In order to find the offset only the size of
the struct has to be added to the DAG somehow.

3) Similar to 2, but, for arguments that should go on registers, let
llvm-gcc do the splitting. In this approach the "byval" argument would
be a much simpler extension. It would mean: this pointer is to be
computed as an offset from the frame pointer.

In order to implement options 2 or 3 we have to add the structure size
to the DAG somehow. Suggestions?

I prefer option 3, but I would like to know the opinion from other developers.

Thanks,

Code generator shouldn't introspect the LLVM type at all, except to get the size of the type. Anything needed should be encoded by the front-end. In the short term, please focus on getting parity with what we already have. This means x86-64 will be wrong, but it already is. As a second step we can then worry about x86-64-specific param attributes that should be added to handle this stuff correctly.

Evan

Sorry for the delay. I am back from a vacation and have been busy
catching up....

That is why I need the types (just for the size). This is necessary to
implement both the current behavior and the correct ABI.

consider the llvm code

I propose that structures that are passed on registers should be slip
into many DAG level arguments (for now this would be all structs) and
the DAG should contain copies instead of loads. The nice thing about
this proposal is that for structures that are passed on the stack, the
DAG doesn't need to know the size. All that we need to add to add to
the DAG is a flag so that the code generators know that the struct
pointer should be computed based on the stack pointer.

Just noticed that we still need the size and the alignment of the
structure so that we determine the stack location of subsequent
arguments. So, any ideas on how to add that information to the DAG?

Cheers,

Just noticed that we still need the size and the alignment of the
structure so that we determine the stack location of subsequent
arguments. So, any ideas on how to add that information to the DAG?

I am trying to use the remaining bits of the flag nodes. Should be
sufficient to implement a first version.

Cheers,

Sorry for the delay. I am back from a vacation and have been busy
catching up....

Welcome back :slight_smile:

That is why I need the types (just for the size). This is necessary to
implement both the current behavior and the correct ABI.

Right.

consider the llvm code
---------------------------------------
%struct.s = type { i64 }

define i64 @f(%struct.s* byval %a) {
entry:
       %tmp2 = getelementptr %struct.s* %a, i32 0, i32 0
; <i64*> [#uses=1]
       %tmp3 = load i64* %tmp2 ; <i64> [#uses=1]
       ret i64 %tmp3
---------------------------------------

When the DAG is constructed it will contain a load from formal_args.

It shouldn't contain a load: the "a" argument (which represents the address by-val arg) will be one of the results of the formal_arguments node. Ah, you probably mean the explicit load will turn into a load SDNode, which is right :slight_smile:

It looks like a lot of work to modify the DAG so that instead it uses
a copy. Even if we decide to do this, we would need to pass the size
of the structure to the DAG, which currently is not available.

Okay, you're saying that in this example, the "byval" argument is supposed to be passed in a register, which does not have an address. Further, the explicit load ideally becomes dead.

There are multiple steps to get optimal code for x86-64. In the short term, I think it makes the most sense for the code generator to lower this into a store onto a stack, and use the address of the stack slot as the "result" value of the formal_argument. Thus you lower this "dag":

   a,chain = formal_arguments (...)

into a 64-bit stack slot (e.g. frame index #1) and:

   chain = store %rax -> (frameindex #1)
   a = frameindex #1

This will leave the loads in the code (ugly codegen) but will work.

Going forward, we can optimize out this stuff by having the front-end generate a simple i64 argument instead of using byval in this case. However, it should always be *safe* to use byval, so the codegen needs to learn how to handle this.

I propose that structures that are passed on registers should be slip
into many DAG level arguments (for now this would be all structs) and
the DAG should contain copies instead of loads. The nice thing about
this proposal is that for structures that are passed on the stack, the
DAG doesn't need to know the size. All that we need to add to add to
the DAG is a flag so that the code generators know that the struct
pointer should be computed based on the stack pointer.

I don't follow, can you explain more what you mean?

-Chris

Yep, sounds good.

-Chris

> ---------------------------------------
> %struct.s = type { i64 }
>
> define i64 @f(%struct.s* byval %a) {
> entry:
> %tmp2 = getelementptr %struct.s* %a, i32 0, i32 0
> ; <i64*> [#uses=1]
> %tmp3 = load i64* %tmp2 ; <i64> [#uses=1]
> ret i64 %tmp3
> ---------------------------------------
>
> When the DAG is constructed it will contain a load from formal_args.

It shouldn't contain a load: the "a" argument (which represents the
address by-val arg) will be one of the results of the formal_arguments
node. Ah, you probably mean the explicit load will turn into a load
SDNode, which is right :slight_smile:

Yes. That is it :slight_smile:

> It looks like a lot of work to modify the DAG so that instead it uses
> a copy. Even if we decide to do this, we would need to pass the size
> of the structure to the DAG, which currently is not available.

Okay, you're saying that in this example, the "byval" argument is supposed
to be passed in a register, which does not have an address. Further, the
explicit load ideally becomes dead.

There are multiple steps to get optimal code for x86-64. In the short
term, I think it makes the most sense for the code generator to lower this
into a store onto a stack, and use the address of the stack slot as the
"result" value of the formal_argument. Thus you lower this "dag":

   a,chain = formal_arguments (...)

into a 64-bit stack slot (e.g. frame index #1) and:

   chain = store %rax -> (frameindex #1)
   a = frameindex #1

This will leave the loads in the code (ugly codegen) but will work.

Looks like a nice first implementation :slight_smile:

Going forward, we can optimize out this stuff by having the front-end
generate a simple i64 argument instead of using byval in this case.
However, it should always be *safe* to use byval, so the codegen needs to
learn how to handle this.

..... (some badly worded proposal)

I don't follow, can you explain more what you mean?

The idea would be to define byval as being always on the stack. This
would make it wrong for the front end (on linux-x86_64) to lower

struct s {
long a;
};
void f(struct s a) {....}

into a f function with a byval argument. It would have to use a i64.
This would make the code gen easier. Given your suggestion of just
storing the incoming register, not much so. I don't have any strong
opinions on which semantics we give to the byval attribute, but I like
the always on the stack a little bit better.

-Chris

Cheers,