mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Merge remote-tracking branch 'origin/topic/vern/zam-EH-coalesce'
* origin/topic/vern/zam-EH-coalesce: BTest updates to accommodate event handler coalescence differences BTests for testing that event handler coalescence operates as expected coalescing of event handlers (ZAM optimization) Minor fixups during merge as commented on the PR.
This commit is contained in:
commit
b0a200a5dc
17 changed files with 395 additions and 69 deletions
8
CHANGES
8
CHANGES
|
@ -1,3 +1,11 @@
|
||||||
|
6.2.0-dev.147 | 2023-11-17 18:00:32 +0100
|
||||||
|
|
||||||
|
* BTest updates to accommodate event handler coalescence differences (Vern Paxson, Corelight)
|
||||||
|
|
||||||
|
* BTests for testing that event handler coalescence operates as expected (Vern Paxson, Corelight)
|
||||||
|
|
||||||
|
* coalescing of event handlers (ZAM optimization) (Vern Paxson, Corelight)
|
||||||
|
|
||||||
6.2.0-dev.143 | 2023-11-17 09:24:36 -0700
|
6.2.0-dev.143 | 2023-11-17 09:24:36 -0700
|
||||||
|
|
||||||
* CI: Install missing packages on opensuse (Tim Wojtulewicz, Corelight)
|
* CI: Install missing packages on opensuse (Tim Wojtulewicz, Corelight)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
6.2.0-dev.143
|
6.2.0-dev.147
|
||||||
|
|
|
@ -170,13 +170,16 @@ EventGroup::EventGroup(EventGroupKind kind, std::string_view name) : kind(kind),
|
||||||
// from the public zeek:: namespace.
|
// from the public zeek:: namespace.
|
||||||
void EventGroup::UpdateFuncBodies() {
|
void EventGroup::UpdateFuncBodies() {
|
||||||
static auto is_group_disabled = [](const auto& g) { return g->IsDisabled(); };
|
static auto is_group_disabled = [](const auto& g) { return g->IsDisabled(); };
|
||||||
|
static auto is_body_enabled = [](const auto& b) { return ! b.disabled; };
|
||||||
|
|
||||||
for ( auto& func : funcs ) {
|
for ( auto& func : funcs ) {
|
||||||
for ( auto& b : func->bodies )
|
func->has_enabled_bodies = false;
|
||||||
|
func->all_bodies_enabled = true;
|
||||||
|
for ( auto& b : func->bodies ) {
|
||||||
b.disabled = std::any_of(b.groups.cbegin(), b.groups.cend(), is_group_disabled);
|
b.disabled = std::any_of(b.groups.cbegin(), b.groups.cend(), is_group_disabled);
|
||||||
|
func->has_enabled_bodies |= is_body_enabled(b);
|
||||||
static auto is_body_enabled = [](const auto& b) { return ! b.disabled; };
|
func->all_bodies_enabled &= is_body_enabled(b);
|
||||||
func->has_enabled_bodies = std::any_of(func->bodies.cbegin(), func->bodies.cend(), is_body_enabled);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,13 @@ public:
|
||||||
*/
|
*/
|
||||||
bool HasEnabledBodies() const { return ! bodies.empty() && has_enabled_bodies; };
|
bool HasEnabledBodies() const { return ! bodies.empty() && has_enabled_bodies; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is every body enabled?
|
||||||
|
*
|
||||||
|
* @return true if all bodies are enabled. (If no bodies, then true.)
|
||||||
|
*/
|
||||||
|
bool HasAllBodiesEnabled() const { return all_bodies_enabled; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls a Zeek function.
|
* Calls a Zeek function.
|
||||||
* @param args the list of arguments to the function call.
|
* @param args the list of arguments to the function call.
|
||||||
|
@ -152,6 +159,7 @@ private:
|
||||||
// expose accessors in the zeek:: public interface.
|
// expose accessors in the zeek:: public interface.
|
||||||
friend class EventGroup;
|
friend class EventGroup;
|
||||||
bool has_enabled_bodies = true;
|
bool has_enabled_bodies = true;
|
||||||
|
bool all_bodies_enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "zeek/script_opt/Inline.h"
|
#include "zeek/script_opt/Inline.h"
|
||||||
|
|
||||||
#include "zeek/Desc.h"
|
#include "zeek/Desc.h"
|
||||||
|
#include "zeek/EventRegistry.h"
|
||||||
|
#include "zeek/module_util.h"
|
||||||
#include "zeek/script_opt/ProfileFunc.h"
|
#include "zeek/script_opt/ProfileFunc.h"
|
||||||
#include "zeek/script_opt/ScriptOpt.h"
|
#include "zeek/script_opt/ScriptOpt.h"
|
||||||
#include "zeek/script_opt/StmtOptInfo.h"
|
#include "zeek/script_opt/StmtOptInfo.h"
|
||||||
|
@ -99,13 +101,13 @@ void Inliner::Analyze() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( auto& f : funcs ) {
|
for ( auto& f : funcs ) {
|
||||||
|
if ( f.ShouldSkip() )
|
||||||
|
continue;
|
||||||
|
|
||||||
const auto& func_ptr = f.FuncPtr();
|
const auto& func_ptr = f.FuncPtr();
|
||||||
const auto& func = func_ptr.get();
|
const auto& func = func_ptr.get();
|
||||||
const auto& body = f.Body();
|
const auto& body = f.Body();
|
||||||
|
|
||||||
if ( ! should_analyze(func_ptr, body) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Candidates are non-event, non-hook, non-recursive,
|
// Candidates are non-event, non-hook, non-recursive,
|
||||||
// non-compiled functions ...
|
// non-compiled functions ...
|
||||||
if ( func->Flavor() != FUNC_FLAVOR_FUNCTION )
|
if ( func->Flavor() != FUNC_FLAVOR_FUNCTION )
|
||||||
|
@ -120,33 +122,165 @@ void Inliner::Analyze() {
|
||||||
inline_ables[func] = f.Profile();
|
inline_ables[func] = f.Profile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoalesceEventHandlers();
|
||||||
|
|
||||||
for ( auto& f : funcs )
|
for ( auto& f : funcs )
|
||||||
if ( should_analyze(f.FuncPtr(), f.Body()) )
|
if ( f.ShouldAnalyze() )
|
||||||
InlineFunction(&f);
|
InlineFunction(&f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inliner::CoalesceEventHandlers() {
|
||||||
|
std::unordered_map<ScriptFunc*, size_t> event_handlers;
|
||||||
|
BodyInfo body_to_info;
|
||||||
|
for ( size_t i = 0U; i < funcs.size(); ++i ) {
|
||||||
|
auto& f = funcs[i];
|
||||||
|
if ( ! f.ShouldAnalyze() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto& func_ptr = f.FuncPtr();
|
||||||
|
const auto& func = func_ptr.get();
|
||||||
|
const auto& func_type = func->GetType();
|
||||||
|
|
||||||
|
if ( func_type->AsFuncType()->Flavor() != FUNC_FLAVOR_EVENT )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Special-case: zeek_init both has tons of event handlers (even
|
||||||
|
// with -b), such that it inevitably blows out the inlining budget,
|
||||||
|
// *and* only runs once, such that even if we could inline it, if
|
||||||
|
// it takes more time to compile it than to just run it via the
|
||||||
|
// interpreter, it's a lose.
|
||||||
|
static std::string zeek_init_name = "zeek_init";
|
||||||
|
if ( func->Name() == zeek_init_name )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto& body = f.Body();
|
||||||
|
|
||||||
|
if ( func->GetKind() == Func::SCRIPT_FUNC && func->GetBodies().size() > 1 ) {
|
||||||
|
++event_handlers[func];
|
||||||
|
ASSERT(body_to_info.count(body.get()) == 0);
|
||||||
|
body_to_info[body.get()] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( auto& e : event_handlers ) {
|
||||||
|
auto func = e.first;
|
||||||
|
auto& bodies = func->GetBodies();
|
||||||
|
if ( bodies.size() != e.second )
|
||||||
|
// It's potentially unsound to inline some-but-not-all event
|
||||||
|
// handlers, because doing so may violate &priority's. We
|
||||||
|
// could do the work of identifying such instances and only
|
||||||
|
// skipping those, but given that ZAM is feature-complete
|
||||||
|
// the mismatch here should only arise when using restrictions
|
||||||
|
// like --optimize-file, which likely aren't the common case.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CoalesceEventHandlers({NewRef{}, func}, bodies, body_to_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inliner::CoalesceEventHandlers(ScriptFuncPtr func, const std::vector<Func::Body>& bodies,
|
||||||
|
const BodyInfo& body_to_info) {
|
||||||
|
auto merged_body = make_intrusive<StmtList>();
|
||||||
|
auto oi = merged_body->GetOptInfo();
|
||||||
|
|
||||||
|
auto& params = func->GetType()->Params();
|
||||||
|
auto nparams = params->NumFields();
|
||||||
|
size_t init_frame_size = static_cast<size_t>(nparams);
|
||||||
|
|
||||||
|
PreInline(oi, init_frame_size);
|
||||||
|
|
||||||
|
// We pattern the new (alternate) body off of the first body.
|
||||||
|
auto& b0 = func->GetBodies()[0].stmts;
|
||||||
|
auto b0_info = body_to_info.find(b0.get());
|
||||||
|
ASSERT(b0_info != body_to_info.end());
|
||||||
|
auto& info0 = funcs[b0_info->second];
|
||||||
|
auto& scope0 = info0.Scope();
|
||||||
|
auto& vars = scope0->OrderedVars();
|
||||||
|
|
||||||
|
// We need to create a new Scope. Otherwise, when inlining the first
|
||||||
|
// body the analysis of identifiers gets confused regarding whether
|
||||||
|
// a given identifier represents the outer instance or the inner.
|
||||||
|
auto empty_attrs = std::make_unique<std::vector<AttrPtr>>();
|
||||||
|
push_scope(scope0->GetID(), std::move(empty_attrs));
|
||||||
|
|
||||||
|
std::vector<IDPtr> param_ids;
|
||||||
|
|
||||||
|
for ( auto i = 0; i < nparams; ++i ) {
|
||||||
|
auto& vi = vars[i];
|
||||||
|
// We use a special scope name so that when debugging issues we can
|
||||||
|
// see that a given variable came from coalescing event handlers.
|
||||||
|
auto p = install_ID(vi->Name(), "<event>", false, false);
|
||||||
|
p->SetType(vi->GetType());
|
||||||
|
param_ids.push_back(std::move(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_scope = pop_scope();
|
||||||
|
|
||||||
|
// Build up the calling arguments.
|
||||||
|
auto args = make_intrusive<ListExpr>();
|
||||||
|
for ( auto& p : param_ids )
|
||||||
|
args->Append(make_intrusive<NameExpr>(p));
|
||||||
|
|
||||||
|
for ( auto& b : bodies ) {
|
||||||
|
auto bp = b.stmts;
|
||||||
|
auto bi_find = body_to_info.find(bp.get());
|
||||||
|
ASSERT(bi_find != body_to_info.end());
|
||||||
|
auto& bi = funcs[bi_find->second];
|
||||||
|
auto ie = DoInline(func, bp, args, bi.Scope(), bi.Profile());
|
||||||
|
|
||||||
|
if ( ! ie )
|
||||||
|
// Failure presumably occurred due to hitting the maximum
|
||||||
|
// AST complexity for inlining. We can give up by simply
|
||||||
|
// returning, as at this point we haven't made any actual
|
||||||
|
// changes other than the function's scope.
|
||||||
|
return;
|
||||||
|
|
||||||
|
merged_body->Stmts().push_back(make_intrusive<ExprStmt>(ie));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto inlined_func = make_intrusive<CoalescedScriptFunc>(merged_body, new_scope, func);
|
||||||
|
inlined_func->SetScope(new_scope);
|
||||||
|
|
||||||
|
// Replace the function for that EventHandler with the delegating one.
|
||||||
|
auto* eh = event_registry->Lookup(func->Name());
|
||||||
|
ASSERT(eh);
|
||||||
|
eh->SetFunc(inlined_func);
|
||||||
|
|
||||||
|
// Likewise, replace the value of the identifier.
|
||||||
|
auto fid = lookup_ID(func->Name(), GLOBAL_MODULE_NAME, false, false, false);
|
||||||
|
ASSERT(fid);
|
||||||
|
fid->SetVal(make_intrusive<FuncVal>(inlined_func));
|
||||||
|
|
||||||
|
PostInline(oi, inlined_func);
|
||||||
|
|
||||||
|
funcs.emplace_back(inlined_func, new_scope, merged_body, 0);
|
||||||
|
|
||||||
|
auto pf = std::make_shared<ProfileFunc>(inlined_func.get(), merged_body, true);
|
||||||
|
funcs.back().SetProfile(std::move(pf));
|
||||||
|
}
|
||||||
|
|
||||||
void Inliner::InlineFunction(FuncInfo* f) {
|
void Inliner::InlineFunction(FuncInfo* f) {
|
||||||
max_inlined_frame_size = 0;
|
|
||||||
|
|
||||||
// It's important that we take the current frame size from the
|
|
||||||
// *scope* and not f->Func(). The latter tracks the maximum required
|
|
||||||
// across all bodies, but we want to track the size for this
|
|
||||||
// particular body.
|
|
||||||
curr_frame_size = f->Scope()->Length();
|
|
||||||
|
|
||||||
auto oi = f->Body()->GetOptInfo();
|
auto oi = f->Body()->GetOptInfo();
|
||||||
|
PreInline(oi, f->Scope()->Length());
|
||||||
|
f->Body()->Inline(this);
|
||||||
|
PostInline(oi, f->FuncPtr());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inliner::PreInline(StmtOptInfo* oi, size_t frame_size) {
|
||||||
|
max_inlined_frame_size = 0;
|
||||||
|
curr_frame_size = frame_size;
|
||||||
num_stmts = oi->num_stmts;
|
num_stmts = oi->num_stmts;
|
||||||
num_exprs = oi->num_exprs;
|
num_exprs = oi->num_exprs;
|
||||||
|
}
|
||||||
|
|
||||||
f->Body()->Inline(this);
|
void Inliner::PostInline(StmtOptInfo* oi, ScriptFuncPtr f) {
|
||||||
|
|
||||||
oi->num_stmts = num_stmts;
|
oi->num_stmts = num_stmts;
|
||||||
oi->num_exprs = num_exprs;
|
oi->num_exprs = num_exprs;
|
||||||
|
|
||||||
int new_frame_size = curr_frame_size + max_inlined_frame_size;
|
int new_frame_size = curr_frame_size + max_inlined_frame_size;
|
||||||
|
|
||||||
if ( new_frame_size > f->Func()->FrameSize() )
|
if ( new_frame_size > f->FrameSize() )
|
||||||
f->Func()->SetFrameSize(new_frame_size);
|
f->SetFrameSize(new_frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
||||||
|
@ -166,21 +300,21 @@ ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
||||||
if ( ! func_v )
|
if ( ! func_v )
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
auto function = func_v->AsFunc();
|
auto function = func_v->AsFuncVal()->AsFuncPtr();
|
||||||
|
|
||||||
if ( function->GetKind() != Func::SCRIPT_FUNC )
|
if ( function->GetKind() != Func::SCRIPT_FUNC )
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
auto func_vf = static_cast<ScriptFunc*>(function);
|
auto func_vf = cast_intrusive<ScriptFunc>(function);
|
||||||
|
|
||||||
auto ia = inline_ables.find(func_vf);
|
auto ia = inline_ables.find(func_vf.get());
|
||||||
if ( ia == inline_ables.end() )
|
if ( ia == inline_ables.end() )
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
if ( c->IsInWhen() ) {
|
if ( c->IsInWhen() ) {
|
||||||
// Don't inline these, as doing so requires propagating
|
// Don't inline these, as doing so requires propagating
|
||||||
// the in-when attribute to the inlined function body.
|
// the in-when attribute to the inlined function body.
|
||||||
skipped_inlining.insert(func_vf);
|
skipped_inlining.insert(func_vf.get());
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,21 +323,32 @@ ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
||||||
// BiFs, which won't happen here, but instead to script functions that
|
// BiFs, which won't happen here, but instead to script functions that
|
||||||
// are misusing/abusing the loophole.)
|
// are misusing/abusing the loophole.)
|
||||||
if ( function->GetType()->Params()->NumFields() == 1 && c->Args()->Exprs().size() != 1 ) {
|
if ( function->GetType()->Params()->NumFields() == 1 && c->Args()->Exprs().size() != 1 ) {
|
||||||
skipped_inlining.insert(func_vf);
|
skipped_inlining.insert(func_vf.get());
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're going to inline the body, unless it's too large.
|
// We're going to inline the body, unless it's too large.
|
||||||
auto body = func_vf->GetBodies()[0].stmts; // there's only 1 body
|
auto body = func_vf->GetBodies()[0].stmts; // there's only 1 body
|
||||||
|
auto scope = func_vf->GetScope();
|
||||||
|
auto ie = DoInline(func_vf, body, c->ArgsPtr(), scope, ia->second);
|
||||||
|
|
||||||
|
if ( ie ) {
|
||||||
|
ie->SetOriginal(c);
|
||||||
|
did_inline.insert(func_vf.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ie;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprPtr Inliner::DoInline(ScriptFuncPtr sf, StmtPtr body, ListExprPtr args, ScopePtr scope, const ProfileFunc* pf) {
|
||||||
|
// Inline the body, unless it's too large.
|
||||||
auto oi = body->GetOptInfo();
|
auto oi = body->GetOptInfo();
|
||||||
|
|
||||||
if ( num_stmts + oi->num_stmts + num_exprs + oi->num_exprs > MAX_INLINE_SIZE ) {
|
if ( num_stmts + oi->num_stmts + num_exprs + oi->num_exprs > MAX_INLINE_SIZE ) {
|
||||||
skipped_inlining.insert(func_vf);
|
skipped_inlining.insert(sf.get());
|
||||||
return nullptr; // signals "stop inlining"
|
return nullptr; // signals "stop inlining"
|
||||||
}
|
}
|
||||||
|
|
||||||
did_inline.insert(func_vf);
|
|
||||||
|
|
||||||
num_stmts += oi->num_stmts;
|
num_stmts += oi->num_stmts;
|
||||||
num_exprs += oi->num_exprs;
|
num_exprs += oi->num_exprs;
|
||||||
|
|
||||||
|
@ -220,10 +365,8 @@ ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
||||||
// with the scope, which gives us all the variables declared in
|
// with the scope, which gives us all the variables declared in
|
||||||
// the function, *using the knowledge that the parameters are
|
// the function, *using the knowledge that the parameters are
|
||||||
// declared first*.
|
// declared first*.
|
||||||
auto scope = func_vf->GetScope();
|
|
||||||
auto& pf = ia->second;
|
|
||||||
auto& vars = scope->OrderedVars();
|
auto& vars = scope->OrderedVars();
|
||||||
int nparam = func_vf->GetType()->Params()->NumFields();
|
int nparam = sf->GetType()->Params()->NumFields();
|
||||||
|
|
||||||
std::vector<IDPtr> params;
|
std::vector<IDPtr> params;
|
||||||
std::vector<bool> param_is_modified;
|
std::vector<bool> param_is_modified;
|
||||||
|
@ -237,7 +380,7 @@ ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
||||||
// Recursively inline the body. This is safe to do because we've
|
// Recursively inline the body. This is safe to do because we've
|
||||||
// ensured there are no recursive loops ... but we have to be
|
// ensured there are no recursive loops ... but we have to be
|
||||||
// careful in accounting for the frame sizes.
|
// careful in accounting for the frame sizes.
|
||||||
int frame_size = func_vf->FrameSize();
|
int frame_size = sf->FrameSize();
|
||||||
|
|
||||||
int hold_curr_frame_size = curr_frame_size;
|
int hold_curr_frame_size = curr_frame_size;
|
||||||
curr_frame_size = frame_size;
|
curr_frame_size = frame_size;
|
||||||
|
@ -255,12 +398,10 @@ ExprPtr Inliner::CheckForInlining(CallExprPtr c) {
|
||||||
else
|
else
|
||||||
max_inlined_frame_size = hold_max_inlined_frame_size;
|
max_inlined_frame_size = hold_max_inlined_frame_size;
|
||||||
|
|
||||||
auto t = c->GetType();
|
auto t = scope->GetReturnType();
|
||||||
auto ie = make_intrusive<InlineExpr>(c->ArgsPtr(), std::move(params), std::move(param_is_modified), body_dup,
|
|
||||||
curr_frame_size, t);
|
|
||||||
ie->SetOriginal(c);
|
|
||||||
|
|
||||||
return ie;
|
ASSERT(params.size() == args->Exprs().size());
|
||||||
|
return make_intrusive<InlineExpr>(args, params, param_is_modified, body_dup, curr_frame_size, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zeek::detail
|
} // namespace zeek::detail
|
||||||
|
|
|
@ -37,9 +37,34 @@ protected:
|
||||||
// recursively inlines eligible ones.
|
// recursively inlines eligible ones.
|
||||||
void Analyze();
|
void Analyze();
|
||||||
|
|
||||||
|
// Maps an event handler body to its corresponding FuncInfo. For the
|
||||||
|
// latter we use a cursor rather than a direct reference or pointer
|
||||||
|
// because the collection of FuncInfo's are maintained in a vector and
|
||||||
|
// can wind up moving around in memory.
|
||||||
|
using BodyInfo = std::unordered_map<const Stmt*, size_t>;
|
||||||
|
|
||||||
|
// Goes through all the event handlers and coalesces those with
|
||||||
|
// multiple bodies into a single "alternative" body.
|
||||||
|
void CoalesceEventHandlers();
|
||||||
|
|
||||||
|
// For a given event handler, collection of bodies, and associated
|
||||||
|
// function information, creates a new FuncInfo entry reflecting an
|
||||||
|
// alternative body for the event handler with all of the bodies
|
||||||
|
// coalesced into a single new body.
|
||||||
|
void CoalesceEventHandlers(ScriptFuncPtr sf, const std::vector<Func::Body>& bodies, const BodyInfo& body_to_info);
|
||||||
|
|
||||||
// Recursively inlines any calls associated with the given function.
|
// Recursively inlines any calls associated with the given function.
|
||||||
void InlineFunction(FuncInfo* f);
|
void InlineFunction(FuncInfo* f);
|
||||||
|
|
||||||
|
// Performs common functionality prior to inlining a call body.
|
||||||
|
void PreInline(StmtOptInfo* oi, size_t frame_size);
|
||||||
|
|
||||||
|
// Performs common functionality that comes after inlining a call body.
|
||||||
|
void PostInline(StmtOptInfo* oi, ScriptFuncPtr f);
|
||||||
|
|
||||||
|
// Inlines the given body using the given arguments.
|
||||||
|
ExprPtr DoInline(ScriptFuncPtr sf, StmtPtr body, ListExprPtr args, ScopePtr scope, const ProfileFunc* pf);
|
||||||
|
|
||||||
// Information about all of the functions (and events/hooks) in
|
// Information about all of the functions (and events/hooks) in
|
||||||
// the full set of scripts.
|
// the full set of scripts.
|
||||||
std::vector<FuncInfo>& funcs;
|
std::vector<FuncInfo>& funcs;
|
||||||
|
|
|
@ -473,16 +473,14 @@ static void analyze_scripts_for_ZAM(std::unique_ptr<ProfileFuncs>& pfs) {
|
||||||
bool did_one = false;
|
bool did_one = false;
|
||||||
|
|
||||||
for ( auto& f : funcs ) {
|
for ( auto& f : funcs ) {
|
||||||
|
if ( ! f.ShouldAnalyze() )
|
||||||
|
continue;
|
||||||
|
|
||||||
auto func = f.Func();
|
auto func = f.Func();
|
||||||
auto l = lambdas.find(func);
|
auto l = lambdas.find(func);
|
||||||
bool is_lambda = l != lambdas.end();
|
bool is_lambda = l != lambdas.end();
|
||||||
|
|
||||||
if ( ! analysis_options.only_funcs.empty() || ! analysis_options.only_files.empty() ) {
|
if ( ! analysis_options.compile_all && ! is_lambda && inl && inl->WasFullyInlined(func) &&
|
||||||
if ( ! should_analyze(f.FuncPtr(), f.Body()) )
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( ! analysis_options.compile_all && ! is_lambda && inl && inl->WasFullyInlined(func) &&
|
|
||||||
func_used_indirectly.count(func) == 0 ) {
|
func_used_indirectly.count(func) == 0 ) {
|
||||||
// No need to compile as it won't be called directly.
|
// No need to compile as it won't be called directly.
|
||||||
// We'd like to zero out the body to recover the
|
// We'd like to zero out the body to recover the
|
||||||
|
@ -563,7 +561,7 @@ void analyze_scripts(bool no_unused_warnings) {
|
||||||
if ( should_analyze(func.FuncPtr(), func.Body()) )
|
if ( should_analyze(func.FuncPtr(), func.Body()) )
|
||||||
have_one_to_do = true;
|
have_one_to_do = true;
|
||||||
else
|
else
|
||||||
func.SetSkip(true);
|
func.SetShouldNotAnalyze();
|
||||||
|
|
||||||
if ( ! have_one_to_do )
|
if ( ! have_one_to_do )
|
||||||
reporter->FatalError("no matching functions/files for C++ compilation");
|
reporter->FatalError("no matching functions/files for C++ compilation");
|
||||||
|
|
|
@ -134,13 +134,18 @@ public:
|
||||||
const ProfileFunc* Profile() const { return pf.get(); }
|
const ProfileFunc* Profile() const { return pf.get(); }
|
||||||
std::shared_ptr<ProfileFunc> ProfilePtr() const { return pf; }
|
std::shared_ptr<ProfileFunc> ProfilePtr() const { return pf; }
|
||||||
|
|
||||||
|
void SetScope(ScopePtr new_scope) { scope = std::move(new_scope); }
|
||||||
void SetBody(StmtPtr new_body) { body = std::move(new_body); }
|
void SetBody(StmtPtr new_body) { body = std::move(new_body); }
|
||||||
void SetProfile(std::shared_ptr<ProfileFunc> _pf) { pf = std::move(_pf); }
|
void SetProfile(std::shared_ptr<ProfileFunc> _pf) { pf = std::move(_pf); }
|
||||||
|
|
||||||
|
bool ShouldAnalyze() const { return should_analyze; }
|
||||||
|
void SetShouldNotAnalyze() {
|
||||||
|
should_analyze = false;
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
|
||||||
// The following provide a way of marking FuncInfo's as
|
// The following provide a way of marking FuncInfo's as
|
||||||
// should-be-skipped for script optimization, generally because
|
// should-be-skipped for a given phase of script optimization.
|
||||||
// the function body has a property that a given script optimizer
|
|
||||||
// doesn't know how to deal with. Defaults to don't-skip.
|
|
||||||
bool ShouldSkip() const { return skip; }
|
bool ShouldSkip() const { return skip; }
|
||||||
void SetSkip(bool should_skip) { skip = should_skip; }
|
void SetSkip(bool should_skip) { skip = should_skip; }
|
||||||
|
|
||||||
|
@ -151,10 +156,39 @@ protected:
|
||||||
std::shared_ptr<ProfileFunc> pf;
|
std::shared_ptr<ProfileFunc> pf;
|
||||||
int priority;
|
int priority;
|
||||||
|
|
||||||
// Whether to skip optimizing this function.
|
// Whether to analyze this function at all, per optimization selection
|
||||||
|
// via --optimize-file/--optimize-func. If those flags aren't used,
|
||||||
|
// then this will remain true, given that both ZAM and -O gen-C++ are
|
||||||
|
// feature-complete.
|
||||||
|
bool should_analyze = true;
|
||||||
|
|
||||||
|
// Whether to skip optimizing this function in a given context. May be
|
||||||
|
// altered during optimization.
|
||||||
bool skip = false;
|
bool skip = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ScriptFunc subclass that runs a single (coalesced) body if possible,
|
||||||
|
// otherwise delegates to the original function with multiple bodies.
|
||||||
|
class CoalescedScriptFunc : public ScriptFunc {
|
||||||
|
public:
|
||||||
|
CoalescedScriptFunc(StmtPtr merged_body, ScopePtr scope, ScriptFuncPtr orig_func)
|
||||||
|
: ScriptFunc(orig_func->Name(), orig_func->GetType(), {merged_body}, {0}), orig_func(orig_func) {
|
||||||
|
SetScope(scope);
|
||||||
|
};
|
||||||
|
|
||||||
|
ValPtr Invoke(zeek::Args* args, Frame* parent) const override {
|
||||||
|
// If the original function has all bodies enabled, run our
|
||||||
|
// coalesced one, otherwise delegate.
|
||||||
|
if ( orig_func->HasAllBodiesEnabled() )
|
||||||
|
return ScriptFunc::Invoke(args, parent);
|
||||||
|
|
||||||
|
return orig_func->Invoke(args, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScriptFuncPtr orig_func;
|
||||||
|
};
|
||||||
|
|
||||||
// We track which functions are definitely not recursive. We do this
|
// We track which functions are definitely not recursive. We do this
|
||||||
// as the negative, rather than tracking functions known to be recursive,
|
// as the negative, rather than tracking functions known to be recursive,
|
||||||
// so that if we don't do the analysis at all (it's driven by inlining),
|
// so that if we don't do the analysis at all (it's driven by inlining),
|
||||||
|
|
|
@ -1,19 +1 @@
|
||||||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp]
|
|
||||||
ftp field missing
|
|
||||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp]
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
packet counting, 1
|
||||||
|
packet observer, 1
|
||||||
|
packet logging, 1
|
||||||
|
packet counting, 2
|
||||||
|
packet observer, 2
|
||||||
|
packet logging, 2
|
||||||
|
packet counting, 3
|
||||||
|
packet observer, 3
|
||||||
|
packet logging, 3
|
||||||
|
packet counting, 4
|
||||||
|
packet observer, 4
|
||||||
|
packet logging, 4
|
||||||
|
packet counting, 5
|
||||||
|
packet observer, 5
|
||||||
|
packet logging, 5
|
||||||
|
packet counting, 6
|
||||||
|
Log::disable_stream()
|
||||||
|
packet observer, 6
|
||||||
|
packet logging, 6
|
||||||
|
packet counting, 7
|
||||||
|
packet counting, 8
|
||||||
|
packet counting, 9
|
||||||
|
packet counting, 10
|
||||||
|
packet counting, 11
|
||||||
|
packet counting, 12
|
||||||
|
packet counting, 13
|
||||||
|
packet counting, 14
|
||||||
|
packet counting, 15
|
||||||
|
packet counting, 16
|
||||||
|
packet counting, 17
|
||||||
|
packet counting, 18
|
||||||
|
packet counting, 19
|
||||||
|
packet counting, 20
|
||||||
|
packet counting, 21
|
||||||
|
packet counting, 22
|
||||||
|
packet counting, 23
|
||||||
|
packet counting, 24
|
||||||
|
packet counting, 25
|
||||||
|
Log::enable_stream()
|
||||||
|
packet observer, 25
|
||||||
|
packet logging, 25
|
||||||
|
packet counting, 26
|
||||||
|
packet observer, 26
|
||||||
|
packet logging, 26
|
||||||
|
packet counting, 27
|
||||||
|
packet observer, 27
|
||||||
|
packet logging, 27
|
||||||
|
packet counting, 28
|
||||||
|
packet observer, 28
|
||||||
|
packet logging, 28
|
|
@ -0,0 +1,19 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
#separator \x09
|
||||||
|
#set_separator ,
|
||||||
|
#empty_field (empty)
|
||||||
|
#unset_field -
|
||||||
|
#path packet
|
||||||
|
#open XXXX-XX-XX-XX-XX-XX
|
||||||
|
#fields ts c ttl len
|
||||||
|
#types time count count count
|
||||||
|
XXXXXXXXXX.XXXXXX 1 64 66
|
||||||
|
XXXXXXXXXX.XXXXXX 2 59 117
|
||||||
|
XXXXXXXXXX.XXXXXX 3 64 80
|
||||||
|
XXXXXXXXXX.XXXXXX 4 59 127
|
||||||
|
XXXXXXXXXX.XXXXXX 5 64 66
|
||||||
|
XXXXXXXXXX.XXXXXX 25 64 64
|
||||||
|
XXXXXXXXXX.XXXXXX 26 59 159
|
||||||
|
XXXXXXXXXX.XXXXXX 27 64 64
|
||||||
|
XXXXXXXXXX.XXXXXX 28 59 226
|
||||||
|
#close XXXX-XX-XX-XX-XX-XX
|
5
testing/btest/Baseline/opt.coalescence/output
Normal file
5
testing/btest/Baseline/opt.coalescence/output
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
my_event
|
||||||
|
ZAM-code my_event
|
||||||
|
second instance, higher priority
|
||||||
|
first instance, lower priority
|
5
testing/btest/Baseline/opt.no-coalescence/output
Normal file
5
testing/btest/Baseline/opt.no-coalescence/output
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
my_event
|
||||||
|
ZAM-code my_event ZAM-code my_event
|
||||||
|
second instance, higher priority
|
||||||
|
first instance, lower priority
|
23
testing/btest/opt/coalescence.zeek
Normal file
23
testing/btest/opt/coalescence.zeek
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# @TEST-DOC: Ensure that event coalescence works properly.
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: zeek -b -O ZAM %INPUT >output
|
||||||
|
# @TEST-EXEC: btest-diff output
|
||||||
|
|
||||||
|
event my_event() &priority=-10
|
||||||
|
{
|
||||||
|
print "first instance, lower priority";
|
||||||
|
}
|
||||||
|
|
||||||
|
event my_event() &priority=10
|
||||||
|
{
|
||||||
|
print "second instance, higher priority";
|
||||||
|
}
|
||||||
|
|
||||||
|
event zeek_init()
|
||||||
|
{
|
||||||
|
# This should print a single event handler body.
|
||||||
|
print my_event;
|
||||||
|
|
||||||
|
# Make sure execution of both handlers happens properly.
|
||||||
|
event my_event();
|
||||||
|
}
|
23
testing/btest/opt/no-coalescence.zeek
Normal file
23
testing/btest/opt/no-coalescence.zeek
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# @TEST-DOC: Ensure that event coalescence doesn't happen if inlining turned off.
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: zeek -b -O ZAM -O no-inline %INPUT >output
|
||||||
|
# @TEST-EXEC: btest-diff output
|
||||||
|
|
||||||
|
event my_event() &priority=-10
|
||||||
|
{
|
||||||
|
print "first instance, lower priority";
|
||||||
|
}
|
||||||
|
|
||||||
|
event my_event() &priority=10
|
||||||
|
{
|
||||||
|
print "second instance, higher priority";
|
||||||
|
}
|
||||||
|
|
||||||
|
event zeek_init()
|
||||||
|
{
|
||||||
|
# This should print two event handler bodies.
|
||||||
|
print my_event;
|
||||||
|
|
||||||
|
# Make sure execution of both handlers happens properly.
|
||||||
|
event my_event();
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
# @TEST-REQUIRES: test "${ZEEK_USE_CPP}" != "1"
|
# @TEST-REQUIRES: test "${ZEEK_USE_CPP}" != "1"
|
||||||
|
# @TEST-REQUIRES: test "${ZEEK_ZAM}" != "1"
|
||||||
# @TEST-EXEC: zeek -b -O inline %INPUT >output
|
# @TEST-EXEC: zeek -b -O inline %INPUT >output
|
||||||
# @TEST-EXEC: btest-diff output
|
# @TEST-EXEC: btest-diff output
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# @TEST-EXEC: btest-bg-run zeek zeek -b ../dirtest.zeek
|
# @TEST-EXEC: btest-bg-run zeek zeek -b ../dirtest.zeek
|
||||||
# @TEST-EXEC: $SCRIPTS/wait-for-file zeek/next1 10 || (btest-bg-wait -k 1 && false)
|
# @TEST-EXEC: $SCRIPTS/wait-for-file zeek/next1 20 || (btest-bg-wait -k 1 && false)
|
||||||
# @TEST-EXEC: rm testdir/bye
|
# @TEST-EXEC: rm testdir/bye
|
||||||
# @TEST-EXEC: touch testdir/newone
|
# @TEST-EXEC: touch testdir/newone
|
||||||
# @TEST-EXEC: $SCRIPTS/wait-for-file zeek/next2 10 || (btest-bg-wait -k 1 && false)
|
# @TEST-EXEC: $SCRIPTS/wait-for-file zeek/next2 10 || (btest-bg-wait -k 1 && false)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue