blob: be07427be7c31e83bcec503d2865c9e788a96f59 [file] [log] [blame]
//===-- go-llvm-bnode.h - decls for '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.
//
//===----------------------------------------------------------------------===//
//
// Defines Bnode class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVMGOFRONTEND_BNODE_H
#define LLVMGOFRONTEND_BNODE_H
// Currently these need to be included before backend.h
#include "go-llvm-linemap.h"
#include "go-location.h"
#include "go-llvm-btype.h"
#include "backend.h"
namespace llvm {
class AllocaInst;
class Instruction;
class Value;
class raw_ostream;
}
// Opaque labelID handle for goto/label nodes.
typedef unsigned LabelId;
class Bexpression;
class Binstructions;
class SwitchDescriptor;
class IntegrityVisitor;
class Llvm_backend;
class TypeManager;
// Use when deleting a Bnode subtree. Controls whether to delete just
// the Bnode objects, just the LLVM instructions they contain, or both.
//
enum WhichDel {
DelInstructions, // delete only instructions
DelWrappers, // delete only wrappers
DelBoth // delete wrappers and instructions
};
// Varieties of nodes. Order is important here, since enum
// values are used to index into tables/arrays.
enum NodeFlavor {
N_Error=0,
N_FirstExpr,
N_Const=N_FirstExpr,
N_Var,
N_FcnAddress,
N_LabelAddress,
N_Conversion,
N_Deref,
N_Address,
N_UnaryOp,
N_StructField,
N_BinaryOp,
N_Compound,
N_ArrayIndex,
N_PointerOffset,
N_Composite,
N_Call,
N_Conditional,
N_LastExpr = N_Conditional,
N_FirstStmt,
N_EmptyStmt=N_FirstStmt,
N_LabelStmt,
N_GotoStmt,
N_ExprStmt,
N_ReturnStmt,
N_DeferStmt,
N_IfStmt,
N_ExcepStmt,
N_BlockStmt,
N_SwitchStmt,
N_LastStmt=N_SwitchStmt
};
// For use by callbacks invoked by UpdatingNodeWalker. A return value
// of StopWalk will terminate the walk immediately after the callback
// in question; a ContineWalk return means that the visitor will continue
// on normally.
enum VisitDisp {
ContinueWalk, StopWalk
};
// Used by the visitChildPre callback invoked by UpdatingNodeWalker,
// to allow specific subtrees to be skipped. A return value of
// VisitChild tells the walker to go ahead and visit all of the nodes
// in the child subtree; a value of SkipChild means that none of the
// nodes in the child subtree will be visited. Example (from go code:
// x.f[v]+10):
//
// +
// / \
// array_index 10
// / \
// field var v
// /
// var x
//
// Suppose the visitChildPre(array_index, field) returns SkipChild --
// in this case no other callbacks will be invoked for the "field" or
// the "var x" nodes, but all other nodes will be walked (ex: "var v").
enum VisitChildDisp {
VisitChild, SkipChild
};
enum VisitOrder {
PreOrder, PostOrder
};
enum VisitWhich {
VisitExprs, VisitStmts, VisitAll
};
// Class Bnode -- this class is not directly used or referred to in
// gofrontend, but it acts as an abstract base for the Bexpression and
// Bstatement classes. A given Bnode has zero or more Bnode children;
// the number and type (expr or stmt) children are determined by
// the Bnode flavor.
class Bnode {
public:
virtual ~Bnode() { }
NodeFlavor flavor() const { return flavor_; }
Location location() const { return location_; }
unsigned id() const { return id_; }
const char *flavstr() const;
Blabel *label() const;
// debugging
void dump();
// dump with source line info
void srcDump(Llvm_linemap *);
// dump to raw_ostream
void osdump(llvm::raw_ostream &os, unsigned ilevel = 0,
Llvm_linemap *linemap = nullptr, bool terse = false);
// Cast to Bexpression. Returns NULL if not correct flavor.
Bexpression *castToBexpression() const;
// Cast to Bstatement. Returns NULL if not correct flavor.
Bstatement *castToBstatement() const;
// Cast to Bblock. Returns NULL if not correct flavor.
Bblock *castToBblock() const;
// For unary and binary op expression nodes, this returns the opcode
Operator op() const;
// For var exprs, this returns the underlying bvariable
Bvariable *var() const;
// Return struct field index for a field expr
unsigned fieldIndex() const;
// Return function associated with this node. Value only for
// function constants, calls, and conditionals. Note that for calls and
// conditionals this will be the function containing the construct.
Bfunction *getFunction() const;
// Return target function for call (will be nil if indirect call).
Bfunction *getCallTarget() const;
// Return index to BnodeBuilder's vector of indices for composite,
// or -1 if no indexing.
int getIndices() const;
template<class Visitor> friend class SimpleNodeWalker;
template<class Visitor> friend class UpdatingNodeWalker;
friend class BnodeBuilder;
friend class IntegrityVisitor;
// TODO: hide this once GenBlocksVisitor is working
const std::vector<Bnode *> &children() const { return kids_; }
bool isStmt() const {
return (flavor() >= N_FirstStmt && flavor() <= N_LastStmt);
}
protected:
Bnode(NodeFlavor flavor, const std::vector<Bnode *> &kids, Location loc);
Bnode(const Bnode &src);
SwitchDescriptor *getSwitchCases();
private:
void replaceChild(unsigned idx, Bnode *newchild);
private:
std::vector<Bnode *> kids_;
union {
Bvariable *var; // filled in only for Var and ExcepStmt nodes
Bfunction *func; // filled in only for fcn constants, calls, conditionals
SwitchDescriptor *swcases;
int indices; // for composite expressions, index to BnodeBuilder's indexvecs_
Blabel *label;
Operator op;
unsigned fieldIndex;
} u;
Location location_;
NodeFlavor flavor_;
unsigned id_;
unsigned flags_;
};
// This helper class handles construction for all Bnode objects.
// Notes on storage allocation: ideally once an LLVM function has been
// constructed and sent off to the back end for a given Go function,
// we would want to delete all of the Bexpression's used by that
// function before moving on to the next function. Putting this into
// practice is tricky, however, since some Bexpressions (for example,
// var exprs and function addresses) wind up being held over and
// reused else where (for example, in emitted GC descriptors). For the
// moment we don't try to free Bexpressions at intermediate points
// in the compilation, but we do free Bstatements.
class BnodeBuilder {
public:
BnodeBuilder(Llvm_backend *be);
~BnodeBuilder();
// Deletes all allocated Bstatements (also switch descriptors)
void freeStmts();
// expressions
Bexpression *mkError(Btype *errortype);
Bexpression *mkConst(Btype *btype, llvm::Value *val);
Bexpression *mkVoidValue(Btype *voidType);
Bexpression *mkVar(Bvariable *var, llvm::Value *val, Location loc);
Bexpression *mkConversion(Btype *btype, llvm::Value *val,
Bexpression *src, Location loc);
Bexpression *mkDeref(Btype *typ, llvm::Value *val,
Bexpression *src, Location loc);
Bexpression *mkAddress(Btype *typ, llvm::Value *val,
Bexpression *src, Location loc);
Bexpression *mkFcnAddress(Btype *typ, llvm::Value *val,
Bfunction *func, Location loc);
Bexpression *mkLabelAddress(Btype *typ, llvm::Value *val,
Blabel *label, Location loc);
Bexpression *mkUnaryOp(Operator op, Btype *typ, llvm::Value *val,
Bexpression *src, Location loc);
Bexpression *mkBinaryOp(Operator op, Btype *typ, llvm::Value *val,
Bexpression *left, Bexpression *right, Location loc);
Bexpression *mkBinaryOp(Operator op, Btype *typ, llvm::Value *val,
Bexpression *left, Bexpression *right,
Binstructions &instructions, Location loc);
Bexpression *mkCompound(Bstatement *st, Bexpression *expr, llvm::Value *val,
Location loc);
Bexpression *mkStructField(Btype *type, llvm::Value *value,
Bexpression *structval, unsigned fieldIndex,
Location loc);
Bexpression *mkArrayIndex(Btype *typ,
llvm::Value *val,
Bexpression *arval,
Bexpression *index,
Location loc);
Bexpression *mkPointerOffset(Btype *typ,
llvm::Value *val,
Bexpression *ptr,
Bexpression *offset,
Location loc);
Bexpression *mkIndexedComposite(Btype *btype, llvm::Value *value,
const std::vector<Bexpression *> &vals,
const std::vector<unsigned long> &indices,
Binstructions &instructions,
Location loc);
Bexpression *mkComposite(Btype *btype, llvm::Value *value,
const std::vector<Bexpression *> &vals,
Binstructions &instructions,
Location loc);
Bexpression *mkCall(Btype *btype,
llvm::Value *value,
Bfunction *caller,
Bexpression *fnExpr,
Bexpression *chainExpr,
const std::vector<Bexpression *> &vals,
Binstructions &instructions,
Location loc);
Bexpression *mkConditional(Bfunction *function,
Btype *btype,
Bexpression *condition,
Bexpression *then_expr,
Bexpression *else_expr,
Location loc);
// statements
Bstatement *mkErrorStmt();
Bstatement *mkLabelDefStmt(Bfunction *func, Blabel *label, Location loc);
Bstatement *mkGotoStmt(Bfunction *func, Blabel *label, Location loc);
Bstatement *mkExprStmt(Bfunction *func, Bexpression *expr, Location loc);
Bstatement *mkReturn(Bfunction *func, Bexpression *returnVal, Location loc);
Bstatement *mkIfStmt(Bfunction *func,
Bexpression *cond, Bblock *trueBlock,
Bblock *thenBlock, Location loc);
Bstatement *mkDeferStmt(Bfunction *func,
Bexpression *undefer,
Bexpression *defer,
Location loc);
Bstatement *mkExcepStmt(Bfunction *func,
Bstatement *body,
Bstatement *onexception,
Bstatement *finally,
Bvariable *finTempVar,
Location loc);
Bstatement *mkSwitchStmt(Bfunction *func,
Bexpression *swvalue,
const std::vector<std::vector<Bexpression *> > &vals,
const std::vector<Bstatement *> &stmts,
Location loc);
// block
Bblock *mkBlock(Bfunction *func,
const std::vector<Bvariable *> &vars,
Location loc);
void addStatementToBlock(Bblock *block, Bstatement *st);
// Create a temporary variable of the specified type, to be
// incorporated into some expression we're building. Although this
// strictly speaking doesn't relate to Bnode building, it helps to
// have this interface here, since IR construction methods will
// always have access to a BnodeBuilder but may not have the current
// function we're processing.
Bvariable *mkTempVar(Btype *varType, TypeManager *tm,
Location loc, const std::string &name);
// This helper looks up the specified variable (as identified by its
// alloca) to see if it is an unparented temp created during IR
// construction (via the mkTempVar method above). If so, the
// variable is extracted from the builder's set and returned. If the
// var is not an unadopted temp, then NULL is returned.
Bvariable *adoptTemporaryVariable(llvm::Instruction *alloca);
// Free up this expr or stmt (it is garbage). Does not free up children.
void freeNode(Bnode *node);
// Delete some or all or this Bnode and its children/contents.
// Here 'which' can be set to DelInstructions (deletes only the
// LLVM instructions found in the subtree), DelWrappers (deletes
// Bnodes but not instructions), or DelBoth (gets rid of nodes and
// instructions).
// If recursive is true, also delete its children recursively.
void destroy(Bnode *node, WhichDel which = DelWrappers,
bool recursive = true);
// Clone an expression subtree.
Bexpression *cloneSubtree(Bexpression *expr);
// Get the indices of a composite expression.
const std::vector<unsigned long> *getIndices(Bexpression *expr) const;
// Inform the builder that we're about to extract all of the
// children of the specified node and incorporate them into a new
// node (after which the old node will be thrown away). Returns vector
// containing expression children.
std::vector<Bexpression *> extractChildenAndDestroy(Bexpression *expr);
// Similar to the routine above, but works at the Bnode level (so as
// to support dealing with Bnodes that have a mix of statement/expr
// children).
std::vector<Bnode *> extractChildNodesAndDestroy(Bnode *node);
// Update the instructions of an expression node after we've finished
// walking its instruction list. During the walk it's possible that
// A) one or more existing instructions were converted into new
// instructions, or B) some instructions were thrown away because
// they appeared after a no-return call in the list. This helper
// fixes up the instruction list and updates ownership info
// in the integrity checker.
void updateInstructions(Bexpression *expr,
std::vector<llvm::Instruction*> newinsts);
// Similar to the above, but used in cases where a parent Bexpression
// inherits the value from a child, and the child's value is rewritten
// to some new value (simpler case here, since there is no book-keeping
// update needed for the integrity checker).
void updateValue(Bexpression *expr, llvm::Value *newValue);
// Get/set whether the tree integrity checker is enabled. It makes sense
// to turn off the integrity checker during tree cloning operations
// (part of sharing repair), and also for unit testing.
bool integrityChecksEnabled() const { return checkIntegrity_; }
void setIntegrityChecks(bool val) { checkIntegrity_ = val; }
private:
void appendInstIfNeeded(Bexpression *rval, llvm::Value *val);
Bexpression *archive(Bexpression *expr);
Bstatement *archive(Bstatement *stmt);
Bblock *archive(Bblock *bb);
Bexpression *cloneSub(Bexpression *expr,
std::map<llvm::Value *, llvm::Value *> &vm);
void checkTreeInteg(Bnode *node);
void destroyRec(Bnode *node, WhichDel which, bool recursive,
std::set<Bnode *> &visited);
void recordDeadInstruction(llvm::Instruction *inst);
private:
std::unique_ptr<Bstatement> errorStatement_;
std::vector<Bexpression *> earchive_;
std::vector<Bstatement *> sarchive_;
std::vector<SwitchDescriptor*> swcases_;
std::vector< std::vector<unsigned long> > indexvecs_;
std::unordered_map<llvm::Instruction*, Bvariable*> tempvars_;
std::unique_ptr<IntegrityVisitor> integrityVisitor_;
bool checkIntegrity_;
std::vector<llvm::Instruction*> deadInstructions_;
unsigned addressSpace_;
};
// This class helps automate walking of a Bnode subtree; it invokes
// callbacks in the supplied visitor object at useful points during
// the walk that is instigated by 'simple_walk_nodes' below.
template<class Visitor>
class SimpleNodeWalker {
public:
SimpleNodeWalker(Visitor &vis) : visitor_(vis) { }
void walk(Bnode *node) {
assert(node);
// pre-node hook
visitor_.visitNodePre(node);
// walk children
for (unsigned idx = 0; idx < node->children().size(); ++idx) {
Bnode *child = node->children()[idx];
walk(child);
}
// post-node hook
visitor_.visitNodePost(node);
}
private:
Visitor &visitor_;
};
template<class Visitor>
inline void simple_walk_nodes(Bnode *root, Visitor &vis) {
SimpleNodeWalker<Visitor> walker(vis);
walker.walk(root);
}
// A more complicated node walker that allows for replacement of child
// nodes, plus stopping the walk if the visitor so decides.
template<class Visitor>
class UpdatingNodeWalker {
public:
UpdatingNodeWalker(Visitor &vis) : visitor_(vis) { }
std::pair<VisitDisp, Bnode *> walk(Bnode *node) {
assert(node);
// pre-node hook
auto pairPre = visitor_.visitNodePre(node);
if (pairPre.second != node)
node = pairPre.second;
if (pairPre.first == StopWalk)
return std::make_pair(StopWalk, node);
std::vector<Bnode *> children = node->children();
for (unsigned idx = 0; idx < children.size(); ++idx) {
Bnode *child = children[idx];
// pre-child hook. Return value here is
// std::pair< std::pair<VisitDisp, VisitChildDisp>, Bnode *>
auto res = visitor_.visitChildPre(node, child);
Bnode *newChild = res.second;
std::pair<VisitDisp, VisitChildDisp> disp = res.first;
enum VisitDisp walkDisp = disp.first;
enum VisitChildDisp childDisp = disp.second;
if (newChild != child) {
node->replaceChild(idx, newChild);
child = newChild;
}
if (walkDisp == StopWalk)
return std::make_pair(StopWalk, node);
// walk child (unless we've been told to skip it)
if (childDisp != SkipChild) {
auto pairChild = walk(child);
if (pairChild.second != child) {
node->replaceChild(idx, pairChild.second);
child = pairChild.second;
}
if (pairChild.first == StopWalk)
return std::make_pair(StopWalk, node);
}
// post-child hook
auto pairPost = visitor_.visitChildPost(node, child);
if (pairPost.second != child)
node->replaceChild(idx, pairPost.second);
if (pairPost.first == StopWalk)
return std::make_pair(StopWalk, node);
}
// post-node hook
auto pairPost = visitor_.visitNodePost(node);
if (pairPost.second != node)
node = pairPost.second;
if (pairPost.first == StopWalk)
return std::make_pair(StopWalk, node);
return std::make_pair(ContinueWalk, node);
}
private:
Visitor &visitor_;
};
template<class Visitor>
inline Bnode *update_walk_nodes(Bnode *root, Visitor &vis) {
UpdatingNodeWalker<Visitor> walker(vis);
auto p = walker.walk(root);
return p.second;
}
// Helper contained class for recording the child indices of switch
// cases and switch statements within the containing Bnode. Here 'st'
// is the child index of the first case value, 'len' is the number of
// matching values for the case, and 'stmt' is the child index of the
// corresponding statement. Example:
//
// switch q { Switch Bnode children:
// case 2, 3, 4: 0: expr q
// return x + y 1: expr 2
// default: 2: expr 3
// return x / y 3: expr 4
// case 5: 4: expr 5
// return 9 5: stmt return x + y
// } 6: stmt return x / y
// 7: stmt return 9
// Case descriptors:
// 1, 3, 5
// 4, 0, 6
// 5, 1, 7
struct SwitchCaseDesc {
unsigned st, len, stmt;
SwitchCaseDesc(unsigned s, unsigned l, unsigned st)
: st(s), len(l), stmt(st) { }
};
// Stores child index info about a switch.
class SwitchDescriptor {
public:
SwitchDescriptor(const std::vector<std::vector<Bexpression *> > &vals);
const std::vector<SwitchCaseDesc> &cases() const { return cases_; }
private:
std::vector<SwitchCaseDesc> cases_;
};
#endif // LLVMGOFRONTEND_GO_BNODE_H