| //===- llvm/tools/gollvm/unittests/BackendCore/BackendCoreTests.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-backend.h" | |
| #include "gtest/gtest.h" | |
| using namespace llvm; | |
| using namespace goBackendUnitTests; | |
| namespace { | |
| TEST(BackendCoreTests, MakeBackend) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> makeit(go_get_backend(C)); | |
| } | |
| TEST(BackendCoreTests, ScalarTypes) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| Btype *et = be->error_type(); | |
| EXPECT_TRUE(et != nullptr); | |
| Btype *vt = be->void_type(); | |
| EXPECT_TRUE(vt != nullptr); | |
| EXPECT_TRUE(vt != et); | |
| Btype *bt = be->bool_type(); | |
| EXPECT_TRUE(bt != nullptr); | |
| Btype *pbt = be->pointer_type(bt); | |
| ASSERT_TRUE(pbt != nullptr); | |
| EXPECT_TRUE(pbt->type()->isPointerTy()); | |
| std::vector<bool> isuns = {false, true}; | |
| std::vector<int> ibits = {8, 16, 32, 64, 128}; | |
| for (auto uns : isuns) { | |
| for (auto nbits : ibits) { | |
| Btype *it = be->integer_type(uns, nbits); | |
| ASSERT_TRUE(it != nullptr); | |
| EXPECT_TRUE(it->type()->isIntegerTy()); | |
| } | |
| } | |
| std::vector<int> fbits = {32, 64, 128}; | |
| for (auto nbits : fbits) { | |
| Btype *ft = be->float_type(nbits); | |
| ASSERT_TRUE(ft != nullptr); | |
| EXPECT_TRUE(ft->type()->isFloatingPointTy()); | |
| } | |
| } | |
| TEST(BackendCoreTests, StructTypes) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| // Empty struct | |
| std::vector<Backend::Btyped_identifier> nofields; | |
| Btype *emptyst = be->struct_type(nofields); | |
| SmallVector<Type *, 3> smv_empty(0); | |
| Type *llvm_emptyst = StructType::get(C, smv_empty); | |
| ASSERT_TRUE(llvm_emptyst != nullptr); | |
| ASSERT_TRUE(emptyst != nullptr); | |
| EXPECT_EQ(llvm_emptyst, emptyst->type()); | |
| // Three-field struct | |
| Btype *best = mkBackendThreeFieldStruct(be.get()); | |
| Type *llst = mkLlvmThreeFieldStruct(C); | |
| ASSERT_TRUE(best != nullptr); | |
| ASSERT_TRUE(llst != nullptr); | |
| EXPECT_EQ(llst, best->type()); | |
| EXPECT_EQ(repr(best->type()), "{ i8, float*, i64 }"); | |
| // If a field has error type, entire struct has error type | |
| std::vector<Backend::Btyped_identifier> fields = { | |
| Backend::Btyped_identifier("f1", be->bool_type(), Location()), | |
| Backend::Btyped_identifier("fe", be->error_type(), Location())}; | |
| Btype *badst = be->struct_type(fields); | |
| EXPECT_TRUE(badst != nullptr); | |
| EXPECT_EQ(badst, be->error_type()); | |
| // Llvm_backend should be caching and reusing anonymous types | |
| Btype *st1 = mkBackendThreeFieldStruct(be.get()); | |
| Btype *st2 = mkBackendThreeFieldStruct(be.get()); | |
| EXPECT_EQ(st1, st2); | |
| } | |
| TEST(BackendCoreTests, TypeHashAndCompare) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| Btype *st1 = mkBackendThreeFieldStruct(be.get()); | |
| Btype *st2 = mkBackendThreeFieldStruct(be.get()); | |
| EXPECT_EQ(st1->hash(), st2->hash()); | |
| EXPECT_TRUE(st1->equal(*st1)); | |
| } | |
| TEST(BackendCoreTests, ComplexTypes) { | |
| LLVMContext C; | |
| Type *ft = Type::getFloatTy(C); | |
| Type *dt = Type::getDoubleTy(C); | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| Btype *c32 = be->complex_type(64); | |
| ASSERT_TRUE(c32 != nullptr); | |
| EXPECT_EQ(c32->type(), mkTwoFieldLLvmStruct(C, ft, ft)); | |
| Btype *c64 = be->complex_type(128); | |
| ASSERT_TRUE(c64 != nullptr); | |
| EXPECT_EQ(c64->type(), mkTwoFieldLLvmStruct(C, dt, dt)); | |
| } | |
| TEST(BackendCoreTests, FunctionTypes) { | |
| LLVMContext C; | |
| Type *i64t = IntegerType::get(C, 64); | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| // func foo() {} | |
| Btype *emptyf = mkFuncTyp(be.get(), L_END); | |
| Type *llemptyf = mkLLFuncTyp(&C, L_END); | |
| ASSERT_TRUE(llemptyf != nullptr && emptyf != nullptr); | |
| EXPECT_TRUE(llemptyf == emptyf->type()); | |
| { | |
| // func (Blah) foo() {} | |
| Btype *pt = be->pointer_type(mkBackendThreeFieldStruct(be.get())); | |
| Btype *befn = | |
| mkFuncTyp(be.get(), L_RCV, pt, L_END); | |
| llvm::PointerType *llpt = | |
| llvm::PointerType::get(mkLlvmThreeFieldStruct(C), 0); | |
| Type *llfn = mkLLFuncTyp(&C, L_RCV, llpt, L_END); | |
| ASSERT_TRUE(befn != nullptr && llfn != nullptr); | |
| EXPECT_TRUE(befn->type() == llfn); | |
| } | |
| { | |
| // func foo(x int64) {} | |
| Btype *befn = | |
| mkFuncTyp(be.get(), L_PARM, be->integer_type(false, 64), L_END); | |
| Type *llfn = mkLLFuncTyp(&C, L_PARM, i64t, L_END); | |
| ASSERT_TRUE(befn != nullptr && llfn != nullptr); | |
| EXPECT_TRUE(befn->type() == llfn); | |
| } | |
| { | |
| // func foo() int64 {} | |
| Btype *befn = | |
| mkFuncTyp(be.get(), L_RES, be->integer_type(false, 64), L_END); | |
| Type *llfn = mkLLFuncTyp(&C, L_RES, i64t, L_END); | |
| ASSERT_TRUE(befn != nullptr && llfn != nullptr); | |
| EXPECT_TRUE(befn->type() == llfn); | |
| } | |
| } | |
| TEST(BackendCoreTests, PlaceholderTypes) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| // Create a placeholder pointer type | |
| Location loc; | |
| Btype *phpt1 = be->placeholder_pointer_type("ph1", loc, false); | |
| ASSERT_TRUE(phpt1 != nullptr); | |
| EXPECT_TRUE(phpt1->type()->isPointerTy()); | |
| // Placeholder pointer types should not be cached | |
| Btype *phpt2 = be->placeholder_pointer_type("ph", loc, false); | |
| Btype *phpt3 = be->placeholder_pointer_type("ph", loc, false); | |
| ASSERT_TRUE(phpt2 != phpt3); | |
| EXPECT_TRUE(phpt2->type() != phpt3->type()); | |
| // Replace placeholder pointer type | |
| Btype *pst = be->pointer_type(mkBackendThreeFieldStruct(be.get())); | |
| be->set_placeholder_pointer_type(phpt1, pst); | |
| ASSERT_TRUE(phpt1->type()->isPointerTy()); | |
| PointerType *llpt = cast<PointerType>(phpt1->type()); | |
| EXPECT_TRUE(llpt->getElementType()->isStructTy()); | |
| // Placeholder struct type | |
| Btype *phst1 = be->placeholder_struct_type("ph", loc); | |
| ASSERT_TRUE(phpt1 != nullptr); | |
| // Named version of above | |
| Btype *nt = be->named_type("named_ph", phst1, loc); | |
| // Replace placeholder struct type | |
| std::vector<Backend::Btyped_identifier> fields = { | |
| Backend::Btyped_identifier("f1", be->integer_type(false, 64), Location()), | |
| Backend::Btyped_identifier("f2", be->integer_type(false, 64), | |
| Location())}; | |
| be->set_placeholder_struct_type(phst1, fields); | |
| Type *i64t = IntegerType::get(C, 64); | |
| EXPECT_TRUE(llvmTypesEquiv(phst1->type(), | |
| mkTwoFieldLLvmStruct(C, i64t, i64t))); | |
| EXPECT_TRUE(llvmTypesEquiv(nt->type(), | |
| mkTwoFieldLLvmStruct(C, i64t, i64t))); | |
| // Placeholder array type | |
| Btype *phat1 = be->placeholder_array_type("pha", loc); | |
| ASSERT_TRUE(phat1 != nullptr); | |
| // Replace placeholder array type | |
| Btype *bi64t = be->integer_type(false, 64); | |
| Bexpression *val10 = mkInt64Const(be.get(), int64_t(10)); | |
| Btype *at10 = be->array_type(bi64t, val10); | |
| ASSERT_TRUE(at10 != nullptr); | |
| be->set_placeholder_array_type(phat1, bi64t, val10); | |
| EXPECT_TRUE(phat1->type() == at10->type()); | |
| // Circular pointer support | |
| Btype *php4 = be->placeholder_pointer_type("ph", loc, false); | |
| Btype *cpt = be->circular_pointer_type(php4, false); | |
| Btype *cpt2 = be->circular_pointer_type(php4, false); | |
| EXPECT_EQ(cpt, cpt2); | |
| be->set_placeholder_pointer_type(php4, cpt); | |
| EXPECT_EQ(php4->type(), cpt->type()); | |
| } | |
| TEST(BackendCoreTests, ArrayTypes) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| Location loc; | |
| // Very basic tests of array type creation: array of integers | |
| Btype *bi32t = be->integer_type(false, 32); | |
| Btype *bi64t = be->integer_type(false, 64); | |
| Bexpression *val10 = mkInt64Const(be.get(), int64_t(10)); | |
| Btype *at10 = be->array_type(bi64t, val10); | |
| ASSERT_TRUE(at10 != nullptr); | |
| EXPECT_EQ(at10->type(), llvm::ArrayType::get(bi64t->type(), 10)); | |
| // Array of structs | |
| Bexpression *val1023 = mkUint64Const(be.get(), uint64_t(1023)); | |
| Btype *st = mkTwoFieldStruct(be.get(), bi32t, bi64t); | |
| Btype *at1023 = be->array_type(st, val1023); | |
| ASSERT_TRUE(at1023 != nullptr); | |
| llvm::Type *lst = mkTwoFieldLLvmStruct(C, bi32t->type(), bi64t->type()); | |
| EXPECT_EQ(at1023->type(), llvm::ArrayType::get(lst, 1023)); | |
| // Zero sized array | |
| Bexpression *val0 = mkInt64Const(be.get(), 0); | |
| Btype *at0 = be->array_type(bi64t, val0); | |
| ASSERT_TRUE(at0 != nullptr); | |
| // Error cases | |
| Bexpression *badval = be->error_expression(); | |
| Btype *badt1 = be->array_type(bi64t, badval); | |
| EXPECT_EQ(badt1, be->error_type()); | |
| Btype *badt2 = be->array_type(be->error_type(), val10); | |
| EXPECT_EQ(badt2, be->error_type()); | |
| } | |
| TEST(BackendCoreTests, NamedTypes) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| Location loc; | |
| Btype *nt = be->named_type("named_int32", be->integer_type(false, 32), loc); | |
| ASSERT_TRUE(nt != nullptr); | |
| Btype *nt2 = | |
| be->named_type("another_int32", be->integer_type(false, 32), loc); | |
| ASSERT_TRUE(nt2 != nullptr); | |
| EXPECT_TRUE(nt != nt2); | |
| EXPECT_TRUE(nt->equivalent(*nt2)); | |
| } | |
| TEST(BackendCoreTests, TypeUtils) { | |
| LLVMContext C; | |
| Location loc; | |
| // Type size and alignment. Size and align are in bytes. | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| Btype *i8t = be->integer_type(false, 8); | |
| Btype *bs1f = mkBackendStruct(be.get(), i8t, "f1", nullptr); | |
| EXPECT_EQ(be->type_size(i8t), int64_t(1)); | |
| EXPECT_EQ(be->type_alignment(i8t), 1); | |
| EXPECT_EQ(be->type_alignment(bs1f), 1); | |
| // Slightly more complicated example | |
| Btype *u64 = be->integer_type(true, 64); | |
| Btype *st = mkTwoFieldStruct(be.get(), u64, u64); | |
| EXPECT_EQ(be->type_size(st), int64_t(16)); | |
| EXPECT_EQ(be->type_alignment(st), 8); | |
| // Strictly speaking, one should be asking for the size | |
| // of a pointer-to-function type, not a function type, however | |
| // it does appear that this needs to be supported. | |
| Btype *emptyf = mkFuncTyp(be.get(), L_END); | |
| EXPECT_EQ(be->type_size(emptyf), be->type_size(be->pointer_type(u64))); | |
| // We need to support type size queries on types that are not fully | |
| // resolved, specifically struct types that incorporate unresolved | |
| // placeholder pointer fields. | |
| Btype *pvt = be->pointer_type(be->void_type()); | |
| Btype *phpt = be->placeholder_pointer_type("ph1", loc, false); | |
| Btype *ptphpt = be->pointer_type(phpt); | |
| Btype *phst = be->placeholder_struct_type("ph2", loc); | |
| std::vector<Backend::Btyped_identifier> fields = { | |
| Backend::Btyped_identifier("f1", phpt, loc), | |
| Backend::Btyped_identifier("f2", ptphpt, loc)}; | |
| be->set_placeholder_struct_type(phst, fields); | |
| Btype *rst = mkTwoFieldStruct(be.get(), pvt, pvt); | |
| EXPECT_EQ(be->type_size(rst), be->type_size(phst)); | |
| EXPECT_EQ(be->type_field_offset(rst,1), be->type_field_offset(phst,1)); | |
| // type field alignment | |
| Btype *u32 = be->integer_type(true, 32); | |
| EXPECT_EQ(be->type_field_alignment(u32), 4); | |
| } | |
| TEST(BackendCoreTests, TestTypeEquivalence) { | |
| LLVMContext C; | |
| std::unique_ptr<Backend> be(go_get_backend(C)); | |
| // Two structs with same field type but different field names | |
| Btype *bi32t = be->integer_type(false, 32); | |
| Btype *pbi32t = be->pointer_type(bi32t); | |
| Btype *s1t = mkBackendStruct(be.get(), pbi32t, "f1", bi32t, "f2", nullptr); | |
| Btype *s2t = mkBackendStruct(be.get(), pbi32t, "f2", bi32t, "f1", nullptr); | |
| // Neither ::equal nor ::equivalent | |
| EXPECT_FALSE(s1t->equal(*s2t)); | |
| EXPECT_FALSE(s1t->equivalent(*s2t)); | |
| // Structs created via placholders | |
| Location loc; | |
| Btype *phst1 = be->placeholder_struct_type("ph1", loc); | |
| std::vector<Backend::Btyped_identifier> fields1 = { | |
| Backend::Btyped_identifier("f1", s1t, loc), | |
| Backend::Btyped_identifier("f2", s2t, loc)}; | |
| be->set_placeholder_struct_type(phst1, fields1); | |
| Btype *phst2 = be->placeholder_struct_type("ph2", loc); | |
| std::vector<Backend::Btyped_identifier> fields2 = { | |
| Backend::Btyped_identifier("f1", s1t, loc), | |
| Backend::Btyped_identifier("f2", s2t, loc)}; | |
| be->set_placeholder_struct_type(phst2, fields2); | |
| EXPECT_FALSE(phst1->equal(*phst2)); | |
| EXPECT_TRUE(phst1->equivalent(*phst2)); | |
| } | |
| TEST(BackendCoreTests, TestFcnPointerCompatible) { | |
| FcnTestHarness h("foo"); | |
| Llvm_backend *be = h.be(); | |
| TypeManager *tm = be->typeManager(); | |
| Location loc; | |
| // Create a struct that looks like | |
| // | |
| // struct S { int8, void(int8)*, *S } | |
| // | |
| Btype *pht = be->placeholder_pointer_type("ph", loc, false); | |
| Btype *bi8t = be->integer_type(false, 8); | |
| BFunctionType *befty1 = mkFuncTyp(be, L_PARM, bi8t, L_END); | |
| Btype *pft = be->pointer_type(befty1); | |
| Btype *s1t = mkBackendStruct(be, bi8t, "f1", pft, "f2", pht, "n", nullptr); | |
| Btype *ps1t = be->pointer_type(s1t); | |
| be->set_placeholder_pointer_type(pht, ps1t); | |
| h.mkLocal("y", s1t); | |
| // Create a struct that looks like | |
| // | |
| // struct S { int8, void*, *S } | |
| // | |
| Btype *pht2 = be->placeholder_pointer_type("ph", loc, false); | |
| Btype *pvt = be->pointer_type(be->void_type()); | |
| Btype *s2t = mkBackendStruct(be, bi8t, "f1", pvt, "f2", pht2, "n", nullptr); | |
| Btype *ps2t = be->pointer_type(s2t); | |
| be->set_placeholder_pointer_type(pht2, ps2t); | |
| h.mkLocal("x", s2t); | |
| // These two types are not structurally equivalent | |
| EXPECT_FALSE(pht2->equal(*pht)); | |
| // However the underlying LLVM types should be considered | |
| // assignment-compatible. | |
| std::set<llvm::Type *> visited; | |
| bool equiv = tm->fcnPointerCompatible(pht->type(), pht2->type(), visited); | |
| EXPECT_TRUE(equiv); | |
| bool broken = h.finish(PreserveDebugInfo); | |
| EXPECT_FALSE(broken && "Module failed to verify."); | |
| } | |
| TEST(BackendCoreTests, TestCompositeInitGvarConvert) { | |
| FcnTestHarness h("foo"); | |
| Llvm_backend *be = h.be(); | |
| Location loc; | |
| // Regular struct of type { i8, *i8, i32 } | |
| Btype *bt = be->bool_type(); | |
| Btype *pbt = be->pointer_type(bt); | |
| Btype *bi32t = be->integer_type(false, 32); | |
| Btype *s3t = mkBackendStruct(be, bt, "f0", pbt, "f1", bi32t, "f2", nullptr); | |
| // Placeholder version of type { i8, *i8, i32 } | |
| Btype *phst1 = be->placeholder_struct_type("ph", loc); | |
| std::vector<Backend::Btyped_identifier> fields = { | |
| Backend::Btyped_identifier("f0", bt, loc), | |
| Backend::Btyped_identifier("f1", pbt, loc), | |
| Backend::Btyped_identifier("f3", bi32t, loc)}; | |
| be->set_placeholder_struct_type(phst1, fields); | |
| // Another struct that uses the both types above as fields | |
| Btype *sqt = mkBackendStruct(be, s3t, "f0", phst1, "f1", nullptr); | |
| // Create a global variable using this type | |
| Bvariable *g1 = | |
| be->global_variable("gv", "gv", sqt, false, /* is_external */ | |
| false, /* is_hidden */ | |
| false, /* unique_section */ | |
| loc); | |
| // Create initializers using the various types. Note: the initial | |
| // value for "f0" (non-placeholder type) is created using the | |
| // corresponding placeholder and vice versa -- the intent is to | |
| // make sure the conversion is handled properly. | |
| Bexpression *f1con, *f2con, *topcon; | |
| { | |
| std::vector<Bexpression *> vals; | |
| vals.push_back(be->zero_expression(bt)); | |
| vals.push_back(be->zero_expression(pbt)); | |
| vals.push_back(mkInt32Const(be, int32_t(101))); | |
| f1con = be->constructor_expression(s3t, vals, loc); | |
| } | |
| { | |
| std::vector<Bexpression *> vals; | |
| vals.push_back(be->zero_expression(bt)); | |
| vals.push_back(be->zero_expression(pbt)); | |
| vals.push_back(mkInt32Const(be, int32_t(101))); | |
| f2con = be->constructor_expression(phst1, vals, loc); | |
| } | |
| { | |
| std::vector<Bexpression *> vals; | |
| Bexpression *bc1 = be->convert_expression(phst1, f1con, loc); | |
| vals.push_back(bc1); | |
| Bexpression *bc2 = be->convert_expression(s3t, f2con, loc); | |
| vals.push_back(bc2); | |
| topcon = be->constructor_expression(sqt, vals, loc); | |
| } | |
| // Set initializer | |
| be->global_variable_set_init(g1, topcon); | |
| bool broken = h.finish(StripDebugInfo); | |
| EXPECT_FALSE(broken && "Module failed to verify."); | |
| bool ok = h.expectModuleDumpContains("@gv = global { { i8, i8*, i32 }, %ph.0 } { { i8, i8*, i32 } { i8 0, i8* null, i32 101 }, %ph.0 { i8 0, i8* null, i32 101 } }"); | |
| EXPECT_TRUE(ok); | |
| } | |
| } |