From ac785122aba50336650c598002c34642754f8af0 Mon Sep 17 00:00:00 2001 From: James Y Knight Date: Tue, 15 Dec 2015 19:23:12 +0000 Subject: [PATCH] [Sparc] Fix handling of double incoming arguments on sparc little-endian. On SparcV8, doubles get passed in two 32-bit integer registers. The call code was already handling endianness correctly, but the incoming argument code was not -- it got the two halves in opposite order. Also remove some dead code in LowerFormalArguments_32 to handle less-than-32bit values, which can't actually happen. Finally, add some test cases for the 32-bit calling convention, cribbed from the 64abi.ll test, and run for both big and little-endian. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@255668 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/Sparc/SparcISelLowering.cpp | 22 +-- test/CodeGen/SPARC/32abi.ll | 191 +++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 test/CodeGen/SPARC/32abi.ll diff --git a/lib/Target/Sparc/SparcISelLowering.cpp b/lib/Target/Sparc/SparcISelLowering.cpp index 799fe00aebc..47c8c3a07b1 100644 --- a/lib/Target/Sparc/SparcISelLowering.cpp +++ b/lib/Target/Sparc/SparcISelLowering.cpp @@ -400,6 +400,7 @@ LowerFormalArguments_32(SDValue Chain, CCInfo.AnalyzeFormalArguments(Ins, CC_Sparc32); const unsigned StackOffset = 92; + bool IsLittleEndian = DAG.getDataLayout().isLittleEndian(); unsigned InIdx = 0; for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i, ++InIdx) { @@ -442,6 +443,10 @@ LowerFormalArguments_32(SDValue Chain, &SP::IntRegsRegClass); LoVal = DAG.getCopyFromReg(Chain, dl, loReg, MVT::i32); } + + if (IsLittleEndian) + std::swap(LoVal, HiVal); + SDValue WholeValue = DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, LoVal, HiVal); WholeValue = DAG.getNode(ISD::BITCAST, dl, VA.getLocVT(), WholeValue); @@ -498,6 +503,9 @@ LowerFormalArguments_32(SDValue Chain, MachinePointerInfo(), false, false, false, 0); + if (IsLittleEndian) + std::swap(LoVal, HiVal); + SDValue WholeValue = DAG.getNode(ISD::BUILD_PAIR, dl, MVT::i64, LoVal, HiVal); WholeValue = DAG.getNode(ISD::BITCAST, dl, VA.getValVT(), WholeValue); @@ -514,16 +522,12 @@ LowerFormalArguments_32(SDValue Chain, Load = DAG.getLoad(VA.getValVT(), dl, Chain, FIPtr, MachinePointerInfo(), false, false, false, 0); + } else if (VA.getValVT() == MVT::f128) { + report_fatal_error("SPARCv8 does not handle f128 in calls; " + "pass indirectly"); } else { - ISD::LoadExtType LoadOp = ISD::SEXTLOAD; - // Sparc is big endian, so add an offset based on the ObjectVT. - unsigned Offset = 4-std::max(1U, VA.getValVT().getSizeInBits()/8); - FIPtr = DAG.getNode(ISD::ADD, dl, MVT::i32, FIPtr, - DAG.getConstant(Offset, dl, MVT::i32)); - Load = DAG.getExtLoad(LoadOp, dl, MVT::i32, Chain, FIPtr, - MachinePointerInfo(), - VA.getValVT(), false, false, false,0); - Load = DAG.getNode(ISD::TRUNCATE, dl, VA.getValVT(), Load); + // We shouldn't see any other value types here. + assert(false && "Unexpected ValVT encountered in frame lowering."); } InVals.push_back(Load); } diff --git a/test/CodeGen/SPARC/32abi.ll b/test/CodeGen/SPARC/32abi.ll new file mode 100644 index 00000000000..7ac1de5c090 --- /dev/null +++ b/test/CodeGen/SPARC/32abi.ll @@ -0,0 +1,191 @@ +; RUN: llc < %s -march=sparc -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-BE +; RUN: llc < %s -march=sparcel -disable-sparc-delay-filler -disable-sparc-leaf-proc | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-LE + +; CHECK-LABEL: intarg: +; The save/restore frame is not strictly necessary here, but we would need to +; refer to %o registers instead. +; CHECK: save %sp, -96, %sp +; CHECK: ld [%fp+96], [[R2:%[gilo][0-7]]] +; CHECK: ld [%fp+92], [[R1:%[gilo][0-7]]] +; CHECK: stb %i0, [%i4] +; CHECK: stb %i1, [%i4] +; CHECK: sth %i2, [%i4] +; CHECK: st %i3, [%i4] +; CHECK: st %i4, [%i4] +; CHECK: st %i5, [%i4] +; CHECK: st [[R1]], [%i4] +; CHECK: st [[R2]], [%i4] +; CHECK: restore +define void @intarg(i8 %a0, ; %i0 + i8 %a1, ; %i1 + i16 %a2, ; %i2 + i32 %a3, ; %i3 + i8* %a4, ; %i4 + i32 %a5, ; %i5 + i32 signext %a6, ; [%fp+92] + i8* %a7) { ; [%fp+96] + store i8 %a0, i8* %a4 + store i8 %a1, i8* %a4 + %p16 = bitcast i8* %a4 to i16* + store i16 %a2, i16* %p16 + %p32 = bitcast i8* %a4 to i32* + store i32 %a3, i32* %p32 + %pp = bitcast i8* %a4 to i8** + store i8* %a4, i8** %pp + store i32 %a5, i32* %p32 + store i32 %a6, i32* %p32 + store i8* %a7, i8** %pp + ret void +} + +; CHECK-LABEL: call_intarg: +; CHECK: save %sp, -104, %sp +; Use %o0-%o5 for outgoing arguments +; CHECK: mov 5, %o5 +; CHECK: st %i0, [%sp+92] +; CHECK: call intarg +; CHECK-NOT: add %sp +; CHECK: restore +define void @call_intarg(i32 %i0, i8* %i1) { + call void @intarg(i8 0, i8 1, i16 2, i32 3, i8* undef, i32 5, i32 %i0, i8* %i1) + ret void +} + +;; Verify doubles starting with an even reg, starting with an odd reg, +;; straddling the boundary of regs and mem, and floats in regs and mem. +; +; CHECK-LABEL: floatarg: +; CHECK: save %sp, -120, %sp +; CHECK: mov %i5, %g2 +; CHECK-NEXT: ld [%fp+92], %g3 +; CHECK-NEXT: mov %i4, %i5 +; CHECK-NEXT: std %g2, [%fp+-24] +; CHECK-NEXT: mov %i3, %i4 +; CHECK-NEXT: std %i4, [%fp+-16] +; CHECK-NEXT: std %i0, [%fp+-8] +; CHECK-NEXT: st %i2, [%fp+-28] +; CHECK-NEXT: ld [%fp+104], %f0 +; CHECK-NEXT: ldd [%fp+96], %f2 +; CHECK-NEXT: ld [%fp+-28], %f1 +; CHECK-NEXT: ldd [%fp+-8], %f4 +; CHECK-NEXT: ldd [%fp+-16], %f6 +; CHECK-NEXT: ldd [%fp+-24], %f8 +; CHECK-NEXT: fstod %f1, %f10 +; CHECK-NEXT: faddd %f4, %f10, %f4 +; CHECK-NEXT: faddd %f6, %f4, %f4 +; CHECK-NEXT: faddd %f8, %f4, %f4 +; CHECK-NEXT: faddd %f2, %f4, %f2 +; CHECK-NEXT: fstod %f0, %f0 +; CHECK-NEXT: faddd %f0, %f2, %f0 +; CHECK-NEXT: restore +define double @floatarg(double %a0, ; %i0,%i1 + float %a1, ; %i2 + double %a2, ; %i3, %i4 + double %a3, ; %i5, [%fp+92] (using 4 bytes) + double %a4, ; [%fp+96] (using 8 bytes) + float %a5) { ; [%fp+104] (using 4 bytes) + %d1 = fpext float %a1 to double + %s1 = fadd double %a0, %d1 + %s2 = fadd double %a2, %s1 + %s3 = fadd double %a3, %s2 + %s4 = fadd double %a4, %s3 + %d5 = fpext float %a5 to double + %s5 = fadd double %d5, %s4 + ret double %s5 +} + +; CHECK-LABEL: call_floatarg: +; CHECK: save %sp, -112, %sp +; CHECK: mov %i2, %o1 +; CHECK-NEXT: mov %i1, %o0 +; CHECK-NEXT: st %i0, [%sp+104] +; CHECK-NEXT: std %o0, [%sp+96] +; CHECK-NEXT: st %o1, [%sp+92] +; CHECK-NEXT: mov %i0, %o2 +; CHECK-NEXT: mov %o0, %o3 +; CHECK-NEXT: mov %o1, %o4 +; CHECK-NEXT: mov %o0, %o5 +; CHECK-NEXT: call floatarg +; CHECK: std %f0, [%i4] +; CHECK: restore +define void @call_floatarg(float %f1, double %d2, float %f5, double *%p) { + %r = call double @floatarg(double %d2, float %f1, double %d2, double %d2, + double %d2, float %f1) + store double %r, double* %p + ret void +} + +;; i64 arguments should effectively work the same as double: split +;; into two locations. This is different for little-endian vs big +;; endian, since the 64-bit math needs to be split +; CHECK-LABEL: i64arg: +; CHECK: save %sp, -96, %sp +; CHECK-BE: ld [%fp+100], %g2 +; CHECK-BE-NEXT: ld [%fp+96], %g3 +; CHECK-BE-NEXT: ld [%fp+92], %g4 +; CHECK-BE-NEXT: addcc %i1, %i2, %i1 +; CHECK-BE-NEXT: addxcc %i0, 0, %i0 +; CHECK-BE-NEXT: addcc %i4, %i1, %i1 +; CHECK-BE-NEXT: addxcc %i3, %i0, %i0 +; CHECK-BE-NEXT: addcc %g4, %i1, %i1 +; CHECK-BE-NEXT: ld [%fp+104], %i2 +; CHECK-BE-NEXT: addxcc %i5, %i0, %i0 +; CHECK-BE-NEXT: addcc %g2, %i1, %i1 +; CHECK-BE-NEXT: addxcc %g3, %i0, %i0 +; CHECK-BE-NEXT: addcc %i2, %i1, %i1 +; CHECK-BE-NEXT: addxcc %i0, 0, %i0 +; +; CHECK-LE: ld [%fp+96], %g2 +; CHECK-LE-NEXT: ld [%fp+100], %g3 +; CHECK-LE-NEXT: ld [%fp+92], %g4 +; CHECK-LE-NEXT: addcc %i0, %i2, %i0 +; CHECK-LE-NEXT: addxcc %i1, 0, %i1 +; CHECK-LE-NEXT: addcc %i3, %i0, %i0 +; CHECK-LE-NEXT: addxcc %i4, %i1, %i1 +; CHECK-LE-NEXT: addcc %i5, %i0, %i0 +; CHECK-LE-NEXT: ld [%fp+104], %i2 +; CHECK-LE-NEXT: addxcc %g4, %i1, %i1 +; CHECK-LE-NEXT: addcc %g2, %i0, %i0 +; CHECK-LE-NEXT: addxcc %g3, %i1, %i1 +; CHECK-LE-NEXT: addcc %i2, %i0, %i0 +; CHECK-LE-NEXT: addxcc %i1, 0, %i1 +; CHECK-NEXT: restore + + +define i64 @i64arg(i64 %a0, ; %i0,%i1 + i32 %a1, ; %i2 + i64 %a2, ; %i3, %i4 + i64 %a3, ; %i5, [%fp+92] (using 4 bytes) + i64 %a4, ; [%fp+96] (using 8 bytes) + i32 %a5) { ; [%fp+104] (using 4 bytes) + %a1L = zext i32 %a1 to i64 + %s1 = add i64 %a0, %a1L + %s2 = add i64 %a2, %s1 + %s3 = add i64 %a3, %s2 + %s4 = add i64 %a4, %s3 + %a5L = zext i32 %a5 to i64 + %s5 = add i64 %a5L, %s4 + ret i64 %s5 +} + +; CHECK-LABEL: call_i64arg: +; CHECK: save %sp, -112, %sp +; CHECK: st %i0, [%sp+104] +; CHECK-NEXT: st %i2, [%sp+100] +; CHECK-NEXT: st %i1, [%sp+96] +; CHECK-NEXT: st %i2, [%sp+92] +; CHECK-NEXT: mov %i1, %o0 +; CHECK-NEXT: mov %i2, %o1 +; CHECK-NEXT: mov %i0, %o2 +; CHECK-NEXT: mov %i1, %o3 +; CHECK-NEXT: mov %i2, %o4 +; CHECK-NEXT: mov %i1, %o5 +; CHECK-NEXT: call i64arg +; CHECK: std %o0, [%i3] +; CHECK-NEXT: restore + +define void @call_i64arg(i32 %a0, i64 %a1, i64* %p) { + %r = call i64 @i64arg(i64 %a1, i32 %a0, i64 %a1, i64 %a1, i64 %a1, i32 %a0) + store i64 %r, i64* %p + ret void +} -- 2.34.1