blob: 454c21661e5438891176b42dd6d860f43efb1d2f [file] [log] [blame]
//==- llvm/tools/gollvm/unittests/BackendCore/BackendCABIOracleTests.cpp -==//
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//===----------------------------------------------------------------------===//
#include "TestUtils.h"
#include "go-llvm-cabi-oracle.h"
#include "go-llvm-backend.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace goBackendUnitTests;
namespace {
class BackendCABIOracleTests
: public testing::TestWithParam<llvm::CallingConv::ID> {};
INSTANTIATE_TEST_CASE_P(
UnitTest, BackendCABIOracleTests,
goBackendUnitTests::CConvs,
[](const testing::TestParamInfo<BackendCABIOracleTests::ParamType> &info) {
std::string name = goBackendUnitTests::ccName(info.param);
return name;
});
TEST_P(BackendCABIOracleTests, Basic) {
LLVMContext C;
auto cc = GetParam();
std::unique_ptr<Llvm_backend> bep(
new Llvm_backend(C, nullptr, nullptr, 0, llvm::Triple(), cc));
Llvm_backend *be = bep.get();
Btype *bi8t = be->integer_type(false, 8);
Btype *bu8t = be->integer_type(true, 8);
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *st0 = mkBackendStruct(be, nullptr);
Btype *st1 = mkBackendStruct(be, bi8t, "a", bu8t, "b", bf32t, "c", nullptr);
Btype *st2 = mkBackendStruct(be, bf64t, "f1", bf64t, "f2", nullptr);
{
BFunctionType *befty1 = mkFuncTyp(be,
L_PARM, bi8t,
L_PARM, bf32t,
L_PARM, st0,
L_PARM, st1,
L_RES, st2,
L_END);
CABIOracle cab(befty1, be->typeManager());
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
Return: Direct { { double, double } } sigOffset: -1
Param 1: Direct AttrNest { i8* } sigOffset: 0
Param 2: Direct AttrSext { i8 } sigOffset: 1
Param 3: Direct { float } sigOffset: 2
Param 4: Ignore { void } sigOffset: -1
Param 5: Direct { i64 } sigOffset: 3
)RAW_RESULT");
std::string reason;
bool equal = difftokens(exp.content, cab.toString(), reason);
EXPECT_EQ("pass", equal ? "pass" : reason);
EXPECT_EQ(repr(cab.getFunctionTypeForABI()),
"{ double, double } (i8*, i8, float, i64)");
}
}
TEST(BackendCABIOracleTests, ExtendedAmd64) {
LLVMContext C;
std::unique_ptr<Llvm_backend> bep(
new Llvm_backend(C, nullptr, nullptr, 0, llvm::Triple(), llvm::CallingConv::X86_64_SysV));
Llvm_backend *be = bep.get();
Btype *bi8t = be->integer_type(false, 8);
Btype *bu8t = be->integer_type(true, 8);
Btype *bu64t = be->integer_type(true, 64);
Btype *bu32t = be->integer_type(true, 32);
Btype *bi16t = be->integer_type(false, 16);
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *bpu64t = be->pointer_type(bu64t);
Btype *bpf64t = be->pointer_type(bf64t);
Btype *st0 = mkBackendStruct(be, nullptr);
Btype *st1 = mkBackendStruct(be, bi8t, "a", bu8t, "b", bf32t, "c", nullptr);
Btype *st2 = mkBackendStruct(be, bf64t, "f1", bf64t, "f2", nullptr);
Btype *st3 = mkBackendStruct(be, st2, "f1", bi8t, "f2", nullptr);
Btype *st4 = mkBackendStruct(be, bf32t, "f1", bf32t, "f2", nullptr);
Btype *st5 = mkBackendStruct(be, bf32t, "f1", nullptr);
Btype *st6 = mkBackendStruct(be, bf32t, "f1", bi8t, "a", bu8t, "b",
bu64t, "c", nullptr);
Btype *st7 = mkBackendStruct(be, bf32t, "f1", bu32t, "f2", nullptr);
Btype *st8 = mkBackendStruct(be, bi8t, "f1", bi16t, "f2", st7, "f3", nullptr);
Btype *stii = mkBackendStruct(be, bu64t, "a", bu64t, "b", nullptr);
Btype *stip = mkBackendStruct(be, bu64t, "a", bpu64t, "b", nullptr);
Btype *stpi = mkBackendStruct(be, bpu64t, "a", bu64t, "b", nullptr);
Btype *stpp = mkBackendStruct(be, bpu64t, "a", bpu64t, "b", nullptr);
Btype *at0 = be->array_type(bu32t, mkInt64Const(be, int64_t(0)));
Btype *at1 = be->array_type(bu32t, mkInt64Const(be, int64_t(1)));
Btype *at2 = be->array_type(bu32t, mkInt64Const(be, int64_t(3)));
Btype *at3 = be->array_type(bu8t, mkInt64Const(be, int64_t(16)));
struct FcnItem {
FcnItem(const std::vector<Btype*> &r,
const std::vector<Btype*> &p,
const char *d, const char *t)
: results(r), parms(p), expDump(d), expTyp(t) { }
std::vector<Btype*> results;
std::vector<Btype*> parms;
const char *expDump;
const char *expTyp;
};
Btype *nt = nullptr;
std::vector<FcnItem> items = {
// 1
FcnItem( { }, { },
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0",
"void (i8*)"),
// 2
FcnItem( { bi8t }, { },
"Return: Direct AttrSext { i8 } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0",
"i8 (i8*)"),
// 3
FcnItem( { }, { bi8t },
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct AttrSext { i8 } sigOffset: 1",
"void (i8*, i8)"),
// 4
FcnItem( { }, { st5, bpf64t },
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { float } sigOffset: 1 "
"Param 3: Direct { double* } sigOffset: 2",
"void (i8*, float, double*)"),
// 5
FcnItem({ bi8t, bf64t }, { bi8t, bu8t, st0 },
"Return: Direct { { i8, double } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct AttrSext { i8 } sigOffset: 1 "
"Param 3: Direct AttrZext { i8 } sigOffset: 2 "
"Param 4: Ignore { void } sigOffset: -1",
"{ i8, double } (i8*, i8, i8)"),
// 6
FcnItem({ st2 }, { st2, st0, st4, st1 },
"Return: Direct { { double, double } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { double, double } sigOffset: 1 "
"Param 3: Ignore { void } sigOffset: -1 "
"Param 4: Direct { <2 x float> } sigOffset: 3 "
"Param 5: Direct { i64 } sigOffset: 4 ",
"{ double, double } (i8*, double, double, <2 x float>, i64)"),
// 7
FcnItem({ st3 }, { st3, st0, bu8t },
"Return: Indirect AttrStructReturn { { { double, double }, i8 }* } sigOffset: 0 "
"Param 1: Direct AttrNest { i8* } sigOffset: 1 "
"Param 2: Indirect AttrByVal { { { double, double }, i8 }* } sigOffset: 2 "
"Param 3: Ignore { void } sigOffset: -1 "
"Param 4: Direct AttrZext { i8 } sigOffset: 3 ",
"void ({ { double, double }, i8 }*, i8*, "
"{ { double, double }, i8 }*, i8)"),
// 8
FcnItem( { st6 }, { st6, st6 },
"Return: Direct { { i64, i64 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i64 } sigOffset: 1 "
"Param 3: Direct { i64, i64 } sigOffset: 3",
"{ i64, i64 } (i8*, i64, i64, i64, i64)"),
// 9
FcnItem( { st8 }, { st8 },
"Return: Direct { { i64, i32 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i32 } sigOffset: 1",
"{ i64, i32 } (i8*, i64, i32)"),
// 10
FcnItem( { at0 }, { at1 },
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i32 } sigOffset: 1",
"void (i8*, i32)"),
// 11
FcnItem( { at2 }, { at3 },
"Return: Direct { { i64, i32 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i64 } sigOffset: 1",
"{ i64, i32 } (i8*, i64, i64)"),
// 12
// Make sure pointerness is preserved.
FcnItem( { stip }, { stii, stpp, stpi },
"Return: Direct { { i64, i8* } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i64 } sigOffset: 1 "
"Param 3: Direct { i8*, i8* } sigOffset: 3 "
"Param 4: Direct { i8*, i64 } sigOffset: 5",
"{ i64, i8* } (i8*, i64, i64, i8*, i8*, i8*, i64)"),
};
unsigned count = 1;
for (auto &item : items) {
std::vector<Backend::Btyped_identifier> results;
std::vector<Backend::Btyped_identifier> params;
for (auto &r : item.results)
results.push_back(mkid(r));
for (auto &p : item.parms)
params.push_back(mkid(p));
Btype *rt = nullptr;
if (results.size() > 1)
rt = be->struct_type(results);
Btype *t = be->function_type(mkid(nt), params, results, rt, Location());
BFunctionType *bft = t->castToBFunctionType();
CABIOracle cab(bft, be->typeManager());
{
std::string reason;
bool equal = difftokens(item.expDump, cab.toString(), reason);
EXPECT_EQ("pass", equal ? "pass" : reason);
if (!equal) {
std::cerr << "count: " << count << "\n";
std::cerr << "exp:\n" << item.expDump << "\n";
std::cerr << "act:\n" << cab.toString() << "\n";
}
}
{
std::string reason;
std::string result(repr(cab.getFunctionTypeForABI()));
bool equal = difftokens(item.expTyp, result, reason);
EXPECT_EQ("pass", equal ? "pass" : reason);
if (!equal) {
std::cerr << "count: " << count << "\n";
std::cerr << "exp:\n" << item.expTyp << "\n";
std::cerr << "act:\n" << result << "\n";
}
}
count++;
}
}
TEST(BackendCABIOracleTests, ExtendedArm64) {
LLVMContext C;
std::unique_ptr<Llvm_backend> bep(
new Llvm_backend(C, nullptr, nullptr, 0, llvm::Triple(), llvm::CallingConv::ARM_AAPCS));
Llvm_backend *be = bep.get();
Btype *bi8t = be->integer_type(false, 8);
Btype *bu8t = be->integer_type(true, 8);
Btype *bu64t = be->integer_type(true, 64);
Btype *bu32t = be->integer_type(true, 32);
Btype *bi16t = be->integer_type(false, 16);
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *bpu64t = be->pointer_type(bu64t);
Btype *bpf64t = be->pointer_type(bf64t);
Btype *st0 = mkBackendStruct(be, nullptr);
Btype *st1 = mkBackendStruct(be, bi8t, "a", bu8t, "b", bf32t, "c", nullptr);
Btype *st2 = mkBackendStruct(be, bf64t, "f1", bf64t, "f2", nullptr);
Btype *st3 = mkBackendStruct(be, st2, "f1", bi8t, "f2", nullptr);
Btype *st4 = mkBackendStruct(be, bf32t, "f1", bf32t, "f2", nullptr);
Btype *st5 = mkBackendStruct(be, bf32t, "f1", nullptr);
Btype *st6 = mkBackendStruct(be, bf32t, "f1", bi8t, "a", bu8t, "b", bu64t,
"c", nullptr);
Btype *st7 = mkBackendStruct(be, bf32t, "f1", bu32t, "f2", nullptr);
Btype *st8 = mkBackendStruct(be, bi8t, "f1", bi16t, "f2", st7, "f3", nullptr);
Btype *stii = mkBackendStruct(be, bu64t, "a", bu64t, "b", nullptr);
Btype *stip = mkBackendStruct(be, bu64t, "a", bpu64t, "b", nullptr);
Btype *stpi = mkBackendStruct(be, bpu64t, "a", bu64t, "b", nullptr);
Btype *stpp = mkBackendStruct(be, bpu64t, "a", bpu64t, "b", nullptr);
Btype *at0 = be->array_type(bu32t, mkInt64Const(be, int64_t(0)));
Btype *at1 = be->array_type(bu32t, mkInt64Const(be, int64_t(1)));
Btype *at2 = be->array_type(bu32t, mkInt64Const(be, int64_t(3)));
Btype *at3 = be->array_type(bu8t, mkInt64Const(be, int64_t(16)));
struct FcnItem {
FcnItem(const std::vector<Btype *> &r, const std::vector<Btype *> &p,
const char *d, const char *t)
: results(r), parms(p), expDump(d), expTyp(t) {}
std::vector<Btype *> results;
std::vector<Btype *> parms;
const char *expDump;
const char *expTyp;
};
Btype *nt = nullptr;
std::vector<FcnItem> items = {
// 1
FcnItem({}, {},
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0",
"void (i8*)"),
// 2
FcnItem({bi8t}, {},
"Return: Direct AttrSext { i8 } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0",
"i8 (i8*)"),
// 3
FcnItem({}, {bi8t},
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct AttrSext { i8 } sigOffset: 1",
"void (i8*, i8)"),
// 4
FcnItem({}, {st5, bpf64t},
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { float } sigOffset: 1 "
"Param 3: Direct { double* } sigOffset: 2",
"void (i8*, float, double*)"),
// 5
FcnItem({bi8t, bf64t}, {bi8t, bu8t, st0},
"Return: Direct { { i64, i64 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct AttrSext { i8 } sigOffset: 1 "
"Param 3: Direct AttrZext { i8 } sigOffset: 2 "
"Param 4: Ignore { void } sigOffset: -1",
"{ i64, i64 } (i8*, i8, i8)"),
// 6
FcnItem({st2}, {st2, st0, st4, st1},
"Return: Direct { { double, double } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { [2 x double] } sigOffset: 1 "
"Param 3: Ignore { void } sigOffset: -1 "
"Param 4: Direct { [2 x float] } sigOffset: 2 "
"Param 5: Direct { i64 } sigOffset: 3",
"{ double, double } (i8*, [2 x double], [2 x float], i64)"),
// 7
FcnItem({st3}, {st3, st0, bu8t},
"Return: Indirect AttrStructReturn { { { double, double }, i8 "
"}* } sigOffset: 0 "
"Param 1: Direct AttrNest { i8* } sigOffset: 1 "
"Param 2: Indirect AttrDoCopy { { { double, double }, i8 }* } "
"sigOffset: 2 "
"Param 3: Ignore { void } sigOffset: -1 "
"Param 4: Direct AttrZext { i8 } sigOffset: 3 ",
"void ({ { double, double }, i8 }*, i8*, "
"{ { double, double }, i8 }*, i8)"),
// 8
FcnItem({st6}, {st6, st6},
"Return: Direct { { i64, i64 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i64 } sigOffset: 1 "
"Param 3: Direct { i64, i64 } sigOffset: 3",
"{ i64, i64 } (i8*, i64, i64, i64, i64)"),
// 9
FcnItem({st8}, {st8},
"Return: Direct { { i64, i32 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i32 } sigOffset: 1",
"{ i64, i32 } (i8*, i64, i32)"),
// 10
FcnItem({at0}, {at1},
"Return: Ignore { void } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i32 } sigOffset: 1",
"void (i8*, i32)"),
// 11
FcnItem({at2}, {at3},
"Return: Direct { { i64, i32 } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i64 } sigOffset: 1",
"{ i64, i32 } (i8*, i64, i64)"),
// 12
// Make sure pointerness is preserved.
FcnItem({stip}, {stii, stpp, stpi},
"Return: Direct { { i64, i8* } } sigOffset: -1 "
"Param 1: Direct AttrNest { i8* } sigOffset: 0 "
"Param 2: Direct { i64, i64 } sigOffset: 1 "
"Param 3: Direct { i8*, i8* } sigOffset: 3 "
"Param 4: Direct { i8*, i64 } sigOffset: 5",
"{ i64, i8* } (i8*, i64, i64, i8*, i8*, i8*, i64)"),
};
unsigned count = 1;
for (auto &item : items) {
std::vector<Backend::Btyped_identifier> results;
std::vector<Backend::Btyped_identifier> params;
for (auto &r : item.results)
results.push_back(mkid(r));
for (auto &p : item.parms)
params.push_back(mkid(p));
Btype *rt = nullptr;
if (results.size() > 1)
rt = be->struct_type(results);
Btype *t = be->function_type(mkid(nt), params, results, rt, Location());
BFunctionType *bft = t->castToBFunctionType();
CABIOracle cab(bft, be->typeManager());
{
std::string reason;
bool equal = difftokens(item.expDump, cab.toString(), reason);
EXPECT_EQ("pass", equal ? "pass" : reason);
if (!equal) {
std::cerr << "count: " << count << "\n";
std::cerr << "exp:\n" << item.expDump << "\n";
std::cerr << "act:\n" << cab.toString() << "\n";
}
}
{
std::string reason;
std::string result(repr(cab.getFunctionTypeForABI()));
bool equal = difftokens(item.expTyp, result, reason);
EXPECT_EQ("pass", equal ? "pass" : reason);
if (!equal) {
std::cerr << "count: " << count << "\n";
std::cerr << "exp:\n" << item.expTyp << "\n";
std::cerr << "act:\n" << result << "\n";
}
}
count++;
}
}
TEST(BackendCABIOracleTests, RecursiveCall1Amd64) {
FcnTestHarness h(llvm::CallingConv::X86_64_SysV);
Llvm_backend *be = h.be();
// type s1 struct {
// f1, f2 float32
// i1, i2, i3 int16
// }
// type s2 struct {
// k float64
// f1, f2 float32
// }
// type s3 struct {
// f1, s1
// f2, s2
// }
// type s4 struct {
// }
// func foo(x s1, y s2, z s4, sm1 uint8, sm2 int8, w s3) s2 {
// if (sm1 == 0) {
// return y
// }
// return foo(x, y, z, sm1-1, sm2, w)
// }
//
// Create struct types
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *bi16t = be->integer_type(false, 16);
Btype *bi8t = be->integer_type(false, 8);
Btype *bu8t = be->integer_type(true, 8);
Btype *s1 = mkBackendStruct(be, bf32t, "f1", bf32t, "f2",
bi16t, "i1", bi16t, "i2", bi16t, "i3", nullptr);
Btype *s2 = mkBackendStruct(be, bf64t, "k", bf32t, "f1", bf32t, "f2",
nullptr);
Btype *s3 = mkBackendStruct(be, s1, "f1", s2, "f2", nullptr);
Btype *s4 = mkBackendStruct(be, nullptr);
// Create function type
BFunctionType *befty1 = mkFuncTyp(be,
L_PARM, s1,
L_PARM, s2,
L_PARM, s4,
L_PARM, bu8t,
L_PARM, bi8t,
L_PARM, s3,
L_RES, s2,
L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// sm1 == 0
Bvariable *p3 = func->getNthParamVar(3);
Location loc;
Bexpression *vex = be->var_expression(p3, loc);
Bexpression *c0 = be->convert_expression(bu8t, mkInt32Const(be, 0), loc);
Bexpression *eq = be->binary_expression(OPERATOR_EQEQ, vex, c0, loc);
// call
Bexpression *fn = be->function_code_expression(func, loc);
std::vector<Bexpression *> args;
Bvariable *p0 = func->getNthParamVar(0);
args.push_back(be->var_expression(p0, loc));
Bvariable *p1 = func->getNthParamVar(1);
args.push_back(be->var_expression(p1, loc));
Bvariable *p2 = func->getNthParamVar(2);
args.push_back(be->var_expression(p2, loc));
Bvariable *p3x = func->getNthParamVar(3);
Bexpression *vex3 = be->var_expression(p3x, loc);
Bexpression *c1 = be->convert_expression(bu8t, mkInt32Const(be, 1), loc);
Bexpression *minus = be->binary_expression(OPERATOR_MINUS, vex3, c1, loc);
args.push_back(minus);
Bvariable *p4 = func->getNthParamVar(4);
args.push_back(be->var_expression(p4, loc));
Bvariable *p5 = func->getNthParamVar(5);
args.push_back(be->var_expression(p5, loc));
Bexpression *call = be->call_expression(func, fn, args, nullptr, h.loc());
// return y
std::vector<Bexpression *> rvals1;
rvals1.push_back(be->var_expression(p1, loc));
Bstatement *rst1 = h.mkReturn(rvals1, FcnTestHarness::NoAppend);
// return call
std::vector<Bexpression *> rvals2;
rvals2.push_back(call);
Bstatement *rst2 = h.mkReturn(rvals2, FcnTestHarness::NoAppend);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
%p3.ld.0 = load i8, i8* %p3.addr, align 1
%sub.0 = sub i8 %p3.ld.0, 1
%p4.ld.0 = load i8, i8* %p4.addr, align 1
%cast.1 = bitcast { float, float, i16, i16, i16 }* %p0.addr to { <2 x float>, i48 }*
%field0.0 = getelementptr inbounds { <2 x float>, i48 }, { <2 x float>, i48 }* %cast.1, i32 0, i32 0
%ld.1 = load <2 x float>, <2 x float>* %field0.0, align 8
%field1.0 = getelementptr inbounds { <2 x float>, i48 }, { <2 x float>, i48 }* %cast.1, i32 0, i32 1
%ld.2 = load i48, i48* %field1.0, align 8
%cast.2 = bitcast { double, float, float }* %p1.addr to { double, <2 x float> }*
%field0.1 = getelementptr inbounds { double, <2 x float> }, { double, <2 x float> }* %cast.2, i32 0, i32 0
%ld.3 = load double, double* %field0.1, align 8
%field1.1 = getelementptr inbounds { double, <2 x float> }, { double, <2 x float> }* %cast.2, i32 0, i32 1
%ld.4 = load <2 x float>, <2 x float>* %field1.1, align 8
%call.0 = call addrspace(0) { double, <2 x float> } @foo(i8* nest undef, <2 x float> %ld.1, i48 %ld.2, double %ld.3, <2 x float> %ld.4, i8 zeroext %sub.0, i8 signext %p4.ld.0, { { float, float, i16, i16, i16 }, { double, float, float } }* byval({ { float, float, i16, i16, i16 }, { double, float, float } }) %p5)
%cast.3 = bitcast { double, float, float }* %sret.actual.0 to { double, <2 x float> }*
store { double, <2 x float> } %call.0, { double, <2 x float> }* %cast.3, align 8
%cast.4 = bitcast { double, float, float }* %sret.actual.0 to { double, <2 x float> }*
%ld.5 = load { double, <2 x float> }, { double, <2 x float> }* %cast.4, align 8
ret { double, <2 x float> } %ld.5
)RAW_RESULT");
bool isOK = h.expectStmt(rst2, exp);
EXPECT_TRUE(isOK && "Statement does not have expected contents");
// if statement
h.mkIf(eq, rst1, rst2);
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendCABIOracleTests, RecursiveCall1Arm64) {
FcnTestHarness h(llvm::CallingConv::ARM_AAPCS);
Llvm_backend *be = h.be();
// type s1 struct {
// f1, f2 float32
// i1, i2, i3 int16
// }
// type s2 struct {
// k float64
// f1, f2 float32
// }
// type s3 struct {
// f1, s1
// f2, s2
// }
// type s4 struct {
// }
// func foo(x s1, y s2, z s4, sm1 uint8, sm2 int8, w s3) s2 {
// if (sm1 == 0) {
// return y
// }
// return foo(x, y, z, sm1-1, sm2, w)
// }
//
// Create struct types
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *bi16t = be->integer_type(false, 16);
Btype *bi8t = be->integer_type(false, 8);
Btype *bu8t = be->integer_type(true, 8);
Btype *s1 = mkBackendStruct(be, bf32t, "f1", bf32t, "f2", bi16t, "i1", bi16t,
"i2", bi16t, "i3", nullptr);
Btype *s2 =
mkBackendStruct(be, bf64t, "k", bf32t, "f1", bf32t, "f2", nullptr);
Btype *s3 = mkBackendStruct(be, s1, "f1", s2, "f2", nullptr);
Btype *s4 = mkBackendStruct(be, nullptr);
// Create function type
BFunctionType *befty1 =
mkFuncTyp(be, L_PARM, s1, L_PARM, s2, L_PARM, s4, L_PARM, bu8t, L_PARM,
bi8t, L_PARM, s3, L_RES, s2, L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// sm1 == 0
Bvariable *p3 = func->getNthParamVar(3);
Location loc;
Bexpression *vex = be->var_expression(p3, loc);
Bexpression *c0 = be->convert_expression(bu8t, mkInt32Const(be, 0), loc);
Bexpression *eq = be->binary_expression(OPERATOR_EQEQ, vex, c0, loc);
// call
Bexpression *fn = be->function_code_expression(func, loc);
std::vector<Bexpression *> args;
Bvariable *p0 = func->getNthParamVar(0);
args.push_back(be->var_expression(p0, loc));
Bvariable *p1 = func->getNthParamVar(1);
args.push_back(be->var_expression(p1, loc));
Bvariable *p2 = func->getNthParamVar(2);
args.push_back(be->var_expression(p2, loc));
Bvariable *p3x = func->getNthParamVar(3);
Bexpression *vex3 = be->var_expression(p3x, loc);
Bexpression *c1 = be->convert_expression(bu8t, mkInt32Const(be, 1), loc);
Bexpression *minus = be->binary_expression(OPERATOR_MINUS, vex3, c1, loc);
args.push_back(minus);
Bvariable *p4 = func->getNthParamVar(4);
args.push_back(be->var_expression(p4, loc));
Bvariable *p5 = func->getNthParamVar(5);
args.push_back(be->var_expression(p5, loc));
Bexpression *call = be->call_expression(func, fn, args, nullptr, h.loc());
// return y
std::vector<Bexpression *> rvals1;
rvals1.push_back(be->var_expression(p1, loc));
Bstatement *rst1 = h.mkReturn(rvals1, FcnTestHarness::NoAppend);
// return call
std::vector<Bexpression *> rvals2;
rvals2.push_back(call);
Bstatement *rst2 = h.mkReturn(rvals2, FcnTestHarness::NoAppend);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
%p3.ld.0 = load i8, i8* %p3.addr, align 1
%sub.0 = sub i8 %p3.ld.0, 1
%p4.ld.0 = load i8, i8* %p4.addr, align 1
%cast.1 = bitcast { float, float, i16, i16, i16 }* %p0.addr to { i64, i48 }*
%field0.0 = getelementptr inbounds { i64, i48 }, { i64, i48 }* %cast.1, i32 0, i32 0
%ld.1 = load i64, i64* %field0.0, align 8
%field1.0 = getelementptr inbounds { i64, i48 }, { i64, i48 }* %cast.1, i32 0, i32 1
%ld.2 = load i48, i48* %field1.0, align 8
%cast.2 = bitcast { double, float, float }* %p1.addr to { i64, i64 }*
%field0.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %cast.2, i32 0, i32 0
%ld.3 = load i64, i64* %field0.1, align 8
%field1.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %cast.2, i32 0, i32 1
%ld.4 = load i64, i64* %field1.1, align 8
%cast.3 = bitcast { { float, float, i16, i16, i16 }, { double, float, float } }* %doCopy.addr.0 to i8*
%cast.4 = bitcast { { float, float, i16, i16, i16 }, { double, float, float } }* %p5 to i8*
call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.3, i8* align 8 %cast.4, i64 32, i1 false)
%call.0 = call addrspace(0) { i64, i64 } @foo(i8* nest undef, i64 %ld.1, i48 %ld.2, i64 %ld.3, i64 %ld.4, i8 zeroext %sub.0, i8 signext %p4.ld.0, { { float, float, i16, i16, i16 }, { double, float, float } }* %doCopy.addr.0)
%cast.5 = bitcast { double, float, float }* %sret.actual.0 to { i64, i64 }*
store { i64, i64 } %call.0, { i64, i64 }* %cast.5, align 8
%cast.6 = bitcast { double, float, float }* %sret.actual.0 to { i64, i64 }*
%ld.5 = load { i64, i64 }, { i64, i64 }* %cast.6, align 8
ret { i64, i64 } %ld.5
)RAW_RESULT");
bool isOK = h.expectStmt(rst2, exp);
EXPECT_TRUE(isOK && "Statement does not have expected contents");
// if statement
h.mkIf(eq, rst1, rst2);
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendCABIOracleTests, PassAndReturnArraysAmd64) {
FcnTestHarness h(llvm::CallingConv::X86_64_SysV);
Llvm_backend *be = h.be();
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *at2f = be->array_type(bf32t, mkInt64Const(be, int64_t(2)));
Btype *at3d = be->array_type(bf64t, mkInt64Const(be, int64_t(3)));
// func foo(fp [2]float32) [3]float64
BFunctionType *befty1 = mkFuncTyp(be,
L_PARM, at2f,
L_RES, at3d,
L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// foo(fp)
Location loc;
Bvariable *p0 = func->getNthParamVar(0);
Bexpression *vex = be->var_expression(p0, loc);
Bexpression *fn = be->function_code_expression(func, loc);
std::vector<Bexpression *> args;
args.push_back(vex);
Bexpression *call = be->call_expression(func, fn, args, nullptr, h.loc());
// return foo(fp)
std::vector<Bexpression *> rvals;
rvals.push_back(call);
h.mkReturn(rvals);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
%cast.0 = bitcast [2 x float]* %p0.addr to <2 x float>*
%ld.0 = load <2 x float>, <2 x float>* %cast.0, align 8
call addrspace(0) void @foo([3 x double]* sret "go_sret" %sret.actual.0, i8* nest undef, <2 x float> %ld.0)
%cast.1 = bitcast [3 x double]* %sret.formal.0 to i8*
%cast.2 = bitcast [3 x double]* %sret.actual.0 to i8*
call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %cast.1, i8* align 8 %cast.2, i64 24, i1 false)
ret void
)RAW_RESULT");
bool isOK = h.expectBlock(exp);
EXPECT_TRUE(isOK && "Block does not have expected contents");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendCABIOracleTests, PassAndReturnArraysArm64) {
FcnTestHarness h(llvm::CallingConv::ARM_AAPCS);
Llvm_backend *be = h.be();
Btype *bf32t = be->float_type(32);
Btype *bf64t = be->float_type(64);
Btype *at2f = be->array_type(bf32t, mkInt64Const(be, int64_t(2)));
Btype *at3d = be->array_type(bf64t, mkInt64Const(be, int64_t(3)));
// func foo(fp [2]float32) [3]float64
BFunctionType *befty1 = mkFuncTyp(be, L_PARM, at2f, L_RES, at3d, L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// foo(fp)
Location loc;
Bvariable *p0 = func->getNthParamVar(0);
Bexpression *vex = be->var_expression(p0, loc);
Bexpression *fn = be->function_code_expression(func, loc);
std::vector<Bexpression *> args;
args.push_back(vex);
Bexpression *call = be->call_expression(func, fn, args, nullptr, h.loc());
// return foo(fp)
std::vector<Bexpression *> rvals;
rvals.push_back(call);
h.mkReturn(rvals);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
%ld.0 = load [2 x float], [2 x float]* %p0.addr, align 4
%call.0 = call addrspace(0) { double, double, double } @foo(i8* nest undef, [2 x float] %ld.0)
%cast.1 = bitcast [3 x double]* %sret.actual.0 to { double, double, double }*
store { double, double, double } %call.0, { double, double, double }* %cast.1, align 8
%cast.2 = bitcast [3 x double]* %sret.actual.0 to { double, double, double }*
%ld.1 = load { double, double, double }, { double, double, double }* %cast.2, align 8
ret { double, double, double } %ld.1
)RAW_RESULT");
bool isOK = h.expectBlock(exp);
EXPECT_TRUE(isOK && "Block does not have expected contents");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST_P(BackendCABIOracleTests, EmptyStructParamsAndReturns) {
auto cc = GetParam();
FcnTestHarness h(cc);
Llvm_backend *be = h.be();
Btype *bi32t = be->integer_type(false, 32);
Btype *set = mkBackendStruct(be, nullptr); // struct with no fields
// func foo(f1 empty, f2 empty, f3 int32, f4 empty) empty
BFunctionType *befty1 = mkFuncTyp(be,
L_RES, set,
L_PARM, set,
L_PARM, set,
L_PARM, bi32t,
L_PARM, set,
L_PARM, set,
L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// foo(f1, f1, 4, f1, f1)
Location loc;
Bexpression *fn = be->function_code_expression(func, loc);
Bvariable *p0 = func->getNthParamVar(0);
std::vector<Bexpression *> args;
args.push_back(be->var_expression(p0, loc));
args.push_back(be->var_expression(p0, loc));
args.push_back(mkInt32Const(be, 4));
args.push_back(be->var_expression(p0, loc));
args.push_back(be->var_expression(p0, loc));
Bexpression *call = be->call_expression(func, fn, args, nullptr, h.loc());
// return the call above
std::vector<Bexpression *> rvals;
rvals.push_back(call);
h.mkReturn(rvals);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
call addrspace(0) void @foo(i8* nest undef, i32 4)
ret void
)RAW_RESULT");
bool isOK = h.expectBlock(exp);
EXPECT_TRUE(isOK && "Block does not have expected contents");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST_P(BackendCABIOracleTests, CallBuiltinFunction) {
auto cc = GetParam();
FcnTestHarness h(cc, "foo");
Llvm_backend *be = h.be();
Bfunction *func = h.func();
Bfunction *tfunc = be->lookup_builtin("__builtin_trap");
// trap()
Location loc;
Bexpression *fn = be->function_code_expression(tfunc, loc);
std::vector<Bexpression *> args;
h.mkExprStmt(be->call_expression(func, fn, args, nullptr, h.loc()));
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
call addrspace(0) void @llvm.trap()
)RAW_RESULT");
bool isOK = h.expectBlock(exp);
EXPECT_TRUE(isOK && "Block does not have expected contents");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendCABIOracleTests, PassAndReturnComplexAmd64) {
FcnTestHarness h(llvm::CallingConv::X86_64_SysV);
Llvm_backend *be = h.be();
Btype *bc64t = be->complex_type(64);
Btype *bc128t = be->complex_type(128);
// func foo(x complex64, y complex128) complex64
BFunctionType *befty1 = mkFuncTyp(be,
L_PARM, bc64t,
L_PARM, bc128t,
L_RES, bc64t,
L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// z = foo(x, y)
Location loc;
Bvariable *x = func->getNthParamVar(0);
Bvariable *y = func->getNthParamVar(1);
Bexpression *xvex = be->var_expression(x, loc);
Bexpression *yvex = be->var_expression(y, loc);
Bexpression *fn1 = be->function_code_expression(func, loc);
std::vector<Bexpression *> args1 = {xvex, yvex};
Bexpression *call1 = be->call_expression(func, fn1, args1, nullptr, h.loc());
h.mkLocal("z", bc64t, call1);
// Call with constant args
// foo(1+2i, 3+4i)
mpc_t mpc_val1, mpc_val2;
mpc_init2(mpc_val1, 256);
mpc_set_d_d(mpc_val1, 1.0, 2.0, GMP_RNDN);
mpc_init2(mpc_val2, 256);
mpc_set_d_d(mpc_val2, 3.0, 4.0, GMP_RNDN);
Bexpression *ccon1 = be->complex_constant_expression(bc64t, mpc_val1);
Bexpression *ccon2 = be->complex_constant_expression(bc128t, mpc_val2);
mpc_clear(mpc_val1);
mpc_clear(mpc_val2);
Bexpression *fn2 = be->function_code_expression(func, loc);
std::vector<Bexpression *> args2 = {ccon1, ccon2};
Bexpression *call2 = be->call_expression(func, fn2, args2, nullptr, h.loc());
// return the call expr above
std::vector<Bexpression *> rvals = {call2};
h.mkReturn(rvals);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
%cast.0 = bitcast { float, float }* %p0.addr to <2 x float>*
%ld.0 = load <2 x float>, <2 x float>* %cast.0, align 8
%field0.0 = getelementptr inbounds { double, double }, { double, double }* %p1.addr, i32 0, i32 0
%ld.1 = load double, double* %field0.0, align 8
%field1.0 = getelementptr inbounds { double, double }, { double, double }* %p1.addr, i32 0, i32 1
%ld.2 = load double, double* %field1.0, align 8
%call.0 = call addrspace(0) <2 x float> @foo(i8* nest undef, <2 x float> %ld.0, double %ld.1, double %ld.2)
%cast.2 = bitcast { float, float }* %sret.actual.0 to <2 x float>*
store <2 x float> %call.0, <2 x float>* %cast.2, align 8
%cast.3 = bitcast { float, float }* %z to i8*
%cast.4 = bitcast { float, float }* %sret.actual.0 to i8*
call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.3, i8* align 4 %cast.4, i64 8, i1 false)
%ld.3 = load <2 x float>, <2 x float>* bitcast ({ float, float }* @const.0 to <2 x float>*), align 8
%ld.4 = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @const.1, i32 0, i32 0), align 8
%ld.5 = load double, double* getelementptr inbounds ({ double, double }, { double, double }* @const.1, i32 0, i32 1), align 8
%call.1 = call addrspace(0) <2 x float> @foo(i8* nest undef, <2 x float> %ld.3, double %ld.4, double %ld.5)
%cast.7 = bitcast { float, float }* %sret.actual.1 to <2 x float>*
store <2 x float> %call.1, <2 x float>* %cast.7, align 8
%cast.8 = bitcast { float, float }* %sret.actual.1 to <2 x float>*
%ld.6 = load <2 x float>, <2 x float>* %cast.8, align 8
ret <2 x float> %ld.6
)RAW_RESULT");
bool isOK = h.expectBlock(exp);
EXPECT_TRUE(isOK && "Block does not have expected contents");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendCABIOracleTests, PassAndReturnComplexArm64) {
FcnTestHarness h(llvm::CallingConv::ARM_AAPCS);
Llvm_backend *be = h.be();
Btype *bc64t = be->complex_type(64);
Btype *bc128t = be->complex_type(128);
// func foo(x complex64, y complex128) complex64
BFunctionType *befty1 =
mkFuncTyp(be, L_PARM, bc64t, L_PARM, bc128t, L_RES, bc64t, L_END);
Bfunction *func = h.mkFunction("foo", befty1);
// z = foo(x, y)
Location loc;
Bvariable *x = func->getNthParamVar(0);
Bvariable *y = func->getNthParamVar(1);
Bexpression *xvex = be->var_expression(x, loc);
Bexpression *yvex = be->var_expression(y, loc);
Bexpression *fn1 = be->function_code_expression(func, loc);
std::vector<Bexpression *> args1 = {xvex, yvex};
Bexpression *call1 = be->call_expression(func, fn1, args1, nullptr, h.loc());
h.mkLocal("z", bc64t, call1);
// Call with constant args
// foo(1+2i, 3+4i)
mpc_t mpc_val1, mpc_val2;
mpc_init2(mpc_val1, 256);
mpc_set_d_d(mpc_val1, 1.0, 2.0, GMP_RNDN);
mpc_init2(mpc_val2, 256);
mpc_set_d_d(mpc_val2, 3.0, 4.0, GMP_RNDN);
Bexpression *ccon1 = be->complex_constant_expression(bc64t, mpc_val1);
Bexpression *ccon2 = be->complex_constant_expression(bc128t, mpc_val2);
mpc_clear(mpc_val1);
mpc_clear(mpc_val2);
Bexpression *fn2 = be->function_code_expression(func, loc);
std::vector<Bexpression *> args2 = {ccon1, ccon2};
Bexpression *call2 = be->call_expression(func, fn2, args2, nullptr, h.loc());
// return the call expr above
std::vector<Bexpression *> rvals = {call2};
h.mkReturn(rvals);
DECLARE_EXPECTED_OUTPUT(exp, R"RAW_RESULT(
%cast.0 = bitcast { float, float }* %p0.addr to [2 x float]*
%ld.0 = load [2 x float], [2 x float]* %cast.0, align 4
%cast.1 = bitcast { double, double }* %p1.addr to [2 x double]*
%ld.1 = load [2 x double], [2 x double]* %cast.1, align 8
%call.0 = call addrspace(0) { float, float } @foo(i8* nest undef, [2 x float] %ld.0, [2 x double] %ld.1)
store { float, float } %call.0, { float, float }* %sret.actual.0, align 4
%cast.3 = bitcast { float, float }* %z to i8*
%cast.4 = bitcast { float, float }* %sret.actual.0 to i8*
call addrspace(0) void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.3, i8* align 4 %cast.4, i64 8, i1 false)
%ld.2 = load [2 x float], [2 x float]* bitcast ({ float, float }* @const.0 to [2 x float]*), align 4
%ld.3 = load [2 x double], [2 x double]* bitcast ({ double, double }* @const.1 to [2 x double]*), align 8
%call.1 = call addrspace(0) { float, float } @foo(i8* nest undef, [2 x float] %ld.2, [2 x double] %ld.3)
store { float, float } %call.1, { float, float }* %sret.actual.1, align 4
%ld.4 = load { float, float }, { float, float }* %sret.actual.1, align 4
ret { float, float } %ld.4
)RAW_RESULT");
bool isOK = h.expectBlock(exp);
EXPECT_TRUE(isOK && "Block does not have expected contents");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
} // namespace