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?
On Mon, Jun 20, 2011 at 11:33 AM, Michael Tindal <babyplatypus at me.com> wrote:> 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. -- With best regards, Anton Korobeynikov Faculty of Mathematics and Mechanics, Saint Petersburg State University
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; } On Jun 20, 2011, at 10:39 PM, Anton Korobeynikov wrote:> 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. > > -- > With best regards, Anton Korobeynikov > Faculty of Mathematics and Mechanics, Saint Petersburg State University