blob: 183a0ae1fa4ced1b135649a7f9fd6b3d39cbcf9e [file] [log] [blame]
//===- llvm/tools/gollvm/unittests/BackendCore/BackendVarTests.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 "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Value.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace goBackendUnitTests;
namespace {
TEST(BackendVarTests, MakeLocalVar) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Bfunction *func1 = h.func();
Bfunction *func2 = mkFunci32o64(be, "bar");
// Manufacture some locals
Location loc;
Btype *bi64t = be->integer_type(false, 64);
Btype *bst = mkBackendThreeFieldStruct(be);
Bvariable *loc1 = h.mkLocal("loc1", bi64t);
ASSERT_TRUE(loc1 != nullptr);
EXPECT_TRUE(loc1 != be->error_variable());
Bvariable *loc2 = h.mkLocal("loc2", bst);
ASSERT_TRUE(loc2 != nullptr);
EXPECT_TRUE(loc2 != be->error_variable());
Bvariable *loc3 = be->local_variable(func2, "loc3", bst, nullptr, false, loc);
ASSERT_TRUE(loc3 != nullptr);
EXPECT_TRUE(loc3 != be->error_variable());
// Examine resulting alloca instructions
EXPECT_TRUE(isa<AllocaInst>(loc1->value()));
EXPECT_TRUE(isa<AllocaInst>(loc2->value()));
EXPECT_TRUE(loc1 != loc2 && loc1->value() != loc2->value());
// Test var_expression created from local variable
Bexpression *ve1 = be->var_expression(loc1, Location());
ASSERT_TRUE(ve1 != nullptr);
EXPECT_EQ(ve1->value(), loc1->value());
// Test var_expression created from local variable
Bexpression *ve2 = be->var_expression(loc1, Location());
ASSERT_TRUE(ve2 != nullptr);
Bstatement *es = h.mkExprStmt(ve2);
EXPECT_EQ(repr(ve2->value()), "%loc1 = alloca i64");
EXPECT_EQ(repr(es), "%loc1.ld.0 = load i64, i64* %loc1");
// Make sure error detection is working
Bvariable *loce = be->local_variable(func1, "", be->error_type(), nullptr,
true, loc);
EXPECT_TRUE(loce == be->error_variable());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, MakeParamVar) {
LLVMContext C;
std::unique_ptr<Backend> be(go_get_backend(C));
bool dontMakeParams = false;
Bfunction *func = mkFunci32o64(be.get(), "foo", dontMakeParams);
// Add params for the function
Btype *bi32t = be->integer_type(false, 32);
Bvariable *p1 = be->parameter_variable(func, "p1", bi32t, false, Location());
Bvariable *p2 = be->parameter_variable(func, "p2", bi32t, false, Location());
ASSERT_TRUE(p1 != nullptr);
ASSERT_TRUE(p2 != nullptr);
EXPECT_TRUE(p1 != p2);
EXPECT_TRUE(p1 != be->error_variable());
// Values for param variables will be the alloca instructions
// created to capture their values
EXPECT_TRUE(isa<AllocaInst>(p1->value()));
EXPECT_TRUE(isa<AllocaInst>(p2->value()));
// Test var_expression created from param variable
Bexpression *ve1 = be->var_expression(p1, Location());
ASSERT_TRUE(ve1 != nullptr);
EXPECT_EQ(ve1->value(), p1->value());
// Error handling
Bfunction *func2 = mkFunci32o64(be.get(), "bar");
Bvariable *p3 =
be->parameter_variable(func2, "p3", be->error_type(), false, Location());
EXPECT_TRUE(p3 == be->error_variable());
}
TEST(BackendVarTests, MakeGlobalVar) {
LLVMContext C;
std::unique_ptr<Backend> be(go_get_backend(C));
Btype *bi32t = be->integer_type(false, 32);
Bvariable *g1 =
be->global_variable("varname", "asmname", bi32t, false, /* is_external */
false, /* is_hidden */
false, /* unique_section */
Location());
ASSERT_TRUE(g1 != nullptr);
Value *g1val = g1->value();
ASSERT_TRUE(g1val != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(g1val));
EXPECT_EQ(g1val->getName(), "asmname");
// Set initializer
be->global_variable_set_init(g1, mkInt32Const(be.get(), 101));
// Test var_expression created from global variable
Bexpression *ve1 = be->var_expression(g1, Location());
ASSERT_TRUE(ve1 != nullptr);
EXPECT_EQ(ve1->value(), g1->value());
// error case
Bvariable *gerr =
be->global_variable("", "", be->error_type(), false, /* is_external */
false, /* is_hidden */
false, /* unique_section */
Location());
EXPECT_TRUE(gerr == be->error_variable());
}
TEST(BackendVarTests, MakeTemporaryVar) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Bfunction *func = h.func();
// var b bool = true
Location loc;
Btype *boolt = be->bool_type();
h.mkLocal("b", boolt, be->boolean_constant_expression(true));
// temporary var [uint64] = 99
Btype *bu64t = be->integer_type(true, 64);
Bstatement *inits = nullptr;
Bexpression *con64 = mkUint64Const(be, 64);
Bvariable *tvar = be->temporary_variable(func, h.block(), bu64t, con64,
false, loc, &inits);
ASSERT_TRUE(tvar != nullptr);
ASSERT_TRUE(inits != nullptr);
EXPECT_EQ(repr(inits), "store i64 64, i64* %tmpv.0");
h.addStmt(inits);
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, MakeImmutableStruct) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkTwoFieldStruct(be, bi32t, bi32t);
const bool is_common[2] = {true, false};
const bool is_hidden[2] = {true, false};
Location loc;
GlobalVariable *gvar = nullptr;
bool first = true;
for (auto hidden : is_hidden) {
for (auto common : is_common) {
if (hidden && common)
continue;
Bvariable *ims =
be->immutable_struct("name", "asmname", hidden, common, bst, loc);
ASSERT_TRUE(ims != nullptr);
Value *ival = ims->value();
ASSERT_TRUE(ival != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(ival));
if (first) {
gvar = cast<GlobalVariable>(ival);
EXPECT_EQ(gvar->getName(), "asmname");
first = false;
}
}
}
// error case
Bvariable *gerr =
be->immutable_struct("", "", false, false, be->error_type(), Location());
EXPECT_TRUE(gerr == be->error_variable());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, MakeImplicitVariable) {
LLVMContext C;
std::unique_ptr<Backend> be(go_get_backend(C));
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkTwoFieldStruct(be.get(), bi32t, bi32t);
const bool is_common[2] = {true, false};
const bool is_constant[2] = {true, false};
const bool is_hidden[2] = {true, false};
GlobalVariable *gvar = nullptr;
bool first = true;
for (auto hidden : is_hidden) {
for (auto common : is_common) {
for (auto iscon : is_constant) {
if (hidden && common)
continue;
Bvariable *ims =
be->implicit_variable("name", "asmname", bst, hidden, iscon,
common, 8);
ASSERT_TRUE(ims != nullptr);
Value *ival = ims->value();
ASSERT_TRUE(ival != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(ival));
if (first) {
gvar = cast<GlobalVariable>(ival);
EXPECT_EQ(gvar->getName(), "asmname");
first = false;
}
}
}
}
// error case
Bvariable *gerr =
be->implicit_variable("", "", be->error_type(), false, false,
false, 0);
EXPECT_TRUE(gerr == be->error_variable());
}
TEST(BackendVarTests, MakeImmutableStructReference) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Location loc;
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkTwoFieldStruct(be, bi32t, bi32t);
Bvariable *ims =
be->immutable_struct_reference("name", "asmname", bst, loc);
ASSERT_TRUE(ims != nullptr);
Value *ival = ims->value();
ASSERT_TRUE(ival != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(ival));
EXPECT_EQ(repr(ival), "@asmname = external constant { i32, i32 }");
// error case
Bvariable *ierr =
be->immutable_struct_reference("name", "asmname", be->error_type(), loc);
EXPECT_TRUE(ierr == be->error_variable());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, ImmutableStructSetInit) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Bfunction *func = h.func();
Location loc;
Btype *bt = be->bool_type();
Btype *pbt = be->pointer_type(bt);
Btype *uintptrt = be->integer_type(true, be->type_size(pbt)*8);
Btype *desct = mkBackendStruct(be, uintptrt, "x", nullptr);
Bvariable *ims = be->immutable_struct("desc", "desc",
true, false, desct, loc);
Bexpression *fp = be->function_code_expression(func, loc);
Bexpression *confp = be->convert_expression(uintptrt, fp, loc);
std::vector<Bexpression *> vals;
vals.push_back(confp);
Bexpression *scon = be->constructor_expression(desct, vals, loc);
be->immutable_struct_set_init(ims, "", false, false,
desct, loc, scon);
{
const char *exp = R"RAW_RESULT(
@desc = internal constant { i64 } { i64 ptrtoint
(i64 (i8*, i32, i32, i64*)* @foo to i64) }
)RAW_RESULT";
bool isOK = h.expectValue(ims->value(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
Bvariable *ims2 = be->immutable_struct("xyz", "abc",
false, true, desct, loc);
be->immutable_struct_set_init(ims2, "", false, true,
desct, loc, be->zero_expression(desct));
{
const char *exp = R"RAW_RESULT(
@abc = weak constant { i64 } zeroinitializer, comdat
)RAW_RESULT";
bool isOK = h.expectValue(ims2->value(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
// check that these don't crash
be->immutable_struct_set_init(be->error_variable(), "", false, false,
desct, loc, scon);
be->immutable_struct_set_init(ims, "", false, false,
desct, loc, be->error_expression());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, MakeImmutableStructReferenceWithSameName) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Location loc;
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkTwoFieldStruct(be, bi32t, bi32t);
// Create an immutable_struct and an immutable_struct_reference
// with same name. They should refer to the same global variable.
bool hidden = false;
bool common = false;
Bvariable *ims =
be->immutable_struct("name", "asmname", hidden, common, bst, loc);
ASSERT_TRUE(ims != nullptr);
Bvariable *imsr =
be->immutable_struct_reference("name", "asmname", bst, loc);
ASSERT_TRUE(imsr != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(imsr->value()));
EXPECT_TRUE(imsr->value() == ims->value());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, ImplicitVariableSetInit) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Location loc;
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkTwoFieldStruct(be, bi32t, bi32t);
// Case 1: non-common, concrete init value.
bool isConst = false;
bool isHidden = false;
bool isCommon = false;
Bvariable *ims1 =
be->implicit_variable("first", "v1", bst,
isHidden, isConst, isCommon, 8);
std::vector<Bexpression *> vals1;
vals1.push_back(mkInt32Const(be, 101));
vals1.push_back(mkInt32Const(be, 202));
Bexpression *con1 = be->constructor_expression(bst, vals1, loc);
be->implicit_variable_set_init(ims1, "x", bst,
isHidden, isConst, isCommon, con1);
const char *exp1 = R"RAW_RESULT(
@v1 = global { i32, i32 } { i32 101, i32 202 }, align 8
)RAW_RESULT";
bool isOK1 = h.expectValue(ims1->value(), exp1);
EXPECT_TRUE(isOK1 && "Value does not have expected contents");
// Case 2: const, common, no init value.
isConst = true;
isCommon = true;
Bvariable *ims2 =
be->implicit_variable("second", "v2", bst,
isHidden, isConst, isCommon, 8);
be->implicit_variable_set_init(ims2, "x", bst,
isHidden, isConst, isCommon, nullptr);
const char *exp2 = R"RAW_RESULT(
@v2 = weak constant { i32, i32 } zeroinitializer, comdat, align 8
)RAW_RESULT";
bool isOK2 = h.expectValue(ims2->value(), exp2);
EXPECT_TRUE(isOK2 && "Value does not have expected contents");
// check that these don't crash
be->implicit_variable_set_init(be->error_variable(), "x", bst,
isHidden, isConst, isCommon, nullptr);
be->implicit_variable_set_init(be->error_variable(), "x", bst,
isHidden, isConst, isCommon,
be->error_expression());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, GlobalVarSetInitToComposite) {
LLVMContext C;
std::unique_ptr<Backend> be(go_get_backend(C));
Location loc;
Btype *bt = be->bool_type();
Btype *pbt = be->pointer_type(bt);
Btype *bi32t = be->integer_type(false, 32);
Btype *s2t = mkBackendStruct(be.get(), pbt, "f1", bi32t, "f2", nullptr);
Bvariable *g1 =
be->global_variable("gv", "gv", s2t, false, /* is_external */
false, /* is_hidden */
false, /* unique_section */
Location());
std::vector<Bexpression *> vals;
vals.push_back(be->zero_expression(pbt));
vals.push_back(mkInt32Const(be.get(), int32_t(101)));
Bexpression *scon =
be->constructor_expression(s2t, vals, loc);
// Set initializer
be->global_variable_set_init(g1, scon);
}
TEST(BackendVarTests, GlobalVarsWithSameName) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Location loc;
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkBackendStruct(be, bi32t, "f", nullptr);
// Create two global variables with same name, one is
// a declaration (external), the other is a definition.
// They should refer to the same underlying global var.
// declare x as external int32
bool hidden = false;
bool common = false;
bool external = true;
Bvariable *gvdecl =
be->global_variable("x", "x", bi32t, external,
hidden, common, loc);
ASSERT_TRUE(gvdecl != nullptr);
// define x as non-external struct
external = false;
Bvariable *gv =
be->global_variable("x", "x", bst, external,
hidden, common, loc);
ASSERT_TRUE(gv != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(gv->value()));
EXPECT_EQ(repr(gv->value()), "@x = global { i32 } zeroinitializer");
EXPECT_EQ(repr(gvdecl->value()),
"i32* getelementptr inbounds ({ i32 }, { i32 }* @x, i32 0, i32 0)");
// Create them in the other order: definition first,
// then external declaration.
// define y as non-external struct
external = false;
Bvariable *gv2 =
be->global_variable("y", "y", bst, external,
hidden, common, loc);
ASSERT_TRUE(gv2 != nullptr);
// declare y as external int32
external = true;
Bvariable *gvdecl2 =
be->global_variable("y", "y", bi32t, external,
hidden, common, loc);
ASSERT_TRUE(gvdecl2 != nullptr);
EXPECT_TRUE(isa<GlobalVariable>(gv2->value()));
EXPECT_EQ(repr(gv2->value()), "@y = global { i32 } zeroinitializer");
EXPECT_EQ(repr(gvdecl2->value()),
"i32* getelementptr inbounds ({ i32 }, { i32 }* @y, i32 0, i32 0)");
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
}
TEST(BackendVarTests, TestVarLifetimeInsertion) {
FcnTestHarness h;
Llvm_backend *be = h.be();
BFunctionType *befty1 = mkFuncTyp(be, L_END);
Bfunction *func = h.mkFunction("foo", befty1);
Location loc;
Btype *bi32t = be->integer_type(false, 32);
Btype *bst = mkTwoFieldStruct(be, bi32t, bi32t);
// Block with two locals
Bvariable *x = be->local_variable(func, "x", bi32t, nullptr, false, loc);
Bvariable *y = be->local_variable(func, "y", bst, nullptr, false, loc);
const std::vector<Bvariable *> vars = { x, y };
Bblock *b1 = be->block(func, nullptr, vars, loc, loc);
Bstatement *is1 = be->init_statement(func, x, be->zero_expression(bi32t));
Bstatement *is2 = be->init_statement(func, y, be->zero_expression(bst));
// x := y.f1
Bexpression *ve1 = be->var_expression(x, loc);
Bexpression *ve2 = be->var_expression(y, loc);
Bexpression *fex = be->struct_field_expression(ve2, 1, loc);
Bstatement *as =
be->assignment_statement(func, ve1, fex, loc);
const std::vector<Bstatement *> slist = { is1, is2, as };
be->block_add_statements(b1, slist);
Bstatement *bs = be->block_statement(b1);
h.addStmt(bs);
bool broken = h.finish(StripDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
const char *exp = R"RAW_RESULT(
define void @foo(i8* nest %nest.0) #0 {
entry:
%x = alloca i32
%y = alloca { i32, i32 }
%0 = bitcast i32* %x to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %0)
%1 = bitcast { i32, i32 }* %y to i8*
call void @llvm.lifetime.start.p0i8(i64 8, i8* %1)
store i32 0, i32* %x
%cast.0 = bitcast { i32, i32 }* %y to i8*
%cast.1 = bitcast { i32, i32 }* @const.0 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %cast.0, i8* align 4 %cast.1, i64 8, i1 false)
%field.0 = getelementptr inbounds { i32, i32 }, { i32, i32 }* %y, i32 0, i32 1
%y.field.ld.0 = load i32, i32* %field.0
store i32 %y.field.ld.0, i32* %x
%2 = bitcast i32* %x to i8*
call void @llvm.lifetime.end.p0i8(i64 4, i8* %2)
%3 = bitcast { i32, i32 }* %y to i8*
call void @llvm.lifetime.end.p0i8(i64 8, i8* %3)
ret void
}
)RAW_RESULT";
bool isOK = h.expectValue(func->function(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
TEST(BackendVarTests, ZeroSizedGlobals) {
FcnTestHarness h;
Llvm_backend *be = h.be();
BFunctionType *befty1 = mkFuncTyp(be, L_END);
Bfunction *func = h.mkFunction("foo", befty1);
Location loc;
// This test case is designed to exercise code in the bridge that
// handles zero-sized globals. It turns out that these critters
// have to be handled carefully due to problems in the way that
// linkers handle them.
// empty struct type
Btype *best = mkBackendStruct(be, nullptr);
// a struct with two empty fields
Btype *bef2 = mkBackendStruct(be, best, "f1", best, "f2", nullptr);
// a zero-length array of the struct above
Bexpression *val0 = mkInt64Const(be, int64_t(0));
Btype *ats1 = be->array_type(bef2, val0);
// a zero-length array of some non-zero sized type
Btype *bi32t = be->integer_type(false, 32);
Btype *atint0 = be->array_type(bi32t, val0);
bool isExternal = false;
bool isHidden = false;
bool uniqueSection = false;
// define globals with the types above
Bvariable *gs1 =
be->global_variable("emptystruct", "", best,
isExternal, isHidden, uniqueSection, loc);
Bvariable *gs2 =
be->global_variable("emptys2f", "", bef2,
isExternal, isHidden, uniqueSection, loc);
Bvariable *ga1 =
be->global_variable("emptyar", "", ats1,
isExternal, isHidden, uniqueSection, loc);
Bvariable *ga2 =
be->global_variable("emptyintar", "", atint0,
isExternal, isHidden, uniqueSection, loc);
// Create initializers for these odd beasties.
// this first one doesn't look so bad...
be->global_variable_set_init(gs1, be->zero_expression(best));
// now we're getting weird, however:
std::vector<Bexpression *> vals;
vals.push_back(be->zero_expression(best));
vals.push_back(be->zero_expression(best));
Bexpression *emptycon = be->constructor_expression(bef2, vals, loc);
be->global_variable_set_init(gs2, emptycon);
// ... and some weird empty array initializers as well
const std::vector<unsigned long> ivals;
const std::vector<Bexpression *> exprs;
Bexpression *ear1 =
be->array_constructor_expression(ats1, ivals, exprs, loc);
be->global_variable_set_init(ga1, ear1);
Bexpression *ear2 =
be->array_constructor_expression(atint0, ivals, exprs, loc);
be->global_variable_set_init(ga2, ear2);
{
const char *exp = R"RAW_RESULT(
@emptystruct = global { i8 } zeroinitializer
)RAW_RESULT";
bool isOK = h.expectValue(gs1->value(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
{
const char *exp = R"RAW_RESULT(
@emptys2f = global { { i8 }, {} } zeroinitializer
)RAW_RESULT";
bool isOK = h.expectValue(gs2->value(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
{
const char *exp = R"RAW_RESULT(
@emptyar = global [1 x { { i8 }, {} }] zeroinitializer
)RAW_RESULT";
bool isOK = h.expectValue(ga1->value(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
{
const char *exp = R"RAW_RESULT(
@emptyintar = global [1 x i32] zeroinitializer
)RAW_RESULT";
bool isOK = h.expectValue(ga2->value(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
// OK, now that we have out empty globals set up, manufacture
// some accesses to them.
// emptys2f.f1 = emptystruct
Bexpression *vex1 = be->var_expression(gs2, loc);
Bexpression *fex1 = be->struct_field_expression(vex1, 0, loc);
Bexpression *vex2 = be->var_expression(gs1, loc);
h.mkAssign(fex1, vex2);
// localemptys2f = emptys2f
Bvariable *loc1 = h.mkLocal("localemptys2f", bef2);
Bexpression *vex3 = be->var_expression(loc1, loc);
Bexpression *vex4 = be->var_expression(gs2, loc);
h.mkAssign(vex3, vex4);
// localemptyintar = emptyintar
Bvariable *loc2 = h.mkLocal("localemptyintar", atint0);
Bexpression *vex5 = be->var_expression(loc2, loc);
Bexpression *vex6 = be->var_expression(ga2, loc);
h.mkAssign(vex5, vex6);
bool broken = h.finish(StripDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
const char *exp = R"RAW_RESULT(
define void @foo(i8* nest %nest.0) #0 {
entry:
%localemptys2f = alloca { {}, {} }
%localemptyintar = alloca [0 x i32]
ret void
}
)RAW_RESULT";
bool isOK = h.expectValue(func->function(), exp);
EXPECT_TRUE(isOK && "Value does not have expected contents");
}
TEST(BackendVarTests, MakeLocalWithDeclVar) {
FcnTestHarness h("foo");
Llvm_backend *be = h.be();
Bfunction *func1 = h.func();
// Create an initial local variable at the top level.
Location loc;
Btype *bi64t = be->integer_type(false, 64);
Bvariable *loc1 = h.mkLocal("loc1", bi64t);
// Now manufacture a second local that refers to the first via the
// "declVar" mechanism.
Bvariable *loc2 = be->local_variable(func1, "loc2", bi64t, loc1, false, loc);
Bstatement *is2 = be->init_statement(func1, loc2, mkInt64Const(be, 9));
// Third regular local.
Bvariable *loc3 = be->local_variable(func1, "loc3", bi64t, nullptr,
false, loc);
Bstatement *is3 = be->init_statement(func1, loc3, mkInt64Const(be, 11));
// Place the second and third variables in a block.
std::vector<Bvariable *> vars;
vars.push_back(loc2);
vars.push_back(loc3);
h.newBlock(&vars);
// Add inits to block
std::vector<Bstatement *> bstmts;
bstmts.push_back(is2);
bstmts.push_back(is3);
be->block_add_statements(h.block(), bstmts);
// The two vars should share the same alloca instruction.
EXPECT_TRUE(loc1->value() == loc2->value());
bool broken = h.finish(PreserveDebugInfo);
EXPECT_FALSE(broken && "Module failed to verify.");
// Expect to see only one lifetime start, since A) loc1 is at the top
// level, and B) loc2 uses loc1 as declVar.
const char *expected = "call void @llvm.lifetime.start.p0i8(i64";
EXPECT_EQ(h.countInstancesInModuleDump(expected), 1u);
}
}