blob: 519fe857f600ecb30d44a497c74a46bfc540909b [file] [log] [blame]
//===-- go-llvm-tree-integrity.h - decls for tree integrity utils ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines IntegrityVisitor class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVMGOFRONTEND_GO_LLVM_TREE_INTEGRITY_H
#define LLVMGOFRONTEND_GO_LLVM_TREE_INTEGRITY_H
#include "llvm/Support/raw_ostream.h"
namespace llvm {
class Instruction;
}
class Bstatement;
class Bexpression;
class Llvm_backend;
class BnodeBuilder;
enum CkTreePtrDisp { DumpPointers, NoDumpPointers };
enum CkTreeVarDisp { CheckVarExprs, IgnoreVarExprs };
enum CkTreeRepairDisp { DontReportRepairableSharing, ReportRepairableSharing };
enum CkTreeVisitDisp { BatchMode, IncrementalMode };
// Options/controls for the tree integrity checker.
struct TreeIntegCtl {
CkTreePtrDisp ptrDisp;
CkTreeVarDisp varDisp;
CkTreeRepairDisp repairDisp;
CkTreeVisitDisp visitDisp;
TreeIntegCtl()
: ptrDisp(DumpPointers),
varDisp(CheckVarExprs),
repairDisp(ReportRepairableSharing),
visitDisp(BatchMode) { }
TreeIntegCtl(CkTreePtrDisp ptrs, CkTreeVarDisp vars,
CkTreeRepairDisp repair, CkTreeVisitDisp visit)
: ptrDisp(ptrs), varDisp(vars),
repairDisp(repair), visitDisp(visit) { }
};
// This visitor class detects malformed IR trees, specifically cases
// where the same Bexpression or Bstatement is pointed to by more than
// containing expr/stmt. For example suppose we have a couple of assignment
// statements
//
// x = y
// z = y
//
// where the Bexpression corresponding to "y" is pointed to both by
// the first assignment stmt and by the second assignment stmt.
//
// It is worth noting that some sharing is allowed in Bexpression trees,
// specifically sharing of Bexpressions corresponding to module-scope
// constants.
//
// In addition, we provide a mode of the checker ("IgnoreVarExprs"
// above) in which we don't try to enforce sharing for var exprs,
// since gofrontend tends to reuse them in a number of places. The
// one place where this can cause issues is with nested functions and
// closures. Example:
//
// func simple(x, q int) int {
// pf := func(xx int) int {
// qm2 := q - 2
// qm1 := q - 1
// return xx + qm2 + qm1
// }
// return pf(x)
// }
//
// Note the references to "q" within the nested function -- in a
// non-nested function these woule be simple var expressions, however
// in the case above they will appear to the backend as loads of
// fields from the closure pointer passed via the static chain. To
// support this case there is yet another option/control that tells
// the checker to "unshare" the nodes in question (clone them to
// restore tree integrity). This unsharing/repairing is applied only
// to a whitelisted set of expression nodes (for example, cloning of
// call expressions is not allowed).
class IntegrityVisitor {
public:
IntegrityVisitor(Llvm_backend *be,
TreeIntegCtl control)
: be_(be), ss_(str_), control_(control),
instShareCount_(0), stmtShareCount_(0), exprShareCount_(0) { }
// Visits the node tree "n", looking for any shared nodes or
// instructions. Returns TRUE if there is no sharing (or if all
// sharing instances are repairable and repair is on), or FALSE if
// there is unrepairable sharing. Note that if the visit mode (in
// 'control' options above) is set to BatchMode, then the visitor
// will walk the entire subtree rooted at "n" and perform repairs
// after the way. Otherwise (checkers is in incremental mode), only
// the chidren of "n" are visited, and not repairs are performed.
bool examine(Bnode *n);
// Returns a message describing the nature of the node sharing (intended
// for a developer, not a compiler user).
std::string msg() { auto rv = ss_.str(); str_ = ""; return rv; }
// Tell the IntegrityVisitor to forget about the specified parent/child
// relationship (used when node child is deleted or repurposed).
void unsetParent(Bnode *child, Bnode *parent, unsigned slot);
private:
Llvm_backend *be_;
typedef std::pair<Bnode *, unsigned> parslot; // parent and child index
std::unordered_map<llvm::Instruction *, parslot> iparent_;
std::unordered_map<Bnode *, parslot> nparent_;
std::vector<std::pair<Bnode *, parslot> > sharing_; // child -> parent+slot
std::string str_;
llvm::raw_string_ostream ss_;
TreeIntegCtl control_;
unsigned instShareCount_;
unsigned stmtShareCount_;
unsigned exprShareCount_;
private:
bool repair(Bnode *n);
void visit(Bnode *n);
bool repairableSubTree(Bexpression *root);
bool shouldBeTracked(Bnode *child);
void setParent(Bnode *child, Bnode *parent, unsigned slot);
void setParent(llvm::Instruction *inst, Bexpression *par, unsigned slot);
void dumpTag(const char *tag, void *ptr);
void dump(llvm::Instruction *inst);
void dump(Bnode *node);
CkTreePtrDisp includePointers() const { return control_.ptrDisp; }
CkTreeVarDisp includeVarExprs() const { return control_.varDisp; }
CkTreeRepairDisp doRepairs() const { return control_.repairDisp; }
CkTreeVisitDisp visitMode() const { return control_.visitDisp; }
friend BnodeBuilder;
};
#endif // LLVMGOFRONTEND_GO_LLVM_TREE_INTEGRITY_H