mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-24 12:19:53 +00:00
[SystemZ] Support Swift Calling Convention
Summary: Port rL265480, rL264754, rL265997 and rL266252 to SystemZ, in order to enable the Swift port on the architecture. SwiftSelf and SwiftError are assigned to R10 and R9, respectively, which are normally callee-saved registers. For more information, see: RFC: Implementing the Swift calling convention in LLVM and Clang https://groups.google.com/forum/#!topic/llvm-dev/epDd2w93kZ0 Reviewers: kbarton, manmanren, rjmccall, uweigand Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D19414 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@267823 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
628f22d543
commit
2c448a07a4
@ -33,6 +33,9 @@ def RetCC_SystemZ : CallingConv<[
|
||||
// Promote i32 to i64 if it has an explicit extension type.
|
||||
CCIfType<[i32], CCIfExtend<CCPromoteToType<i64>>>,
|
||||
|
||||
// A SwiftError is returned in R9.
|
||||
CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R9D]>>>,
|
||||
|
||||
// ABI-compliant code returns 64-bit integers in R2. Make the other
|
||||
// call-clobbered argument registers available for code that doesn't
|
||||
// care about the ABI. (R6 is an argument register too, but is
|
||||
@ -65,6 +68,12 @@ def CC_SystemZ : CallingConv<[
|
||||
// are smaller than 64 bits shouldn't.
|
||||
CCIfType<[i32], CCIfExtend<CCPromoteToType<i64>>>,
|
||||
|
||||
// A SwiftSelf is passed in callee-saved R10.
|
||||
CCIfSwiftSelf<CCIfType<[i64], CCAssignToReg<[R10D]>>>,
|
||||
|
||||
// A SwiftError is passed in callee-saved R9.
|
||||
CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R9D]>>>,
|
||||
|
||||
// Force long double values to the stack and pass i64 pointers to them.
|
||||
CCIfType<[f128], CCPassIndirect<i64>>,
|
||||
// Same for i128 values. These are already split into two i64 here,
|
||||
@ -108,3 +117,6 @@ def CC_SystemZ : CallingConv<[
|
||||
//===----------------------------------------------------------------------===//
|
||||
def CSR_SystemZ : CalleeSavedRegs<(add (sequence "R%dD", 6, 15),
|
||||
(sequence "F%dD", 8, 15))>;
|
||||
|
||||
// R9 is used to return SwiftError; remove it from CSR.
|
||||
def CSR_SystemZ_SwiftError : CalleeSavedRegs<(sub CSR_SystemZ, R9D)>;
|
||||
|
@ -1004,9 +1004,11 @@ LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
||||
}
|
||||
|
||||
static bool canUseSiblingCall(const CCState &ArgCCInfo,
|
||||
SmallVectorImpl<CCValAssign> &ArgLocs) {
|
||||
SmallVectorImpl<CCValAssign> &ArgLocs,
|
||||
SmallVectorImpl<ISD::OutputArg> &Outs) {
|
||||
// Punt if there are any indirect or stack arguments, or if the call
|
||||
// needs the call-saved argument register R6.
|
||||
// needs the callee-saved argument register R6, or if the call uses
|
||||
// the callee-saved register arguments SwiftSelf and SwiftError.
|
||||
for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) {
|
||||
CCValAssign &VA = ArgLocs[I];
|
||||
if (VA.getLocInfo() == CCValAssign::Indirect)
|
||||
@ -1016,6 +1018,8 @@ static bool canUseSiblingCall(const CCState &ArgCCInfo,
|
||||
unsigned Reg = VA.getLocReg();
|
||||
if (Reg == SystemZ::R6H || Reg == SystemZ::R6L || Reg == SystemZ::R6D)
|
||||
return false;
|
||||
if (Outs[I].Flags.isSwiftSelf() || Outs[I].Flags.isSwiftError())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1049,7 +1053,7 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI,
|
||||
|
||||
// We don't support GuaranteedTailCallOpt, only automatically-detected
|
||||
// sibling calls.
|
||||
if (IsTailCall && !canUseSiblingCall(ArgCCInfo, ArgLocs))
|
||||
if (IsTailCall && !canUseSiblingCall(ArgCCInfo, ArgLocs, Outs))
|
||||
IsTailCall = false;
|
||||
|
||||
// Get a count of how many bytes are to be pushed on the stack.
|
||||
|
@ -459,6 +459,10 @@ public:
|
||||
SelectionDAG &DAG) const override;
|
||||
SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override;
|
||||
|
||||
bool supportSwiftError() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SystemZSubtarget &Subtarget;
|
||||
|
||||
|
@ -24,12 +24,20 @@ SystemZRegisterInfo::SystemZRegisterInfo()
|
||||
|
||||
const MCPhysReg *
|
||||
SystemZRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
if (MF->getSubtarget().getTargetLowering()->supportSwiftError() &&
|
||||
MF->getFunction()->getAttributes().hasAttrSomewhere(
|
||||
Attribute::SwiftError))
|
||||
return CSR_SystemZ_SwiftError_SaveList;
|
||||
return CSR_SystemZ_SaveList;
|
||||
}
|
||||
|
||||
const uint32_t *
|
||||
SystemZRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
|
||||
CallingConv::ID CC) const {
|
||||
if (MF.getSubtarget().getTargetLowering()->supportSwiftError() &&
|
||||
MF.getFunction()->getAttributes().hasAttrSomewhere(
|
||||
Attribute::SwiftError))
|
||||
return CSR_SystemZ_SwiftError_RegMask;
|
||||
return CSR_SystemZ_RegMask;
|
||||
}
|
||||
|
||||
|
203
test/CodeGen/SystemZ/swift-return.ll
Normal file
203
test/CodeGen/SystemZ/swift-return.ll
Normal file
@ -0,0 +1,203 @@
|
||||
; RUN: llc < %s -mtriple=s390x-linux-gnu -verify-machineinstrs | FileCheck %s
|
||||
; RUN: llc < %s -mtriple=s390x-linux-gnu -O0 -verify-machineinstrs | FileCheck --check-prefix=CHECK-O0 %s
|
||||
|
||||
@var = global i32 0
|
||||
|
||||
; Test how llvm handles return type of {i16, i8}. The return value will be
|
||||
; passed in %r2 and %r3.
|
||||
; CHECK-LABEL: test:
|
||||
; CHECK: st %r2
|
||||
; CHECK: brasl %r14, gen
|
||||
; CHECK-DAG: lhr %r2, %r2
|
||||
; CHECK-DAG: lbr %[[REG1:r[0-9]+]], %r3
|
||||
; CHECK: ar %r2, %[[REG1]]
|
||||
; CHECK-O0-LABEL: test
|
||||
; CHECK-O0: st %r2
|
||||
; CHECK-O0: brasl %r14, gen
|
||||
; CHECK-O0-DAG: lhr %[[REG1:r[0-9]+]], %r2
|
||||
; CHECK-O0-DAG: lbr %[[REG2:r[0-9]+]], %r3
|
||||
; CHECK-O0: ar %[[REG1]], %[[REG2]]
|
||||
; CHECK-O0: lr %r2, %[[REG1]]
|
||||
define i16 @test(i32 %key) {
|
||||
entry:
|
||||
%key.addr = alloca i32, align 4
|
||||
store i32 %key, i32* %key.addr, align 4
|
||||
%0 = load i32, i32* %key.addr, align 4
|
||||
%call = call swiftcc { i16, i8 } @gen(i32 %0)
|
||||
%v3 = extractvalue { i16, i8 } %call, 0
|
||||
%v1 = sext i16 %v3 to i32
|
||||
%v5 = extractvalue { i16, i8 } %call, 1
|
||||
%v2 = sext i8 %v5 to i32
|
||||
%add = add nsw i32 %v1, %v2
|
||||
%conv = trunc i32 %add to i16
|
||||
ret i16 %conv
|
||||
}
|
||||
|
||||
declare swiftcc { i16, i8 } @gen(i32)
|
||||
|
||||
; If we can't pass every return value in registers, we will pass everything
|
||||
; in memroy. The caller provides space for the return value and passes
|
||||
; the address in %r2. The first input argument will be in %r3.
|
||||
; CHECK-LABEL: test2:
|
||||
; CHECK: lr %[[REG1:r[0-9]+]], %r2
|
||||
; CHECK-DAG: la %r2, 160(%r15)
|
||||
; CHECK-DAG: lr %r3, %[[REG1]]
|
||||
; CHECK: brasl %r14, gen2
|
||||
; CHECK: l %r2, 160(%r15)
|
||||
; CHECK: a %r2, 164(%r15)
|
||||
; CHECK: a %r2, 168(%r15)
|
||||
; CHECK: a %r2, 172(%r15)
|
||||
; CHECK: a %r2, 176(%r15)
|
||||
; CHECK-O0-LABEL: test2:
|
||||
; CHECK-O0: la %[[REG1:r[0-9]+]], 168(%r15)
|
||||
; CHECK-O0: st %r2, [[SPILL1:[0-9]+]](%r15)
|
||||
; CHECK-O0: lgr %r2, %[[REG1]]
|
||||
; CHECK-O0: l %r3, [[SPILL1]](%r15)
|
||||
; CHECK-O0: brasl %r14, gen2
|
||||
; CHECK-O0-DAG: l %r{{.*}}, 184(%r15)
|
||||
; CHECK-O0-DAG: l %r{{.*}}, 180(%r15)
|
||||
; CHECK-O0-DAG: l %r{{.*}}, 176(%r15)
|
||||
; CHECK-O0-DAG: l %r{{.*}}, 172(%r15)
|
||||
; CHECK-O0-DAG: l %r{{.*}}, 168(%r15)
|
||||
; CHECK-O0: ar
|
||||
; CHECK-O0: ar
|
||||
; CHECK-O0: ar
|
||||
; CHECK-O0: ar
|
||||
; CHECK-O0: lr %r2
|
||||
define i32 @test2(i32 %key) #0 {
|
||||
entry:
|
||||
%key.addr = alloca i32, align 4
|
||||
store i32 %key, i32* %key.addr, align 4
|
||||
%0 = load i32, i32* %key.addr, align 4
|
||||
%call = call swiftcc { i32, i32, i32, i32, i32 } @gen2(i32 %0)
|
||||
|
||||
%v3 = extractvalue { i32, i32, i32, i32, i32 } %call, 0
|
||||
%v5 = extractvalue { i32, i32, i32, i32, i32 } %call, 1
|
||||
%v6 = extractvalue { i32, i32, i32, i32, i32 } %call, 2
|
||||
%v7 = extractvalue { i32, i32, i32, i32, i32 } %call, 3
|
||||
%v8 = extractvalue { i32, i32, i32, i32, i32 } %call, 4
|
||||
|
||||
%add = add nsw i32 %v3, %v5
|
||||
%add1 = add nsw i32 %add, %v6
|
||||
%add2 = add nsw i32 %add1, %v7
|
||||
%add3 = add nsw i32 %add2, %v8
|
||||
ret i32 %add3
|
||||
}
|
||||
|
||||
; The address of the return value is passed in %r2.
|
||||
; On return, %r2 will contain the adddress that has been passed in by the caller in %r2.
|
||||
; CHECK-LABEL: gen2:
|
||||
; CHECK: st %r3, 16(%r2)
|
||||
; CHECK: st %r3, 12(%r2)
|
||||
; CHECK: st %r3, 8(%r2)
|
||||
; CHECK: st %r3, 4(%r2)
|
||||
; CHECK: st %r3, 0(%r2)
|
||||
; CHECK-O0-LABEL: gen2:
|
||||
; CHECK-O0-DAG: st %r3, 16(%r2)
|
||||
; CHECK-O0-DAG: st %r3, 12(%r2)
|
||||
; CHECK-O0-DAG: st %r3, 8(%r2)
|
||||
; CHECK-O0-DAG: st %r3, 4(%r2)
|
||||
; CHECK-O0-DAG: st %r3, 0(%r2)
|
||||
define swiftcc { i32, i32, i32, i32, i32 } @gen2(i32 %key) {
|
||||
%Y = insertvalue { i32, i32, i32, i32, i32 } undef, i32 %key, 0
|
||||
%Z = insertvalue { i32, i32, i32, i32, i32 } %Y, i32 %key, 1
|
||||
%Z2 = insertvalue { i32, i32, i32, i32, i32 } %Z, i32 %key, 2
|
||||
%Z3 = insertvalue { i32, i32, i32, i32, i32 } %Z2, i32 %key, 3
|
||||
%Z4 = insertvalue { i32, i32, i32, i32, i32 } %Z3, i32 %key, 4
|
||||
ret { i32, i32, i32, i32, i32 } %Z4
|
||||
}
|
||||
|
||||
; The return value {i32, i32, i32, i32} will be returned via registers
|
||||
; %r2, %r3, %r4, %r5.
|
||||
; CHECK-LABEL: test3:
|
||||
; CHECK: brasl %r14, gen3
|
||||
; CHECK: ar %r2, %r3
|
||||
; CHECK: ar %r2, %r4
|
||||
; CHECK: ar %r2, %r5
|
||||
; CHECK-O0-LABEL: test3:
|
||||
; CHECK-O0: brasl %r14, gen3
|
||||
; CHECK-O0: ar %r2, %r3
|
||||
; CHECK-O0: ar %r2, %r4
|
||||
; CHECK-O0: ar %r2, %r5
|
||||
define i32 @test3(i32 %key) #0 {
|
||||
entry:
|
||||
%key.addr = alloca i32, align 4
|
||||
store i32 %key, i32* %key.addr, align 4
|
||||
%0 = load i32, i32* %key.addr, align 4
|
||||
%call = call swiftcc { i32, i32, i32, i32 } @gen3(i32 %0)
|
||||
|
||||
%v3 = extractvalue { i32, i32, i32, i32 } %call, 0
|
||||
%v5 = extractvalue { i32, i32, i32, i32 } %call, 1
|
||||
%v6 = extractvalue { i32, i32, i32, i32 } %call, 2
|
||||
%v7 = extractvalue { i32, i32, i32, i32 } %call, 3
|
||||
|
||||
%add = add nsw i32 %v3, %v5
|
||||
%add1 = add nsw i32 %add, %v6
|
||||
%add2 = add nsw i32 %add1, %v7
|
||||
ret i32 %add2
|
||||
}
|
||||
|
||||
declare swiftcc { i32, i32, i32, i32 } @gen3(i32 %key)
|
||||
|
||||
; The return value {float, float, float, float} will be returned via registers
|
||||
; %f0, %f2, %f4, %f6.
|
||||
; CHECK-LABEL: test4:
|
||||
; CHECK: brasl %r14, gen4
|
||||
; CHECK: aebr %f0, %f2
|
||||
; CHECK: aebr %f0, %f4
|
||||
; CHECK: aebr %f0, %f6
|
||||
; CHECK-O0-LABEL: test4:
|
||||
; CHECK-O0: brasl %r14, gen4
|
||||
; CHECK-O0: aebr %f0, %f2
|
||||
; CHECK-O0: aebr %f0, %f4
|
||||
; CHECK-O0: aebr %f0, %f6
|
||||
define float @test4(float %key) #0 {
|
||||
entry:
|
||||
%key.addr = alloca float, align 4
|
||||
store float %key, float* %key.addr, align 4
|
||||
%0 = load float, float* %key.addr, align 4
|
||||
%call = call swiftcc { float, float, float, float } @gen4(float %0)
|
||||
|
||||
%v3 = extractvalue { float, float, float, float } %call, 0
|
||||
%v5 = extractvalue { float, float, float, float } %call, 1
|
||||
%v6 = extractvalue { float, float, float, float } %call, 2
|
||||
%v7 = extractvalue { float, float, float, float } %call, 3
|
||||
|
||||
%add = fadd float %v3, %v5
|
||||
%add1 = fadd float %add, %v6
|
||||
%add2 = fadd float %add1, %v7
|
||||
ret float %add2
|
||||
}
|
||||
|
||||
declare swiftcc { float, float, float, float } @gen4(float %key)
|
||||
|
||||
; CHECK-LABEL: consume_i1_ret:
|
||||
; CHECK: brasl %r14, produce_i1_ret
|
||||
; CHECK: nilf %r2, 1
|
||||
; CHECK: nilf %r3, 1
|
||||
; CHECK: nilf %r4, 1
|
||||
; CHECK: nilf %r5, 1
|
||||
; CHECK-O0-LABEL: consume_i1_ret:
|
||||
; CHECK-O0: brasl %r14, produce_i1_ret
|
||||
; CHECK-O0: nilf %r2, 1
|
||||
; CHECK-O0: nilf %r3, 1
|
||||
; CHECK-O0: nilf %r4, 1
|
||||
; CHECK-O0: nilf %r5, 1
|
||||
define void @consume_i1_ret() {
|
||||
%call = call swiftcc { i1, i1, i1, i1 } @produce_i1_ret()
|
||||
%v3 = extractvalue { i1, i1, i1, i1 } %call, 0
|
||||
%v5 = extractvalue { i1, i1, i1, i1 } %call, 1
|
||||
%v6 = extractvalue { i1, i1, i1, i1 } %call, 2
|
||||
%v7 = extractvalue { i1, i1, i1, i1 } %call, 3
|
||||
%val = zext i1 %v3 to i32
|
||||
store i32 %val, i32* @var
|
||||
%val2 = zext i1 %v5 to i32
|
||||
store i32 %val2, i32* @var
|
||||
%val3 = zext i1 %v6 to i32
|
||||
store i32 %val3, i32* @var
|
||||
%val4 = zext i1 %v7 to i32
|
||||
store i32 %val4, i32* @var
|
||||
ret void
|
||||
}
|
||||
|
||||
declare swiftcc { i1, i1, i1, i1 } @produce_i1_ret()
|
358
test/CodeGen/SystemZ/swifterror.ll
Normal file
358
test/CodeGen/SystemZ/swifterror.ll
Normal file
@ -0,0 +1,358 @@
|
||||
; RUN: llc < %s -mtriple=s390x-linux-gnu| FileCheck %s
|
||||
; RUN: llc < %s -O0 -mtriple=s390x-linux-gnu | FileCheck --check-prefix=CHECK-O0 %s
|
||||
|
||||
declare i8* @malloc(i64)
|
||||
declare void @free(i8*)
|
||||
%swift_error = type {i64, i8}
|
||||
|
||||
; This tests the basic usage of a swifterror parameter. "foo" is the function
|
||||
; that takes a swifterror parameter and "caller" is the caller of "foo".
|
||||
define float @foo(%swift_error** swifterror %error_ptr_ref) {
|
||||
; CHECK-LABEL: foo:
|
||||
; CHECK: lghi %r2, 16
|
||||
; CHECK: brasl %r14, malloc
|
||||
; CHECK: mvi 8(%r2), 1
|
||||
; CHECK: lgr %r9, %r2
|
||||
; CHECK-O0-LABEL: foo:
|
||||
; CHECK-O0: lghi %r2, 16
|
||||
; CHECK-O0: brasl %r14, malloc
|
||||
; CHECK-O0: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK-O0: mvi 8(%r2), 1
|
||||
; CHECK-O0: lgr %r9, %r[[REG1]]
|
||||
entry:
|
||||
%call = call i8* @malloc(i64 16)
|
||||
%call.0 = bitcast i8* %call to %swift_error*
|
||||
store %swift_error* %call.0, %swift_error** %error_ptr_ref
|
||||
%tmp = getelementptr inbounds i8, i8* %call, i64 8
|
||||
store i8 1, i8* %tmp
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; "caller" calls "foo" that takes a swifterror parameter.
|
||||
define float @caller(i8* %error_ref) {
|
||||
; CHECK-LABEL: caller:
|
||||
; Make a copy of error_ref because r2 is getting clobbered
|
||||
; CHECK: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK: lghi %r9, 0
|
||||
; CHECK: brasl %r14, foo
|
||||
; CHECK: cgijlh %r9, 0,
|
||||
; Access part of the error object and save it to error_ref
|
||||
; CHECK: lb %r[[REG2:[0-9]+]], 8(%r9)
|
||||
; CHECK: stc %r[[REG2]], 0(%r[[REG1]])
|
||||
; CHECK: lgr %r2, %r9
|
||||
; CHECK: brasl %r14, free
|
||||
; CHECK-O0-LABEL: caller:
|
||||
; CHECK-O0: lghi %r9, 0
|
||||
; CHECK-O0: brasl %r14, foo
|
||||
; CHECK-O0: cghi %r9, 0
|
||||
; CHECK-O0: jlh
|
||||
entry:
|
||||
%error_ptr_ref = alloca swifterror %swift_error*
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref
|
||||
%call = call float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
%error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref
|
||||
%had_error_from_foo = icmp ne %swift_error* %error_from_foo, null
|
||||
%tmp = bitcast %swift_error* %error_from_foo to i8*
|
||||
br i1 %had_error_from_foo, label %handler, label %cont
|
||||
cont:
|
||||
%v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1
|
||||
%t = load i8, i8* %v1
|
||||
store i8 %t, i8* %error_ref
|
||||
br label %handler
|
||||
handler:
|
||||
call void @free(i8* %tmp)
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; "caller2" is the caller of "foo", it calls "foo" inside a loop.
|
||||
define float @caller2(i8* %error_ref) {
|
||||
; CHECK-LABEL: caller2:
|
||||
; Make a copy of error_ref because r2 is getting clobbered
|
||||
; CHECK: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK: lghi %r9, 0
|
||||
; CHECK: brasl %r14, foo
|
||||
; CHECK: cgijlh %r9, 0,
|
||||
; CHECK: ceb %f0,
|
||||
; CHECK: jnh
|
||||
; Access part of the error object and save it to error_ref
|
||||
; CHECK: lb %r[[REG2:[0-9]+]], 8(%r9)
|
||||
; CHECK: stc %r[[REG2]], 0(%r[[REG1]])
|
||||
; CHECK: lgr %r2, %r9
|
||||
; CHECK: brasl %r14, free
|
||||
; CHECK-O0-LABEL: caller2:
|
||||
; CHECK-O0: lghi %r9, 0
|
||||
; CHECK-O0: brasl %r14, foo
|
||||
; CHECK-O0: cghi %r9, 0
|
||||
; CHECK-O0: jlh
|
||||
entry:
|
||||
%error_ptr_ref = alloca swifterror %swift_error*
|
||||
br label %bb_loop
|
||||
bb_loop:
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref
|
||||
%call = call float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
%error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref
|
||||
%had_error_from_foo = icmp ne %swift_error* %error_from_foo, null
|
||||
%tmp = bitcast %swift_error* %error_from_foo to i8*
|
||||
br i1 %had_error_from_foo, label %handler, label %cont
|
||||
cont:
|
||||
%cmp = fcmp ogt float %call, 1.000000e+00
|
||||
br i1 %cmp, label %bb_end, label %bb_loop
|
||||
bb_end:
|
||||
%v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1
|
||||
%t = load i8, i8* %v1
|
||||
store i8 %t, i8* %error_ref
|
||||
br label %handler
|
||||
handler:
|
||||
call void @free(i8* %tmp)
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; "foo_if" is a function that takes a swifterror parameter, it sets swifterror
|
||||
; under a certain condition.
|
||||
define float @foo_if(%swift_error** swifterror %error_ptr_ref, i32 %cc) {
|
||||
; CHECK-LABEL: foo_if:
|
||||
; CHECK: cije %r2, 0
|
||||
; CHECK: lghi %r2, 16
|
||||
; CHECK: brasl %r14, malloc
|
||||
; CHECK: mvi 8(%r2), 1
|
||||
; CHECK: lgr %r9, %r2
|
||||
; CHECK-NOT: %r9
|
||||
; CHECK: br %r14
|
||||
; CHECK-O0-LABEL: foo_if:
|
||||
; CHECK-O0: chi %r2, 0
|
||||
; spill to stack
|
||||
; CHECK-O0: stg %r9, [[OFFS:[0-9]+]](%r15)
|
||||
; CHECK-O0: je
|
||||
; CHECK-O0: lghi %r2, 16
|
||||
; CHECK-O0: brasl %r14, malloc
|
||||
; CHECK-O0: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK-O0: mvi 8(%r2), 1
|
||||
; CHECK-O0: lgr %r9, %r[[REG1]]
|
||||
; CHECK-O0: br %r14
|
||||
; reload from stack
|
||||
; CHECK-O0: lg %r9, [[OFFS]](%r15)
|
||||
; CHECK-O0: br %r14
|
||||
entry:
|
||||
%cond = icmp ne i32 %cc, 0
|
||||
br i1 %cond, label %gen_error, label %normal
|
||||
|
||||
gen_error:
|
||||
%call = call i8* @malloc(i64 16)
|
||||
%call.0 = bitcast i8* %call to %swift_error*
|
||||
store %swift_error* %call.0, %swift_error** %error_ptr_ref
|
||||
%tmp = getelementptr inbounds i8, i8* %call, i64 8
|
||||
store i8 1, i8* %tmp
|
||||
ret float 1.0
|
||||
|
||||
normal:
|
||||
ret float 0.0
|
||||
}
|
||||
|
||||
; "foo_loop" is a function that takes a swifterror parameter, it sets swifterror
|
||||
; under a certain condition inside a loop.
|
||||
define float @foo_loop(%swift_error** swifterror %error_ptr_ref, i32 %cc, float %cc2) {
|
||||
; CHECK-LABEL: foo_loop:
|
||||
; CHECK: lr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK: cije %r[[REG1]], 0
|
||||
; CHECK: lghi %r2, 16
|
||||
; CHECK: brasl %r14, malloc
|
||||
; CHECK: mvi 8(%r2), 1
|
||||
; CHECK: ceb %f8,
|
||||
; CHECK: jnh
|
||||
; CHECK: lgr %r9, %r2
|
||||
; CHECK: br %r14
|
||||
; CHECK-O0-LABEL: foo_loop:
|
||||
; spill to stack
|
||||
; CHECK-O0: stg %r9, [[OFFS:[0-9]+]](%r15)
|
||||
; CHECK-O0: chi %r{{.*}}, 0
|
||||
; CHECK-O0: je
|
||||
; CHECK-O0: lghi %r2, 16
|
||||
; CHECK-O0: brasl %r14, malloc
|
||||
; CHECK-O0: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK-O0: mvi 8(%r2), 1
|
||||
; CHECK-O0: jnh
|
||||
; reload from stack
|
||||
; CHECK-O0: lg %r9, [[OFFS:[0-9]+]](%r15)
|
||||
; CHECK-O0: br %r14
|
||||
entry:
|
||||
br label %bb_loop
|
||||
|
||||
bb_loop:
|
||||
%cond = icmp ne i32 %cc, 0
|
||||
br i1 %cond, label %gen_error, label %bb_cont
|
||||
|
||||
gen_error:
|
||||
%call = call i8* @malloc(i64 16)
|
||||
%call.0 = bitcast i8* %call to %swift_error*
|
||||
store %swift_error* %call.0, %swift_error** %error_ptr_ref
|
||||
%tmp = getelementptr inbounds i8, i8* %call, i64 8
|
||||
store i8 1, i8* %tmp
|
||||
br label %bb_cont
|
||||
|
||||
bb_cont:
|
||||
%cmp = fcmp ogt float %cc2, 1.000000e+00
|
||||
br i1 %cmp, label %bb_end, label %bb_loop
|
||||
bb_end:
|
||||
ret float 0.0
|
||||
}
|
||||
|
||||
%struct.S = type { i32, i32, i32, i32, i32, i32 }
|
||||
|
||||
; "foo_sret" is a function that takes a swifterror parameter, it also has a sret
|
||||
; parameter.
|
||||
define void @foo_sret(%struct.S* sret %agg.result, i32 %val1, %swift_error** swifterror %error_ptr_ref) {
|
||||
; CHECK-LABEL: foo_sret:
|
||||
; CHECK-DAG: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK-DAG: lr %r[[REG2:[0-9]+]], %r3
|
||||
; CHECK: lghi %r2, 16
|
||||
; CHECK: brasl %r14, malloc
|
||||
; CHECK: mvi 8(%r2), 1
|
||||
; CHECK: st %r[[REG2]], 4(%r[[REG1]])
|
||||
; CHECK: lgr %r9, %r2
|
||||
; CHECK-NOT: %r9
|
||||
; CHECK: br %r14
|
||||
|
||||
; CHECK-O0-LABEL: foo_sret:
|
||||
; CHECK-O0: lghi %r{{.*}}, 16
|
||||
; spill sret to stack
|
||||
; CHECK-O0: stg %r2, [[OFFS1:[0-9]+]](%r15)
|
||||
; CHECK-O0: lgr %r2, %r{{.*}}
|
||||
; CHECK-O0: st %r3, [[OFFS2:[0-9]+]](%r15)
|
||||
; CHECK-O0: brasl %r14, malloc
|
||||
; CHECK-O0: lgr {{.*}}, %r2
|
||||
; CHECK-O0: mvi 8(%r2), 1
|
||||
; CHECK-O0-DAG: lg %r[[REG1:[0-9]+]], [[OFFS1]](%r15)
|
||||
; CHECK-O0-DAG: l %r[[REG2:[0-9]+]], [[OFFS2]](%r15)
|
||||
; CHECK-O0: st %r[[REG2]], 4(%r[[REG1]])
|
||||
; CHECK-O0: lgr %r9, {{.*}}
|
||||
; CHECK-O0: br %r14
|
||||
entry:
|
||||
%call = call i8* @malloc(i64 16)
|
||||
%call.0 = bitcast i8* %call to %swift_error*
|
||||
store %swift_error* %call.0, %swift_error** %error_ptr_ref
|
||||
%tmp = getelementptr inbounds i8, i8* %call, i64 8
|
||||
store i8 1, i8* %tmp
|
||||
%v2 = getelementptr inbounds %struct.S, %struct.S* %agg.result, i32 0, i32 1
|
||||
store i32 %val1, i32* %v2
|
||||
ret void
|
||||
}
|
||||
|
||||
; "caller3" calls "foo_sret" that takes a swifterror parameter.
|
||||
define float @caller3(i8* %error_ref) {
|
||||
; CHECK-LABEL: caller3:
|
||||
; Make a copy of error_ref because r2 is getting clobbered
|
||||
; CHECK: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK: lhi %r3, 1
|
||||
; CHECK: lghi %r9, 0
|
||||
; CHECK: brasl %r14, foo_sret
|
||||
; CHECK: cgijlh %r9, 0,
|
||||
; Access part of the error object and save it to error_ref
|
||||
; CHECK: lb %r0, 8(%r9)
|
||||
; CHECK: stc %r0, 0(%r[[REG1]])
|
||||
; CHECK: lgr %r2, %r9
|
||||
; CHECK: brasl %r14, free
|
||||
|
||||
; CHECK-O0-LABEL: caller3:
|
||||
; CHECK-O0: lghi %r9, 0
|
||||
; CHECK-O0: lhi %r3, 1
|
||||
; CHECK-O0: stg %r2, {{.*}}(%r15)
|
||||
; CHECK-O0: lgr %r2, {{.*}}
|
||||
; CHECK-O0: brasl %r14, foo_sret
|
||||
; CHECK-O0: lgr {{.*}}, %r9
|
||||
; CHECK-O0: cghi %r9, 0
|
||||
; CHECK-O0: jlh
|
||||
; Access part of the error object and save it to error_ref
|
||||
; CHECK-O0: lb %r0, 8(%r{{.*}})
|
||||
; CHECK-O0: stc %r0, 0(%r{{.*}})
|
||||
; reload from stack
|
||||
; CHECK-O0: lg %r2, {{.*}}(%r15)
|
||||
; CHECK-O0: brasl %r14, free
|
||||
entry:
|
||||
%s = alloca %struct.S, align 8
|
||||
%error_ptr_ref = alloca swifterror %swift_error*
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref
|
||||
call void @foo_sret(%struct.S* sret %s, i32 1, %swift_error** swifterror %error_ptr_ref)
|
||||
%error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref
|
||||
%had_error_from_foo = icmp ne %swift_error* %error_from_foo, null
|
||||
%tmp = bitcast %swift_error* %error_from_foo to i8*
|
||||
br i1 %had_error_from_foo, label %handler, label %cont
|
||||
cont:
|
||||
%v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1
|
||||
%t = load i8, i8* %v1
|
||||
store i8 %t, i8* %error_ref
|
||||
br label %handler
|
||||
handler:
|
||||
call void @free(i8* %tmp)
|
||||
ret float 1.0
|
||||
}
|
||||
|
||||
; This is a caller with multiple swifterror values, it calls "foo" twice, each
|
||||
; time with a different swifterror value, from "alloca swifterror".
|
||||
define float @caller_with_multiple_swifterror_values(i8* %error_ref, i8* %error_ref2) {
|
||||
; CHECK-LABEL: caller_with_multiple_swifterror_values:
|
||||
; CHECK-DAG: lgr %r[[REG1:[0-9]+]], %r2
|
||||
; CHECK-DAG: lgr %r[[REG2:[0-9]+]], %r3
|
||||
; The first swifterror value:
|
||||
; CHECK: lghi %r9, 0
|
||||
; CHECK: brasl %r14, foo
|
||||
; CHECK: cgijlh %r9, 0,
|
||||
; Access part of the error object and save it to error_ref
|
||||
; CHECK: lb %r0, 8(%r9)
|
||||
; CHECK: stc %r0, 0(%r[[REG1]])
|
||||
; CHECK: lgr %r2, %r9
|
||||
; CHECK: brasl %r14, free
|
||||
|
||||
; The second swifterror value:
|
||||
; CHECK: lghi %r9, 0
|
||||
; CHECK: brasl %r14, foo
|
||||
; CHECK: cgijlh %r9, 0,
|
||||
; Access part of the error object and save it to error_ref
|
||||
; CHECK: lb %r0, 8(%r9)
|
||||
; CHECK: stc %r0, 0(%r[[REG2]])
|
||||
; CHECK: lgr %r2, %r9
|
||||
; CHECK: brasl %r14, free
|
||||
|
||||
; CHECK-O0-LABEL: caller_with_multiple_swifterror_values:
|
||||
|
||||
; The first swifterror value:
|
||||
; CHECK-O0: lghi %r9, 0
|
||||
; CHECK-O0: brasl %r14, foo
|
||||
; CHECK-O0: jlh
|
||||
|
||||
; The second swifterror value:
|
||||
; CHECK-O0: lghi %r9, 0
|
||||
; CHECK-O0: brasl %r14, foo
|
||||
; CHECK-O0: jlh
|
||||
entry:
|
||||
%error_ptr_ref = alloca swifterror %swift_error*
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref
|
||||
%call = call float @foo(%swift_error** swifterror %error_ptr_ref)
|
||||
%error_from_foo = load %swift_error*, %swift_error** %error_ptr_ref
|
||||
%had_error_from_foo = icmp ne %swift_error* %error_from_foo, null
|
||||
%tmp = bitcast %swift_error* %error_from_foo to i8*
|
||||
br i1 %had_error_from_foo, label %handler, label %cont
|
||||
cont:
|
||||
%v1 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo, i64 0, i32 1
|
||||
%t = load i8, i8* %v1
|
||||
store i8 %t, i8* %error_ref
|
||||
br label %handler
|
||||
handler:
|
||||
call void @free(i8* %tmp)
|
||||
|
||||
%error_ptr_ref2 = alloca swifterror %swift_error*
|
||||
store %swift_error* null, %swift_error** %error_ptr_ref2
|
||||
%call2 = call float @foo(%swift_error** swifterror %error_ptr_ref2)
|
||||
%error_from_foo2 = load %swift_error*, %swift_error** %error_ptr_ref2
|
||||
%had_error_from_foo2 = icmp ne %swift_error* %error_from_foo2, null
|
||||
%bitcast2 = bitcast %swift_error* %error_from_foo2 to i8*
|
||||
br i1 %had_error_from_foo2, label %handler2, label %cont2
|
||||
cont2:
|
||||
%v2 = getelementptr inbounds %swift_error, %swift_error* %error_from_foo2, i64 0, i32 1
|
||||
%t2 = load i8, i8* %v2
|
||||
store i8 %t2, i8* %error_ref2
|
||||
br label %handler2
|
||||
handler2:
|
||||
call void @free(i8* %bitcast2)
|
||||
|
||||
ret float 1.0
|
||||
}
|
66
test/CodeGen/SystemZ/swiftself.ll
Normal file
66
test/CodeGen/SystemZ/swiftself.ll
Normal file
@ -0,0 +1,66 @@
|
||||
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
|
||||
|
||||
; Parameter with swiftself should be allocated to r10.
|
||||
; CHECK-LABEL: swiftself_param:
|
||||
; CHECK: lgr %r2, %r10
|
||||
define i8 *@swiftself_param(i8* swiftself %addr0) {
|
||||
ret i8 *%addr0
|
||||
}
|
||||
|
||||
; Check that r10 is used to pass a swiftself argument.
|
||||
; CHECK-LABEL: call_swiftself:
|
||||
; CHECK: lgr %r10, %r2
|
||||
; CHECK: brasl %r14, swiftself_param
|
||||
define i8 *@call_swiftself(i8* %arg) {
|
||||
%res = call i8 *@swiftself_param(i8* swiftself %arg)
|
||||
ret i8 *%res
|
||||
}
|
||||
|
||||
; r10 should be saved by the callee even if used for swiftself
|
||||
; CHECK-LABEL: swiftself_clobber:
|
||||
; CHECK: stmg %r10,
|
||||
; ...
|
||||
; CHECK: lmg %r10,
|
||||
; CHECK: br %r14
|
||||
define i8 *@swiftself_clobber(i8* swiftself %addr0) {
|
||||
call void asm sideeffect "", "~{r10}"()
|
||||
ret i8 *%addr0
|
||||
}
|
||||
|
||||
; Demonstrate that we do not need any loads when calling multiple functions
|
||||
; with swiftself argument.
|
||||
; CHECK-LABEL: swiftself_passthrough:
|
||||
; CHECK-NOT: lg{{.*}}r10,
|
||||
; CHECK: brasl %r14, swiftself_param
|
||||
; CHECK-NOT: lg{{.*}}r10,
|
||||
; CHECK-NEXT: brasl %r14, swiftself_param
|
||||
define void @swiftself_passthrough(i8* swiftself %addr0) {
|
||||
call i8 *@swiftself_param(i8* swiftself %addr0)
|
||||
call i8 *@swiftself_param(i8* swiftself %addr0)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Normally, we can use a tail call if the callee swiftself is the same as the
|
||||
; caller one. Not yet supported on SystemZ.
|
||||
; CHECK-LABEL: swiftself_tail:
|
||||
; CHECK: lgr %r[[REG1:[0-9]+]], %r10
|
||||
; CHECK: lgr %r10, %r[[REG1]]
|
||||
; CHECK: brasl %r14, swiftself_param
|
||||
; CHECK: br %r14
|
||||
define i8* @swiftself_tail(i8* swiftself %addr0) {
|
||||
call void asm sideeffect "", "~{r10}"()
|
||||
%res = tail call i8* @swiftself_param(i8* swiftself %addr0)
|
||||
ret i8* %res
|
||||
}
|
||||
|
||||
; We can not use a tail call if the callee swiftself is not the same as the
|
||||
; caller one.
|
||||
; CHECK-LABEL: swiftself_notail:
|
||||
; CHECK: lgr %r10, %r2
|
||||
; CHECK: brasl %r14, swiftself_param
|
||||
; CHECK: lmg %r10,
|
||||
; CHECK: br %r14
|
||||
define i8* @swiftself_notail(i8* swiftself %addr0, i8* %addr1) nounwind {
|
||||
%res = tail call i8* @swiftself_param(i8* swiftself %addr1)
|
||||
ret i8* %res
|
||||
}
|
Loading…
Reference in New Issue
Block a user