| //===- llvm/tools/gollvm/unittests/BackendCore/BackendFcnTests.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(BackendStmtTests, TestInitStmt) { |
| |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| Btype *bi64t = be->integer_type(false, 64); |
| Location loc; |
| |
| // local variable with init |
| Bvariable *loc1 = be->local_variable(func, "loc1", bi64t, nullptr, true, loc); |
| Bstatement *is = be->init_statement(func, loc1, mkInt64Const(be, 10)); |
| ASSERT_TRUE(is != nullptr); |
| h.addStmt(is); |
| EXPECT_EQ(repr(is), "store i64 10, i64* %loc1"); |
| |
| // error handling |
| Bvariable *loc2 = be->local_variable(func, "loc2", bi64t, nullptr, true, loc); |
| Bstatement *bad = be->init_statement(func, loc2, be->error_expression()); |
| ASSERT_TRUE(bad != nullptr); |
| EXPECT_EQ(bad, be->error_statement()); |
| Bstatement *notsobad = be->init_statement(func, loc2, nullptr); |
| h.addStmt(notsobad); |
| |
| bool broken = h.finish(PreserveDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| } |
| |
| TEST(BackendStmtTests, TestAssignmentStmt) { |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| Btype *bi64t = be->integer_type(false, 64); |
| Location loc; |
| |
| // assign a constant to a variable |
| Bvariable *loc1 = h.mkLocal("loc1", bi64t); |
| Bexpression *ve1 = be->var_expression(loc1, loc); |
| Bstatement *as = |
| be->assignment_statement(func, ve1, mkInt64Const(be, 123), loc); |
| ASSERT_TRUE(as != nullptr); |
| h.addStmt(as); |
| |
| // assign a variable to a variable |
| Bvariable *loc2 = h.mkLocal("loc2", bi64t); |
| Bexpression *ve2 = be->var_expression(loc2, loc); |
| Bexpression *ve3 = be->var_expression(loc1, loc); |
| Bstatement *as2 = be->assignment_statement(func, ve2, ve3, loc); |
| ASSERT_TRUE(as2 != nullptr); |
| h.addStmt(as2); |
| |
| const char *exp = R"RAW_RESULT( |
| store i64 0, i64* %loc1 |
| store i64 123, i64* %loc1 |
| store i64 0, i64* %loc2 |
| %loc1.ld.0 = load i64, i64* %loc1 |
| store i64 %loc1.ld.0, i64* %loc2 |
| )RAW_RESULT"; |
| bool isOK = h.expectBlock(exp); |
| EXPECT_TRUE(isOK && "Block does not have expected contents"); |
| |
| // error handling |
| Bvariable *loc3 = h.mkLocal("loc3", bi64t); |
| Bexpression *ve4 = be->var_expression(loc3, loc); |
| Bstatement *badas = |
| be->assignment_statement(func, ve4, be->error_expression(), loc); |
| ASSERT_TRUE(badas != nullptr); |
| EXPECT_EQ(badas, be->error_statement()); |
| |
| bool broken = h.finish(PreserveDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| } |
| |
| TEST(BackendStmtTests, TestReturnStmt) { |
| |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *loc1 = h.mkLocal("loc1", bi64t, mkInt64Const(be, 10)); |
| |
| // return loc1 |
| Location loc; |
| Bexpression *ve1 = be->var_expression(loc1, loc); |
| Bstatement *ret = h.mkReturn(ve1); |
| |
| const char *exp = R"RAW_RESULT( |
| %loc1.ld.0 = load i64, i64* %loc1 |
| ret i64 %loc1.ld.0 |
| )RAW_RESULT"; |
| std::string reason; |
| bool equal = difftokens(exp, repr(ret), reason); |
| EXPECT_EQ("pass", equal ? "pass" : reason); |
| |
| // error handling |
| std::vector<Bexpression *> bvals; |
| bvals.push_back(be->error_expression()); |
| Bstatement *bret = be->return_statement(func, bvals, loc); |
| EXPECT_EQ(bret, be->error_statement()); |
| |
| bool broken = h.finish(PreserveDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| } |
| |
| TEST(BackendStmtTests, TestReturnStmt2) { |
| // Test that dead code after the return statement is handled |
| // correctly. |
| |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| Location loc; |
| |
| // var x int64 = 10 |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *x = h.mkLocal("x", bi64t, mkInt64Const(be, 10)); |
| |
| // return x |
| Bexpression *ve1 = be->var_expression(x, loc); |
| h.mkReturn(ve1); |
| |
| // some dead code |
| // return x+20 |
| Bexpression *ve2 = be->var_expression(x, loc); |
| Bexpression *addexpr = be->binary_expression(OPERATOR_PLUS, ve2, mkInt64Const(be, 20), loc); |
| h.mkReturn(addexpr); |
| |
| const char *exp = R"RAW_RESULT( |
| define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 { |
| entry: |
| %param1.addr = alloca i32 |
| %param2.addr = alloca i32 |
| %param3.addr = alloca i64* |
| %x = alloca i64 |
| store i32 %param1, i32* %param1.addr |
| store i32 %param2, i32* %param2.addr |
| store i64* %param3, i64** %param3.addr |
| store i64 10, i64* %x |
| %x.ld.0 = load i64, i64* %x |
| ret i64 %x.ld.0 |
| } |
| )RAW_RESULT"; |
| |
| bool broken = h.finish(StripDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| TEST(BackendStmtTests, TestLabelGotoStmts) { |
| |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| // loc1 = 10 |
| Location loc; |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *loc1 = h.mkLocal("loc1", bi64t, mkInt64Const(be, 10)); |
| |
| // goto labeln |
| Blabel *lab1 = be->label(func, "foolab", loc); |
| Bstatement *gots = be->goto_statement(lab1, loc); |
| h.addStmt(gots); |
| |
| // dead stmt: loc1 = 11 |
| h.mkAssign(be->var_expression(loc1, loc), |
| mkInt64Const(be, 11)); |
| |
| // labeldef |
| Bstatement *ldef = be->label_definition_statement(lab1); |
| h.addStmt(ldef); |
| |
| bool broken = h.finish(PreserveDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| } |
| |
| TEST(BackendStmtTests, TestLabelAddressExpression) { |
| |
| FcnTestHarness h; |
| Llvm_backend *be = h.be(); |
| BFunctionType *befty = mkFuncTyp(be, L_END); |
| Bfunction *func = h.mkFunction("foo", befty); |
| Btype *bu8t = be->integer_type(true, 8); |
| Bvariable *loc1 = h.mkLocal("loc1", bu8t); |
| Btype *bpvt = be->pointer_type(be->void_type()); |
| BFunctionType *befty2 = mkFuncTyp(be, L_PARM, bpvt, L_END); |
| Bfunction *f2 = mkFuncFromType(be, "bar", befty2); |
| |
| // Create a label and take it's address, then pass the |
| // value in question in a call to the function "bar". |
| Blabel *lab1 = be->label(func, "retaddr", h.newloc()); |
| Bexpression *labadex = be->label_address(lab1, h.newloc()); |
| Bexpression *fn2ex = be->function_code_expression(f2, h.loc()); |
| std::vector<Bexpression *> args; |
| args.push_back(labadex); |
| Bexpression *call = be->call_expression(func, fn2ex, args, nullptr, h.loc()); |
| h.mkExprStmt(call); |
| |
| // Now define the label, throw a statement in it. |
| Bstatement *ldef = be->label_definition_statement(lab1); |
| h.addStmt(ldef); |
| h.mkAssign(be->var_expression(loc1, h.loc()), |
| be->zero_expression(bu8t)); |
| |
| 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: |
| %loc1 = alloca i8 |
| store i8 0, i8* %loc1 |
| call void @bar(i8* nest undef, i8* blockaddress(@foo, %label.0)) |
| br label %label.0 |
| label.0: ; preds = %entry |
| store i8 0, i8* %loc1 |
| ret void |
| } |
| )RAW_RESULT"; |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| static Bstatement *CreateIfStmt(FcnTestHarness &h) |
| { |
| Location loc; |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *loc1 = h.mkLocal("loc1", bi64t); |
| |
| // loc1 = 123 |
| Bexpression *ve1 = be->var_expression(loc1, loc); |
| Bexpression *c123 = mkInt64Const(be, 123); |
| Bstatement *as1 = be->assignment_statement(func, ve1, c123, loc); |
| |
| // loc1 = 987 |
| Bexpression *ve2 = be->var_expression(loc1, loc); |
| Bexpression *c987 = mkInt64Const(be, 987); |
| Bstatement *as2 = be->assignment_statement(func, ve2, c987, loc); |
| |
| // if true b1 else b2 |
| Bexpression *trueval = be->boolean_constant_expression(true); |
| Bstatement *ifst = h.mkIf(trueval, as1, as2, FcnTestHarness::NoAppend); |
| |
| return ifst; |
| } |
| |
| TEST(BackendStmtTests, TestIfStmt) { |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| Location loc; |
| Bstatement *ifst = CreateIfStmt(h); |
| |
| // if true if |
| Bexpression *tv2 = be->boolean_constant_expression(true); |
| |
| // loc2 = 456 |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *loc2 = h.mkLocal("loc2", bi64t); |
| Bexpression *ve3 = be->var_expression(loc2, loc); |
| Bexpression *c456 = mkInt64Const(be, 456); |
| Bstatement *as3 = be->assignment_statement(func, ve3, c456, loc); |
| |
| h.mkIf(tv2, ifst, as3); |
| |
| // return 10101 |
| h.mkReturn(mkInt64Const(be, 10101)); |
| |
| bool broken = h.finish(StripDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| |
| // verify |
| const char *exp = R"RAW_RESULT( |
| define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 { |
| entry: |
| %param1.addr = alloca i32 |
| %param2.addr = alloca i32 |
| %param3.addr = alloca i64* |
| %loc1 = alloca i64 |
| %loc2 = alloca i64 |
| store i32 %param1, i32* %param1.addr |
| store i32 %param2, i32* %param2.addr |
| store i64* %param3, i64** %param3.addr |
| store i64 0, i64* %loc1 |
| store i64 0, i64* %loc2 |
| br i1 true, label %then.0, label %else.0 |
| then.0: ; preds = %entry |
| br i1 true, label %then.1, label %else.1 |
| fallthrough.0: ; preds = %else.0, %fallthrough.1 |
| ret i64 10101 |
| else.0: ; preds = %entry |
| store i64 456, i64* %loc2 |
| br label %fallthrough.0 |
| then.1: ; preds = %then.0 |
| store i64 123, i64* %loc1 |
| br label %fallthrough.1 |
| fallthrough.1: ; preds = %else.1, %then.1 |
| br label %fallthrough.0 |
| else.1: ; preds = %then.0 |
| store i64 987, i64* %loc1 |
| br label %fallthrough.1 |
| } |
| )RAW_RESULT"; |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| // Create a switch statement and append to the test harness current block. |
| |
| static void CreateSwitchStmt(FcnTestHarness &h) |
| { |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| Location loc; |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *loc1 = h.mkLocal("loc1", bi64t); |
| |
| // label for break |
| Blabel *brklab = be->label(func, "break", loc); |
| |
| // first case |
| // loc1 = loc1 / 123 |
| Bexpression *ve1 = be->var_expression(loc1, loc); |
| Bexpression *ve1r = be->var_expression(loc1, loc); |
| Bexpression *c123 = mkInt64Const(be, 123); |
| Bexpression *div = be->binary_expression(OPERATOR_DIV, ve1r, c123, loc); |
| Bstatement *as1 = be->assignment_statement(func, ve1, div, loc); |
| Bstatement *goto1 = be->goto_statement(brklab, loc); |
| Bstatement *cs1 = be->compound_statement(as1, goto1); |
| |
| // second case |
| // loc1 = loc1 < 987 ? loc1 : 987 * loc1 |
| Bexpression *ve2 = be->var_expression(loc1, loc); |
| Bexpression *ve2r = be->var_expression(loc1, loc); |
| Bexpression *ve2r2 = be->var_expression(loc1, loc); |
| Bexpression *ve2r3 = be->var_expression(loc1, loc); |
| Bexpression *c987 = mkInt64Const(be, 987); |
| Bexpression *mul = be->binary_expression(OPERATOR_MULT, c987, ve2r, loc); |
| Bexpression *cmp = be->binary_expression(OPERATOR_LE, ve2r2, c987, loc); |
| Bexpression *condex = |
| be->conditional_expression(func, bi64t, cmp, ve2r3, mul, loc); |
| Bstatement *as2 = be->assignment_statement(func, ve2, condex, loc); |
| // implicit fallthrough |
| |
| // third case |
| Bstatement *st3 = nullptr; // case has only a fallthough statement |
| |
| // fourth case |
| // loc1 = 456 |
| Bexpression *ve4 = be->var_expression(loc1, loc); |
| Bexpression *c456 = mkInt64Const(be, 456); |
| Bstatement *as4 = be->assignment_statement(func, ve4, c456, loc); |
| Bstatement *goto4 = be->goto_statement(brklab, loc); |
| Bstatement *cs4 = be->compound_statement(as4, goto4); |
| |
| // Set up switch statements |
| std::vector<Bstatement*> statements = {cs1, as2, st3, cs4}; |
| std::vector<std::vector<Bexpression*> > cases = { |
| { mkInt64Const(be, 1), mkInt64Const(be, 2) }, |
| { mkInt64Const(be, 3), mkInt64Const(be, 4) }, |
| { mkInt64Const(be, 5)}, |
| }; |
| cases.push_back(std::vector<Bexpression*>()); // default |
| |
| // switch |
| Bexpression *vesw = be->var_expression(loc1, loc); |
| h.mkSwitch(vesw, cases, statements); |
| |
| // label definition |
| Bstatement *labdef = be->label_definition_statement(brklab); |
| h.addStmt(labdef); |
| } |
| |
| TEST(BackendStmtTests, TestSwitchStmt) { |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| Bfunction *func = h.func(); |
| |
| CreateSwitchStmt(h); |
| |
| // return 10101 |
| h.mkReturn(mkInt64Const(be, 10101)); |
| |
| bool broken = h.finish(StripDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| |
| // verify |
| const char *exp = R"RAW_RESULT( |
| define i64 @foo(i8* nest %nest.0, i32 %param1, i32 %param2, i64* %param3) #0 { |
| entry: |
| %param1.addr = alloca i32 |
| %param2.addr = alloca i32 |
| %param3.addr = alloca i64* |
| %loc1 = alloca i64 |
| %tmpv.0 = alloca i64 |
| store i32 %param1, i32* %param1.addr |
| store i32 %param2, i32* %param2.addr |
| store i64* %param3, i64** %param3.addr |
| store i64 0, i64* %loc1 |
| %loc1.ld.4 = load i64, i64* %loc1 |
| switch i64 %loc1.ld.4, label %default.0 [ |
| i64 1, label %case.0 |
| i64 2, label %case.0 |
| i64 3, label %case.1 |
| i64 4, label %case.1 |
| i64 5, label %case.2 |
| ] |
| |
| case.0: ; preds = %entry, %entry |
| %loc1.ld.0 = load i64, i64* %loc1 |
| %div.0 = sdiv i64 %loc1.ld.0, 123 |
| store i64 %div.0, i64* %loc1 |
| br label %label.0 |
| |
| case.1: ; preds = %entry, %entry |
| %loc1.ld.1 = load i64, i64* %loc1 |
| %icmp.0 = icmp sle i64 %loc1.ld.1, 987 |
| %zext.0 = zext i1 %icmp.0 to i8 |
| %trunc.0 = trunc i8 %zext.0 to i1 |
| br i1 %trunc.0, label %then.0, label %else.0 |
| |
| case.2: ; preds = %entry, %fallthrough.0 |
| br label %default.0 |
| |
| default.0: ; preds = %entry, %case.2 |
| store i64 456, i64* %loc1 |
| br label %label.0 |
| |
| label.0: ; preds = %default.0, %case.0 |
| ret i64 10101 |
| |
| then.0: ; preds = %case.1 |
| %loc1.ld.3 = load i64, i64* %loc1 |
| store i64 %loc1.ld.3, i64* %tmpv.0 |
| br label %fallthrough.0 |
| |
| fallthrough.0: ; preds = %else.0, %then.0 |
| %tmpv.0.ld.0 = load i64, i64* %tmpv.0 |
| store i64 %tmpv.0.ld.0, i64* %loc1 |
| br label %case.2 |
| |
| else.0: ; preds = %case.1 |
| %loc1.ld.2 = load i64, i64* %loc1 |
| %mul.0 = mul i64 987, %loc1.ld.2 |
| store i64 %mul.0, i64* %tmpv.0 |
| br label %fallthrough.0 |
| } |
| )RAW_RESULT"; |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| TEST(BackendStmtTests, TestStaticallyUnreachableStmts) { |
| FcnTestHarness h("foo"); |
| Llvm_backend *be = h.be(); |
| |
| // This test is designed to insure that there are no crashes, |
| // errors, or memory leaks when the function being compiled |
| // contains statically unreachable control flow. |
| |
| // Create a return, which will make what comes next unreachable. |
| h.mkReturn(mkInt64Const(be, 10101)); |
| |
| // Statically unreachable if and switch stmts. |
| Bstatement *ifst = CreateIfStmt(h); |
| h.addStmt(ifst); |
| CreateSwitchStmt(h); |
| |
| // Don't check for orphan blocks; we're only interested in making |
| // sure the compilation goes through. |
| h.allowOrphans(); |
| |
| bool broken = h.finish(PreserveDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| } |
| |
| static Bstatement *CreateDeferStmt(Llvm_backend *be, |
| FcnTestHarness &h, |
| Bfunction *func, |
| Bvariable *loc1) |
| { |
| Location loc; |
| Btype *bi8t = be->integer_type(false, 8); |
| |
| // Declare checkdefer, deferreturn |
| Btype *befty = mkFuncTyp(be, L_PARM, be->pointer_type(bi8t), L_END); |
| unsigned fflags = (Backend::function_is_visible | |
| Backend::function_is_declaration); |
| Bfunction *bchkfcn = be->function(befty, "checkdefer", "checkdefer", |
| fflags, h.newloc()); |
| Bfunction *bdefretfcn = be->function(befty, "deferreturn", "deferreturn", |
| fflags, h.newloc()); |
| |
| |
| // Materialize call to deferreturn |
| Bexpression *retfn = be->function_code_expression(bdefretfcn, h.newloc()); |
| std::vector<Bexpression *> args1; |
| Bexpression *ve1 = be->var_expression(loc1, h.newloc()); |
| Bexpression *adve1 = be->address_expression(ve1, h.newloc()); |
| args1.push_back(adve1); |
| Bexpression *undcall = be->call_expression(func, retfn, args1, |
| nullptr, h.newloc()); |
| |
| // Materialize call to checkdefer |
| Bexpression *ckfn = be->function_code_expression(bchkfcn, h.newloc()); |
| std::vector<Bexpression *> args2; |
| Bexpression *ve2 = be->var_expression(loc1, h.loc()); |
| Bexpression *adve2 = be->address_expression(ve2, h.loc()); |
| args2.push_back(adve2); |
| Bexpression *ckdefcall = be->call_expression(func, ckfn, args2, |
| nullptr, h.loc()); |
| |
| // Defer statement based on the calls above. |
| Bstatement *defer = be->function_defer_statement(func, |
| undcall, |
| ckdefcall, |
| h.newloc()); |
| return defer; |
| } |
| |
| TEST(BackendStmtTests, TestDeferStmt) { |
| FcnTestHarness h; |
| Llvm_backend *be = h.be(); |
| BFunctionType *befty = mkFuncTyp(be, L_END); |
| Bfunction *func = h.mkFunction("foo", befty); |
| Btype *bi8t = be->integer_type(false, 8); |
| Bvariable *loc1 = h.mkLocal("x", bi8t); |
| |
| Bstatement *defer = CreateDeferStmt(be, h, func, loc1); |
| h.addStmt(defer); |
| |
| 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 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 { |
| entry: |
| %x = alloca i8 |
| store i8 0, i8* %x |
| br label %finish.0 |
| |
| pad.0: ; preds = %finish.0 |
| %ex.0 = landingpad { i8*, i32 } |
| catch i8* null |
| br label %catch.0 |
| |
| catch.0: ; preds = %pad.0 |
| call void @checkdefer(i8* nest undef, i8* %x) |
| br label %finish.0 |
| |
| finish.0: ; preds = %catch.0, %entry |
| invoke void @deferreturn(i8* nest undef, i8* %x) |
| to label %cont.0 unwind label %pad.0 |
| |
| cont.0: ; preds = %finish.0 |
| ret void |
| } |
| )RAW_RESULT"; |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| TEST(BackendStmtTests, TestExceptionHandlingStmt) { |
| FcnTestHarness h; |
| Llvm_backend *be = h.be(); |
| BFunctionType *befty = mkFuncTyp(be, L_END); |
| Bfunction *func = h.mkFunction("baz", befty); |
| Btype *bi64t = be->integer_type(false, 64); |
| Bvariable *loc1 = h.mkLocal("x", bi64t); |
| Btype *bi8t = be->integer_type(false, 8); |
| Bvariable *loc2 = h.mkLocal("y", bi8t); |
| Btype *s2t = mkBackendStruct(be, bi8t, "f1", bi8t, "f2", nullptr); |
| BFunctionType *beftynr = mkFuncTyp(be, L_RES, s2t, L_END); |
| |
| BFunctionType *befty2 = mkFuncTyp(be, |
| L_PARM, bi64t, |
| L_RES, bi64t, |
| L_END); |
| |
| unsigned fflags = (Backend::function_is_visible | |
| Backend::function_is_declaration); |
| const char *fnames[] = { "plark", "plix" }; |
| Bfunction *fcns[4]; |
| Bexpression *calls[5]; |
| for (unsigned ii = 0; ii < 2; ++ii) { |
| fcns[ii] = be->function(befty, fnames[ii], fnames[ii], fflags, h.newloc()); |
| Bexpression *pfn = be->function_code_expression(fcns[ii], h.newloc()); |
| std::vector<Bexpression *> args; |
| calls[ii] = be->call_expression(func, pfn, args, |
| nullptr, h.newloc()); |
| } |
| fcns[2] = be->function(befty2, "id", "id", fflags, h.newloc()); |
| Bexpression *idfn = be->function_code_expression(fcns[2], h.newloc()); |
| std::vector<Bexpression *> iargs; |
| iargs.push_back(mkInt64Const(be, 99)); |
| calls[2] = be->call_expression(func, idfn, iargs, |
| nullptr, h.newloc()); |
| unsigned fflags2 = (Backend::function_is_visible | |
| Backend::function_does_not_return | |
| Backend::function_is_declaration); |
| fcns[3] = be->function(beftynr, "noret", "noret", fflags2, h.newloc()); |
| for (unsigned ii = 0; ii < 2; ++ii) { |
| Bexpression *nrfn = be->function_code_expression(fcns[3], h.newloc()); |
| std::vector<Bexpression *> noargs; |
| calls[3+ii] = be->call_expression(func, nrfn, noargs, |
| nullptr, h.newloc()); |
| } |
| |
| // body: |
| // x = id(99) |
| // plark() |
| // if false { y = noret(); y = noret() } |
| // x = 123 |
| Bexpression *ve1 = be->var_expression(loc1, h.newloc()); |
| Bstatement *as1 = |
| be->assignment_statement(func, ve1, calls[2], h.newloc()); |
| Bblock *bb1 = mkBlockFromStmt(be, func, as1); |
| addStmtToBlock(be, bb1, h.mkExprStmt(calls[0], FcnTestHarness::NoAppend)); |
| Bstatement *nrcs = h.mkExprStmt(calls[3], FcnTestHarness::NoAppend); |
| Bstatement *nrcs2 = h.mkExprStmt(calls[4], FcnTestHarness::NoAppend); |
| Bblock *bbnr = mkBlockFromStmt(be, func, nrcs); |
| addStmtToBlock(be, bbnr, nrcs2); |
| Bexpression *cond = be->boolean_constant_expression(false); |
| Bstatement *ifst = h.mkIf(cond, bbnr, nullptr, FcnTestHarness::NoAppend); |
| addStmtToBlock(be, bb1, ifst); |
| Bexpression *ve2 = be->var_expression(loc1, h.newloc()); |
| Bstatement *as2 = |
| be->assignment_statement(func, ve2, mkInt64Const(be, 123), h.newloc()); |
| addStmtToBlock(be, bb1, as2); |
| Bstatement *body = be->block_statement(bb1); |
| |
| // catch: |
| // plix() |
| Bstatement *catchst = h.mkExprStmt(calls[1], FcnTestHarness::NoAppend); |
| |
| // finally: |
| // defer statement like above |
| Bstatement *finally = CreateDeferStmt(be, h, func, loc2); |
| |
| // Now exception statement |
| Bstatement *est = |
| be->exception_handler_statement(body, catchst, finally, h.newloc()); |
| |
| h.addStmt(est); |
| |
| bool broken = h.finish(StripDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| |
| const char *exp = R"RAW_RESULT( |
| define void @baz(i8* nest %nest.0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 { |
| entry: |
| %ehtmp.0 = alloca { i8*, i32 } |
| %x = alloca i64 |
| %y = alloca i8 |
| %sret.actual.0 = alloca { i8, i8 } |
| %sret.actual.1 = alloca { i8, i8 } |
| %finvar.0 = alloca i8 |
| store i64 0, i64* %x |
| store i8 0, i8* %y |
| %call.0 = invoke i64 @id(i8* nest undef, i64 99) |
| to label %cont.1 unwind label %pad.1 |
| |
| finok.0: ; preds = %cont.4 |
| store i8 1, i8* %finvar.0 |
| br label %finally.0 |
| |
| finally.0: ; preds = %catchpad.0, %finok.0 |
| br label %finish.0 |
| |
| pad.0: ; preds = %fallthrough.0, %finish.0 |
| %ex.0 = landingpad { i8*, i32 } |
| catch i8* null |
| br label %catch.0 |
| |
| catch.0: ; preds = %pad.0 |
| call void @checkdefer(i8* nest undef, i8* %y) |
| br label %finish.0 |
| |
| finish.0: ; preds = %catch.0, %finally.0 |
| invoke void @deferreturn(i8* nest undef, i8* %y) |
| to label %cont.0 unwind label %pad.0 |
| |
| cont.0: ; preds = %fallthrough.0, %finish.0 |
| %fload.0 = load i8, i8* %finvar.0 |
| %icmp.0 = icmp eq i8 %fload.0, 1 |
| br i1 %icmp.0, label %finret.0, label %finres.0 |
| |
| pad.1: ; preds = %then.0, %cont.1, %entry |
| %ex.1 = landingpad { i8*, i32 } |
| catch i8* null |
| br label %catch.1 |
| |
| catch.1: ; preds = %pad.1 |
| invoke void @plix(i8* nest undef) |
| to label %cont.4 unwind label %catchpad.0 |
| |
| catchpad.0: ; preds = %catch.1 |
| %ex2.0 = landingpad { i8*, i32 } |
| cleanup |
| store { i8*, i32 } %ex2.0, { i8*, i32 }* %ehtmp.0 |
| store i8 0, i8* %finvar.0 |
| br label %finally.0 |
| |
| cont.1: ; preds = %entry |
| store i64 %call.0, i64* %x |
| invoke void @plark(i8* nest undef) |
| to label %cont.2 unwind label %pad.1 |
| |
| cont.2: ; preds = %cont.1 |
| br i1 false, label %then.0, label %else.0 |
| |
| then.0: ; preds = %cont.2 |
| %call.1 = invoke i16 @noret(i8* nest undef) |
| to label %cont.3 unwind label %pad.1 |
| |
| fallthrough.0: ; preds = %else.0 |
| store i64 123, i64* %x |
| store i8 1, i8* %finvar.0 |
| invoke void @deferreturn(i8* nest undef, i8* %y) |
| to label %cont.0 unwind label %pad.0 |
| |
| else.0: ; preds = %cont.2 |
| br label %fallthrough.0 |
| |
| cont.3: ; preds = %then.0 |
| unreachable |
| |
| cont.4: ; preds = %catch.1 |
| br label %finok.0 |
| |
| finres.0: ; preds = %cont.0 |
| %excv.0 = load { i8*, i32 }, { i8*, i32 }* %ehtmp.0 |
| resume { i8*, i32 } %excv.0 |
| |
| finret.0: ; preds = %cont.0 |
| ret void |
| } |
| )RAW_RESULT"; |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| static Bstatement *mkMemReturn(Llvm_backend *be, |
| Bfunction *func, |
| Bvariable *rtmp, |
| Bexpression *val) |
| { |
| // Store value to tmp |
| Location loc; |
| Bexpression *ve = be->var_expression(rtmp, loc); |
| Bstatement *as = be->assignment_statement(func, ve, val, loc); |
| Bblock *block = mkBlockFromStmt(be, func, as); |
| |
| // Load from temp and return |
| Bexpression *ve2 = be->var_expression(rtmp, loc); |
| std::vector<Bexpression *> vals; |
| vals.push_back(ve2); |
| Bstatement *rst = be->return_statement(func, vals, loc); |
| addStmtToBlock(be, block, rst); |
| return be->block_statement(block); |
| } |
| |
| TEST(BackendStmtTests, TestExceptionHandlingStmtWithReturns) { |
| FcnTestHarness h; |
| Llvm_backend *be = h.be(); |
| Btype *bi64t = be->integer_type(false, 64); |
| BFunctionType *befty = mkFuncTyp(be, |
| L_PARM, bi64t, |
| L_RES, bi64t, |
| L_END); |
| Bfunction *func = h.mkFunction("baz", befty); |
| Bvariable *rtmp = h.mkLocal("ret", bi64t); |
| |
| unsigned fflags = (Backend::function_is_visible | |
| Backend::function_is_declaration); |
| Bfunction *sfn = be->function(befty, "splat", "splat", fflags, h.newloc()); |
| Bexpression *splfn = be->function_code_expression(sfn, h.newloc()); |
| Btype *bi8t = be->integer_type(false, 8); |
| Bvariable *loc1 = h.mkLocal("x", bi8t); |
| |
| // body: |
| // if splat(99) == 88 { |
| // return 22 |
| // } else { |
| // return parm |
| // return 33 // add some dead code |
| // } |
| Bstatement *body = nullptr; |
| { |
| // call to splat |
| std::vector<Bexpression *> iargs; |
| iargs.push_back(mkInt64Const(be, 99)); |
| Bexpression *splcall = be->call_expression(func, splfn, iargs, |
| nullptr, h.newloc()); |
| Bexpression *eq = be->binary_expression(OPERATOR_EQEQ, |
| splcall, |
| mkInt64Const(be, 88), |
| h.loc()); |
| Bvariable *p0 = func->getNthParamVar(0); |
| Bexpression *ve1 = be->var_expression(p0, h.newloc()); |
| |
| Bstatement *retparm = mkMemReturn(be, func, rtmp, ve1); |
| Bstatement *ret22 = mkMemReturn(be, func, rtmp, mkInt64Const(be, 22)); |
| Bstatement *ret33 = mkMemReturn(be, func, rtmp, mkInt64Const(be, 33)); |
| Bblock *thenblock = mkBlockFromStmt(be, func, ret22); |
| Bblock *elseblock = mkBlockFromStmt(be, func, retparm); |
| addStmtToBlock(be, elseblock, ret33); |
| Bstatement *ifst = be->if_statement(func, eq, |
| thenblock, elseblock, h.newloc()); |
| body = ifst; |
| } |
| |
| // catch: |
| // splat(13) |
| Bstatement *catchst = nullptr; |
| { |
| std::vector<Bexpression *> args; |
| args.push_back(mkInt64Const(be, 13)); |
| Bexpression *splcall = be->call_expression(func, splfn, args, |
| nullptr, h.newloc()); |
| catchst = h.mkExprStmt(splcall, FcnTestHarness::NoAppend); |
| } |
| |
| // finally: |
| // defer statement like above |
| Bstatement *finally = CreateDeferStmt(be, h, func, loc1); |
| |
| // Now exception statement |
| Bstatement *est = |
| be->exception_handler_statement(body, catchst, finally, h.newloc()); |
| h.addStmt(est); |
| |
| bool broken = h.finish(StripDebugInfo); |
| EXPECT_FALSE(broken && "Module failed to verify."); |
| |
| const char *exp = R"RAW_RESULT( |
| define i64 @baz(i8* nest %nest.0, i64 %p0) #0 personality i32 (i32, i32, i64, i8*, i8*)* @__gccgo_personality_v0 { |
| entry: |
| %ehtmp.0 = alloca { i8*, i32 } |
| %p0.addr = alloca i64 |
| %ret = alloca i64 |
| %x = alloca i8 |
| %finvar.0 = alloca i8 |
| store i64 %p0, i64* %p0.addr |
| store i64 0, i64* %ret |
| store i8 0, i8* %x |
| %call.0 = invoke i64 @splat(i8* nest undef, i64 99) |
| to label %cont.1 unwind label %pad.1 |
| |
| finok.0: ; preds = %cont.2 |
| store i8 1, i8* %finvar.0 |
| br label %finally.0 |
| |
| finally.0: ; preds = %catchpad.0, %finok.0 |
| br label %finish.0 |
| |
| pad.0: ; preds = %else.0, %then.0, %finish.0 |
| %ex.0 = landingpad { i8*, i32 } |
| catch i8* null |
| br label %catch.0 |
| |
| catch.0: ; preds = %pad.0 |
| call void @checkdefer(i8* nest undef, i8* %x) |
| br label %finish.0 |
| |
| finish.0: ; preds = %catch.0, %finally.0 |
| invoke void @deferreturn(i8* nest undef, i8* %x) |
| to label %cont.0 unwind label %pad.0 |
| |
| cont.0: ; preds = %else.0, %then.0, %finish.0 |
| %fload.0 = load i8, i8* %finvar.0 |
| %icmp.1 = icmp eq i8 %fload.0, 1 |
| br i1 %icmp.1, label %finret.0, label %finres.0 |
| |
| pad.1: ; preds = %entry |
| %ex.1 = landingpad { i8*, i32 } |
| catch i8* null |
| br label %catch.1 |
| |
| catch.1: ; preds = %pad.1 |
| %call.1 = invoke i64 @splat(i8* nest undef, i64 13) |
| to label %cont.2 unwind label %catchpad.0 |
| |
| catchpad.0: ; preds = %catch.1 |
| %ex2.0 = landingpad { i8*, i32 } |
| cleanup |
| store { i8*, i32 } %ex2.0, { i8*, i32 }* %ehtmp.0 |
| store i8 0, i8* %finvar.0 |
| br label %finally.0 |
| |
| cont.1: ; preds = %entry |
| %icmp.0 = icmp eq i64 %call.0, 88 |
| %zext.0 = zext i1 %icmp.0 to i8 |
| %trunc.0 = trunc i8 %zext.0 to i1 |
| br i1 %trunc.0, label %then.0, label %else.0 |
| |
| then.0: ; preds = %cont.1 |
| store i64 22, i64* %ret |
| store i8 1, i8* %finvar.0 |
| invoke void @deferreturn(i8* nest undef, i8* %x) |
| to label %cont.0 unwind label %pad.0 |
| |
| else.0: ; preds = %cont.1 |
| %p0.ld.0 = load i64, i64* %p0.addr |
| store i64 %p0.ld.0, i64* %ret |
| store i8 1, i8* %finvar.0 |
| invoke void @deferreturn(i8* nest undef, i8* %x) |
| to label %cont.0 unwind label %pad.0 |
| |
| cont.2: ; preds = %catch.1 |
| br label %finok.0 |
| |
| finres.0: ; preds = %cont.0 |
| %excv.0 = load { i8*, i32 }, { i8*, i32 }* %ehtmp.0 |
| resume { i8*, i32 } %excv.0 |
| |
| finret.0: ; preds = %cont.0 |
| %ret.ld.1 = load i64, i64* %ret |
| ret i64 %ret.ld.1 |
| } |
| )RAW_RESULT"; |
| |
| bool isOK = h.expectValue(func->function(), exp); |
| EXPECT_TRUE(isOK && "Function does not have expected contents"); |
| } |
| |
| } |