| //===- llvm/tools/gollvm/unittests/BackendCore/TestUtils.cpp ------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TestUtils.h" |
| #include "llvm/IR/DebugInfo.h" |
| #include "llvm/IR/CFG.h" |
| |
| namespace goBackendUnitTests { |
| |
| std::string trimsp(const std::string &s) { |
| size_t firstsp = s.find_first_not_of(" \n"); |
| if (firstsp == std::string::npos) |
| return s; |
| size_t lastsp = s.find_last_not_of(" \n"); |
| return s.substr(firstsp, (lastsp - firstsp + 1)); |
| } |
| |
| std::vector<std::string> tokenize(const std::string &s) { |
| std::vector<std::string> tokens; |
| |
| const std::string del(" \t\n"); |
| size_t np = s.find_first_not_of(del, 0); |
| size_t pos = s.find_first_of(del, np); |
| while (pos != std::string::npos || np != std::string::npos) { |
| tokens.push_back(s.substr(np, pos - np)); |
| np = s.find_first_not_of(del, pos); |
| pos = s.find_first_of(del, np); |
| } |
| return tokens; |
| } |
| |
| std::string vectostr(const std::vector<std::string> &tv) { |
| std::stringstream ss; |
| unsigned wc = 0; |
| for (auto s : tv) |
| ss << " " << wc++ << "[" << s << "]"; |
| return ss.str(); |
| } |
| |
| bool difftokens(const std::string &expected, const std::string &result, |
| std::string &diffreason) |
| { |
| std::vector<std::string> expv = tokenize(expected); |
| std::vector<std::string> resv = tokenize(result); |
| unsigned mins = std::min(expv.size(), resv.size()); |
| unsigned maxs = std::max(expv.size(), resv.size()); |
| std::stringstream ss; |
| bool rval = true; |
| if (expv.size() != resv.size()) { |
| ss << "lengths differ (" << expv.size() << " vs " << resv.size() |
| << ") extra " << (expv.size() > resv.size() ? "result" : "expected") |
| << " tokens: "; |
| for (unsigned idx = 0; idx < maxs; ++idx) { |
| if (idx >= mins) |
| ss << (idx < expv.size() ? expv[idx] : resv[idx]) << " "; |
| } |
| ss << "\n"; |
| rval = false; |
| } |
| for (unsigned idx = 0; idx < mins; ++idx) { |
| if (expv[idx] != resv[idx]) { |
| ss << "token vector diff at slot " << idx << " (expected '" << expv[idx] |
| << "' result '" << resv[idx] << "')"; |
| rval = false; |
| break; |
| } |
| } |
| if (! rval) |
| diffreason = ss.str(); |
| return rval; |
| } |
| |
| bool containstokens(const std::string &text, const std::string &pat) |
| { |
| std::vector<std::string> textToks = tokenize(text); |
| std::vector<std::string> patToks = tokenize(pat); |
| for (unsigned ti = 0; ti < textToks.size(); ++ti) { |
| bool failed = false; |
| for (unsigned tic = ti, pi = 0; pi < patToks.size(); ++pi, ++tic) { |
| if (tic >= textToks.size() || patToks[pi] != textToks[tic]) { |
| failed = true; |
| break; |
| } |
| } |
| if (failed) |
| continue; |
| return true; |
| } |
| return false; |
| } |
| |
| unsigned countinstances(const std::string &text, const std::string &pat) |
| { |
| unsigned instances = 0; |
| std::vector<std::string> textToks = tokenize(text); |
| std::vector<std::string> patToks = tokenize(pat); |
| for (unsigned ti = 0; ti < textToks.size(); ++ti) { |
| bool fail = false; |
| for (unsigned tic = ti, pi = 0; pi < patToks.size(); ++pi, ++tic) { |
| if (tic >= textToks.size() || patToks[pi] != textToks[tic]) { |
| fail = true; |
| break; |
| } |
| } |
| if (!fail) |
| instances += 1; |
| } |
| return instances; |
| } |
| |
| std::string repr(llvm::Value *val) { |
| std::string res; |
| llvm::raw_string_ostream os(res); |
| if (!val) |
| os << "<null_value>"; |
| else |
| val->print(os); |
| return trimsp(os.str()); |
| } |
| |
| std::string repr(llvm::Type *t) { |
| std::string res; |
| llvm::raw_string_ostream os(res); |
| if (t == NULL) { |
| os << "<null_type>"; |
| } else { |
| t->print(os); |
| } |
| return trimsp(os.str()); |
| } |
| |
| Btype *mkBackendStruct(Backend *be, ...) |
| { |
| va_list ap; |
| va_start(ap, be); |
| |
| std::vector<Backend::Btyped_identifier> fields; |
| Btype *ct = va_arg(ap, Btype *); |
| while (ct != nullptr) { |
| Location loc; |
| const char *fieldName = va_arg(ap, const char *); |
| assert(fieldName); |
| fields.push_back(Backend::Btyped_identifier(fieldName, ct, loc)); |
| ct = va_arg(ap, Btype *); |
| } |
| return be->struct_type(fields); |
| } |
| |
| Btype *mkBackendThreeFieldStruct(Backend *be) { |
| return mkBackendStruct(be, |
| be->bool_type(), "f1", |
| be->pointer_type(be->float_type(32)), "f2", |
| be->integer_type(true, 64), "f3", |
| nullptr); |
| } |
| |
| llvm::StructType *mkLlvmStruct(llvm::LLVMContext *context, ...) |
| { |
| va_list ap; |
| va_start(ap, context); |
| |
| llvm::SmallVector<llvm::Type *, 64> smv; |
| llvm::Type *typ = va_arg(ap, llvm::Type *); |
| assert(typ); |
| while (typ != nullptr) { |
| smv.push_back(typ); |
| typ = va_arg(ap, llvm::Type *); |
| } |
| return llvm::StructType::get(*context, smv); |
| } |
| |
| llvm::StructType *mkLlvmThreeFieldStruct(llvm::LLVMContext &context) { |
| llvm::Type *flt = llvm::Type::getFloatTy(context); |
| return mkLlvmStruct(&context, |
| llvm::Type::getInt8Ty(context), |
| llvm::PointerType::get(flt, 0), |
| llvm::IntegerType::get(context, 64), |
| nullptr); |
| } |
| |
| Btype *mkTwoFieldStruct(Backend *be, Btype *t1, Btype *t2) |
| { |
| return mkBackendStruct(be, t1, "f1", t2, "f2", nullptr); |
| } |
| |
| llvm::Type *mkTwoFieldLLvmStruct(llvm::LLVMContext &context, |
| llvm::Type *t1, llvm::Type *t2) |
| { |
| return mkLlvmStruct(&context, t1, t2, nullptr); |
| } |
| |
| Backend::Btyped_identifier mkid(Btype *t) { |
| static unsigned ctr = 1; |
| char buf[128]; |
| sprintf(buf, "id%u", ctr++); |
| return Backend::Btyped_identifier(buf, t, Location()); |
| } |
| |
| BFunctionType *mkFuncTyp(Backend *be, ...) { |
| va_list ap; |
| |
| va_start(ap, be); |
| |
| Backend::Btyped_identifier receiver("rec", NULL, Location()); |
| std::vector<Backend::Btyped_identifier> results; |
| std::vector<Backend::Btyped_identifier> params; |
| Btype *result_type = NULL; |
| |
| unsigned tok = va_arg(ap, unsigned); |
| while (tok != L_END) { |
| switch (tok) { |
| case L_RCV: |
| receiver.btype = va_arg(ap, Btype *); |
| break; |
| case L_PARM: |
| params.push_back(mkid(va_arg(ap, Btype *))); |
| break; |
| case L_RES: |
| results.push_back(mkid(va_arg(ap, Btype *))); |
| break; |
| case L_RES_ST: |
| result_type = va_arg(ap, Btype *); |
| break; |
| default: { |
| assert(false && "internal error"); |
| return NULL; |
| } |
| } |
| tok = va_arg(ap, unsigned); |
| } |
| |
| if (results.size() > 1) |
| result_type = be->struct_type(results); |
| |
| Btype *ft = be->function_type(receiver, params, results, |
| result_type, Location()); |
| return ft->castToBFunctionType(); |
| } |
| |
| llvm::Type *mkLLFuncTyp(llvm::LLVMContext *context, ...) { |
| va_list ap; |
| |
| llvm::SmallVector<llvm::Type *, 4> params(0); |
| llvm::SmallVector<llvm::Type *, 4> results(0); |
| llvm::Type *recv_typ = NULL; |
| llvm::Type *res_styp = NULL; |
| |
| va_start(ap, context); |
| unsigned tok = va_arg(ap, unsigned); |
| while (tok != L_END) { |
| switch (tok) { |
| case L_RCV: |
| recv_typ = va_arg(ap, llvm::Type *); |
| break; |
| case L_PARM: |
| params.push_back(va_arg(ap, llvm::Type *)); |
| break; |
| case L_RES: |
| results.push_back(va_arg(ap, llvm::Type *)); |
| break; |
| case L_RES_ST: |
| res_styp = va_arg(ap, llvm::Type *); |
| break; |
| default: { |
| assert(false && "internal error"); |
| return NULL; |
| } |
| } |
| tok = va_arg(ap, unsigned); |
| } |
| |
| llvm::SmallVector<llvm::Type *, 4> elems(0); |
| llvm::Type *llit = llvm::IntegerType::get(*context, 8); |
| elems.push_back(llvm::PointerType::get(llit, 0)); |
| if (recv_typ) |
| elems.push_back(recv_typ); |
| for (auto pt : params) |
| elems.push_back(pt); |
| |
| llvm::Type *rtyp = NULL; |
| if (results.empty()) |
| rtyp = llvm::Type::getVoidTy(*context); |
| else if (results.size() == 1) |
| rtyp = results[0]; |
| else { |
| rtyp = res_styp; |
| } |
| return llvm::FunctionType::get(rtyp, elems, false); |
| } |
| |
| bool llvmTypesEquiv(llvm::Type *t1, llvm::Type *t2) |
| { |
| if (t1->getTypeID() != t2->getTypeID()) |
| return false; |
| if (t1->getNumContainedTypes() != t2->getNumContainedTypes()) |
| return false; |
| for (unsigned idx = 0; idx < t1->getNumContainedTypes(); ++idx) |
| if (!llvmTypesEquiv(t1->getContainedType(idx), |
| t2->getContainedType(idx))) |
| return false; |
| return true; |
| } |
| |
| Bfunction *mkFunci32o64(Backend *be, const char *fname, bool mkParams) { |
| Btype *bi64t = be->integer_type(false, 64); |
| Btype *bi32t = be->integer_type(false, 32); |
| Btype *bpi64t = be->pointer_type(bi64t); |
| Btype *befty = |
| mkFuncTyp(be, |
| L_PARM, bi32t, L_PARM, bi32t, L_PARM, bpi64t, |
| L_RES, bi64t, L_END); |
| bool visible = true; |
| bool is_declaration = false; |
| bool is_inl = true; |
| bool split_stack = true; |
| bool unique_sec = false; |
| bool no_ret = false; |
| Location loc; |
| Bfunction *func = be->function(befty, fname, fname, visible, |
| is_declaration, is_inl, |
| split_stack, no_ret, unique_sec, loc); |
| if (mkParams) { |
| be->parameter_variable(func, "param1", bi32t, false, loc); |
| be->parameter_variable(func, "param2", bi32t, false, loc); |
| be->parameter_variable(func, "param3", bpi64t, false, loc); |
| } |
| return func; |
| } |
| |
| Bfunction *mkFuncFromType(Backend *be, const char *fname, BFunctionType *befty) |
| { |
| bool visible = true; |
| bool is_declaration = false; |
| bool is_inl = true; |
| bool split_stack = true; |
| bool unique_sec = false; |
| bool no_ret = false; |
| Location loc; |
| Bfunction *func = be->function(befty, fname, fname, visible, |
| is_declaration, is_inl, |
| split_stack, no_ret, unique_sec, loc); |
| const std::vector<Btype *> ¶mTypes = befty->paramTypes(); |
| for (unsigned idx = 0; idx < paramTypes.size(); ++idx) { |
| std::stringstream ss; |
| ss << "p" << idx; |
| be->parameter_variable(func, ss.str(), paramTypes[idx], false, loc); |
| } |
| return func; |
| } |
| |
| Bexpression *mkUIntConst(Backend *be, uint64_t val, unsigned bits) { |
| mpz_t mpz_val; |
| memset(&mpz_val, '0', sizeof(mpz_val)); |
| mpz_init_set_ui(mpz_val, val); |
| Btype *but = be->integer_type(true, bits); |
| Bexpression *rval = be->integer_constant_expression(but, mpz_val); |
| mpz_clear(mpz_val); |
| return rval; |
| } |
| |
| Bexpression *mkIntConst(Backend *be, int64_t val, unsigned bits) { |
| mpz_t mpz_val; |
| memset(&mpz_val, '0', sizeof(mpz_val)); |
| mpz_init_set_si(mpz_val, val); |
| Btype *bit = be->integer_type(false, bits); |
| Bexpression *rval = be->integer_constant_expression(bit, mpz_val); |
| mpz_clear(mpz_val); |
| return rval; |
| } |
| |
| Bexpression *mkInt64Const(Backend *be, int64_t val) { |
| return mkIntConst(be, val, 64); |
| } |
| |
| Bexpression *mkUint64Const(Backend *be, uint64_t val) { |
| return mkUIntConst(be, val, 64); |
| } |
| |
| Bexpression *mkInt32Const(Backend *be, int32_t val) { |
| return mkIntConst(be, val, 32); |
| } |
| |
| Bexpression *mkFloat64Const(Backend *be, double val) { |
| mpfr_t mpfr_val; |
| |
| mpfr_init(mpfr_val); |
| mpfr_set_d(mpfr_val, val, GMP_RNDN); |
| Btype *bf64t = be->float_type(64); |
| Bexpression *beval = be->float_constant_expression(bf64t, mpfr_val); |
| mpfr_clear(mpfr_val); |
| return beval; |
| } |
| |
| // Return func desc type |
| Btype *mkFuncDescType(Backend *be) |
| { |
| assert(be); |
| Btype *bt = be->bool_type(); |
| Btype *pbt = be->pointer_type(bt); |
| Btype *uintptrt = be->integer_type(true, be->type_size(pbt)*8); |
| Btype *fdescst = mkBackendStruct(be, uintptrt, "f1", nullptr); |
| return fdescst; |
| } |
| |
| Bexpression *mkFuncDescExpr(Backend *be, Bfunction *fcn) |
| { |
| assert(be); |
| assert(fcn); |
| 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 *fdescst = mkFuncDescType(be); |
| Bexpression *fp = be->function_code_expression(fcn, loc); |
| Bexpression *fpuint = be->convert_expression(uintptrt, fp, loc); |
| std::vector<Bexpression *> vals; |
| vals.push_back(fpuint); |
| return be->constructor_expression(fdescst, vals, loc); |
| } |
| |
| Bblock *mkBlockFromStmt(Backend *be, Bfunction *func, Bstatement *st) { |
| const std::vector<Bvariable *> empty; |
| Bblock *b = be->block(func, nullptr, empty, Location(), Location()); |
| std::vector<Bstatement *> stlist; |
| stlist.push_back(st); |
| be->block_add_statements(b, stlist); |
| return b; |
| } |
| |
| Bstatement *addStmtToBlock(Backend *be, Bblock *block, Bstatement *st) { |
| std::vector<Bstatement *> stlist; |
| stlist.push_back(st); |
| be->block_add_statements(block, stlist); |
| return st; |
| } |
| |
| Bstatement *addExprToBlock(Backend *be, Bfunction *func, |
| Bblock *block, Bexpression *e) { |
| Bstatement *es = be->expression_statement(func, e); |
| std::vector<Bstatement *> stlist; |
| stlist.push_back(es); |
| be->block_add_statements(block, stlist); |
| return es; |
| } |
| |
| class NodeReprVisitor { |
| public: |
| NodeReprVisitor() : os_(str_) { } |
| |
| std::string result() { return os_.str(); } |
| |
| void visitNodePre(Bnode *node) { } |
| |
| void visitNodePost(Bnode *node) { |
| assert(node); |
| Bexpression *expr = node->castToBexpression(); |
| if (expr) { |
| for (auto inst : expr->instructions()) { |
| inst->print(os_); |
| os_ << "\n"; |
| } |
| } |
| assert(node->flavor() != N_IfStmt && |
| node->flavor() != N_GotoStmt && |
| node->flavor() != N_LabelStmt && |
| node->flavor() != N_SwitchStmt); |
| } |
| |
| private: |
| std::string str_; |
| llvm::raw_string_ostream os_; |
| }; |
| |
| std::string repr(Bnode *node) { |
| if (!node) |
| return "<null Bnode ptr>"; |
| |
| NodeReprVisitor vis; |
| simple_walk_nodes(node, vis); |
| return trimsp(vis.result()); |
| } |
| |
| FcnTestHarness::FcnTestHarness(const char *fcnName) |
| : context_() |
| , be_(new Llvm_backend(context_, nullptr, nullptr)) |
| , func_(nullptr) |
| , entryBlock_(nullptr) |
| , curBlock_(nullptr) |
| , nextLabel_(nullptr) |
| , lineNum_(1) |
| , finished_(false) |
| , returnAdded_(false) |
| , emitDumpFilesOnDiff_(false) |
| , findOrphanBBs_(true) |
| { |
| // establish initial file so as to make verifier happy |
| be_->linemap()->start_file("unit_testing.go", 1); |
| loc_ = be_->linemap()->get_location(lineNum_); |
| |
| // Eager function creation if name not specified |
| if (fcnName) { |
| func_ = mkFunci32o64(be(), fcnName); |
| entryBlock_ = be()->block(func_, nullptr, emptyVarList_, loc_, loc_); |
| curBlock_ = be()->block(func_, nullptr, emptyVarList_, loc_, loc_); |
| } |
| |
| // debugging |
| if (getenv("GOLLVM_UNITTESTS_BACKENDCORE_EMITDUMPFILES")) |
| emitDumpFilesOnDiff_ = true; |
| } |
| |
| FcnTestHarness::~FcnTestHarness() |
| { |
| assert(finished_); |
| } |
| |
| Location FcnTestHarness::newloc() |
| { |
| loc_ = be_->linemap()->get_location(++lineNum_); |
| return loc_; |
| } |
| |
| Bfunction *FcnTestHarness::mkFunction(const char *fcnName, BFunctionType *befty) |
| { |
| func_ = mkFuncFromType(be(), fcnName, befty); |
| entryBlock_ = be()->block(func_, nullptr, emptyVarList_, loc_, loc_); |
| curBlock_ = be()->block(func_, nullptr, emptyVarList_, loc_, loc_); |
| return func_; |
| } |
| |
| Bvariable *FcnTestHarness::mkLocal(const char *name, |
| Btype *typ, |
| Bexpression *init) |
| { |
| assert(func_); |
| Bvariable *v = be()->local_variable(func_, name, typ, nullptr, true, loc_); |
| if (!init) |
| init = be()->zero_expression(typ); |
| Bstatement *is = be()->init_statement(func_, v, init); |
| addStmtToBlock(be(), curBlock_, is); |
| return v; |
| } |
| |
| void FcnTestHarness::mkAssign(Bexpression *lhs, |
| Bexpression *rhs, |
| AppendDisp disp) |
| { |
| assert(func_); |
| Bstatement *as = be()->assignment_statement(func_, lhs, rhs, loc_); |
| if (disp == YesAppend) |
| addStmtToBlock(be(), curBlock_, as); |
| } |
| |
| Bstatement *FcnTestHarness::mkExprStmt(Bexpression *expr, AppendDisp disp) |
| { |
| assert(func_); |
| Bstatement *es = be()->expression_statement(func_, expr); |
| if (disp == YesAppend) |
| addStmtToBlock(be(), curBlock_, es); |
| return es; |
| } |
| |
| Bexpression *FcnTestHarness::mkCallExpr(Backend *be, Bfunction *fun, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fun); |
| Bexpression *fn = be->function_code_expression(fun, loc_); |
| std::vector<Bexpression *> args; |
| Bexpression *e = va_arg(ap, Bexpression *); |
| while (e) { |
| args.push_back(e); |
| e = va_arg(ap, Bexpression *); |
| } |
| Bexpression *call = be->call_expression(func_, fn, args, nullptr, loc_); |
| return call; |
| } |
| |
| Bstatement *FcnTestHarness::mkReturn(Bexpression *expr, AppendDisp disp) |
| { |
| std::vector<Bexpression *> vals; |
| vals.push_back(expr); |
| return mkReturn(vals, disp); |
| } |
| |
| Bstatement *FcnTestHarness::mkReturn(const std::vector<Bexpression *> &vals, |
| AppendDisp disp) |
| { |
| assert(func_); |
| Bstatement *ret = be()->return_statement(func_, vals, loc_); |
| if (disp == YesAppend) { |
| addStmtToBlock(be(), curBlock_, ret); |
| returnAdded_ = true; |
| } |
| return ret; |
| } |
| |
| Bstatement *FcnTestHarness::mkIf(Bexpression *cond, |
| Bstatement *trueStmt, |
| Bstatement *falseStmt, |
| AppendDisp disp) |
| { |
| assert(func_); |
| Bblock *trueBlock = mkBlockFromStmt(be(), func_, trueStmt); |
| Bblock *falseBlock = nullptr; |
| if (falseStmt) |
| falseBlock = mkBlockFromStmt(be(), func_, falseStmt); |
| Bstatement *ifst = be()->if_statement(func_, cond, |
| trueBlock, falseBlock, loc_); |
| if (disp == YesAppend) |
| addStmtToBlock(be(), curBlock_, ifst); |
| return ifst; |
| } |
| |
| Bstatement *FcnTestHarness::mkSwitch(Bexpression *swval, |
| const std::vector<std::vector<Bexpression*> >& cases, |
| const std::vector<Bstatement*>& statements, |
| AppendDisp disp) |
| { |
| assert(func_); |
| Bstatement *swst = be()->switch_statement(func_, swval, |
| cases, statements, loc_); |
| if (disp == YesAppend) |
| addStmtToBlock(be(), curBlock_, swst); |
| return swst; |
| } |
| |
| |
| void FcnTestHarness::addStmt(Bstatement *stmt) |
| { |
| assert(func_); |
| addStmtToBlock(be(), curBlock_, stmt); |
| } |
| |
| void FcnTestHarness::newBlock(std::vector<Bvariable *> *varlist) |
| { |
| assert(func_); |
| |
| if (!varlist) |
| varlist = &emptyVarList_; |
| |
| // Create label for new block and append jump to current block |
| std::string lab = be()->namegen("_lab"); |
| Blabel *blab = be()->label(func_, lab, loc_); |
| Bstatement *gots = be()->goto_statement(blab, loc_); |
| addStmtToBlock(be(), curBlock_, gots); |
| |
| // Turn current block into statement and tack onto entry block. Weird, |
| // but this is the pardigm for gofrontend. |
| Bstatement *bst = be()->block_statement(curBlock_); |
| if (nextLabel_) { |
| Bstatement *ldef = be()->label_definition_statement(nextLabel_); |
| bst = be()->compound_statement(ldef, bst); |
| } |
| addStmtToBlock(be(), entryBlock_, bst); |
| nextLabel_ = blab; |
| returnAdded_ = false; |
| |
| // New block |
| curBlock_ = be()->block(func_, nullptr, *varlist, loc_, loc_); |
| } |
| |
| static void emitStringToDumpFile(const char *tag, |
| unsigned version, |
| const std::string &payload) |
| { |
| std::stringstream ss; |
| ss << "/tmp/" << tag << ".dump." << version << ".txt"; |
| FILE *fp = fopen(ss.str().c_str(), "w"); |
| if (fp) { |
| fprintf(fp, "%s\n", payload.c_str()); |
| fclose(fp); |
| std::cerr << "emitted dump file " << ss.str() << "\n"; |
| } |
| } |
| |
| static void complainOnNequal(const std::string &reason, |
| const std::string &expected, |
| const std::string &actual, |
| bool emitDump) |
| { |
| std::cerr << reason << "\n"; |
| std::cerr << "expected dump:\n" << expected << "\n"; |
| std::cerr << "statement dump:\n" << actual << "\n"; |
| if (emitDump) { |
| static unsigned filecount; |
| emitStringToDumpFile("expected", filecount, expected); |
| emitStringToDumpFile("actual", filecount, actual); |
| filecount++; |
| } |
| } |
| |
| bool FcnTestHarness::expectStmt(Bstatement *st, const std::string &expected) |
| { |
| std::string reason; |
| std::string actual(repr(st)); |
| bool equal = difftokens(expected, actual, reason); |
| if (! equal) |
| complainOnNequal(reason, expected, actual, emitDumpFilesOnDiff_); |
| return equal; |
| } |
| |
| bool FcnTestHarness::expectValue(llvm::Value *val, const std::string &expected) |
| { |
| std::string reason; |
| std::string actual(repr(val)); |
| bool equal = difftokens(expected, actual, reason); |
| if (! equal) |
| complainOnNequal(reason, expected, actual, emitDumpFilesOnDiff_); |
| return equal; |
| } |
| |
| bool FcnTestHarness::expectBlock(const std::string &expected) |
| { |
| std::string reason; |
| std::string actual(repr(curBlock_)); |
| bool equal = difftokens(expected, actual, reason); |
| if (! equal) |
| complainOnNequal(reason, expected, actual, emitDumpFilesOnDiff_); |
| return equal; |
| } |
| |
| bool FcnTestHarness::expectModuleDumpContains(const std::string &expected) |
| { |
| std::string res; |
| llvm::raw_string_ostream os(res); |
| be()->module().print(os, nullptr); |
| std::string actual(trimsp(os.str())); |
| return containstokens(actual, expected); |
| } |
| |
| unsigned FcnTestHarness::countInstancesInModuleDump(const std::string &expected) |
| { |
| std::string res; |
| llvm::raw_string_ostream os(res); |
| be()->module().print(os, nullptr); |
| std::string actual(trimsp(os.str())); |
| return countinstances(actual, expected); |
| } |
| |
| bool FcnTestHarness::expectRepr(Bnode *node, const std::string &expected) |
| { |
| std::string reason; |
| std::string actual(repr(node)); |
| bool equal = difftokens(expected, actual, reason); |
| if (! equal) |
| complainOnNequal(reason, expected, actual, emitDumpFilesOnDiff_); |
| return equal; |
| } |
| |
| bool FcnTestHarness::finish(DebugDisposition whatToDoWithDebugInfo) |
| { |
| if (func_) { |
| |
| // Emit a label def for the pending block if needed |
| Bstatement *bst = be()->block_statement(curBlock_); |
| if (nextLabel_) { |
| Bstatement *ldef = be()->label_definition_statement(nextLabel_); |
| bst = be()->compound_statement(ldef, bst); |
| } |
| |
| // Add current block as stmt to entry block |
| addStmtToBlock(be(), entryBlock_, bst); |
| |
| // Set function body |
| be()->function_set_body(func_, entryBlock_); |
| |
| } |
| // Finalize export data. This has the side effect of finalizing |
| // debug meta-data, which we need to do before invoking the verifier. |
| be()->finalizeExportData(); |
| |
| // Strip debug info now if requested |
| if (whatToDoWithDebugInfo == StripDebugInfo) |
| llvm::StripDebugInfo(be()->module()); |
| |
| // Look for non-entry blocks with no CFG preds, since they |
| // are nearly always an indication that something is wrong. |
| bool brokenBlock = false; |
| if (func_ && findOrphanBBs_) { |
| for (llvm::BasicBlock &bb : *func_->function()) { |
| if (&bb == &func_->function()->getEntryBlock()) |
| continue; |
| if (! bb.getParent()) { |
| std::cerr << "** block with no parent:\n"; |
| llvm::Value *blockVal = &bb; |
| std::cerr << repr(blockVal); |
| brokenBlock = true; |
| } |
| if (llvm::pred_begin(&bb) == llvm::pred_end(&bb)) { |
| std::cerr << "** block has no predecessors:\n"; |
| llvm::Value *blockVal = &bb; |
| std::cerr << repr(blockVal); |
| brokenBlock = true; |
| } |
| } |
| } |
| |
| // Verify module |
| bool broken = llvm::verifyModule(be()->module(), &llvm::dbgs()); |
| |
| // Mark finished |
| finished_ = true; |
| curBlock_ = entryBlock_; |
| |
| return broken || brokenBlock; |
| } |
| |
| } // end namespace goBackEndUnitTests |