| //===-- go-llvm-bnode.cpp - implementation of 'Bnode' class ---------------===// |
| // |
| // 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. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Methods for class Bnode and related classes. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "go-llvm.h" |
| #include "go-llvm-btype.h" |
| #include "go-llvm-bnode.h" |
| #include "go-llvm-bvariable.h" |
| #include "go-llvm-bexpression.h" |
| #include "go-llvm-bstatement.h" |
| #include "go-llvm-tree-integrity.h" |
| #include "go-system.h" |
| |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/IR/DataLayout.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/Value.h" |
| |
| // Table of Bnode properties |
| |
| enum StmtDisp : unsigned char { IsStmt, IsExpr }; |
| |
| struct BnodePropVals { |
| const char *str; |
| unsigned numChildren; |
| StmtDisp stmt; |
| }; |
| |
| static constexpr unsigned Variadic = 0xffffffff; |
| |
| static BnodePropVals BnodeProperties[] = { |
| |
| /* N_Error */ { "error", 0, IsExpr }, |
| /* N_Const */ { "const", 0, IsExpr }, |
| /* N_Var */ { "var", 0, IsExpr }, |
| /* N_FcnAddress */ { "fcn", 0, IsExpr }, |
| /* N_LabelAddress */ { "labelad", 0, IsExpr }, |
| /* N_Conversion */ { "conv", 1, IsExpr }, |
| /* N_Deref */ { "deref", 1, IsExpr }, |
| /* N_Address */ { "addr", 1, IsExpr }, |
| /* N_UnaryOp */ { "unary", 1, IsExpr }, |
| /* N_StructField */ { "field", 1, IsExpr }, |
| /* N_BinaryOp */ { "binary", 2, IsExpr }, |
| /* N_Compound */ { "compound", 2, IsExpr }, |
| /* N_ArrayIndex */ { "arindex", 2, IsExpr }, |
| /* N_PointerOffset */ { "ptroff", 2, IsExpr }, |
| /* N_Composite */ { "composite", Variadic, IsExpr }, |
| /* N_Call */ { "call", Variadic, IsExpr }, |
| /* N_Conditional */ { "conditional", Variadic, IsExpr }, |
| |
| /* N_EmptyStmt */ { "empty", 0, IsStmt }, |
| /* N_LabelStmt */ { "label", 0, IsStmt }, |
| /* N_GotoStmt */ { "goto", 0, IsStmt }, |
| /* N_ExprStmt */ { "exprst", 1, IsStmt }, |
| /* N_ReturnStmt */ { "return", 1, IsStmt }, |
| /* N_DeferStmt */ { "defer", 2, IsStmt }, |
| /* N_IfStmt */ { "ifstmt", 3, IsStmt }, |
| /* N_ExcepStmt */ { "excepstmt", 3, IsStmt }, |
| /* N_BlockStmt */ { "block", Variadic, IsStmt }, |
| /* N_SwitchStmt */ { "switch", Variadic, IsStmt } |
| }; |
| |
| Bnode::Bnode(NodeFlavor flavor, const std::vector<Bnode *> &kids, Location loc) |
| : kids_(kids) |
| , location_(loc) |
| , flavor_(flavor) |
| , id_(0xfeedface) |
| , flags_(0) |
| { |
| memset(&u, '\0', sizeof(u)); |
| assert(BnodeProperties[flavor].numChildren == Variadic || |
| BnodeProperties[flavor].numChildren == kids.size()); |
| } |
| |
| Bnode::Bnode(const Bnode &src) |
| : location_(src.location_) |
| , flavor_(src.flavor_) |
| , id_(0xfeedface) |
| , flags_(0) |
| { |
| assert(! isStmt()); |
| memcpy(&u, &src.u, sizeof(u)); |
| } |
| |
| Bexpression *Bnode::castToBexpression() const { |
| if (isStmt()) |
| return nullptr; |
| return const_cast<Bexpression*>(static_cast<const Bexpression*>(this)); |
| } |
| |
| Bstatement *Bnode::castToBstatement() const { |
| if (!isStmt()) |
| return nullptr; |
| return const_cast<Bstatement*>(static_cast<const Bstatement*>(this)); |
| } |
| |
| Bblock *Bnode::castToBblock() const { |
| if (flavor() != N_BlockStmt) |
| return nullptr; |
| return const_cast<Bblock*>(static_cast<const Bblock*>(this)); |
| } |
| |
| void Bnode::replaceChild(unsigned idx, Bnode *newchild) |
| { |
| assert(idx < kids_.size()); |
| kids_[idx] = newchild; |
| } |
| |
| static const char *opToString(Operator op) |
| { |
| switch(op) { |
| case OPERATOR_INVALID: return "<invalid>"; |
| case OPERATOR_OROR: return "||"; // || |
| case OPERATOR_ANDAND: return "&&"; // && |
| case OPERATOR_EQEQ: return "=="; // == |
| case OPERATOR_NOTEQ: return "!="; // != |
| case OPERATOR_LT: return "<"; // < |
| case OPERATOR_LE: return "<="; // <= |
| case OPERATOR_GT: return ">"; // > |
| case OPERATOR_GE: return ">="; // >= |
| case OPERATOR_PLUS: return "+"; // + |
| case OPERATOR_MINUS: return "-"; // - |
| case OPERATOR_OR: return "|"; // | |
| case OPERATOR_XOR: return "^"; // ^ |
| case OPERATOR_MULT: return "*"; // * |
| case OPERATOR_DIV: return "/"; // / |
| case OPERATOR_MOD: return "%"; // % |
| case OPERATOR_LSHIFT: return "<<"; // << |
| case OPERATOR_RSHIFT: return ">>"; // >> |
| case OPERATOR_AND: return "&"; // & |
| case OPERATOR_NOT: return "!"; // ! |
| case OPERATOR_EQ: return "="; // = |
| case OPERATOR_BITCLEAR: return "&^";// &^ |
| default: break; |
| } |
| assert(false && "operator unhandled"); |
| return ""; |
| } |
| |
| const char *Bnode::flavstr() const { |
| if (flavor() == N_UnaryOp || flavor() == N_BinaryOp) |
| return opToString(u.op); |
| return BnodeProperties[flavor()].str; |
| } |
| |
| static void indent(llvm::raw_ostream &os, unsigned ilevel) { |
| for (unsigned i = 0; i < ilevel; ++i) |
| os << " "; |
| } |
| |
| void Bnode::dump() |
| { |
| std::string s; |
| llvm::raw_string_ostream os(s); |
| osdump(os, 0, nullptr, false); |
| std::cerr << os.str(); |
| } |
| |
| void Bnode::osdump(llvm::raw_ostream &os, unsigned ilevel, |
| Llvm_linemap *linemap, bool terse) |
| { |
| if (! terse) { |
| if (linemap) { |
| indent(os, ilevel); |
| os << linemap->to_string(location()) << "\n"; |
| linemap = nullptr; // just want top-level src, not src for all kids |
| } |
| } |
| |
| // Basic description of node |
| indent(os, ilevel); |
| os << flavstr() << ": "; |
| |
| // Additional info |
| const Bexpression *expr = castToBexpression(); |
| if (expr) { |
| // expression type |
| os << "[ btype: "; |
| expr->btype()->osdump(os, 0); |
| os << " ] "; |
| } |
| switch(flavor()) { |
| case N_Error: { |
| os << "\n"; |
| return; |
| } |
| case N_Const: { |
| assert(expr); |
| if (expr->value()) |
| expr->value()->print(os); |
| else |
| os << "<nil const>"; |
| os << "\n"; |
| return; |
| } |
| case N_Composite: { |
| assert(expr); |
| if (expr->value() && llvm::isa<llvm::Constant>(expr->value())) { |
| expr->value()->print(os); |
| os << "\n"; |
| return; |
| } |
| break; |
| } |
| case N_FcnAddress: { |
| assert(expr); |
| if (llvm::isa<llvm::Function>(expr->value())) { |
| llvm::Function *f = llvm::cast<llvm::Function>(expr->value()); |
| os << f->getName() << "\n"; |
| return; |
| } |
| break; |
| } |
| case N_LabelAddress: { |
| assert(expr); |
| os << "label_addr " << u.label->label(); |
| break; |
| } |
| case N_Var: { |
| os << "'" << u.var->name() << "' type: "; |
| u.var->btype()->osdump(os, 0); |
| break; |
| } |
| case N_StructField: { |
| os << "field " << u.fieldIndex; |
| break; |
| } |
| case N_BlockStmt: { |
| Bblock *block = castToBblock(); |
| if (block->vars().size()) { |
| os << " { "; |
| unsigned tmps = 0; |
| for (auto &v : block->vars()) { |
| if (v->isTemporary()) { |
| tmps += 1; |
| continue; |
| } |
| os << v->name() << " "; |
| } |
| os << "} "; |
| if (tmps) |
| os << tmps << " tmps"; |
| } |
| break; |
| } |
| case N_LabelStmt: |
| case N_GotoStmt: { |
| os << "label " << u.label->label(); |
| break; |
| } |
| default: break; |
| } |
| if (!terse && expr && expr->varExprPending()) { |
| const VarContext &vc = expr->varContext(); |
| os << " [VP lvalue=" << (vc.lvalue() ? "yes" : "no") |
| << " addrLevel=" << vc.addrLevel() << "]"; |
| } |
| os << "\n"; |
| if (expr) |
| expr->dumpInstructions(os, ilevel, linemap, terse); |
| |
| // Now children |
| for (auto &kid : kids_) |
| kid->osdump(os, ilevel + 2, linemap, terse); |
| } |
| |
| void BnodeBuilder::destroy(Bnode *node, WhichDel which, bool recursive) |
| { |
| std::set<Bnode *> visited; |
| destroyRec(node, which, recursive, visited); |
| } |
| |
| void BnodeBuilder::destroyRec(Bnode *node, |
| WhichDel which, |
| bool recursive, |
| std::set<Bnode *> &visited) |
| { |
| if (visited.find(node) != visited.end()) |
| return; |
| visited.insert(node); |
| if (which != DelWrappers) { |
| Bexpression *expr = node->castToBexpression(); |
| if (expr) { |
| unsigned idx = 0; |
| for (auto inst : expr->instructions()) { |
| integrityVisitor_->unsetParent(inst, expr, idx); |
| inst->dropAllReferences(); |
| idx++; |
| } |
| for (auto inst : expr->instructions()) |
| recordDeadInstruction(inst); |
| expr->clear(); |
| } |
| } |
| if (recursive) |
| for (auto &kid : node->kids_) |
| destroyRec(kid, which, true, visited); |
| if (which != DelInstructions) |
| freeNode(node); |
| } |
| |
| void BnodeBuilder::recordDeadInstruction(llvm::Instruction *inst) |
| { |
| assert(!inst->getParent()); |
| deadInstructions_.push_back(inst); |
| } |
| |
| SwitchDescriptor *Bnode::getSwitchCases() |
| { |
| assert(flavor() == N_SwitchStmt); |
| assert(u.swcases); |
| return u.swcases; |
| } |
| |
| Blabel *Bnode::label() const |
| { |
| assert(flavor() == N_LabelStmt || flavor() == N_GotoStmt || |
| flavor() == N_LabelAddress); |
| return u.label; |
| } |
| |
| Operator Bnode::op() const |
| { |
| assert(flavor() == N_UnaryOp || flavor() == N_BinaryOp); |
| return u.op; |
| } |
| |
| Bvariable *Bnode::var() const |
| { |
| assert(flavor() == N_Var || flavor() == N_ExcepStmt); |
| return u.var; |
| } |
| |
| unsigned Bnode::fieldIndex() const |
| { |
| assert(flavor() == N_StructField); |
| return u.fieldIndex; |
| } |
| |
| Bfunction *Bnode::getFunction() const |
| { |
| assert(flavor() == N_FcnAddress || |
| flavor() == N_Call || |
| flavor() == N_Conditional); |
| return u.func; |
| } |
| |
| Bfunction *Bnode::getCallTarget() const |
| { |
| assert(flavor() == N_Call); |
| if (kids_[0]->flavor() == N_FcnAddress) |
| return kids_[0]->getFunction(); |
| return nullptr; |
| } |
| |
| int Bnode::getIndices() const |
| { |
| assert(flavor() == N_Composite); |
| return u.indices; |
| } |
| |
| //...................................................................... |
| |
| BnodeBuilder::BnodeBuilder(Llvm_backend *be) |
| : integrityVisitor_(new IntegrityVisitor(be, TreeIntegCtl(DumpPointers, DontReportRepairableSharing, IncrementalMode))) |
| , checkIntegrity_(true) |
| , addressSpace_(be->addressSpace()) |
| { |
| } |
| |
| // A note on the llvm::Instruction deletions below: ordinarily, the |
| // expectation would be that any expressions encountered in the |
| // earchive_ loop below would already have had their instructions |
| // transferred to some basic block. If errors were encountered during |
| // the compilation, however there may be unparented expressions whose |
| // instructions are left dangling. If so, we want to delete them here |
| // so as to avoid asserts in the llvmContext destructor. |
| |
| BnodeBuilder::~BnodeBuilder() |
| { |
| freeStmts(); |
| std::vector<llvm::Instruction *> todel; |
| for (auto &expr : earchive_) { |
| if (expr) { |
| for (auto inst : expr->instructions()) { |
| if (inst->getParent() == nullptr) { |
| inst->dropAllReferences(); |
| todel.push_back(inst); |
| } |
| } |
| delete expr; |
| } |
| } |
| for (auto ivpair : tempvars_) { |
| llvm::Instruction *inst = ivpair.first; |
| if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(inst)) { |
| inst = llvm::cast<llvm::Instruction>(ascast->getPointerOperand()); |
| ascast->dropAllReferences(); |
| todel.push_back(ascast); |
| } |
| assert(llvm::isa<llvm::AllocaInst>(inst)); |
| todel.push_back(inst); |
| delete ivpair.second; |
| } |
| for (auto v : todel) |
| v->deleteValue(); |
| for (auto inst : deadInstructions_) |
| inst->deleteValue(); |
| } |
| |
| void BnodeBuilder::freeStmts() |
| { |
| for (auto &stmt : sarchive_) { |
| if (stmt) |
| delete stmt; |
| } |
| sarchive_.clear(); |
| for (auto &c : swcases_) |
| delete c; |
| swcases_.clear(); |
| } |
| |
| void BnodeBuilder::freeNode(Bnode *node) |
| { |
| assert(node); |
| Bexpression *expr = node->castToBexpression(); |
| if (expr) { |
| earchive_[expr->id()] = nullptr; |
| if (expr->id() == earchive_.size()-1) |
| earchive_.pop_back(); |
| delete expr; |
| } else { |
| Bstatement *stmt = node->castToBstatement(); |
| sarchive_[stmt->id()] = nullptr; |
| if (stmt->id() == sarchive_.size()-1) |
| sarchive_.pop_back(); |
| delete stmt; |
| } |
| } |
| |
| void BnodeBuilder::checkTreeInteg(Bnode *node) |
| { |
| if (checkIntegrity_ && !integrityVisitor_->examine(node)) { |
| std::cerr << integrityVisitor_->msg(); |
| assert(false); |
| } |
| } |
| |
| Bexpression *BnodeBuilder::archive(Bexpression *expr) |
| { |
| expr->id_ = earchive_.size(); |
| earchive_.push_back(expr); |
| checkTreeInteg(expr); |
| return expr; |
| } |
| |
| Bstatement *BnodeBuilder::archive(Bstatement *stmt) |
| { |
| stmt->id_ = sarchive_.size(); |
| sarchive_.push_back(stmt); |
| checkTreeInteg(stmt); |
| return stmt; |
| } |
| |
| Bblock *BnodeBuilder::archive(Bblock *bb) |
| { |
| Bstatement *stmt = bb; |
| return static_cast<Bblock*>(archive(stmt)); |
| } |
| |
| Bexpression *BnodeBuilder::mkError(Btype *errortype) |
| { |
| std::vector<Bnode *> kids; |
| Location loc; |
| llvm::Value *noval = nullptr; |
| return archive(new Bexpression(N_Error, kids, noval, errortype, loc)); |
| } |
| |
| Bexpression *BnodeBuilder::mkConst(Btype *btype, llvm::Value *value) |
| { |
| assert(btype); |
| assert(value); |
| std::vector<Bnode *> kids; |
| Location loc; |
| return archive(new Bexpression(N_Const, kids, value, btype, loc)); |
| } |
| |
| Bexpression *BnodeBuilder::mkVoidValue(Btype *btype) |
| { |
| assert(btype); |
| std::vector<Bnode *> kids; |
| Location loc; |
| return archive(new Bexpression(N_Const, kids, nullptr, btype, loc)); |
| } |
| |
| Bexpression *BnodeBuilder::mkVar(Bvariable *var, llvm::Value *val, Location loc) |
| { |
| assert(var); |
| Btype *vt = var->btype(); |
| std::vector<Bnode *> kids; |
| Bexpression *rval = |
| new Bexpression(N_Var, kids, val, vt, loc); |
| rval->u.var = var; |
| return archive(rval); |
| } |
| |
| Bvariable *BnodeBuilder::mkTempVar(Btype *varType, |
| TypeManager *tm, |
| Location loc, |
| const std::string &name) |
| { |
| assert(varType); |
| llvm::Type *typ = varType->type(); |
| llvm::Instruction *insBefore = nullptr; |
| llvm::Align aaAlign = tm->datalayout()->getABITypeAlign(typ); |
| llvm::Value *aaSize = nullptr; |
| llvm::Instruction *inst = new llvm::AllocaInst(typ, 0, aaSize, aaAlign, |
| name, insBefore); |
| Bvariable *tvar = new Bvariable(varType, loc, name, LocalVar, true, inst); |
| tempvars_[inst] = tvar; |
| tvar->markAsTemporary(); |
| return tvar; |
| } |
| |
| Bvariable *BnodeBuilder::adoptTemporaryVariable(llvm::Instruction *alloca) |
| { |
| assert(alloca); |
| auto mit = tempvars_.find(alloca); |
| if (mit == tempvars_.end()) |
| return nullptr; |
| Bvariable *ret = mit->second; |
| tempvars_.erase(mit); |
| return ret; |
| } |
| |
| // This is somewhat unpleasant but necessary due to the way LLVM's |
| // IRBuilder works. If you do something like |
| // |
| // newinst = builder.CreateOr(left, right) |
| // |
| // and it turns out that 'right' is the constant zero, then the |
| // builder will just return left (meaning that we don't want the |
| // instruction to be appended to the new Bexpression). |
| |
| void BnodeBuilder::appendInstIfNeeded(Bexpression *rval, llvm::Value *val) |
| { |
| if (val == nullptr || !llvm::isa<llvm::Instruction>(val)) |
| return; |
| llvm::Instruction *inst = llvm::cast<llvm::Instruction>(val); |
| for (auto &kid : rval->kids_) { |
| Bexpression *expr = kid->castToBexpression(); |
| if (expr && expr->value() == inst) |
| return; |
| } |
| rval->appendInstruction(inst); |
| } |
| |
| Bexpression *BnodeBuilder::mkBinaryOp(Operator op, Btype *typ, llvm::Value *val, |
| Bexpression *left, Bexpression *right, |
| Location loc) |
| { |
| assert(left); |
| assert(right); |
| std::vector<Bnode *> kids = { left, right }; |
| Bexpression *rval = |
| new Bexpression(N_BinaryOp, kids, val, typ, loc); |
| if (val) |
| appendInstIfNeeded(rval, val); |
| rval->u.op = op; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkBinaryOp(Operator op, Btype *typ, llvm::Value *val, |
| Bexpression *left, Bexpression *right, |
| Binstructions &instructions, Location loc) |
| { |
| assert(left); |
| assert(right); |
| std::vector<Bnode *> kids = { left, right }; |
| Bexpression *rval = |
| new Bexpression(N_BinaryOp, kids, val, typ, loc); |
| for (auto &inst : instructions.instructions()) |
| rval->appendInstruction(inst); |
| rval->u.op = op; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkUnaryOp(Operator op, Btype *typ, llvm::Value *val, |
| Bexpression *src, Location loc) |
| { |
| assert(src); |
| std::vector<Bnode *> kids = { src }; |
| Bexpression *rval = |
| new Bexpression(N_UnaryOp, kids, val, typ, loc); |
| rval->u.op = op; |
| appendInstIfNeeded(rval, val); |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkConversion(Btype *typ, llvm::Value *val, |
| Bexpression *src, Location loc) |
| { |
| std::vector<Bnode *> kids = { src }; |
| Bexpression *rval = |
| new Bexpression(N_Conversion, kids, val, typ, loc); |
| appendInstIfNeeded(rval, val); |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkAddress(Btype *typ, llvm::Value *val, |
| Bexpression *src, Location loc) |
| { |
| std::vector<Bnode *> kids = { src }; |
| Bexpression *rval = new Bexpression(N_Address, kids, val, typ, loc); |
| appendInstIfNeeded(rval, val); |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkFcnAddress(Btype *typ, llvm::Value *val, |
| Bfunction *func, Location loc) |
| { |
| std::vector<Bnode *> kids; |
| Bexpression *rval = new Bexpression(N_FcnAddress, kids, val, typ, loc); |
| rval->u.func = func; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkLabelAddress(Btype *typ, |
| llvm::Value *val, |
| Blabel *label, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| Bexpression *rval = new Bexpression(N_LabelAddress, kids, val, typ, loc); |
| rval->u.label = label; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkDeref(Btype *typ, llvm::Value *val, |
| Bexpression *src, Location loc) |
| { |
| std::vector<Bnode *> kids = { src }; |
| Bexpression *rval = new Bexpression(N_Deref, kids, val, typ, loc); |
| return archive(rval); |
| } |
| |
| Bexpression * |
| BnodeBuilder::mkIndexedComposite(Btype *btype, |
| llvm::Value *value, |
| const std::vector<Bexpression *> &vals, |
| const std::vector<unsigned long> &indices, |
| Binstructions &instructions, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| for (auto &v : vals) |
| kids.push_back(v); |
| Bexpression *rval = |
| new Bexpression(N_Composite, kids, value, btype, loc); |
| for (auto &inst : instructions.instructions()) |
| rval->appendInstruction(inst); |
| indexvecs_.push_back(indices); |
| rval->u.indices = indexvecs_.size() - 1; |
| return archive(rval); |
| } |
| |
| Bexpression * |
| BnodeBuilder::mkComposite(Btype *btype, |
| llvm::Value *value, |
| const std::vector<Bexpression *> &vals, |
| Binstructions &instructions, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| for (auto &v : vals) |
| kids.push_back(v); |
| Bexpression *rval = |
| new Bexpression(N_Composite, kids, value, btype, loc); |
| for (auto &inst : instructions.instructions()) |
| rval->appendInstruction(inst); |
| rval->u.indices = -1; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkStructField(Btype *typ, |
| llvm::Value *val, |
| Bexpression *structval, |
| unsigned fieldIndex, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { structval }; |
| Bexpression *rval = |
| new Bexpression(N_StructField, kids, val, typ, loc); |
| appendInstIfNeeded(rval, val); |
| rval->u.fieldIndex = fieldIndex; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkArrayIndex(Btype *typ, |
| llvm::Value *val, |
| Bexpression *arval, |
| Bexpression *index, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { arval, index }; |
| Bexpression *rval = |
| new Bexpression(N_ArrayIndex, kids, val, typ, loc); |
| appendInstIfNeeded(rval, val); |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkPointerOffset(Btype *typ, |
| llvm::Value *val, |
| Bexpression *ptr, |
| Bexpression *offset, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { ptr, offset }; |
| Bexpression *rval = |
| new Bexpression(N_PointerOffset, kids, val, typ, loc); |
| appendInstIfNeeded(rval, val); |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkCompound(Bstatement *st, |
| Bexpression *expr, |
| llvm::Value *val, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { st, expr }; |
| Bexpression *rval = |
| new Bexpression(N_Compound, kids, val, expr->btype(), loc); |
| rval->setTag(expr->tag()); |
| return archive(rval); |
| } |
| |
| static bool isAlloca(llvm::Value *val) |
| { |
| if (llvm::isa<llvm::AllocaInst>(val)) |
| return true; |
| if (auto *ascast = llvm::dyn_cast<llvm::AddrSpaceCastInst>(val)) |
| return llvm::isa<llvm::AllocaInst>(ascast->getPointerOperand()); |
| return false; |
| } |
| |
| Bexpression *BnodeBuilder::mkCall(Btype *btype, |
| llvm::Value *val, |
| Bfunction *caller, |
| Bexpression *fnExpr, |
| Bexpression *chainExpr, |
| const std::vector<Bexpression *> &args, |
| Binstructions &instructions, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| kids.push_back(fnExpr); |
| kids.push_back(chainExpr); |
| for (auto &a : args) |
| kids.push_back(a); |
| Bexpression *rval = |
| new Bexpression(N_Call, kids, val, btype, loc); |
| bool found = false; |
| for (auto &inst : instructions.instructions()) { |
| if (inst == val) |
| found = true; |
| rval->appendInstruction(inst); |
| } |
| if (!found && val && !isAlloca(val)) |
| appendInstIfNeeded(rval, val); |
| rval->u.func = caller; |
| return archive(rval); |
| } |
| |
| Bexpression *BnodeBuilder::mkConditional(Bfunction *function, |
| Btype *btype, |
| Bexpression *condition, |
| Bexpression *then_expr, |
| Bexpression *else_expr, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| kids.push_back(condition); |
| kids.push_back(then_expr); |
| if (else_expr) |
| kids.push_back(else_expr); |
| Bexpression *rval = |
| new Bexpression(N_Conditional, kids, nullptr, btype, loc); |
| rval->u.func = function; |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkErrorStmt() |
| { |
| assert(! errorStatement_.get()); |
| std::vector<Bnode *> kids; |
| errorStatement_.reset(new Bstatement(N_Error, nullptr, kids, Location())); |
| return errorStatement_.get(); |
| } |
| |
| Bstatement *BnodeBuilder::mkExprStmt(Bfunction *func, |
| Bexpression *expr, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { expr }; |
| Bstatement *rval = new Bstatement(N_ExprStmt, func, kids, loc); |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkReturn(Bfunction *func, |
| Bexpression *returnVal, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { returnVal }; |
| Bstatement *rval = new Bstatement(N_ReturnStmt, func, kids, loc); |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkLabelDefStmt(Bfunction *func, |
| Blabel *label, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| Bstatement *rval = new Bstatement(N_LabelStmt, func, kids, loc); |
| rval->u.label = label; |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkGotoStmt(Bfunction *func, |
| Blabel *label, |
| Location loc) |
| { |
| std::vector<Bnode *> kids; |
| Bstatement *rval = new Bstatement(N_GotoStmt, func, kids, loc); |
| rval->u.label = label; |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkIfStmt(Bfunction *func, |
| Bexpression *cond, Bblock *trueBlock, |
| Bblock *falseBlock, Location loc) |
| { |
| if (falseBlock == nullptr) |
| falseBlock = mkBlock(func, std::vector<Bvariable *>(), loc); |
| std::vector<Bnode *> kids = { cond, trueBlock, falseBlock }; |
| Bstatement *rval = new Bstatement(N_IfStmt, func, kids, loc); |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkDeferStmt(Bfunction *func, |
| Bexpression *undefer, |
| Bexpression *defer, |
| Location loc) |
| { |
| assert(func); |
| assert(undefer); |
| assert(defer); |
| std::vector<Bnode *> kids = { undefer, defer }; |
| Bstatement *rval = new Bstatement(N_DeferStmt, func, kids, loc); |
| return archive(rval); |
| } |
| |
| Bstatement *BnodeBuilder::mkExcepStmt(Bfunction *func, |
| Bstatement *body, |
| Bstatement *onexception, |
| Bstatement *finally, |
| Bvariable *finTempVar, |
| Location loc) |
| { |
| assert(body); |
| assert(onexception); |
| assert(finally); |
| assert(finTempVar); |
| std::vector<Bnode *> kids = { body, onexception, finally }; |
| Bstatement *rval = new Bstatement(N_ExcepStmt, func, kids, loc); |
| rval->u.var = finTempVar; |
| return archive(rval); |
| } |
| |
| SwitchDescriptor::SwitchDescriptor(const std::vector<std::vector<Bexpression *> > &vals) |
| { |
| // Determine child index of first stmt |
| unsigned stidx = 1; |
| for (auto &vvec : vals) |
| stidx += vvec.size(); |
| // Construct case descriptors |
| unsigned idx = 1; |
| for (auto &vvec : vals) { |
| cases_.push_back(SwitchCaseDesc(idx, vvec.size(), stidx++)); |
| idx += vvec.size(); |
| } |
| } |
| |
| Bstatement *BnodeBuilder::mkSwitchStmt(Bfunction *func, |
| Bexpression *swvalue, |
| const std::vector<std::vector<Bexpression *> > &vals, |
| const std::vector<Bstatement *> &stmts, |
| Location loc) |
| { |
| std::vector<Bnode *> kids = { swvalue }; |
| for (auto &vvec : vals) |
| for (auto &v : vvec) |
| kids.push_back(v); |
| |
| for (auto &st : stmts) |
| // For cases that has only a fallthrough statement, the FE passes a null |
| // block. Having a null node in the tree may cause various problems. |
| // Make an empty block instead. |
| kids.push_back(st ? st : mkBlock(func, {}, loc)); |
| |
| SwitchDescriptor *d = new SwitchDescriptor(vals); |
| swcases_.push_back(d); |
| Bstatement *rval = new Bstatement(N_SwitchStmt, func, kids, loc); |
| rval->u.swcases = d; |
| return archive(rval); |
| |
| } |
| |
| Bblock *BnodeBuilder::mkBlock(Bfunction *func, |
| const std::vector<Bvariable *> &vars, |
| Location loc) |
| { |
| Bblock *rval = new Bblock(func, vars, loc); |
| return archive(rval); |
| } |
| |
| void BnodeBuilder::addStatementToBlock(Bblock *block, Bstatement *st) |
| { |
| assert(block); |
| assert(st); |
| block->kids_.push_back(st); |
| } |
| |
| Bexpression * |
| BnodeBuilder::cloneSub(Bexpression *expr, |
| std::map<llvm::Value *, llvm::Value *> &vm) |
| { |
| assert(expr); |
| std::vector<Bnode *> newChildren; |
| for (auto &c : expr->children()) { |
| assert(c); |
| Bexpression *ce = c->castToBexpression(); |
| assert(ce); |
| Bexpression *clc = cloneSubtree(ce); |
| newChildren.push_back(clc); |
| } |
| Bexpression *res = new Bexpression(*expr); |
| res->kids_ = newChildren; |
| archive(res); |
| |
| llvm::Value *iv = expr->value(); |
| llvm::Value *newv = nullptr; |
| for (auto inst : expr->instructions()) { |
| if (inst == iv) { |
| assert(! newv); |
| newv = inst; |
| } |
| llvm::Instruction *icl = inst->clone(); |
| unsigned nops = inst->getNumOperands(); |
| for (unsigned idx = 0; idx < nops; ++idx) { |
| llvm::Value *v = inst->getOperand(idx); |
| auto it = vm.find(v); |
| if (it != vm.end()) { |
| llvm::Value *cv = it->second; |
| icl->setOperand(idx, cv); |
| } |
| } |
| icl->setName(inst->getName()); |
| res->appendInstruction(icl); |
| } |
| if (newv) |
| res->setValue(newv); |
| return res; |
| } |
| |
| Bexpression *BnodeBuilder::cloneSubtree(Bexpression *expr) { |
| std::map<llvm::Value *, llvm::Value *> vm; |
| return cloneSub(expr, vm); |
| } |
| |
| std::vector<Bexpression *> |
| BnodeBuilder::extractChildenAndDestroy(Bexpression *expr) |
| { |
| assert(expr); |
| assert(expr->value() == nullptr); |
| assert(expr->instructions().empty()); |
| std::vector<Bexpression *> orphanExprs; |
| std::vector<Bnode *> orphanNodes = extractChildNodesAndDestroy(expr); |
| for (auto k : orphanNodes) { |
| Bexpression *ekid = k->castToBexpression(); |
| assert(ekid); |
| orphanExprs.push_back(ekid); |
| } |
| return orphanExprs; |
| } |
| |
| std::vector<Bnode *> |
| BnodeBuilder::extractChildNodesAndDestroy(Bnode *node) |
| { |
| std::vector<Bnode *> orphans; |
| assert(node); |
| for (unsigned idx = 0; idx < node->kids_.size(); ++idx) { |
| Bnode *kid = node->kids_[idx]; |
| integrityVisitor_->unsetParent(kid, node, idx); |
| orphans.push_back(kid); |
| } |
| integrityVisitor_->deletePending(node); |
| Bexpression *expr = node->castToBexpression(); |
| assert(expr); // statements not yet supported, could be if needed |
| freeNode(expr); |
| return orphans; |
| } |
| |
| const std::vector<unsigned long> *BnodeBuilder::getIndices(Bexpression *expr) const |
| { |
| int i = expr->getIndices(); |
| if (i < 0) |
| return nullptr; |
| assert((unsigned)i < indexvecs_.size()); |
| return &indexvecs_[i]; |
| } |
| |
| void BnodeBuilder::updateInstructions(Bexpression *expr, |
| std::vector<llvm::Instruction*> newinsts) |
| { |
| assert(expr->instructions().size() >= newinsts.size()); |
| llvm::Instruction *deleteAfter = nullptr; |
| unsigned newSize = newinsts.size(); |
| unsigned idx = 0; |
| |
| for (auto originst : expr->instructions()) { |
| if (idx >= newSize) { |
| if (idx == newSize) |
| deleteAfter = originst; |
| integrityVisitor_->unsetParent(originst, expr, idx); |
| } else { |
| llvm::Instruction *newinst = newinsts[idx]; |
| if (originst != newinst) { |
| integrityVisitor_->unsetParent(originst, expr, idx); |
| integrityVisitor_->setParent(newinst, expr, idx); |
| if (expr->value() == originst) |
| expr->setValue(newinst); |
| } |
| } |
| idx++; |
| } |
| if (deleteAfter != nullptr) { |
| std::vector<llvm::Instruction *> todel = |
| expr->extractInstsAfter(deleteAfter); |
| for (auto it = todel.rbegin(); it != todel.rend(); ++it) { |
| auto victim = *it; |
| victim->deleteValue(); |
| } |
| } |
| } |
| |
| void BnodeBuilder::updateValue(Bexpression *expr, llvm::Value *newValue) |
| { |
| assert(expr != NULL && newValue != NULL && expr->value() != NULL); |
| |
| // New value should be different from old value |
| assert(expr->value() != newValue); |
| |
| // Types should match |
| assert(expr->value()->getType() == newValue->getType()); |
| |
| expr->setValue(newValue); |
| } |