C struct as function argument

I've been working on a wrapper for LLVM's C++ API to use from Objective-C for a scripting language I'm working on. I currently have an issue with passing arguments to a function that takes a struct argument.

typedef struct _test_struct {
    int x;
    int y;
} test_struct;

id testLLVMStructFuncCall(test_struct x) {
    NSLog(@"%d %d",x.x,x.y);
    return N(x.x + x.y);
}

-(void) testLLVMStructFuncCall {
    CGKModule* myMod = [CGKModule moduleWithName:@"llvm_structfunccall_test"];
    CGKType* testStructType = [CGKType structTypeWithElementTypes:[NSArray arrayWithObjects:[CGKType intTypeWith32Bits],[CGKType intTypeWith32Bits],nil]];
    CGKFunction* lfunc = [CGKFunction functionWithName:@"testLLVMStructFuncCall" types:[NSArray arrayWithObjects:[CGKType idType],testStructType,nil] intoModule:myMod];
    CGKFunction* rfunc = [CGKBuilder createStandaloneCallForFunction:lfunc withArguments:[NSArray
                                                                                          arrayWithObjects:
                                                                                          [CGKConstant getStructOfType:testStructType
                                                                                                            withValues:[NSArray arrayWithObjects:[CGKConstant getIntConstant:N(10) bits:32],
                                                                                                                        [CGKConstant getIntConstant:N(25) bits:32],nil]],nil]
                                                            inModule:myMod];
    [myMod dump];
    id var = [[CGKRunner runnerForModule:myMod] runCGKFunction:rfunc];
    assertThat(var,is(equalTo(N(35))));
}

Thats the code in question. The CGK* types are part of my wrapper, and simply wrap the necessary part of the LLVM API.

Test Case '-[SVFunctionTests testLLVMStructFuncCall]' started.
; ModuleID = 'llvm_structfunccall_test'

%0 = type { i32, i32 }

declare i64* @testLLVMStructFuncCall(%0)

define i64* @0() {
entry:
  %0 = call i64* @testLLVMStructFuncCall(%0 { i32 10, i32 25 })
  ret i64* %0
}
2011-06-20 21:25:54.821 otest-x86_64[3369:707] 10 0
/Users/mtindal/Projects/Silver/Tests/SVFunctionTests.m:576: error: -[SVFunctionTests testLLVMStructFuncCall] : Expected <35>, but was <10>
Test Case '-[SVFunctionTests testLLVMStructFuncCall]' failed (0.016 seconds).

The module dump suggests everything is right, i can see in the function call the struct with the values 10 and 25, but the function is only received the 10 for the x field, nothing for the y field. Am I missing something?

The LLVM IR can't really completely represent calling conventions, so
there are hacks in the frontends for C-family languages to make the
generated code compatible with other compilers. Take a look at the IR
clang generates for testLLVMStructFuncCall.

-Eli

Hello Michael,

The module dump suggests everything is right, i can see in the function call the struct with the values 10 and 25, but the function is only received the 10 for the x field, nothing for the y field. Am I missing something?

You're missing the Platform ABI. I assume you're on x86-64, then your
struct (according to the ABI) should be passed in a single 64 bit
register as a whole. However you're passing {10, 25} as two separate
32-bit values. Given that 32-bit operations do implicit zero extension
it's clear why you have 0 as the second value.

To be precise: the C code expects to receive 25 in the top 32 bits of
the first argument register, but you're passing the value in the low
32 bits of the second argument register.

Thank you for your reply, and that makes a lot of sense. Changing the y value to double makes the code I had work just fine. The problem is that the code I showed was more of just a test to make sure things work. This is code that will used in code that is used to generate a bridge between my language and C. Is there a relatively simple way to tell the JIT or whoever which ABI we're using, so that, for example, LLVM would generate the necessary bit casts to ensure it worked, or will I have to hard code these special cases? For reference, this is the code I use to generate the function declarations:

-(id) initWithName:(NSString *)name types:(NSArray *)types intoModule:(CGKModule *)module {
   if((self = [super init])) {
       __strong std::vector<const Type*>* _types = new std::vector<const Type*>();
       CGKType* rettype = [types objectAtIndex:0];
       NSMutableArray* __types = [NSMutableArray arrayWithArray:[types subarrayWithRange:NSMakeRange(1,[types count] - 1)]];
       for(NSUInteger i = 0; i < [__types count]; i++) {
           _types->push_back([(CGKType*)[__types objectAtIndex:i] type]);
       }
       FunctionType* _ftype = FunctionType::get(rettype.type, *_types, false);
       self.function = Function::Create(_ftype, Function::ExternalLinkage, [name UTF8String], module.module);
   }
   return self;
}