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