diff --git a/src/script_opt/ZAM/AM-Opt.h b/src/script_opt/ZAM/AM-Opt.h new file mode 100644 index 0000000000..60e325d7c8 --- /dev/null +++ b/src/script_opt/ZAM/AM-Opt.h @@ -0,0 +1,101 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for low-level optimization of the ZAM abstract machine. +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +// Optimizing the low-level compiled instructions. +void OptimizeInsts(); + +// Tracks which instructions can be branched to via the given +// set of switches. +template +void TallySwitchTargets(const CaseMapsI& switches); + +// Remove code that can't be reached. True if some removal happened. +bool RemoveDeadCode(); + +// Collapse chains of gotos. True if some something changed. +bool CollapseGoTos(); + +// Prune statements that are unnecessary. True if something got +// pruned. +bool PruneUnused(); + +// For the current state of insts1, compute lifetimes of frame +// denizens (variable(s) using a given frame slot) in terms of +// first-instruction-to-last-instruction during which they're +// relevant, including consideration for loops. +void ComputeFrameLifetimes(); + +// Given final frame lifetime information, remaps frame members +// with non-overlapping lifetimes to share slots. +void ReMapFrame(); + +// Given final frame lifetime information, remaps slots in the +// interpreter frame. (No longer strictly necessary.) +void ReMapInterpreterFrame(); + +// Computes the remapping for a variable currently in the given slot, +// whose scope begins at the given instruction. +void ReMapVar(const ID* id, int slot, zeek_uint_t inst); + +// Look to initialize the beginning of local lifetime based on slot +// assignment at instruction inst. +void CheckSlotAssignment(int slot, const ZInstI* inst); + +// Track that a local's lifetime begins at the given statement. +void SetLifetimeStart(int slot, const ZInstI* inst); + +// Look for extension of local lifetime based on slot usage +// at instruction inst. +void CheckSlotUse(int slot, const ZInstI* inst); + +// Extend (or create) the end of a local's lifetime. +void ExtendLifetime(int slot, const ZInstI* inst); + +// Returns the (live) instruction at the beginning/end of the loop(s) +// within which the given instruction lies; or that instruction +// itself if it's not inside a loop. The second argument specifies +// the loop depth. For example, a value of '2' means "extend to +// the beginning/end of any loop(s) of depth >= 2". +const ZInstI* BeginningOfLoop(const ZInstI* inst, int depth) const; +const ZInstI* EndOfLoop(const ZInstI* inst, int depth) const; + +// True if any statement other than a frame sync uses the given slot. +bool VarIsUsed(int slot) const; + +// Find the first non-dead instruction after i (inclusive). +// If follow_gotos is true, then if that instruction is +// an unconditional branch, continues the process until +// a different instruction is found (and report if there +// are infinite loops). +// +// First form returns nil if there's nothing live after i. +// Second form returns insts1.size() in that case. +ZInstI* FirstLiveInst(ZInstI* i, bool follow_gotos = false); +zeek_uint_t FirstLiveInst(zeek_uint_t i, bool follow_gotos = false); + +// Same, but not including i. +ZInstI* NextLiveInst(ZInstI* i, bool follow_gotos = false) { + if ( i->inst_num == static_cast(insts1.size()) - 1 ) + return nullptr; + return FirstLiveInst(insts1[i->inst_num + 1], follow_gotos); +} +int NextLiveInst(int i, bool follow_gotos = false) { return FirstLiveInst(i + 1, follow_gotos); } + +// Mark an instruction as unnecessary and remove its influence on +// other statements. The instruction is indicated as an offset +// into insts1; any labels associated with it are transferred +// to its next live successor, if any. +void KillInst(ZInstI* i) { KillInst(i->inst_num); } +void KillInst(zeek_uint_t i); + +// Helper function for propagating control flow (of a given type) +// backwards, when the instruction at the given offset has been killed. +void BackPropagateCFT(int inst_num, ControlFlowType cf_type); + +// The same, but kills any successor instructions until finding +// one that's labeled. +void KillInsts(ZInstI* i) { KillInsts(i->inst_num); } +void KillInsts(zeek_uint_t i); diff --git a/src/script_opt/ZAM/Branches.h b/src/script_opt/ZAM/Branches.h new file mode 100644 index 0000000000..e6461751d3 --- /dev/null +++ b/src/script_opt/ZAM/Branches.h @@ -0,0 +1,56 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for managing low-level ZAM control flow, which is implemented +// using go-to branches. +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +void PushNexts() { PushGoTos(nexts); } +void PushBreaks() { PushGoTos(breaks); } +void PushFallThroughs() { PushGoTos(fallthroughs); } +void PushCatchReturns() { PushGoTos(catches); } + +void ResolveNexts(const InstLabel l) { ResolveGoTos(nexts, l, CFT_NEXT); } +void ResolveBreaks(const InstLabel l) { ResolveGoTos(breaks, l, CFT_BREAK); } +void ResolveFallThroughs(const InstLabel l) { ResolveGoTos(fallthroughs, l); } +void ResolveCatchReturns(const InstLabel l) { ResolveGoTos(catches, l, CFT_INLINED_RETURN); } + +using GoToSet = std::vector; +using GoToSets = std::vector; + +void PushGoTos(GoToSets& gotos); +void ResolveGoTos(GoToSets& gotos, const InstLabel l, ControlFlowType cft = CFT_NONE); + +ZAMStmt GenGoTo(GoToSet& v); +ZAMStmt GoToStub(); +ZAMStmt GoTo(const InstLabel l); +InstLabel GoToTarget(const ZAMStmt s); +InstLabel GoToTargetBeyond(const ZAMStmt s); + +void SetTarget(ZInstI* inst, const InstLabel l, int slot); + +// Given a GoTo target, find its live equivalent (first instruction +// at that location or beyond that's live). +ZInstI* FindLiveTarget(ZInstI* goto_target); + +// Given an instruction that has a slot associated with the +// given target, updates the slot to correspond with the current +// instruction number of the target. +void ConcretizeBranch(ZInstI* inst, ZInstI* target, int target_slot); + +void SetV(ZAMStmt s, const InstLabel l, int v) { + if ( v == 1 ) + SetV1(s, l); + else if ( v == 2 ) + SetV2(s, l); + else if ( v == 3 ) + SetV3(s, l); + else + SetV4(s, l); +} + +void SetV1(ZAMStmt s, const InstLabel l); +void SetV2(ZAMStmt s, const InstLabel l); +void SetV3(ZAMStmt s, const InstLabel l); +void SetV4(ZAMStmt s, const InstLabel l); +void SetGoTo(ZAMStmt s, const InstLabel targ) { SetV1(s, targ); } diff --git a/src/script_opt/ZAM/Compile.h b/src/script_opt/ZAM/Compile.h index b4d56cab75..f802de717b 100644 --- a/src/script_opt/ZAM/Compile.h +++ b/src/script_opt/ZAM/Compile.h @@ -51,14 +51,22 @@ public: ZInstAux* aux; }; +// Most of the methods for the compiler are either in separate header source +// files, or in headers generated by auxil/gen-zam. We include these within +// the private part of the compiler class definitions, so a few methods that +// need to be public are specified here directly, rather than via such +// headers. +// +// We declare member variables here, rather than in included headers, since +// many of them are used across different source files, and don't necessarily +// have a natural "home". + class ZAMCompiler { public: ZAMCompiler(ScriptFuncPtr f, std::shared_ptr pfs, std::shared_ptr pf, ScopePtr scope, StmtPtr body, std::shared_ptr ud, std::shared_ptr rd); ~ZAMCompiler(); - StmtPtr CompileBody(); - const FrameReMap& FrameDenizens() const { return shared_frame_denizens_final; } const std::vector& ManagedSlots() const { return managed_slotsI; } @@ -82,6 +90,8 @@ public: return str_cases; } + StmtPtr CompileBody(); + void Dump(); private: @@ -92,406 +102,27 @@ private: friend class CatZBI; friend class MultiZBI; - void Init(); - void InitGlobals(); - void InitArgs(); - void InitCaptures(); - void InitLocals(); - void TrackMemoryManagement(); - - void ResolveHookBreaks(); - void ComputeLoopLevels(); - void AdjustBranches(); - void RetargetBranches(); - void RemapFrameDenizens(const std::vector& inst1_to_inst2); - void CreateSharedFrameDenizens(); - void ConcretizeSwitches(); - - // The following are used for switch statements, mapping the - // switch value (which can be any atomic type) to a branch target. - // We have vectors of them because functions can contain multiple - // switches. - // See ZBody.h for their concrete counterparts, which we've - // already #include'd. + // The following are used for switch statements, mapping the switch value + // (which can be any atomic type) to a branch target. We have vectors of + // them because functions can contain multiple switches. + // + // See ZBody.h for their concrete counterparts, which we've already #include'd. template using CaseMapI = std::map; template using CaseMapsI = std::vector>; - template - void AdjustSwitchTables(CaseMapsI& abstract_cases); - - template - void ConcretizeSwitchTables(const CaseMapsI& abstract_cases, CaseMaps& concrete_cases); - - template - void DumpCases(const CaseMaps& cases, const char* type_name) const; - void DumpInsts1(const FrameReMap* remappings); - -#include "zeek/ZAM-MethodDecls.h" - - const ZAMStmt CompileStmt(const StmtPtr& body) { return CompileStmt(body.get()); } - const ZAMStmt CompileStmt(const Stmt* body); - - const ZAMStmt CompilePrint(const PrintStmt* ps); - const ZAMStmt CompileExpr(const ExprStmt* es); - const ZAMStmt CompileIf(const IfStmt* is); - const ZAMStmt CompileSwitch(const SwitchStmt* sw); - const ZAMStmt CompileWhile(const WhileStmt* ws); - const ZAMStmt CompileFor(const ForStmt* f); - const ZAMStmt CompileReturn(const ReturnStmt* r); - const ZAMStmt CompileCatchReturn(const CatchReturnStmt* cr); - const ZAMStmt CompileStmts(const StmtList* sl); - const ZAMStmt CompileInit(const InitStmt* is); - const ZAMStmt CompileWhen(const WhenStmt* ws); - - const ZAMStmt CompileNext() { return GenGoTo(nexts.back()); } - const ZAMStmt CompileBreak() { return GenGoTo(breaks.back()); } - const ZAMStmt CompileFallThrough() { return GenGoTo(fallthroughs.back()); } - const ZAMStmt CompileCatchReturn() { return GenGoTo(catches.back()); } - - const ZAMStmt IfElse(const Expr* e, const Stmt* s1, const Stmt* s2); - const ZAMStmt While(const Stmt* cond_stmt, const Expr* cond, const Stmt* body); - - const ZAMStmt InitRecord(IDPtr id, RecordType* rt); - const ZAMStmt InitVector(IDPtr id, VectorType* vt); - const ZAMStmt InitTable(IDPtr id, TableType* tt, Attributes* attrs); - - const ZAMStmt ValueSwitch(const SwitchStmt* sw, const NameExpr* v, const ConstExpr* c); - const ZAMStmt TypeSwitch(const SwitchStmt* sw, const NameExpr* v, const ConstExpr* c); - const ZAMStmt GenSwitch(const SwitchStmt* sw, int slot, InternalTypeTag it); - - void PushNexts() { PushGoTos(nexts); } - void PushBreaks() { PushGoTos(breaks); } - void PushFallThroughs() { PushGoTos(fallthroughs); } - void PushCatchReturns() { PushGoTos(catches); } - - void ResolveNexts(const InstLabel l) { ResolveGoTos(nexts, l, CFT_NEXT); } - void ResolveBreaks(const InstLabel l) { ResolveGoTos(breaks, l, CFT_BREAK); } - void ResolveFallThroughs(const InstLabel l) { ResolveGoTos(fallthroughs, l); } - void ResolveCatchReturns(const InstLabel l) { ResolveGoTos(catches, l, CFT_INLINED_RETURN); } - - const ZAMStmt LoopOverTable(const ForStmt* f, const NameExpr* val); - const ZAMStmt LoopOverVector(const ForStmt* f, const NameExpr* val); - const ZAMStmt LoopOverString(const ForStmt* f, const Expr* e); - - const ZAMStmt FinishLoop(const ZAMStmt iter_head, ZInstI& iter_stmt, const Stmt* body, int iter_slot, - bool is_table); - - const ZAMStmt Loop(const Stmt* body); - - const ZAMStmt CompileExpr(const ExprPtr& e) { return CompileExpr(e.get()); } - const ZAMStmt CompileExpr(const Expr* body); - - const ZAMStmt CompileIncrExpr(const IncrExpr* e); - const ZAMStmt CompileAppendToExpr(const AppendToExpr* e); - const ZAMStmt CompileAdd(const AggrAddExpr* e); - const ZAMStmt CompileDel(const AggrDelExpr* e); - const ZAMStmt CompileAddToExpr(const AddToExpr* e); - const ZAMStmt CompileRemoveFromExpr(const RemoveFromExpr* e); - const ZAMStmt CompileAssignExpr(const AssignExpr* e); - const ZAMStmt CompileRecFieldUpdates(const RecordFieldUpdatesExpr* e); - const ZAMStmt CompileZAMBuiltin(const NameExpr* lhs, const ScriptOptBuiltinExpr* zbi); - const ZAMStmt CompileAssignToIndex(const NameExpr* lhs, const IndexExpr* rhs); - const ZAMStmt CompileFieldLHSAssignExpr(const FieldLHSAssignExpr* e); - const ZAMStmt CompileScheduleExpr(const ScheduleExpr* e); - const ZAMStmt CompileSchedule(const NameExpr* n, const ConstExpr* c, int is_interval, EventHandler* h, - const ListExpr* l); - const ZAMStmt CompileEvent(EventHandler* h, const ListExpr* l); - - const ZAMStmt CompileInExpr(const NameExpr* n1, const NameExpr* n2, const NameExpr* n3) { - return CompileInExpr(n1, n2, nullptr, n3, nullptr); - } - - const ZAMStmt CompileInExpr(const NameExpr* n1, const NameExpr* n2, const ConstExpr* c) { - return CompileInExpr(n1, n2, nullptr, nullptr, c); - } - - const ZAMStmt CompileInExpr(const NameExpr* n1, const ConstExpr* c, const NameExpr* n3) { - return CompileInExpr(n1, nullptr, c, n3, nullptr); - } - - // In the following, one of n2 or c2 (likewise, n3/c3) will be nil. - const ZAMStmt CompileInExpr(const NameExpr* n1, const NameExpr* n2, const ConstExpr* c2, const NameExpr* n3, - const ConstExpr* c3); - - const ZAMStmt CompileInExpr(const NameExpr* n1, const ListExpr* l, const NameExpr* n2) { - return CompileInExpr(n1, l, n2, nullptr); - } - - const ZAMStmt CompileInExpr(const NameExpr* n, const ListExpr* l, const ConstExpr* c) { - return CompileInExpr(n, l, nullptr, c); - } - - const ZAMStmt CompileInExpr(const NameExpr* n1, const ListExpr* l, const NameExpr* n2, const ConstExpr* c); - - const ZAMStmt CompileIndex(const NameExpr* n1, const NameExpr* n2, const ListExpr* l, bool in_when); - const ZAMStmt CompileIndex(const NameExpr* n1, const ConstExpr* c, const ListExpr* l, bool in_when); - const ZAMStmt CompileIndex(const NameExpr* n1, int n2_slot, const TypePtr& n2_type, const ListExpr* l, - bool in_when); - - const ZAMStmt BuildLambda(const NameExpr* n, ExprPtr le); - const ZAMStmt BuildLambda(int n_slot, ExprPtr le); - - // Second argument is which instruction slot holds the branch target. - const ZAMStmt GenCond(const Expr* e, int& branch_v); - - const ZAMStmt Call(const ExprStmt* e); - const ZAMStmt AssignToCall(const ExprStmt* e); - const ZAMStmt DoCall(const CallExpr* c, const NameExpr* n); - bool CheckForBuiltIn(const ExprPtr& e, CallExprPtr c); - - const ZAMStmt AssignVecElems(const Expr* e); - const ZAMStmt AssignTableElem(const Expr* e); - - const ZAMStmt ConstructTable(const NameExpr* n, const Expr* e); - const ZAMStmt ConstructSet(const NameExpr* n, const Expr* e); - const ZAMStmt ConstructRecord(const NameExpr* n, const Expr* e) { return ConstructRecord(n, e, false); } - const ZAMStmt ConstructRecordFromRecord(const NameExpr* n, const Expr* e) { return ConstructRecord(n, e, true); } - const ZAMStmt ConstructRecord(const NameExpr* n, const Expr* e, bool is_from_rec); - const ZAMStmt ConstructVector(const NameExpr* n, const Expr* e); - - const ZAMStmt ArithCoerce(const NameExpr* n, const Expr* e); - const ZAMStmt RecordCoerce(const NameExpr* n, const Expr* e); - const ZAMStmt TableCoerce(const NameExpr* n, const Expr* e); - const ZAMStmt VectorCoerce(const NameExpr* n, const Expr* e); - - const ZAMStmt Is(const NameExpr* n, const Expr* e); - +#include "zeek/script_opt/ZAM/AM-Opt.h" +#include "zeek/script_opt/ZAM/Branches.h" +#include "zeek/script_opt/ZAM/Driver.h" +#include "zeek/script_opt/ZAM/Expr.h" #include "zeek/script_opt/ZAM/Inst-Gen.h" +#include "zeek/script_opt/ZAM/Low-Level.h" +#include "zeek/script_opt/ZAM/Stmt.h" +#include "zeek/script_opt/ZAM/Vars.h" - int ConvertToInt(const Expr* e) { - if ( e->Tag() == EXPR_NAME ) - return FrameSlot(e->AsNameExpr()->Id()); - else - return e->AsConstExpr()->Value()->AsInt(); - } - - int ConvertToCount(const Expr* e) { - if ( e->Tag() == EXPR_NAME ) - return FrameSlot(e->AsNameExpr()->Id()); - else - return e->AsConstExpr()->Value()->AsCount(); - } - - using GoToSet = std::vector; - using GoToSets = std::vector; - - void PushGoTos(GoToSets& gotos); - void ResolveGoTos(GoToSets& gotos, const InstLabel l, ControlFlowType cft = CFT_NONE); - - ZAMStmt GenGoTo(GoToSet& v); - ZAMStmt GoToStub(); - ZAMStmt GoTo(const InstLabel l); - InstLabel GoToTarget(const ZAMStmt s); - InstLabel GoToTargetBeyond(const ZAMStmt s); - - void SetTarget(ZInstI* inst, const InstLabel l, int slot); - - // Given a GoTo target, find its live equivalent (first instruction - // at that location or beyond that's live). - ZInstI* FindLiveTarget(ZInstI* goto_target); - - // Given an instruction that has a slot associated with the - // given target, updates the slot to correspond with the current - // instruction number of the target. - void ConcretizeBranch(ZInstI* inst, ZInstI* target, int target_slot); - - void SetV(ZAMStmt s, const InstLabel l, int v) { - if ( v == 1 ) - SetV1(s, l); - else if ( v == 2 ) - SetV2(s, l); - else if ( v == 3 ) - SetV3(s, l); - else - SetV4(s, l); - } - - void SetV1(ZAMStmt s, const InstLabel l); - void SetV2(ZAMStmt s, const InstLabel l); - void SetV3(ZAMStmt s, const InstLabel l); - void SetV4(ZAMStmt s, const InstLabel l); - void SetGoTo(ZAMStmt s, const InstLabel targ) { SetV1(s, targ); } - - const ZAMStmt StartingBlock(); - const ZAMStmt FinishBlock(const ZAMStmt start); - - bool NullStmtOK() const; - - const ZAMStmt EmptyStmt(); - const ZAMStmt ErrorStmt(); - const ZAMStmt LastInst(); - - // Adds control flow information to an instruction. - void AddCFT(ZInstI* inst, ControlFlowType cft); - - // Returns a handle to state associated with building - // up a list of values. - std::unique_ptr BuildVals(const ListExprPtr&); - - // "stride" is how many slots each element of l will consume. - ZInstAux* InternalBuildVals(const ListExpr* l, int stride = 1); - - // Returns how many values were added. - int InternalAddVal(ZInstAux* zi, int i, Expr* e); - - // Adds the given instruction to the ZAM program. The second - // argument, if true, suppresses generation of any pending - // global/capture store for this instruction. - const ZAMStmt AddInst(const ZInstI& inst, bool suppress_non_local = false); - - // Returns the statement just before the given one. - ZAMStmt PrevStmt(const ZAMStmt s); - - // Returns the last (interpreter) statement in the body. - const Stmt* LastStmt(const Stmt* s) const; - - // Returns the most recent added instruction *other* than those - // added for bookkeeping. - ZInstI* TopMainInst() { return insts1[top_main_inst]; } - - bool IsUnused(const IDPtr& id, const Stmt* where) const; - - bool IsCapture(const IDPtr& id) const { return IsCapture(id.get()); } - bool IsCapture(const ID* id) const; - - int CaptureOffset(const IDPtr& id) const { return IsCapture(id.get()); } - int CaptureOffset(const ID* id) const; - - void LoadParam(const ID* id); - const ZAMStmt LoadGlobal(const ID* id); - const ZAMStmt LoadCapture(const ID* id); - - int AddToFrame(const ID*); - - int FrameSlot(const IDPtr& id) { return FrameSlot(id.get()); } - int FrameSlot(const ID* id); - int FrameSlotIfName(const Expr* e) { - auto n = e->Tag() == EXPR_NAME ? e->AsNameExpr() : nullptr; - return n ? FrameSlot(n->Id()) : -1; - } - - int FrameSlot(const NameExpr* id) { return FrameSlot(id->AsNameExpr()->Id()); } - int Frame1Slot(const NameExpr* id, ZOp op) { return Frame1Slot(id->AsNameExpr()->Id(), op); } - - int Frame1Slot(const ID* id, ZOp op) { return Frame1Slot(id, op1_flavor[op]); } - int Frame1Slot(const NameExpr* n, ZAMOp1Flavor fl) { return Frame1Slot(n->Id(), fl); } - int Frame1Slot(const ID* id, ZAMOp1Flavor fl); - - // The slot without doing any global-related checking. - int RawSlot(const NameExpr* n) { return RawSlot(n->Id()); } - int RawSlot(const ID* id); - - bool HasFrameSlot(const ID* id) const; - - int NewSlot(const TypePtr& t) { return NewSlot(ZVal::IsManagedType(t)); } - int NewSlot(bool is_managed); - - int TempForConst(const ConstExpr* c); - - //////////////////////////////////////////////////////////// - // The following methods relate to optimizing the low-level - // ZAM function body after it is initially generated. They're - // factored out into ZOpt.cc since they're structurally quite - // different from the methods above that relate to the initial - // compilation. - - // Optimizing the low-level compiled instructions. - void OptimizeInsts(); - - // Tracks which instructions can be branched to via the given - // set of switches. - template - void TallySwitchTargets(const CaseMapsI& switches); - - // Remove code that can't be reached. True if some removal happened. - bool RemoveDeadCode(); - - // Collapse chains of gotos. True if some something changed. - bool CollapseGoTos(); - - // Prune statements that are unnecessary. True if something got - // pruned. - bool PruneUnused(); - - // For the current state of insts1, compute lifetimes of frame - // denizens (variable(s) using a given frame slot) in terms of - // first-instruction-to-last-instruction during which they're - // relevant, including consideration for loops. - void ComputeFrameLifetimes(); - - // Given final frame lifetime information, remaps frame members - // with non-overlapping lifetimes to share slots. - void ReMapFrame(); - - // Given final frame lifetime information, remaps slots in the - // interpreter frame. (No longer strictly necessary.) - void ReMapInterpreterFrame(); - - // Computes the remapping for a variable currently in the given slot, - // whose scope begins at the given instruction. - void ReMapVar(const ID* id, int slot, zeek_uint_t inst); - - // Look to initialize the beginning of local lifetime based on slot - // assignment at instruction inst. - void CheckSlotAssignment(int slot, const ZInstI* inst); - - // Track that a local's lifetime begins at the given statement. - void SetLifetimeStart(int slot, const ZInstI* inst); - - // Look for extension of local lifetime based on slot usage - // at instruction inst. - void CheckSlotUse(int slot, const ZInstI* inst); - - // Extend (or create) the end of a local's lifetime. - void ExtendLifetime(int slot, const ZInstI* inst); - - // Returns the (live) instruction at the beginning/end of the loop(s) - // within which the given instruction lies; or that instruction - // itself if it's not inside a loop. The second argument specifies - // the loop depth. For example, a value of '2' means "extend to - // the beginning/end of any loop(s) of depth >= 2". - const ZInstI* BeginningOfLoop(const ZInstI* inst, int depth) const; - const ZInstI* EndOfLoop(const ZInstI* inst, int depth) const; - - // True if any statement other than a frame sync uses the given slot. - bool VarIsUsed(int slot) const; - - // Find the first non-dead instruction after i (inclusive). - // If follow_gotos is true, then if that instruction is - // an unconditional branch, continues the process until - // a different instruction is found (and report if there - // are infinite loops). - // - // First form returns nil if there's nothing live after i. - // Second form returns insts1.size() in that case. - ZInstI* FirstLiveInst(ZInstI* i, bool follow_gotos = false); - zeek_uint_t FirstLiveInst(zeek_uint_t i, bool follow_gotos = false); - - // Same, but not including i. - ZInstI* NextLiveInst(ZInstI* i, bool follow_gotos = false) { - if ( i->inst_num == static_cast(insts1.size()) - 1 ) - return nullptr; - return FirstLiveInst(insts1[i->inst_num + 1], follow_gotos); - } - int NextLiveInst(int i, bool follow_gotos = false) { return FirstLiveInst(i + 1, follow_gotos); } - - // Mark an instruction as unnecessary and remove its influence on - // other statements. The instruction is indicated as an offset - // into insts1; any labels associated with it are transferred - // to its next live successor, if any. - void KillInst(ZInstI* i) { KillInst(i->inst_num); } - void KillInst(zeek_uint_t i); - - // Helper function for propagating control flow (of a given type) - // backwards, when the instruction at the given offset has been killed. - void BackPropagateCFT(int inst_num, ControlFlowType cf_type); - - // The same, but kills any successor instructions until finding - // one that's labeled. - void KillInsts(ZInstI* i) { KillInsts(i->inst_num); } - void KillInsts(zeek_uint_t i); +// Headers auto-generated by gen-zam. +#include "zeek/ZAM-MethodDecls.h" // The first of these is used as we compile down to ZInstI's. // The second is the final intermediary code. They're separate diff --git a/src/script_opt/ZAM/Driver.h b/src/script_opt/ZAM/Driver.h new file mode 100644 index 0000000000..af0ebb06a5 --- /dev/null +++ b/src/script_opt/ZAM/Driver.h @@ -0,0 +1,31 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for driving the overall ZAM compilation process. +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +void Init(); +void InitGlobals(); +void InitArgs(); +void InitCaptures(); +void InitLocals(); +void TrackMemoryManagement(); + +template +void AdjustSwitchTables(CaseMapsI& abstract_cases); + +template +void ConcretizeSwitchTables(const CaseMapsI& abstract_cases, CaseMaps& concrete_cases); +void ConcretizeSwitches(); + +void RetargetBranches(); +void RemapFrameDenizens(const std::vector& inst1_to_inst2); +void CreateSharedFrameDenizens(); + +void ResolveHookBreaks(); +void ComputeLoopLevels(); +void AdjustBranches(); + +template +void DumpCases(const CaseMaps& cases, const char* type_name) const; +void DumpInsts1(const FrameReMap* remappings); diff --git a/src/script_opt/ZAM/Expr.h b/src/script_opt/ZAM/Expr.h new file mode 100644 index 0000000000..e03df24f3d --- /dev/null +++ b/src/script_opt/ZAM/Expr.h @@ -0,0 +1,79 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for ZAM compilation of expression AST nodes (Expr's). +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +const ZAMStmt CompileExpr(const ExprPtr& e) { return CompileExpr(e.get()); } +const ZAMStmt CompileExpr(const Expr* body); + +const ZAMStmt CompileIncrExpr(const IncrExpr* e); +const ZAMStmt CompileAppendToExpr(const AppendToExpr* e); +const ZAMStmt CompileAdd(const AggrAddExpr* e); +const ZAMStmt CompileDel(const AggrDelExpr* e); +const ZAMStmt CompileAddToExpr(const AddToExpr* e); +const ZAMStmt CompileRemoveFromExpr(const RemoveFromExpr* e); +const ZAMStmt CompileAssignExpr(const AssignExpr* e); +const ZAMStmt CompileRecFieldUpdates(const RecordFieldUpdatesExpr* e); +const ZAMStmt CompileZAMBuiltin(const NameExpr* lhs, const ScriptOptBuiltinExpr* zbi); +const ZAMStmt CompileAssignToIndex(const NameExpr* lhs, const IndexExpr* rhs); +const ZAMStmt CompileFieldLHSAssignExpr(const FieldLHSAssignExpr* e); +const ZAMStmt CompileScheduleExpr(const ScheduleExpr* e); +const ZAMStmt CompileSchedule(const NameExpr* n, const ConstExpr* c, int is_interval, EventHandler* h, + const ListExpr* l); +const ZAMStmt CompileEvent(EventHandler* h, const ListExpr* l); + +const ZAMStmt CompileInExpr(const NameExpr* n1, const NameExpr* n2, const NameExpr* n3) { + return CompileInExpr(n1, n2, nullptr, n3, nullptr); +} + +const ZAMStmt CompileInExpr(const NameExpr* n1, const NameExpr* n2, const ConstExpr* c) { + return CompileInExpr(n1, n2, nullptr, nullptr, c); +} + +const ZAMStmt CompileInExpr(const NameExpr* n1, const ConstExpr* c, const NameExpr* n3) { + return CompileInExpr(n1, nullptr, c, n3, nullptr); +} + +// In the following, one of n2 or c2 (likewise, n3/c3) will be nil. +const ZAMStmt CompileInExpr(const NameExpr* n1, const NameExpr* n2, const ConstExpr* c2, const NameExpr* n3, + const ConstExpr* c3); + +const ZAMStmt CompileInExpr(const NameExpr* n1, const ListExpr* l, const NameExpr* n2) { + return CompileInExpr(n1, l, n2, nullptr); +} + +const ZAMStmt CompileInExpr(const NameExpr* n, const ListExpr* l, const ConstExpr* c) { + return CompileInExpr(n, l, nullptr, c); +} + +const ZAMStmt CompileInExpr(const NameExpr* n1, const ListExpr* l, const NameExpr* n2, const ConstExpr* c); + +const ZAMStmt CompileIndex(const NameExpr* n1, const NameExpr* n2, const ListExpr* l, bool in_when); +const ZAMStmt CompileIndex(const NameExpr* n1, const ConstExpr* c, const ListExpr* l, bool in_when); +const ZAMStmt CompileIndex(const NameExpr* n1, int n2_slot, const TypePtr& n2_type, const ListExpr* l, bool in_when); + +const ZAMStmt BuildLambda(const NameExpr* n, ExprPtr le); +const ZAMStmt BuildLambda(int n_slot, ExprPtr le); + +const ZAMStmt AssignVecElems(const Expr* e); +const ZAMStmt AssignTableElem(const Expr* e); + +const ZAMStmt Call(const ExprStmt* e); +const ZAMStmt AssignToCall(const ExprStmt* e); +bool CheckForBuiltIn(const ExprPtr& e, CallExprPtr c); +const ZAMStmt DoCall(const CallExpr* c, const NameExpr* n); + +const ZAMStmt ConstructTable(const NameExpr* n, const Expr* e); +const ZAMStmt ConstructSet(const NameExpr* n, const Expr* e); +const ZAMStmt ConstructRecord(const NameExpr* n, const Expr* e) { return ConstructRecord(n, e, false); } +const ZAMStmt ConstructRecordFromRecord(const NameExpr* n, const Expr* e) { return ConstructRecord(n, e, true); } +const ZAMStmt ConstructRecord(const NameExpr* n, const Expr* e, bool is_from_rec); +const ZAMStmt ConstructVector(const NameExpr* n, const Expr* e); + +const ZAMStmt ArithCoerce(const NameExpr* n, const Expr* e); +const ZAMStmt RecordCoerce(const NameExpr* n, const Expr* e); +const ZAMStmt TableCoerce(const NameExpr* n, const Expr* e); +const ZAMStmt VectorCoerce(const NameExpr* n, const Expr* e); + +const ZAMStmt Is(const NameExpr* n, const Expr* e); diff --git a/src/script_opt/ZAM/Inst-Gen.h b/src/script_opt/ZAM/Inst-Gen.h index 9cc7993615..253b4dcd59 100644 --- a/src/script_opt/ZAM/Inst-Gen.h +++ b/src/script_opt/ZAM/Inst-Gen.h @@ -4,8 +4,7 @@ // NameExpr*'s to slots. Some aren't needed, but we provide a complete // set mirroring the ZInstI constructors for consistency. // -// Maintained separately from Compile.h to make it conceptually simple to -// add new helpers. +// This file is included by Compile.h to insert into the ZAMCompiler class. ZInstI GenInst(ZOp op); ZInstI GenInst(ZOp op, const NameExpr* v1); diff --git a/src/script_opt/ZAM/Low-Level.h b/src/script_opt/ZAM/Low-Level.h new file mode 100644 index 0000000000..d40802f981 --- /dev/null +++ b/src/script_opt/ZAM/Low-Level.h @@ -0,0 +1,42 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for low-level manipulation of ZAM instructions/statements. +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +const ZAMStmt StartingBlock(); +const ZAMStmt FinishBlock(const ZAMStmt start); + +bool NullStmtOK() const; + +const ZAMStmt EmptyStmt(); +const ZAMStmt ErrorStmt(); +const ZAMStmt LastInst(); + +// Adds control flow information to an instruction. +void AddCFT(ZInstI* inst, ControlFlowType cft); + +// Returns a handle to state associated with building +// up a list of values. +std::unique_ptr BuildVals(const ListExprPtr&); + +// "stride" is how many slots each element of l will consume. +ZInstAux* InternalBuildVals(const ListExpr* l, int stride = 1); + +// Returns how many values were added. +int InternalAddVal(ZInstAux* zi, int i, Expr* e); + +// Adds the given instruction to the ZAM program. The second +// argument, if true, suppresses generation of any pending +// global/capture store for this instruction. +const ZAMStmt AddInst(const ZInstI& inst, bool suppress_non_local = false); + +// Returns the statement just before the given one. +ZAMStmt PrevStmt(const ZAMStmt s); + +// Returns the last (interpreter) statement in the body. +const Stmt* LastStmt(const Stmt* s) const; + +// Returns the most recent added instruction *other* than those +// added for bookkeeping. +ZInstI* TopMainInst() { return insts1[top_main_inst]; } diff --git a/src/script_opt/ZAM/Stmt.h b/src/script_opt/ZAM/Stmt.h new file mode 100644 index 0000000000..8a85ed4539 --- /dev/null +++ b/src/script_opt/ZAM/Stmt.h @@ -0,0 +1,48 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for ZAM compilation of statement AST nodes (Stmt's). +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +// Note, we first list the AST nodes and then the helper functions, though +// in the definitions source these are intermingled. +const ZAMStmt CompileStmt(const StmtPtr& body) { return CompileStmt(body.get()); } +const ZAMStmt CompileStmt(const Stmt* body); + +const ZAMStmt CompilePrint(const PrintStmt* ps); +const ZAMStmt CompileExpr(const ExprStmt* es); +const ZAMStmt CompileIf(const IfStmt* is); +const ZAMStmt CompileSwitch(const SwitchStmt* sw); +const ZAMStmt CompileWhile(const WhileStmt* ws); +const ZAMStmt CompileFor(const ForStmt* f); +const ZAMStmt CompileReturn(const ReturnStmt* r); +const ZAMStmt CompileCatchReturn(const CatchReturnStmt* cr); +const ZAMStmt CompileStmts(const StmtList* sl); +const ZAMStmt CompileInit(const InitStmt* is); +const ZAMStmt CompileWhen(const WhenStmt* ws); + +const ZAMStmt CompileNext() { return GenGoTo(nexts.back()); } +const ZAMStmt CompileBreak() { return GenGoTo(breaks.back()); } +const ZAMStmt CompileFallThrough() { return GenGoTo(fallthroughs.back()); } +const ZAMStmt CompileCatchReturn() { return GenGoTo(catches.back()); } + +const ZAMStmt IfElse(const Expr* e, const Stmt* s1, const Stmt* s2); +// Second argument is which instruction slot holds the branch target. +const ZAMStmt GenCond(const Expr* e, int& branch_v); + +const ZAMStmt While(const Stmt* cond_stmt, const Expr* cond, const Stmt* body); + +const ZAMStmt ValueSwitch(const SwitchStmt* sw, const NameExpr* v, const ConstExpr* c); +const ZAMStmt TypeSwitch(const SwitchStmt* sw, const NameExpr* v, const ConstExpr* c); +const ZAMStmt GenSwitch(const SwitchStmt* sw, int slot, InternalTypeTag it); + +const ZAMStmt LoopOverTable(const ForStmt* f, const NameExpr* val); +const ZAMStmt LoopOverVector(const ForStmt* f, const NameExpr* val); +const ZAMStmt LoopOverString(const ForStmt* f, const Expr* e); + +const ZAMStmt Loop(const Stmt* body); +const ZAMStmt FinishLoop(const ZAMStmt iter_head, ZInstI& iter_stmt, const Stmt* body, int iter_slot, bool is_table); + +const ZAMStmt InitRecord(IDPtr id, RecordType* rt); +const ZAMStmt InitVector(IDPtr id, VectorType* vt); +const ZAMStmt InitTable(IDPtr id, TableType* tt, Attributes* attrs); diff --git a/src/script_opt/ZAM/Vars.h b/src/script_opt/ZAM/Vars.h new file mode 100644 index 0000000000..5fe46e9fe4 --- /dev/null +++ b/src/script_opt/ZAM/Vars.h @@ -0,0 +1,44 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Methods for managing Zeek function variables. +// +// This file is included by Compile.h to insert into the ZAMCompiler class. + +bool IsUnused(const IDPtr& id, const Stmt* where) const; + +bool IsCapture(const IDPtr& id) const { return IsCapture(id.get()); } +bool IsCapture(const ID* id) const; + +int CaptureOffset(const IDPtr& id) const { return IsCapture(id.get()); } +int CaptureOffset(const ID* id) const; + +void LoadParam(const ID* id); +const ZAMStmt LoadGlobal(const ID* id); +const ZAMStmt LoadCapture(const ID* id); + +int AddToFrame(const ID*); + +int FrameSlot(const IDPtr& id) { return FrameSlot(id.get()); } +int FrameSlot(const ID* id); +int FrameSlotIfName(const Expr* e) { + auto n = e->Tag() == EXPR_NAME ? e->AsNameExpr() : nullptr; + return n ? FrameSlot(n->Id()) : -1; +} + +int FrameSlot(const NameExpr* id) { return FrameSlot(id->AsNameExpr()->Id()); } +int Frame1Slot(const NameExpr* id, ZOp op) { return Frame1Slot(id->AsNameExpr()->Id(), op); } + +int Frame1Slot(const ID* id, ZOp op) { return Frame1Slot(id, op1_flavor[op]); } +int Frame1Slot(const NameExpr* n, ZAMOp1Flavor fl) { return Frame1Slot(n->Id(), fl); } +int Frame1Slot(const ID* id, ZAMOp1Flavor fl); + +// The slot without doing any global-related checking. +int RawSlot(const NameExpr* n) { return RawSlot(n->Id()); } +int RawSlot(const ID* id); + +bool HasFrameSlot(const ID* id) const; + +int NewSlot(const TypePtr& t) { return NewSlot(ZVal::IsManagedType(t)); } +int NewSlot(bool is_managed); + +int TempForConst(const ConstExpr* c);