gollvm: introduce nil check size threshold
Support for configuring size threshold for nil checks introduced
by the front end (requires corresponding gofrontend change
https://go-review.googlesource.com/c/gofrontend/+/80996).
Also in this patch is a small set of tweaks to the tree integrity
checker (better unit tests, support for unsharing conditional exprs).
Change-Id: Id8aef7db97c7b463ac4512b9551c2d96e2f7fe24
Reviewed-on: https://go-review.googlesource.com/81475
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/goparse-llvm.cpp b/goparse-llvm.cpp
index 0fcf275..0d8548b 100644
--- a/goparse-llvm.cpp
+++ b/goparse-llvm.cpp
@@ -383,6 +383,7 @@
args.check_divide_overflow = CheckDivideOverflow;
args.compiling_runtime = CompilingRuntime;
args.debug_escape_level = EscapeDebugLevel;
+ args.nil_check_size_threshold = -1;
args.linemap = linemap_.get();
args.backend = bridge_.get();
go_create_gogo (&args);
diff --git a/llvm-gofrontend/go-c.h b/llvm-gofrontend/go-c.h
index 213efe0..74a3f58 100644
--- a/llvm-gofrontend/go-c.h
+++ b/llvm-gofrontend/go-c.h
@@ -47,6 +47,7 @@
bool check_divide_overflow;
bool compiling_runtime;
int debug_escape_level;
+ int64_t nil_check_size_threshold;
};
extern void go_create_gogo (const struct go_create_gogo_args*);
diff --git a/llvm-gofrontend/go-llvm-bnode.cpp b/llvm-gofrontend/go-llvm-bnode.cpp
index 6190aa0..d0e92b0 100644
--- a/llvm-gofrontend/go-llvm-bnode.cpp
+++ b/llvm-gofrontend/go-llvm-bnode.cpp
@@ -354,6 +354,14 @@
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);
diff --git a/llvm-gofrontend/go-llvm-bnode.h b/llvm-gofrontend/go-llvm-bnode.h
index 20d753c..f841bda 100644
--- a/llvm-gofrontend/go-llvm-bnode.h
+++ b/llvm-gofrontend/go-llvm-bnode.h
@@ -168,10 +168,14 @@
// Return struct field index for a field expr
unsigned fieldIndex() const;
- // Return Bfunction operand (valid only for function constants, calls,
- // and conditionals).
+ // 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;
diff --git a/llvm-gofrontend/go-llvm-tree-integrity.cpp b/llvm-gofrontend/go-llvm-tree-integrity.cpp
index 405ab49..2dbc70f 100644
--- a/llvm-gofrontend/go-llvm-tree-integrity.cpp
+++ b/llvm-gofrontend/go-llvm-tree-integrity.cpp
@@ -18,6 +18,26 @@
#include "llvm/IR/Instruction.h"
+IntegrityVisitor::IntegrityVisitor(Llvm_backend *be,
+ TreeIntegCtl control)
+ : be_(be), ss_(str_), control_(control),
+ instShareCount_(0), stmtShareCount_(0), exprShareCount_(0)
+{
+ acceptableNodes_.insert(N_Const);
+ acceptableNodes_.insert(N_Call); // subject to acceptable target
+ acceptableNodes_.insert(N_FcnAddress);
+ acceptableNodes_.insert(N_Conditional);
+ acceptableNodes_.insert(N_Var);
+ acceptableNodes_.insert(N_Conversion);
+ acceptableNodes_.insert(N_Deref);
+ acceptableNodes_.insert(N_StructField);
+ acceptableNodes_.insert(N_BinaryOp); // subject to acceptableOpcodes
+ acceptableOpcodes_.insert(OPERATOR_PLUS);
+ acceptableOpcodes_.insert(OPERATOR_MINUS);
+ acceptableOpcodes_.insert(OPERATOR_EQEQ);
+ acceptableOpcodes_.insert(OPERATOR_NOTEQ);
+}
+
void IntegrityVisitor::dumpTag(const char *tag, void *ptr) {
ss_ << tag << ": ";
if (includePointers() == DumpPointers)
@@ -147,12 +167,13 @@
}
// capture a description of the error
- ss_ << "error: " << wh << " has multiple parents\n";
+ ss_ << "error: " << wh << " has multiple parents"
+ << " [ID=" << child->id() << "]\n";
ss_ << "child " << wh << ":\n";
dump(child);
- ss_ << "parent 1:\n";
+ ss_ << "parent 1: [ID=" << prevParent->id() << "]\n";
dump(prevParent);
- ss_ << "parent 2:\n";
+ ss_ << "parent 2: [ID=" << parent->id() << "]\n";
dump(parent);
return;
}
@@ -184,16 +205,6 @@
bool IntegrityVisitor::repairableSubTree(Bexpression *root)
{
- std::set<NodeFlavor> acceptable;
- acceptable.insert(N_Const);
- acceptable.insert(N_Var);
- acceptable.insert(N_Conversion);
- acceptable.insert(N_Deref);
- acceptable.insert(N_StructField);
-
- // Allow a limited set of these.
- acceptable.insert(N_BinaryOp);
-
std::set<Bexpression *> visited;
visited.insert(root);
std::vector<Bexpression *> workList;
@@ -202,11 +213,16 @@
while (! workList.empty()) {
Bexpression *e = workList.back();
workList.pop_back();
- if (acceptable.find(e->flavor()) == acceptable.end())
+ if (acceptableNodes_.find(e->flavor()) == acceptableNodes_.end())
return false;
+ if (e->flavor() == N_Call) {
+ // ok to duplicate runtime error calls, but not other calls
+ Bfunction *target = e->getCallTarget();
+ if (!target || target->name() != "__go_runtime_error")
+ return false;
+ }
if (e->flavor() == N_BinaryOp &&
- (e->op() != OPERATOR_PLUS &&
- e->op() != OPERATOR_MINUS))
+ acceptableOpcodes_.find(e->op()) == acceptableOpcodes_.end())
return false;
for (auto &c : e->children()) {
Bexpression *ce = c->castToBexpression();
diff --git a/llvm-gofrontend/go-llvm-tree-integrity.h b/llvm-gofrontend/go-llvm-tree-integrity.h
index aa7347c..4bf3e8e 100644
--- a/llvm-gofrontend/go-llvm-tree-integrity.h
+++ b/llvm-gofrontend/go-llvm-tree-integrity.h
@@ -71,10 +71,7 @@
class IntegrityVisitor {
public:
- IntegrityVisitor(Llvm_backend *be,
- TreeIntegCtl control)
- : be_(be), ss_(str_), control_(control),
- instShareCount_(0), stmtShareCount_(0), exprShareCount_(0) { }
+ IntegrityVisitor(Llvm_backend *be, TreeIntegCtl control);
// Visits the node tree "n", looking for any shared nodes or
// instructions. Returns TRUE if there is no sharing (or if all
@@ -103,12 +100,17 @@
// node, so please remove any outstanding references to it.
void deletePending(Bnode *node);
+ // Exposed for unit testing.
+ bool repairableSubTree(Bexpression *root);
+
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_;
pairhashset<Bnode *, unsigned> sharing_;
+ std::set<NodeFlavor> acceptableNodes_;
+ std::set<Operator> acceptableOpcodes_;
std::string str_;
llvm::raw_string_ostream ss_;
TreeIntegCtl control_;
@@ -119,7 +121,6 @@
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);
diff --git a/unittests/BackendCore/BackendTreeIntegrity.cpp b/unittests/BackendCore/BackendTreeIntegrity.cpp
index a03dc2b..78f0a2e 100644
--- a/unittests/BackendCore/BackendTreeIntegrity.cpp
+++ b/unittests/BackendCore/BackendTreeIntegrity.cpp
@@ -128,4 +128,78 @@
be->function_set_body(func, block2);
}
+TEST(BackendTreeIntegrity, CheckTreeIntegrityRepairableSubtree) {
+ FcnTestHarness h;
+ Llvm_backend *be = h.be();
+ Btype *bi32t = be->integer_type(false, 32);
+ Btype *bpi32t = be->pointer_type(bi32t);
+ BFunctionType *befty = mkFuncTyp(be, L_PARM, bpi32t, L_END);
+ Bfunction *func = h.mkFunction("x", befty);
+ Location loc;
+
+ TreeIntegCtl ctl(NoDumpPointers, ReportRepairableSharing, BatchMode);
+ IntegrityVisitor ivis(be, ctl);
+
+ // *p0 + 2
+ Bvariable *p0v = func->getNthParamVar(0);
+ Bexpression *vex0 = be->var_expression(p0v, loc);
+ Bexpression *deref = be->indirect_expression(bi32t, vex0, false, loc);
+ Bexpression *add =
+ be->binary_expression(OPERATOR_PLUS, mkInt32Const(be, 2), deref, loc);
+ EXPECT_TRUE(ivis.repairableSubTree(vex0));
+ EXPECT_TRUE(ivis.repairableSubTree(deref));
+ EXPECT_TRUE(ivis.repairableSubTree(add));
+
+ // p0 == nil ? 1 : *p0
+ Bexpression *vex2 = be->var_expression(p0v, loc);
+ Bexpression *npe = be->nil_pointer_expression();
+ Bexpression *cmp =
+ be->binary_expression(OPERATOR_EQEQ, npe, vex2, loc);
+ Bexpression *der2 = be->indirect_expression(bi32t, vex2, false, loc);
+ Bexpression *const1 = mkInt32Const(be, 1);
+ Bexpression *condex = be->conditional_expression(func, bi32t, cmp, const1,
+ der2, loc);
+ EXPECT_TRUE(ivis.repairableSubTree(cmp));
+ EXPECT_TRUE(ivis.repairableSubTree(condex));
+
+ // Not legal to share calls (in general)
+ Bexpression *vex3 = be->var_expression(p0v, loc);
+ Bexpression *call2 = h.mkCallExpr(be, func, vex3, nullptr);
+ EXPECT_FALSE(ivis.repairableSubTree(call2));
+
+ // Create runtime error function.
+ const char *rtename = "__go_runtime_error";
+ BFunctionType *bfterr = mkFuncTyp(be,
+ L_PARM, bi32t,
+ L_END);
+ bool visible = true; bool is_inl = true;
+ bool split_stack = false; bool uniq_sec = false;
+ bool is_decl = true;
+ Bfunction *rtefcn = be->function(bfterr, rtename, rtename, visible, is_decl,
+ is_inl, split_stack, uniq_sec, loc);
+
+ // p0 != nil ? *p0 + 3 : runtime_error(6)
+ Bexpression *cmp2 =
+ be->binary_expression(OPERATOR_NOTEQ, vex3, npe, loc);
+ Bexpression *der3 = be->indirect_expression(bi32t, vex3, false, loc);
+ Bexpression *add2 =
+ be->binary_expression(OPERATOR_PLUS, mkInt32Const(be, 3), der3, loc);
+ Bexpression *const6 = mkInt32Const(be, 6);
+ Bexpression *call3 = h.mkCallExpr(be, rtefcn, const6, nullptr);
+ Bexpression *condex2 = be->conditional_expression(func, bi32t, cmp2, add2,
+ call3, loc);
+
+ // Runtime error calls are ok to unshare/duplicate.
+ EXPECT_TRUE(ivis.repairableSubTree(call3));
+ EXPECT_TRUE(ivis.repairableSubTree(condex2));
+
+ h.mkExprStmt(call2);
+ h.mkExprStmt(condex);
+ h.mkExprStmt(condex2);
+ h.mkExprStmt(add);
+
+ bool broken = h.finish(StripDebugInfo);
+ EXPECT_FALSE(broken && "Module failed to verify.");
+}
+
}