Argument type coercion for big-endian targets

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