Description
There are a handful of testsuite failures on SPARC that seem to be related, and they have to do with passing structs by value into C. Although in at least one case (extern-fn-struct-passing-abi
), the C code isn't getting the right values (but there it's a floating-point value), in the integer case Shawn and I have dived into (extern-pass-TwoU8s
), that part appears to be correct. It's the retrieval of the struct's return value that seems to be wrong.
The problem becomes a bit more apparent if we rebuild the test with -g
instead of -O
. I can attach full files if necessary, but here is (what I think is the relevant part of) the IR for the main()
function:
; extern_pass_TwoU8s::main
; Function Attrs: uwtable
define internal void @_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE() unnamed_addr #0 !dbg !294 {
start:
%tmp_ret1 = alloca %"core::fmt::ArgumentV1"
%tmp_ret = alloca %"core::fmt::ArgumentV1"
%abi_cast = alloca i16
%arg = alloca %TwoU8s
%__arg1 = alloca %TwoU8s**
%__arg0 = alloca %TwoU8s**
%_22 = alloca { %TwoU8s**, [0 x i8], %TwoU8s**, [0 x i8] }
%_21 = alloca [2 x %"core::fmt::ArgumentV1"]
%_16 = alloca %"core::fmt::Arguments"
%right_val = alloca %TwoU8s*
%left_val = alloca %TwoU8s*
%_5 = alloca { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }
%y = alloca %TwoU8s
%x = alloca %TwoU8s
call void @llvm.dbg.declare(metadata %TwoU8s* %x, metadata !297, metadata !144), !dbg !299
call void @llvm.dbg.declare(metadata %TwoU8s* %y, metadata !300, metadata !144), !dbg !302
call void @llvm.dbg.declare(metadata %TwoU8s** %left_val, metadata !303, metadata !144), !dbg !306
call void @llvm.dbg.declare(metadata %TwoU8s** %right_val, metadata !307, metadata !144), !dbg !306
call void @llvm.dbg.declare(metadata %TwoU8s*** %__arg0, metadata !308, metadata !144), !dbg !311
call void @llvm.dbg.declare(metadata %TwoU8s*** %__arg1, metadata !312, metadata !144), !dbg !311
%0 = getelementptr inbounds %TwoU8s, %TwoU8s* %x, i32 0, i32 0, !dbg !313
store i8 22, i8* %0, !dbg !313
%1 = getelementptr inbounds %TwoU8s, %TwoU8s* %x, i32 0, i32 2, !dbg !313
store i8 23, i8* %1, !dbg !313
%2 = getelementptr inbounds %TwoU8s, %TwoU8s* %x, i32 0, i32 0, !dbg !314
%3 = getelementptr inbounds %TwoU8s, %TwoU8s* %x, i32 0, i32 2, !dbg !314
%4 = load i8, i8* %2, !dbg !314
%5 = load i8, i8* %3, !dbg !314
%6 = getelementptr inbounds %TwoU8s, %TwoU8s* %arg, i32 0, i32 0, !dbg !314
store i8 %4, i8* %6, !dbg !314
%7 = getelementptr inbounds %TwoU8s, %TwoU8s* %arg, i32 0, i32 2, !dbg !314
store i8 %5, i8* %7, !dbg !314
%8 = bitcast %TwoU8s* %arg to i64*, !dbg !314
%9 = load i64, i64* %8, align 1, !dbg !314
%10 = call i16 @rust_dbg_extern_identity_TwoU8s(i64 %9), !dbg !314
store i16 %10, i16* %abi_cast, !dbg !314
%11 = bitcast %TwoU8s* %y to i8*, !dbg !314
%12 = bitcast i16* %abi_cast to i8*, !dbg !314
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %11, i8* %12, i64 2, i32 1, i1 false), !dbg !314
br label %bb1, !dbg !314
bb1: ; preds = %start
%13 = getelementptr inbounds { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }, { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }* %_5, i32 0, i32 0, !dbg !315
store %TwoU8s* %x, %TwoU8s** %13, !dbg !315
%14 = getelementptr inbounds { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }, { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }* %_5, i32 0, i32 2, !dbg !315
store %TwoU8s* %y, %TwoU8s** %14, !dbg !315
%15 = getelementptr inbounds { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }, { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }* %_5, i32 0, i32 0, !dbg !315
%16 = load %TwoU8s*, %TwoU8s** %15, !dbg !315, !nonnull !91
store %TwoU8s* %16, %TwoU8s** %left_val, !dbg !315
%17 = getelementptr inbounds { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }, { %TwoU8s*, [0 x i8], %TwoU8s*, [0 x i8] }* %_5, i32 0, i32 2, !dbg !315
%18 = load %TwoU8s*, %TwoU8s** %17, !dbg !315, !nonnull !91
store %TwoU8s* %18, %TwoU8s** %right_val, !dbg !315
%19 = load %TwoU8s*, %TwoU8s** %left_val, !dbg !306, !nonnull !91
%20 = load %TwoU8s*, %TwoU8s** %right_val, !dbg !306, !nonnull !91
; call <extern_pass_TwoU8s::TwoU8s as core::cmp::PartialEq>::eq
%21 = call zeroext i1 @"_ZN67_$LT$extern_pass_TwoU8s..TwoU8s$u20$as$u20$core..cmp..PartialEq$GT$2eq17h9059cf2eb03b4778E"(%TwoU8s* noalias readonly dereferenceable(2) %19, %TwoU8s* noalias readonly dereferenceable(2) %20), !dbg !306
br label %bb2, !dbg !306
and the resulting assembly (more or less):
extern-pass-TwoU8s.stage2-sparcv> _ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE::dis
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE: save %sp, -0x1c0, %sp
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+4: call +0x8 <_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0xc>
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+8: sethi %hi(0x106c00), %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0xc: or %i0, 0x168, %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x10: add %i0, %o7, %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x14: mov 0x16, %i1
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x18: stb %i1, [%fp + 0x737]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x1c: add %fp, 0x737, %i1
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x20: or %i1, 0x1, %i1
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x24: mov 0x17, %i2
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x28: stb %i2, [%i1]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x2c: ldub [%fp + 0x737], %i1
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x30: stb %i1, [%fp + 0x7d7]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x34: add %fp, 0x7d7, %i1
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x38: or %i1, 0x1, %i1
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x3c: stb %i2, [%i1]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x40: ldx [%fp + 0x7d7], %o0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x44: call +0x724 <rust_dbg_extern_identity_TwoU8s>
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x48: stx %i0, [%fp + 0x72f]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x4c: sth %o0, [%fp + 0x7dd]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x50: lduh [%fp + 0x7dd], %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x54: ba +0x8 <_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x5c>
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x58: sth %i0, [%fp + 0x73f]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x5c: add %fp, 0x737, %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x60: stx %i0, [%fp + 0x747]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x64: add %fp, 0x73f, %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x68: stx %i0, [%fp + 0x74f]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x6c: ldx [%fp + 0x747], %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x70: stx %i0, [%fp + 0x757]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x74: ldx [%fp + 0x74f], %i0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x78: stx %i0, [%fp + 0x75f]
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x7c: ldx [%fp + 0x757], %o0
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x80: call +0x240 <_ZN67_$LT$extern_pass_TwoU8s..TwoU8s$u20$as$u20$core..cmp..PartialEq$GT$2eq17h9059cf2eb03b4778E>
_ZN18extern_pass_TwoU8s4main17h7a27acd65aa2f60dE+0x84: mov %i0, %o1
We believe the sth
instruction after the call to rust_dbg_extern_identity_TwoU8s
is where it goes wrong, implying something wrong with the store
instruction in the IR.
Here is the IR for the equivalent C function (albeit compiled by clang from a different version of LLVM):
; Function Attrs: nounwind
define signext i32 @main() #0 {
%1 = alloca i32, align 4
%t = alloca %struct.TwoU8s, align 1
%u = alloca %struct.TwoU8s, align 1
%2 = alloca i64, align 8
%3 = alloca %struct.TwoU8s, align 1
%4 = alloca i64, align 8
store i32 0, i32* %1, align 4
%5 = getelementptr inbounds %struct.TwoU8s, %struct.TwoU8s* %t, i32 0, i32 0
store i8 22, i8* %5, align 1
%6 = getelementptr inbounds %struct.TwoU8s, %struct.TwoU8s* %t, i32 0, i32 1
store i8 23, i8* %6, align 1
%7 = bitcast i64* %2 to i8*
%8 = bitcast %struct.TwoU8s* %t to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %7, i8* %8, i64 2, i32 1, i1 false)
%9 = load i64, i64* %2, align 8
%10 = call i64 @rust_dbg_extern_identity_TwoU8s(i64 %9)
store i64 %10, i64* %4, align 8
%11 = bitcast i64* %4 to i8*
%12 = bitcast %struct.TwoU8s* %3 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %12, i8* %11, i64 2, i32 1, i1 false)
%13 = bitcast %struct.TwoU8s* %u to i8*
%14 = bitcast %struct.TwoU8s* %3 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %13, i8* %14, i64 2, i32 1, i1 false)
%15 = getelementptr inbounds %struct.TwoU8s, %struct.TwoU8s* %u, i32 0, i32 0
%16 = load i8, i8* %15, align 1
%17 = zext i8 %16 to i32
%18 = icmp eq i32 %17, 22
br i1 %18, label %21, label %19
Anyway, this was about as far as we got before we needed to take a break, but figured it was worth writing up and seeing if anyone here had any insights. This is all on the beta branch, at commit f38ffa8, though the failure happens on all versions of rust I've run the testsuite for.