[cfe-dev] Problem with struct arguments passed by value

Shantonu Sen ssen at apple.com
Wed Apr 9 13:06:36 PDT 2008


I have the following program that I'm trying to compile with clang on  
Mac OS X on Intel, which clang fails to codegen correctly.

$ cat a.c

typedef struct {
     long location;
     long length;
} CFRange;

void *CFArrayCreate(void *allocator, const void **values, long  
numValues,
						 const void *callBacks);
char CFArrayContainsValue(void *theArray, CFRange range, const void  
*value);

#if CHECK
int checkrange(CFRange range);
#endif

int main(int argc, char *argv[]) {
	void *array;
	CFRange range;
	const void *values[1] = { "foo" };
	
	array = CFArrayCreate((void *)0, values, 1, (void *)0);
	
	range.location = 0;
	range.length = 1;
	
#if CHECK
	if (checkrange(range)) {
		return 2;
	}
#endif

	if (CFArrayContainsValue(array, range, values[0])) {
		return 0;
	} else {
		return 1;
	}
}

#if CHECK
int checkrange(CFRange range)
{
	if (range.length != 1 || range.location != 0) return 1;

	return 0;
}
#endif
$

The problem is that clang, both with -arch i386 and -arch x86_64,  
generates LLVM IR that passes a pointer to a CFRange as the second  
argument to CFArrayContainsValue, instead of either inlining the  
structure in the stack parameter area for 32-bit, or splitting the  
structure across the integer GPRs %rsi/%rdx for 64-bit mode. llvm- 
gcc-4.2 gets this right (as does gcc obviously), so I think it's a  
problem with how clang is codegen-ing function calls (and also how it  
decodes struct arguments to a function. clang is at least self- 
consistent between the caller and callee, even if it doesn't match the  
ABI of code compiled with gcc. CHECK verifies this).

$ gcc -o a a.c -framework CoreFoundation -arch i386
$ ./a
$ echo $?
0
$ gcc -o a a.c -framework CoreFoundation -arch x86_64
$ ./a
$ echo $?
0
$ clang -arch i386 a.c -emit-llvm-bc -o - | llc -o - | as -arch i386 -  
-o a.o
$ gcc -o a a.o -framework CoreFoundation -arch i386
$ ./a
Segmentation fault
$ clang -arch x86_64 a.c -emit-llvm-bc -o - | llc -o - | as -arch  
x86_64 - -o a.o
$ gcc -o a a.o -framework CoreFoundation -arch x86_64
$ ./a
Segmentation fault
$

Looking at just the i386 LLVM code between llvm-gcc-4.2 and clang shows:

$ llvm-gcc-4.2 -S -o - -emit-llvm a.c
...
	%tmp11 = getelementptr %struct.CFRange* %range, i32 0, i32 0		;  
<i32*> [#uses=1]
	%tmp12 = load i32* %tmp11		; <i32> [#uses=1]
	%tmp13 = getelementptr %struct.CFRange* %range, i32 0, i32 1		;  
<i32*> [#uses=1]
	%tmp14 = load i32* %tmp13		; <i32> [#uses=1]
	%tmp15 = call i8 @CFArrayContainsValue( i8* %tmp10, i32 %tmp12, i32  
%tmp14, i8* %tmp9 ) signext nounwind 		; <i8> [#uses=1]
...
$ clang a.c -o - -emit-llvm
...
	%range = alloca %struct.CFRange		; <%struct.CFRange*> [#uses=3]
	%values = alloca [1 x i8*]		; <[1 x i8*]*> [#uses=3]
	%tmp4 = alloca %struct.CFRange		; <%struct.CFRange*> [#uses=2]
...
	%tmp5 = bitcast %struct.CFRange* %tmp4 to i8*		; <i8*> [#uses=1]
	%tmp6 = bitcast %struct.CFRange* %range to i8*		; <i8*> [#uses=1]
	call void @llvm.memcpy.i32( i8* %tmp5, i8* %tmp6, i32 8, i32 4 )
...
	%call9 = call i8 @CFArrayContainsValue( i8* %tmp3, %struct.CFRange*  
%tmp4, i8* %tmp8 )		; <i8> [#uses=1]

When the system CoreFoundation.framework attempts to decode the  
arguments CFArrayContainsValue, it explodes pretty spectacularly.

 From a quick look it looks like this is intentional (although perhaps  
not correct) in CodeGenFunction::EmitCallExpr in clang/lib/CodeGen/ 
CGExpr.cpp and the use of CreateTempAlloca/EmitAggExpr. Maybe  
something different is needed?

Shantonu Sen
ssen at apple.com

Sent from my Mac Pro




More information about the cfe-dev mailing list