Files
c3c/test/test_suite/abi/riscv64-lp64d-abi.c3t

452 lines
13 KiB
Plaintext

// #target: linux-riscv64
// #opt: --riscvfloat=double
module test;
// Verify that the tracking of used GPRs and FPRs works correctly by checking
// that small integers are sign/zero extended when passed in registers.
// Doubles are passed in FPRs, so argument 'i' will be passed zero-extended
// because it will be passed in a GPR.
fn void f_fpr_tracking(double a, double b, double c, double d, double e, double f,
double g, double h, char i) {}
// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will
// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are
// available the widths are <= XLEN and FLEN, and should be expanded to
// separate arguments in IR. They are passed by the same rules for returns,
// but will be lowered to simple two-element structs if necessary (as LLVM IR
// functions cannot return multiple values).
// A struct containing just one floating-point real is passed as though it
// were a standalone floating-point real.
struct Double_s { double f; }
fn void f_double_s_arg(Double_s a) {}
fn Double_s f_ret_double_s() {
return {1.0};
}
// A struct containing a double and any number of zero-width bitfields is
// passed as though it were a standalone floating-point real.
// Check that structs containing two floating point values (FLEN <= width) are
// expanded provided sufficient FPRs are available.
struct Double_double_s { double f; double g; }
struct Double_float_s { double f; float g; }
fn void f_double_double_s_arg(Double_double_s a) {}
fn Double_double_s f_ret_double_double_s() {
return {1.0, 2.0};
}
fn void f_double_float_s_arg(Double_float_s a) {}
fn Double_float_s f_ret_double_float_s() {
return {1.0, 2.0};
}
fn void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d,
double e, double f, double g, Double_double_s h) {}
// Check that structs containing int+double values are expanded, provided
// sufficient FPRs and GPRs are available. The integer components are neither
// sign or zero-extended.
struct Double_int8_s { double f; ichar i; }
struct Double_uint8_s { double f; char i; }
struct Double_int32_s { double f; int i; }
struct Double_int64_s { double f; long i; }
fn void f_double_int8_s_arg(Double_int8_s a) {}
fn Double_int8_s f_ret_double_int8_s() {
return {1.0, 2};
}
fn void f_double_uint8_s_arg(Double_uint8_s a) {}
fn Double_uint8_s f_ret_double_uint8_s() {
return {1.0, 2};
}
fn void f_double_int32_s_arg(Double_int32_s a) {}
fn Double_int32_s f_ret_double_int32_s() {
return {1.0, 2};
}
fn void f_double_int64_s_arg(Double_int64_s a) {}
fn Double_int64_s f_ret_double_int64_s() {
return {1.0, 2};
}
fn void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e,
int f, int g, int h, Double_int8_s i) {}
fn void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d,
double e, double f, double g, double h, Double_int8_s i) {}
// Test single or two-element structs that need flattening. e.g. those
// containing nested structs, doubles in small arrays, zero-length structs etc.
struct Doublearr1_s { double[1] a; }
fn void f_doublearr1_s_arg(Doublearr1_s a) {}
fn Doublearr1_s f_ret_doublearr1_s() {
return {{1.0}};
}
struct Doublearr2_s { double[2] a; }
fn void f_doublearr2_s_arg(Doublearr2_s a) {}
fn Doublearr2_s f_ret_doublearr2_s() {
return {{1.0, 2.0}};
}
struct Inner { double[1] f; }
struct Doublearr2_tricky1_s { Inner[2] g; }
fn void f_doublearr2_tricky1_s_arg(Doublearr2_tricky1_s a) {}
fn Doublearr2_tricky1_s f_ret_doublearr2_tricky1_s() {
return {{{{1.0}}, {{2.0}}}};
}
// Test structs that should be passed according to the normal integer calling
// convention.
struct Int_double_int_s { int a; double b; int c; }
fn void f_int_double_int_s_arg(Int_double_int_s a) {}
fn Int_double_int_s f_ret_int_double_int_s() {
return {1, 2.0, 3};
}
struct Char_char_double_s { char a; char b; double c; }
fn void f_char_char_double_s_arg(Char_char_double_s a) {}
fn Char_char_double_s f_ret_char_char_double_s() {
return {1, 2, 3.0};
}
// Unions are always passed according to the integer calling convention, even
// if they can only contain a double.
union Double_u { double a; }
fn void f_double_u_arg(Double_u a) {}
fn Double_u f_ret_double_u() {
return {1.0};
}
/* #expect: test.ll
define void @test.f_fpr_tracking(double %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, i8 zeroext %8) #0 {
entry:
ret void
}
define void @test.f_double_s_arg(double %0) #0 {
entry:
%a = alloca %Double_s, align 8
store double %0, ptr %a, align 8
ret void
}
define double @test.f_ret_double_s() #0 {
entry:
%literal = alloca %Double_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const, i32 8, i1 false)
%0 = load double, ptr %literal, align 8
ret double %0
}
define void @test.f_double_double_s_arg(double %0, double %1) #0 {
entry:
%a = alloca %Double_double_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store double %1, ptr %a, align 8
ret void
}
define { double, double } @test.f_ret_double_double_s() #0 {
entry:
%literal = alloca %Double_double_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.1, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load double, ptr %literal, align 8
%2 = insertvalue { double, double } undef, double %0, 0
%3 = insertvalue { double, double } %2, double %1, 1
ret { double, double } %3
}
define void @test.f_double_float_s_arg(double %0, float %1) #0 {
entry:
%a = alloca %Double_float_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store float %1, ptr %a, align 8
ret void
}
define { double, float } @test.f_ret_double_float_s() #0 {
entry:
%literal = alloca %Double_float_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.2, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load float, ptr %literal, align 8
%2 = insertvalue { double, float } undef, double %0, 0
%3 = insertvalue { double, float } %2, float %1, 1
ret { double, float } %3
}
define void @test.f_double_double_s_arg_insufficient_fprs(float %0, double %1, double %2, double %3, double %4, double %5, double %6, [2 x i64] %7) #0 {
entry:
%h = alloca %Double_double_s, align 8
store [2 x i64] %7, ptr %h, align 8
ret void
}
define void @test.f_double_int8_s_arg(double %0, i8 %1) #0 {
entry:
%a = alloca %Double_int8_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store i8 %1, ptr %a, align 8
ret void
}
define { double, i8 } @test.f_ret_double_int8_s() #0 {
entry:
%literal = alloca %Double_int8_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.3, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load i8, ptr %literal, align 8
%2 = insertvalue { double, i8 } undef, double %0, 0
%3 = insertvalue { double, i8 } %2, i8 %1, 1
ret { double, i8 } %3
}
define void @test.f_double_uint8_s_arg(double %0, i8 %1) #0 {
entry:
%a = alloca %Double_uint8_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store i8 %1, ptr %a, align 8
ret void
}
define { double, i8 } @test.f_ret_double_uint8_s() #0 {
entry:
%literal = alloca %Double_uint8_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.4, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load i8, ptr %literal, align 8
%2 = insertvalue { double, i8 } undef, double %0, 0
%3 = insertvalue { double, i8 } %2, i8 %1, 1
ret { double, i8 } %3
}
define void @test.f_double_int32_s_arg(double %0, i32 %1) #0 {
entry:
%a = alloca %Double_int32_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store i32 %1, ptr %a, align 8
ret void
}
define { double, i32 } @test.f_ret_double_int32_s() #0 {
entry:
%literal = alloca %Double_int32_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.5, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load i32, ptr %literal, align 8
%2 = insertvalue { double, i32 } undef, double %0, 0
%3 = insertvalue { double, i32 } %2, i32 %1, 1
ret { double, i32 } %3
}
define void @test.f_double_int64_s_arg(double %0, i64 %1) #0 {
entry:
%a = alloca %Double_int64_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store i64 %1, ptr %a, align 8
ret void
}
define { double, i64 } @test.f_ret_double_int64_s() #0 {
entry:
%literal = alloca %Double_int64_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.6, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load i64, ptr %literal, align 8
%2 = insertvalue { double, i64 } undef, double %0, 0
%3 = insertvalue { double, i64 } %2, i64 %1, 1
ret { double, i64 } %3
}
define void @test.f_double_int8_s_arg_insufficient_gprs(i32 signext %0, i32 signext %1, i32 signext %2, i32 signext %3, i32 signext %4, i32 signext %5, i32 signext %6, i32 signext %7, [2 x i64] %8) #0 {
entry:
%i = alloca %Double_int8_s, align 8
store [2 x i64] %8, ptr %i, align 8
ret void
}
define void @test.f_struct_double_int8_insufficient_fprs(float %0, double %1, double %2, double %3, double %4, double %5, double %6, double %7, [2 x i64] %8) #0 {
entry:
%i = alloca %Double_int8_s, align 8
store [2 x i64] %8, ptr %i, align 8
ret void
}
define void @test.f_doublearr1_s_arg(double %0) #0 {
entry:
%a = alloca %Doublearr1_s, align 8
store double %0, ptr %a, align 8
ret void
}
define double @test.f_ret_doublearr1_s() #0 {
entry:
%literal = alloca %Doublearr1_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.7, i32 8, i1 false)
%0 = load double, ptr %literal, align 8
ret double %0
}
define void @test.f_doublearr2_s_arg(double %0, double %1) #0 {
entry:
%a = alloca %Doublearr2_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store double %1, ptr %a, align 8
ret void
}
define { double, double } @test.f_ret_doublearr2_s() #0 {
entry:
%literal = alloca %Doublearr2_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.8, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load double, ptr %literal, align 8
%2 = insertvalue { double, double } undef, double %0, 0
%3 = insertvalue { double, double } %2, double %1, 1
ret { double, double } %3
}
define void @test.f_doublearr2_tricky1_s_arg(double %0, double %1) #0 {
entry:
%a = alloca %Doublearr2_tricky1_s, align 8
store double %0, ptr %a, align 8
%ptradd = getelementptr inbounds i8, ptr %a, i64 8
store double %1, ptr %a, align 8
ret void
}
define { double, double } @test.f_ret_doublearr2_tricky1_s() #0 {
entry:
%literal = alloca %Doublearr2_tricky1_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.9, i32 16, i1 false)
%0 = load double, ptr %literal, align 8
%ptradd = getelementptr inbounds i8, ptr %literal, i64 8
%1 = load double, ptr %literal, align 8
%2 = insertvalue { double, double } undef, double %0, 0
%3 = insertvalue { double, double } %2, double %1, 1
ret { double, double } %3
}
define void @test.f_int_double_int_s_arg(ptr align 8 %0) #0 {
entry:
ret void
}
define void @test.f_ret_int_double_int_s(ptr noalias sret(%Int_double_int_s) align 8 %0) #0 {
entry:
%literal = alloca %Int_double_int_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.10, i32 24, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %0, ptr align 8 %literal, i32 24, i1 false)
ret void
}
define void @test.f_char_char_double_s_arg([2 x i64] %0) #0 {
entry:
%a = alloca %Char_char_double_s, align 8
store [2 x i64] %0, ptr %a, align 8
ret void
}
define [2 x i64] @test.f_ret_char_char_double_s() #0 {
entry:
%literal = alloca %Char_char_double_s, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.11, i32 16, i1 false)
%0 = load [2 x i64], ptr %literal, align 8
ret [2 x i64] %0
}
define void @test.f_double_u_arg(i64 %0) #0 {
entry:
%a = alloca %Double_u, align 8
store i64 %0, ptr %a, align 8
ret void
}
define i64 @test.f_ret_double_u() #0 {
entry:
%literal = alloca %Double_u, align 8
call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const.12, i32 8, i1 false)
%0 = load i64, ptr %literal, align 8
ret i64 %0
}