| //===- 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); |
| |
| } |
| |
| } |