[cfe-dev] Argument type coercion for big-endian targets
Jakob Stoklund Olesen
stoklund at 2pi.dk
Mon May 27 22:20:35 PDT 2013
SPARC v9 is a big-endian ABI which passes small structs in registers. Structs that are not a multiple of 64 bits are padded with undefined bits.
For example:
struct tiny {
char a;
};
int f_tiny(struct tiny x) {
return x.a;
}
The single-byte struct is passed in the high 8 bits of an i64 register.
Currently, SparcV9ABIInfo returns ABIArgInfo::getDirect(i64) for struct tiny. That produces this code:
; Function Attrs: nounwind
define signext i32 @f_tiny(i64 %x.coerce) #0 {
entry:
%x = alloca %struct.tiny, align 8
%coerce.dive = getelementptr %struct.tiny* %x, i32 0, i32 0
%coerce.val.ii = trunc i64 %x.coerce to i8
store i8 %coerce.val.ii, i8* %coerce.dive
%a = getelementptr inbounds %struct.tiny* %x, i32 0, i32 0
%0 = load i8* %a, align 1
%conv = sext i8 %0 to i32
ret i32 %conv
}
Notice the trunc instruction. IIUC, that is not right for a big-endian target. The coercion should happen as if through memory, right?
If I add an element to the struct, I get the right result:
struct tiny {
char a;
double b;
};
SparcV9ABIInfo returns ABIArgInfo::getDirect( { i64, double } ) for this struct which generates the right code:
%struct.tiny = type { i8, double }
; Function Attrs: nounwind
define signext i32 @f_tiny(i64 %x.coerce0, double %x.coerce1) #0 {
entry:
%x = alloca %struct.tiny, align 8
%0 = bitcast %struct.tiny* %x to { i64, double }*
%1 = getelementptr { i64, double }* %0, i32 0, i32 0
store i64 %x.coerce0, i64* %1
%2 = getelementptr { i64, double }* %0, i32 0, i32 1
store double %x.coerce1, double* %2
%a = getelementptr inbounds %struct.tiny* %x, i32 0, i32 0
%3 = load i8* %a, align 1
%conv = sext i8 %3 to i32
ret i32 %conv
}
The problem seems to be this part of CreateCoercedLoad() in CGCall.cpp:
// If the source and destination are integer or pointer types, just do an
// extension or truncation to the desired type.
if ((isa<llvm::IntegerType>(Ty) || isa<llvm::PointerType>(Ty)) &&
(isa<llvm::IntegerType>(SrcTy) || isa<llvm::PointerType>(SrcTy))) {
llvm::LoadInst *Load = CGF.Builder.CreateLoad(SrcPtr);
return CoerceIntOrPtrToIntOrPtr(Load, Ty, CGF);
}
The CoerceIntOrPtrToIntOrPtr() function creates trunc/zext/sext instructions if the src and dst types don't have the same size. That seems to assume a little-endian target.
What's the right fix here? Should CoerceIntOrPtrToIntOrPtr() be inserting shifts on big-endian targets?
/jakob
More information about the cfe-dev
mailing list