mirror of
https://github.com/zeek/zeek.git
synced 2025-10-10 02:28:21 +00:00
reductions of expressions in ASTs - code compiles but doesn't yet link
This commit is contained in:
parent
6aa84087b0
commit
10e80dfcd3
8 changed files with 3264 additions and 164 deletions
186
src/script_opt/Reduce.h
Normal file
186
src/script_opt/Reduce.h
Normal file
|
@ -0,0 +1,186 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "zeek/IntrusivePtr.h"
|
||||
|
||||
namespace zeek::detail {
|
||||
|
||||
class Expr;
|
||||
class TempVar;
|
||||
class ProfileFunc;
|
||||
|
||||
class Reducer {
|
||||
public:
|
||||
Reducer(Scope* s);
|
||||
~Reducer();
|
||||
|
||||
StmtPtr Reduce(StmtPtr s)
|
||||
{
|
||||
return s->Reduce(this);
|
||||
}
|
||||
|
||||
ExprPtr GenTemporaryExpr(const TypePtr& t, ExprPtr rhs);
|
||||
|
||||
NameExpr* UpdateName(NameExpr* n);
|
||||
bool NameIsReduced(const NameExpr* n) const;
|
||||
|
||||
void UpdateIDs(IDPList* ids);
|
||||
bool IDsAreReduced(const IDPList* ids) const;
|
||||
|
||||
void UpdateIDs(std::vector<IDPtr>& ids);
|
||||
bool IDsAreReduced(const std::vector<IDPtr>& ids) const;
|
||||
|
||||
IDPtr UpdateID(IDPtr id);
|
||||
bool ID_IsReduced(const IDPtr& id) const
|
||||
{ return ID_IsReduced(id.get()); }
|
||||
bool ID_IsReduced(const ID* id) const;
|
||||
|
||||
// This is called *prior* to pushing a new inline block, in
|
||||
// order to generate the equivalent of function parameters.
|
||||
NameExprPtr GenInlineBlockName(IDPtr id);
|
||||
|
||||
int NumNewLocals() const { return new_locals.size(); }
|
||||
|
||||
// Returns the name of a temporary for holding the return
|
||||
// value of the block, or nil if the type indicates there's
|
||||
// o return value.
|
||||
NameExprPtr PushInlineBlock(TypePtr type);
|
||||
void PopInlineBlock();
|
||||
|
||||
// Whether it's okay to split a statement into two copies for if-else
|
||||
// expansion. We only allow this to a particular depth because
|
||||
// beyond that a function body can get too large to analyze.
|
||||
bool BifurcationOkay() const { return bifurcation_level <= 12; }
|
||||
int BifurcationLevel() const { return bifurcation_level; }
|
||||
|
||||
void PushBifurcation() { ++bifurcation_level; }
|
||||
void PopBifurcation() { --bifurcation_level; }
|
||||
|
||||
int NumTemps() const { return temps.length(); }
|
||||
|
||||
// True if this name already reflects the replacement.
|
||||
bool IsNewLocal(const NameExpr* n) const
|
||||
{ return IsNewLocal(n->Id()); }
|
||||
bool IsNewLocal(const ID* id) const;
|
||||
|
||||
bool IsTemporary(const ID* id) const
|
||||
{ return FindTemporary(id) != nullptr; }
|
||||
|
||||
// This is a stub for now, since it's not relevant for AST
|
||||
// reduction by itself. However, many of the Reduce methods
|
||||
// ultimately will call this predicate to control how they
|
||||
// function during the second traversal used to optimize
|
||||
// the reduced form, so we provide the hook now.
|
||||
bool Optimizing() const { return false; }
|
||||
|
||||
// A stub for now, but ultimately a predicate that indicates whether
|
||||
// a given reduction pass is being made to prune unused statements.
|
||||
bool IsPruning() const { return false; }
|
||||
|
||||
// A stub for now, ultimately a predicate that returns true if
|
||||
// the given statement should be removed due to AST optimization.
|
||||
bool ShouldOmitStmt(const StmtPtr& s) const { return false; }
|
||||
|
||||
// A stub for now, ultimately provides a replacement for the
|
||||
// given statement due to AST optimization, or nil if there's
|
||||
// no replacement.
|
||||
StmtPtr ReplacementStmt(const StmtPtr& s) const { return nullptr; }
|
||||
|
||||
// NOT YET IMPLEMENTED, SO CURRENTLY A STUB:
|
||||
// Given the LHS and RHS of an assignment, returns true
|
||||
// if the RHS is a common subexpression (meaning that the
|
||||
// current assignment statement should be deleted). In
|
||||
// that case, has the side effect of associating an alias
|
||||
// for the LHS with the temporary holding the equivalent RHS.
|
||||
//
|
||||
// Assumes reduction (including alias propagation) has
|
||||
// already been applied.
|
||||
bool IsCSE(const AssignExpr* a, const NameExpr* lhs, const Expr* rhs)
|
||||
{ return false; }
|
||||
|
||||
// Given an lhs=rhs statement followed by succ_stmt, returns
|
||||
// a (new) merge of the two if they're of the form tmp=rhs, var=tmp;
|
||||
// otherwise, nil.
|
||||
Stmt* MergeStmts(const NameExpr* lhs, ExprPtr rhs, Stmt* succ_stmt);
|
||||
|
||||
// The following two methods will, in the future, update expressions
|
||||
// with optimized versions. They are distinct because the first
|
||||
// one (meant for calls in a Stmt reduction context) will also Reduce
|
||||
// the expression, whereas the second one (meant for calls in an Expr
|
||||
// context) does not, to avoid circularity.
|
||||
//
|
||||
// For now, they are stubs.
|
||||
//
|
||||
// These two are used for use in optimizing expressions that appear in
|
||||
// a Stmt context.
|
||||
ExprPtr OptExpr(Expr* e) { return {NewRef{}, e}; }
|
||||
ExprPtr OptExpr(ExprPtr e) { return e; }
|
||||
// This one for expressions appearing in an Expr context.
|
||||
ExprPtr UpdateExpr(ExprPtr e) { return e; }
|
||||
|
||||
const Scope* FuncScope() const { return scope; }
|
||||
|
||||
protected:
|
||||
bool SameVal(const Val* v1, const Val* v2) const;
|
||||
|
||||
IDPtr GenTemporary(const TypePtr& t, ExprPtr rhs);
|
||||
TempVar* FindTemporary(const ID* id) const;
|
||||
|
||||
// Retrieve the identifier corresponding to the new local for
|
||||
// the given expression. Creates the local if necessary.
|
||||
IDPtr FindNewLocal(ID* id);
|
||||
IDPtr FindNewLocal(const NameExpr* n)
|
||||
{ return FindNewLocal(n->Id()); }
|
||||
|
||||
// Generate a new local to use in lieu of the original (seen
|
||||
// in an inlined block). The difference is that the new
|
||||
// version has a distinct name and has a correct frame offset
|
||||
// for the current function.
|
||||
IDPtr GenLocal(ID* orig);
|
||||
|
||||
// Track that we're replacing instances of "orig" with a new
|
||||
// expression. This allows us to locate the RDs associated
|
||||
// with "orig" in the context of the new expression, without
|
||||
// requiring an additional RD propagation pass.
|
||||
void TrackExprReplacement(const Expr* orig, const Expr* e);
|
||||
|
||||
Scope* scope;
|
||||
PList<TempVar> temps;
|
||||
|
||||
// Temps for which we've processed their associated expression
|
||||
// (and they didn't wind up being aliases).
|
||||
PList<TempVar> expr_temps;
|
||||
|
||||
// Let's us go from an identifier to an associated temporary
|
||||
// variable, if it corresponds to one.
|
||||
std::unordered_map<const ID*, TempVar*> ids_to_temps;
|
||||
|
||||
std::unordered_set<ID*> new_locals;
|
||||
std::unordered_map<const ID*, IDPtr> orig_to_new_locals;
|
||||
|
||||
// Tracks whether we're inside an inline block, and if so then
|
||||
// how deeply.
|
||||
int inline_block_level = 0;
|
||||
|
||||
// Tracks how deeply we are in "bifurcation", i.e., duplicating
|
||||
// code for if-else cascades. We need to cap this at a certain
|
||||
// depth or else we can get functions whose size blows up
|
||||
// exponentially.
|
||||
int bifurcation_level = 0;
|
||||
|
||||
// For a new expression we've created, map it to the expression
|
||||
// it's replacing. This allows us to locate the RDs associated
|
||||
// with the usage.
|
||||
std::unordered_map<const Expr*, const Expr*> new_expr_to_orig;
|
||||
};
|
||||
|
||||
// Used for debugging, to communicate which expression wasn't
|
||||
// reduced when we expected them all to be.
|
||||
extern const Expr* non_reduced_perp;
|
||||
extern bool checking_reduction;
|
||||
|
||||
// Used to report a non-reduced expression.
|
||||
extern bool NonReduced(const Expr* perp);
|
||||
|
||||
} // zeek::detail
|
Loading…
Add table
Add a link
Reference in a new issue