added &no_ZAM_opt/&no_CPP_opt attributes and --no-opt-files/--no-opt-funcs for controlling skipping script optimization

This commit is contained in:
Vern Paxson 2025-09-12 11:03:37 -06:00
parent b25a844210
commit acedb1cb9a
16 changed files with 191 additions and 57 deletions

View file

@ -41,6 +41,8 @@ const char* attr_name(AttrTag t) {
"&is_assigned",
"&is_used",
"&ordered",
"&no_ZAM_opt",
"&no_CPP_opt",
};
// clang-format on
@ -194,9 +196,19 @@ void Attributes::AddAttr(AttrPtr attr, bool is_redef) {
return true;
}
return new_tag == ATTR_LOG || new_tag == ATTR_OPTIONAL || new_tag == ATTR_REDEF ||
new_tag == ATTR_BROKER_STORE_ALLOW_COMPLEX || new_tag == ATTR_RAW_OUTPUT ||
new_tag == ATTR_ERROR_HANDLER || new_tag == ATTR_IS_USED;
static const std::set<AttrTag> acceptable = {
ATTR_BROKER_STORE_ALLOW_COMPLEX,
ATTR_ERROR_HANDLER,
ATTR_IS_USED,
ATTR_LOG,
ATTR_NO_CPP_OPT,
ATTR_NO_ZAM_OPT,
ATTR_OPTIONAL,
ATTR_RAW_OUTPUT,
ATTR_REDEF,
};
return acceptable.contains(new_tag);
};
// A `redef` is allowed to overwrite an existing attribute instead of
@ -217,7 +229,7 @@ void Attributes::AddAttr(AttrPtr attr, bool is_redef) {
// that's a signal to skip the checking. If the type is error,
// there's no point checking attributes either.
if ( type && ! IsErrorType(type->Tag()) ) {
if ( ! CheckAttr(attr.get()) ) {
if ( ! CheckAttr(attr.get(), type) ) {
// Get rid of it, so we don't get error cascades down the line.
RemoveAttr(attr->Tag());
return;
@ -285,7 +297,7 @@ void Attributes::DescribeReST(ODesc* d, bool shorten) const {
}
}
bool Attributes::CheckAttr(Attr* a) {
bool Attributes::CheckAttr(Attr* a, const TypePtr& attrs_t) {
switch ( a->Tag() ) {
case ATTR_DEPRECATED:
case ATTR_REDEF:
@ -541,6 +553,15 @@ bool Attributes::CheckAttr(Attr* a) {
return AttrError("&ordered only applicable to tables");
break;
case ATTR_NO_ZAM_OPT:
case ATTR_NO_CPP_OPT: {
bool is_no_zam = a->Tag() == ATTR_NO_ZAM_OPT;
if ( attrs_t->Tag() != TYPE_FUNC ) {
Error(util::fmt("&no_%s_opt must apply to a function", is_no_zam ? "ZAM" : "CPP"), attrs_t.get());
return false;
}
} break;
default: BadTag("Attributes::CheckAttr", attr_name(a->Tag()));
}

View file

@ -50,6 +50,8 @@ enum AttrTag : uint8_t {
ATTR_IS_ASSIGNED, // to suppress usage warnings
ATTR_IS_USED, // to suppress usage warnings
ATTR_ORDERED, // used to store tables in ordered mode
ATTR_NO_ZAM_OPT, // avoid ZAM optimization
ATTR_NO_CPP_OPT, // avoid -O gen-C++ optimization
NUM_ATTRS // this item should always be last
};
@ -138,7 +140,7 @@ public:
protected:
// Returns true if the attribute is okay, false if not.
bool CheckAttr(Attr* attr);
bool CheckAttr(Attr* attr, const TypePtr& attrs_t);
// Reports an attribute error and returns false (handy for CheckAttr()).
bool AttrError(const char* msg);

View file

@ -817,7 +817,10 @@ static int get_func_priority(const std::vector<AttrPtr>& attrs) {
int priority = 0;
for ( const auto& a : attrs ) {
if ( a->Tag() == ATTR_DEPRECATED || a->Tag() == ATTR_IS_USED || a->Tag() == ATTR_GROUP )
static const std::set<AttrTag> ok_for_func = {
ATTR_DEPRECATED, ATTR_GROUP, ATTR_IS_USED, ATTR_NO_CPP_OPT, ATTR_NO_ZAM_OPT,
};
if ( ok_for_func.contains(a->Tag()) )
continue;
if ( a->Tag() != ATTR_PRIORITY ) {
@ -888,12 +891,16 @@ FunctionIngredients::FunctionIngredients(ScopePtr _scope, StmtPtr _body, const s
groups = get_func_groups(*attrs);
for ( const auto& a : *attrs )
if ( a->Tag() == ATTR_IS_USED ) {
for ( const auto& a : *attrs ) {
static const std::set<AttrTag> assoc_with_id = {
ATTR_IS_USED,
ATTR_NO_CPP_OPT,
ATTR_NO_ZAM_OPT,
};
if ( assoc_with_id.contains(a->Tag()) )
// Associate this with the identifier, too.
id->AddAttr(make_intrusive<Attr>(ATTR_IS_USED));
break;
}
id->AddAttr(make_intrusive<Attr>(a->Tag()));
}
}
else
priority = 0;

View file

@ -124,9 +124,15 @@ void usage(const char* prog) {
printf(
" -0|--optimize-files=<pat> | enable script optimization for all "
"functions in files with names containing the given pattern\n");
printf(
" --no-opt-files=<pat> | skip script optimization for all "
"functions in files with names containing the given pattern\n");
printf(
" -o|--optimize-funcs=<pat> | enable script optimization for "
"functions with names fully matching the given pattern\n");
printf(
" --no-opt-funcs=<pat> | skip script optimization for "
"functions with names fully matching the given pattern\n");
printf(" -P|--prime-dns | prime DNS\n");
printf(" -Q|--time | print execution time summary to stderr\n");
printf(" -S|--debug-rules | enable rule debugging\n");
@ -393,6 +399,8 @@ Options parse_cmdline(int argc, char** argv) {
{"re-level", required_argument, nullptr, 'T'},
{"watchdog", no_argument, nullptr, 'W'},
{"print-id", required_argument, nullptr, 'I'},
{"no-opt-funcs", required_argument, nullptr, '\001'},
{"no-opt-files", required_argument, nullptr, '\002'},
{"status-file", required_argument, nullptr, 'U'},
{"debug", required_argument, nullptr, 'B'},
@ -512,8 +520,10 @@ Options parse_cmdline(int argc, char** argv) {
case 'I': rval.identifier_to_print = optarg; break;
case 'N': ++rval.print_plugins; break;
case 'O': set_analysis_option(optarg, rval); break;
case 'o': add_func_analysis_pattern(rval.analysis_options, optarg); break;
case '0': add_file_analysis_pattern(rval.analysis_options, optarg); break;
case 'o': add_func_analysis_pattern(rval.analysis_options, optarg, true); break;
case '0': add_file_analysis_pattern(rval.analysis_options, optarg, true); break;
case '\001': add_func_analysis_pattern(rval.analysis_options, optarg, false); break;
case '\002': add_file_analysis_pattern(rval.analysis_options, optarg, false); break;
case 'P':
if ( rval.dns_mode != detail::DNS_DEFAULT ) {
fprintf(stderr, "ERROR: can only change DNS manager mode once\n");

View file

@ -5,7 +5,7 @@
// Switching parser table type fixes ambiguity problems.
%define lr.type ielr
%expect 217
%expect 229
%token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY TOK_ASSERT
%token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF
@ -31,6 +31,7 @@
%token TOK_ATTR_PRIORITY TOK_ATTR_LOG TOK_ATTR_ERROR_HANDLER TOK_ATTR_GROUP
%token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED
%token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED
%token TOK_ATTR_NO_ZAM_OPT TOK_ATTR_NO_CPP_OPT
%token TOK_DEBUG
@ -52,7 +53,7 @@
%left '*' '/' '%'
%left TOK_INCR TOK_DECR
%right '!' '~'
%left '$' '[' ']' '(' ')' TOK_HAS_FIELD TOK_HAS_ATTR
%left '$' '[' ']' '(' ')' TOK_HAS_FIELD
%nonassoc TOK_AS TOK_IS
%type <b> opt_no_test opt_no_test_block opt_deep when_flavor
@ -1793,6 +1794,10 @@ attr:
}
| TOK_ATTR_ORDERED
{ $$ = new Attr(ATTR_ORDERED); }
| TOK_ATTR_NO_ZAM_OPT
{ $$ = new Attr(ATTR_NO_ZAM_OPT); }
| TOK_ATTR_NO_CPP_OPT
{ $$ = new Attr(ATTR_NO_CPP_OPT); }
;
stmt:

View file

@ -394,6 +394,8 @@ when return TOK_WHEN;
&broker_allow_complex_type return TOK_ATTR_BROKER_STORE_ALLOW_COMPLEX;
&backend return TOK_ATTR_BACKEND;
&ordered return TOK_ATTR_ORDERED;
&no_ZAM_opt return TOK_ATTR_NO_ZAM_OPT;
&no_CPP_opt return TOK_ATTR_NO_CPP_OPT;
@deprecated.* {
auto num_files = file_stack.length();

View file

@ -51,11 +51,11 @@ void CPPCompile::Compile(bool report_uncompilable) {
reporter->FatalError("aborting standalone compilation to C++ due to having to skip some functions");
for ( auto& g : global_scope()->OrderedVars() ) {
bool compiled_global = obj_matches_opt_files(g);
bool compiled_global = obj_matches_opt_files(g) == AnalyzeDecision::SHOULD;
if ( ! compiled_global )
for ( const auto& i_e : g->GetOptInfo()->GetInitExprs() )
if ( obj_matches_opt_files(i_e) ) {
if ( obj_matches_opt_files(i_e) == AnalyzeDecision::SHOULD ) {
compiled_global = true;
break;
}
@ -87,11 +87,11 @@ void CPPCompile::Compile(bool report_uncompilable) {
}
for ( auto& l : pfs->Lambdas() )
if ( obj_matches_opt_files(l) )
if ( obj_matches_opt_files(l) == AnalyzeDecision::SHOULD )
accessed_lambdas.insert(l);
for ( auto& ea : pfs->ExprAttrs() )
if ( obj_matches_opt_files(ea.first) ) {
if ( obj_matches_opt_files(ea.first) == AnalyzeDecision::SHOULD ) {
auto& attr = ea.first;
attrs.insert(attr);
auto& t = attr->GetExpr()->GetType();
@ -184,11 +184,11 @@ void CPPCompile::Compile(bool report_uncompilable) {
// to generate, make sure we track their attributes.
for ( const auto& fd : field_decls ) {
auto td = fd.second;
if ( obj_matches_opt_files(td->type) ) {
if ( obj_matches_opt_files(td->type) == AnalyzeDecision::SHOULD ) {
TypePtr tp = {NewRef{}, const_cast<Type*>(TypeRep(td->type))};
RegisterType(tp);
}
if ( obj_matches_opt_files(td->attrs) )
if ( obj_matches_opt_files(td->attrs) == AnalyzeDecision::SHOULD )
RegisterAttributes(td->attrs);
}
@ -202,6 +202,19 @@ bool CPPCompile::AnalyzeFuncBody(FuncInfo& fi, unordered_set<string>& filenames_
string fn = body->GetLocationInfo()->FileName();
if ( fi.ShouldAnalyze() && fi.ShouldSkip() ) {
const char* reason = nullptr;
auto is_compilable = is_CPP_compilable(fi.Profile(), &reason);
ASSERT(! is_compilable);
ASSERT(reason);
if ( standalone || report_uncompilable )
reporter->Warning("%s cannot be compiled to C++ due to %s", f->GetName().c_str(), reason);
fi.SetSkip(true);
if ( standalone )
return false;
}
if ( ! analysis_options.allow_cond && ! fi.ShouldSkip() ) {
if ( ! analysis_options.only_files.empty() && files_with_conditionals.contains(fn) ) {
if ( report_uncompilable )

View file

@ -1278,7 +1278,7 @@ string CPPCompile::GenEnum(const TypePtr& t, const ValPtr& ev) {
mapping_slot = num_ev_mappings++;
string enum_name = et->Lookup(v);
bool create_if_missing = standalone && obj_matches_opt_files(ev);
bool create_if_missing = standalone && obj_matches_opt_files(ev) == AnalyzeDecision::SHOULD;
enum_names.emplace_back(EnumMappingInfo{TypeOffset(t), std::move(enum_name), create_if_missing});
if ( evm != enum_val_mappings.end() ) {

View file

@ -115,7 +115,7 @@ void CPPCompile::InitializeFieldMappings() {
// We can assess whether this field is one we need to generate
// because if it is, it will have an &optional attribute that
// is local to one of the cmopiled source files.
if ( td->attrs && obj_matches_opt_files(td->attrs) ) {
if ( td->attrs && obj_matches_opt_files(td->attrs) == AnalyzeDecision::SHOULD ) {
type_arg = Fmt(TypeOffset(td->type));
attrs_arg = Fmt(AttributesOffset(td->attrs));
}
@ -197,7 +197,7 @@ void CPPCompile::InitializeGlobals() {
for ( const auto& ginit : IDOptInfo::GetGlobalInitExprs() ) {
IDPtr g{NewRef{}, const_cast<ID*>(ginit.Id())};
if ( ! ofiles.empty() && ! obj_matches_opt_files(g) )
if ( ! ofiles.empty() && obj_matches_opt_files(g) != AnalyzeDecision::SHOULD )
continue;
if ( ! accessed_globals.contains(g) )

View file

@ -570,7 +570,7 @@ RecordTypeInfo::RecordTypeInfo(CPPCompile* _c, TypePtr _t, int _addl_fields)
field_types.push_back(r_i->type);
if ( r_i->attrs && c->TargetingStandalone() && obj_matches_opt_files(r_i->attrs) ) {
if ( r_i->attrs && c->TargetingStandalone() && obj_matches_opt_files(r_i->attrs) == AnalyzeDecision::SHOULD ) {
gi = c->RegisterAttributes(r_i->attrs);
final_init_cohort = max(final_init_cohort, gi->InitCohort() + 1);
field_attrs.push_back(gi->Offset());

View file

@ -198,14 +198,14 @@ shared_ptr<CPP_InitInfo> CPPCompile::RegisterType(const TypePtr& tp) {
// If any of those conditions don't hold, then this variable will remain 0.
int addl_fields = 0;
bool type_init_needed = standalone && obj_matches_opt_files(tp);
bool type_init_needed = standalone && obj_matches_opt_files(tp) == AnalyzeDecision::SHOULD;
if ( standalone && ! type_init_needed ) {
if ( tp->Tag() == TYPE_RECORD ) {
auto tr = tp->AsRecordType();
for ( auto i = tr->NumOrigFields(); i < tr->NumFields(); ++i ) {
auto fd = tr->FieldDecl(i);
if ( filename_matches_opt_files(fd->GetLocationInfo()->FileName()) ) {
if ( filename_matches_opt_files(fd->GetLocationInfo()->FileName()) == AnalyzeDecision::SHOULD ) {
if ( addl_fields == 0 )
addl_fields = i;
}

View file

@ -39,6 +39,17 @@ string scope_prefix(const string& scope) { return "zeek::detail::CPP_" + scope;
string scope_prefix(int scope) { return scope_prefix(to_string(scope)); }
bool is_CPP_compilable(const ProfileFunc* pf, const char** reason) {
auto func = pf->ProfiledFunc(); // can be nil for lambdas
if ( func ) {
auto& scope_id = pf->ProfiledScope()->GetID();
if ( scope_id && scope_id->GetAttr(ATTR_NO_CPP_OPT) ) {
if ( reason )
*reason = "&no_CPP_opt attribute";
return false;
}
}
if ( has_AST_node_unknown_to_script_opt(pf, false) ) {
if ( reason )
*reason = "unknown AST node type";

View file

@ -76,7 +76,7 @@ std::shared_ptr<CPP_InitInfo> CPPCompile::GenerateGlobalInit(IDPtr g) {
if ( ! standalone )
return make_shared<GlobalLookupInitInfo>(this, g, globals[gn]);
if ( obj_matches_opt_files(g) )
if ( obj_matches_opt_files(g) == AnalyzeDecision::SHOULD )
return make_shared<GlobalInitInfo>(this, g, globals[gn]);
// It's not a global that's created by the scripts we're compiling,
@ -86,7 +86,7 @@ std::shared_ptr<CPP_InitInfo> CPPCompile::GenerateGlobalInit(IDPtr g) {
bool needs_redef = false;
for ( const auto& i_e : g->GetOptInfo()->GetInitExprs() )
if ( obj_matches_opt_files(i_e) ) {
if ( obj_matches_opt_files(i_e) == AnalyzeDecision::SHOULD ) {
needs_redef = true;
break;
}

View file

@ -57,7 +57,7 @@ bool is_lambda(const ScriptFunc* f) { return lambdas.contains(f); }
bool is_when_lambda(const ScriptFunc* f) { return when_lambdas.contains(f); }
void analyze_global_stmts(Stmt* stmts) {
if ( analysis_options.gen_standalone_CPP && obj_matches_opt_files(stmts) )
if ( analysis_options.gen_standalone_CPP && obj_matches_opt_files(stmts) == AnalyzeDecision::SHOULD )
reporter->FatalError("cannot include global statements with -O gen-standalone-C++: %s",
obj_desc(stmts).c_str());
@ -87,19 +87,25 @@ std::pair<StmtPtr, ScopePtr> get_global_stmts() {
return std::pair<StmtPtr, ScopePtr>{fi.Body(), fi.Scope()};
}
void add_func_analysis_pattern(AnalyOpt& opts, const char* pat) {
void add_func_analysis_pattern(AnalyOpt& opts, const char* pat, bool is_only) {
try {
std::string full_pat = std::string("^(") + pat + ")$";
opts.only_funcs.emplace_back(full_pat);
if ( is_only )
opts.only_funcs.emplace_back(full_pat);
else
opts.skip_funcs.emplace_back(full_pat);
} catch ( const std::regex_error& e ) {
reporter->FatalError("bad file analysis pattern: %s", pat);
}
}
void add_file_analysis_pattern(AnalyOpt& opts, const char* pat) {
void add_file_analysis_pattern(AnalyOpt& opts, const char* pat, bool is_only) {
try {
std::string full_pat = std::string("^.*(") + pat + ").*$";
opts.only_files.emplace_back(full_pat);
if ( is_only )
opts.only_files.emplace_back(full_pat);
else
opts.skip_files.emplace_back(full_pat);
} catch ( const std::regex_error& e ) {
reporter->FatalError("bad file analysis pattern: %s", pat);
}
@ -107,39 +113,66 @@ void add_file_analysis_pattern(AnalyOpt& opts, const char* pat) {
bool should_analyze(const ScriptFuncPtr& f, const StmtPtr& body) {
auto& ofuncs = analysis_options.only_funcs;
auto& sfuncs = analysis_options.skip_funcs;
auto& ofiles = analysis_options.only_files;
auto& sfiles = analysis_options.skip_files;
if ( ofiles.empty() && ofuncs.empty() )
bool have_onlies = ! ofiles.empty() || ! ofuncs.empty();
if ( ! have_onlies && sfiles.empty() && sfuncs.empty() )
// It's the default of compile-everything.
return true;
if ( obj_matches_opt_files(body.get()) )
return true;
auto file_decision = obj_matches_opt_files(body.get());
if ( file_decision == AnalyzeDecision::SHOULD_NOT )
return false;
// Even if the file decision is SHOULD, that can be overridden by
// a function decision of "skip".
const auto& fun = f->GetName();
for ( auto& s : sfuncs )
if ( std::regex_match(fun, s) )
return false; // matches a "skip" function
if ( file_decision == AnalyzeDecision::SHOULD )
// It matches a specified file, and there's no "skip" for the function.
return true;
for ( auto& o : ofuncs )
if ( std::regex_match(fun, o) )
return true;
return true; // matches an "only" function
return false;
// If we get here, neither the file nor the function has an "only"
// or "skip" decision. If our sole directives were for skip's, then
// we should analyze this function. If we have any only's, then we
// shouldn't.
return ! have_onlies;
}
bool filename_matches_opt_files(const char* filename) {
AnalyzeDecision filename_matches_opt_files(const char* filename) {
auto& ofiles = analysis_options.only_files;
auto& sfiles = analysis_options.skip_files;
if ( ofiles.empty() )
return false;
if ( ofiles.empty() && sfiles.empty() )
return AnalyzeDecision::DEFAULT;
auto fin = util::detail::normalize_path(filename);
for ( auto& s : analysis_options.skip_files )
if ( std::regex_match(fin, s) )
return AnalyzeDecision::SHOULD_NOT;
for ( auto& o : ofiles )
if ( std::regex_match(fin, o) )
return true;
return AnalyzeDecision::SHOULD;
return false;
return AnalyzeDecision::DEFAULT;
}
bool obj_matches_opt_files(const Obj* obj) { return filename_matches_opt_files(obj->GetLocationInfo()->FileName()); }
AnalyzeDecision obj_matches_opt_files(const Obj* obj) {
return filename_matches_opt_files(obj->GetLocationInfo()->FileName());
}
static bool optimize_AST(ScriptFuncPtr f, std::shared_ptr<ProfileFunc>& pf, std::shared_ptr<Reducer>& rc,
ScopePtr scope, StmtPtr& body) {
@ -328,13 +361,25 @@ static void init_options() {
if ( analysis_options.only_funcs.empty() ) {
auto zo = getenv("ZEEK_OPT_FUNCS");
if ( zo )
add_func_analysis_pattern(analysis_options, zo);
add_func_analysis_pattern(analysis_options, zo, true);
}
if ( analysis_options.skip_funcs.empty() ) {
auto zo = getenv("ZEEK_SKIP_FUNCS");
if ( zo )
add_func_analysis_pattern(analysis_options, zo, false);
}
if ( analysis_options.only_files.empty() ) {
auto zo = getenv("ZEEK_OPT_FILES");
if ( zo )
add_file_analysis_pattern(analysis_options, zo);
add_file_analysis_pattern(analysis_options, zo, true);
}
if ( analysis_options.skip_files.empty() ) {
auto zo = getenv("ZEEK_SKIP_FILES");
if ( zo )
add_file_analysis_pattern(analysis_options, zo, false);
}
if ( analysis_options.profile_ZAM ) {

View file

@ -34,6 +34,11 @@ struct AnalyOpt {
// Same, but for the filenames where the function is found.
std::vector<std::regex> only_files;
// The inverses of those - functions and files to skip. These
// have higher precedence than the only_'s.
std::vector<std::regex> skip_funcs;
std::vector<std::regex> skip_files;
// For a given compilation target, report functions that can't
// be compiled.
bool report_uncompilable = false;
@ -245,21 +250,23 @@ extern std::pair<StmtPtr, ScopePtr> get_global_stmts();
// Used to associate module names with profiling information.
extern void switch_to_module(const char* module);
// Add a pattern to the "only_funcs" list.
extern void add_func_analysis_pattern(AnalyOpt& opts, const char* pat);
// Add a pattern to the "only_funcs" (if is_only true) or "skip_funcs" list.
extern void add_func_analysis_pattern(AnalyOpt& opts, const char* pat, bool is_only);
// Add a pattern to the "only_files" list.
extern void add_file_analysis_pattern(AnalyOpt& opts, const char* pat);
// Add a pattern to the "only_files" / "skip_files" list.
extern void add_file_analysis_pattern(AnalyOpt& opts, const char* pat, bool is_only);
// True if the given script function & body should be analyzed; otherwise
// it should be skipped.
extern bool should_analyze(const ScriptFuncPtr& f, const StmtPtr& body);
// True if the given filename or object location matches one specified by
// --optimize-files=...
extern bool filename_matches_opt_files(const char* filename);
extern bool obj_matches_opt_files(const Obj* obj);
inline bool obj_matches_opt_files(const ObjPtr& obj) { return obj_matches_opt_files(obj.get()); }
// SHOULD if the given filename or object location matches one specified by
// --optimize-files=..., SHOULD_NOT if it matches one specified by
// --no-opt-files=... (which takes precedence), DEFAULT if neither.
enum class AnalyzeDecision { SHOULD, SHOULD_NOT, DEFAULT };
extern AnalyzeDecision filename_matches_opt_files(const char* filename);
extern AnalyzeDecision obj_matches_opt_files(const Obj* obj);
inline auto obj_matches_opt_files(const ObjPtr& obj) { return obj_matches_opt_files(obj.get()); }
// Analyze all of the parsed scripts collectively for usage issues (unless
// suppressed by the flag) and optimization.

View file

@ -117,6 +117,17 @@ bool file_mgr_set_reassembly_buffer(StringVal* file_id, uint64_t max) {
bool ZAM_error = false;
bool is_ZAM_compilable(const ProfileFunc* pf, const char** reason) {
auto func = pf->ProfiledFunc(); // can be nil for lambdas
if ( func ) {
auto& scope_id = pf->ProfiledScope()->GetID();
if ( scope_id && scope_id->GetAttr(ATTR_NO_ZAM_OPT) ) {
if ( reason )
*reason = "&no_ZAM_opt attribute";
return false;
}
}
if ( has_AST_node_unknown_to_script_opt(pf, true) ) {
if ( reason )
*reason = "unknown AST node type";