diff --git a/src/Attr.cc b/src/Attr.cc index 9a8aa3396a..3d8741bf6f 100644 --- a/src/Attr.cc +++ b/src/Attr.cc @@ -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 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())); } diff --git a/src/Attr.h b/src/Attr.h index d1fefa6f5a..744f68fb7b 100644 --- a/src/Attr.h +++ b/src/Attr.h @@ -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); diff --git a/src/Func.cc b/src/Func.cc index b0d1889282..f3f41ae7d0 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -817,7 +817,10 @@ static int get_func_priority(const std::vector& 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 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 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_IS_USED)); - break; - } + id->AddAttr(make_intrusive(a->Tag())); + } } else priority = 0; diff --git a/src/Options.cc b/src/Options.cc index 5f4494977c..ff67cc5539 100644 --- a/src/Options.cc +++ b/src/Options.cc @@ -124,9 +124,15 @@ void usage(const char* prog) { printf( " -0|--optimize-files= | enable script optimization for all " "functions in files with names containing the given pattern\n"); + printf( + " --no-opt-files= | skip script optimization for all " + "functions in files with names containing the given pattern\n"); printf( " -o|--optimize-funcs= | enable script optimization for " "functions with names fully matching the given pattern\n"); + printf( + " --no-opt-funcs= | 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"); diff --git a/src/parse.y b/src/parse.y index 0815a3c356..b8930804e2 100644 --- a/src/parse.y +++ b/src/parse.y @@ -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 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: diff --git a/src/scan.l b/src/scan.l index 2abe6a98b8..f93dfc5d79 100644 --- a/src/scan.l +++ b/src/scan.l @@ -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(); diff --git a/src/script_opt/CPP/Driver.cc b/src/script_opt/CPP/Driver.cc index 7011a7d315..66ad6b7fa7 100644 --- a/src/script_opt/CPP/Driver.cc +++ b/src/script_opt/CPP/Driver.cc @@ -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(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& 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 ) diff --git a/src/script_opt/CPP/Exprs.cc b/src/script_opt/CPP/Exprs.cc index 57ac8b66c6..f52bc15bad 100644 --- a/src/script_opt/CPP/Exprs.cc +++ b/src/script_opt/CPP/Exprs.cc @@ -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() ) { diff --git a/src/script_opt/CPP/Inits.cc b/src/script_opt/CPP/Inits.cc index a73b96a379..72fafef9a4 100644 --- a/src/script_opt/CPP/Inits.cc +++ b/src/script_opt/CPP/Inits.cc @@ -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(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) ) diff --git a/src/script_opt/CPP/InitsInfo.cc b/src/script_opt/CPP/InitsInfo.cc index afbb168ff2..f8859c5df4 100644 --- a/src/script_opt/CPP/InitsInfo.cc +++ b/src/script_opt/CPP/InitsInfo.cc @@ -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()); diff --git a/src/script_opt/CPP/Types.cc b/src/script_opt/CPP/Types.cc index eb8ccc3f7c..10f2e7a838 100644 --- a/src/script_opt/CPP/Types.cc +++ b/src/script_opt/CPP/Types.cc @@ -198,14 +198,14 @@ shared_ptr 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; } diff --git a/src/script_opt/CPP/Util.cc b/src/script_opt/CPP/Util.cc index 84e1846151..5b64b30544 100644 --- a/src/script_opt/CPP/Util.cc +++ b/src/script_opt/CPP/Util.cc @@ -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"; diff --git a/src/script_opt/CPP/Vars.cc b/src/script_opt/CPP/Vars.cc index cc3e9613b3..bc00b96970 100644 --- a/src/script_opt/CPP/Vars.cc +++ b/src/script_opt/CPP/Vars.cc @@ -76,7 +76,7 @@ std::shared_ptr CPPCompile::GenerateGlobalInit(IDPtr g) { if ( ! standalone ) return make_shared(this, g, globals[gn]); - if ( obj_matches_opt_files(g) ) + if ( obj_matches_opt_files(g) == AnalyzeDecision::SHOULD ) return make_shared(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 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; } diff --git a/src/script_opt/ScriptOpt.cc b/src/script_opt/ScriptOpt.cc index b13649b488..3d2bd494bd 100644 --- a/src/script_opt/ScriptOpt.cc +++ b/src/script_opt/ScriptOpt.cc @@ -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 get_global_stmts() { return std::pair{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& pf, std::shared_ptr& 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 ) { diff --git a/src/script_opt/ScriptOpt.h b/src/script_opt/ScriptOpt.h index ac188cd979..4a2f79d586 100644 --- a/src/script_opt/ScriptOpt.h +++ b/src/script_opt/ScriptOpt.h @@ -34,6 +34,11 @@ struct AnalyOpt { // Same, but for the filenames where the function is found. std::vector only_files; + // The inverses of those - functions and files to skip. These + // have higher precedence than the only_'s. + std::vector skip_funcs; + std::vector 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 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. diff --git a/src/script_opt/ZAM/Support.cc b/src/script_opt/ZAM/Support.cc index 3c606335cb..6dee876bfc 100644 --- a/src/script_opt/ZAM/Support.cc +++ b/src/script_opt/ZAM/Support.cc @@ -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";