mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
reintroduction of "-O add-C++" option
This commit is contained in:
parent
7a6a81c200
commit
8c59626eb9
12 changed files with 74 additions and 488 deletions
|
@ -390,7 +390,6 @@ set(MAIN_SRCS
|
|||
script_opt/CPP/Exprs.cc
|
||||
script_opt/CPP/Func.cc
|
||||
script_opt/CPP/GenFunc.cc
|
||||
script_opt/CPP/HashMgr.cc
|
||||
script_opt/CPP/Inits.cc
|
||||
script_opt/CPP/InitsInfo.cc
|
||||
script_opt/CPP/RuntimeInits.cc
|
||||
|
|
|
@ -198,6 +198,7 @@ static void print_analysis_help()
|
|||
fprintf(stderr, " xform transform scripts to \"reduced\" form\n");
|
||||
|
||||
fprintf(stderr, "\n--optimize options when generating C++:\n");
|
||||
fprintf(stderr, " add-C++ add C++ script bodies to existing generated code\n");
|
||||
fprintf(stderr, " gen-C++ generate C++ script bodies\n");
|
||||
fprintf(stderr, " gen-standalone-C++ generate \"standalone\" C++ script bodies\n");
|
||||
fprintf(stderr, " help print this list\n");
|
||||
|
@ -230,6 +231,8 @@ static void set_analysis_option(const char* opt, Options& opts)
|
|||
a_o.activate = a_o.dump_xform = true;
|
||||
else if ( util::streq(opt, "dump-ZAM") )
|
||||
a_o.activate = a_o.dump_ZAM = true;
|
||||
else if ( util::streq(opt, "add-C++") )
|
||||
a_o.add_CPP = true;
|
||||
else if ( util::streq(opt, "gen-C++") )
|
||||
a_o.gen_CPP = true;
|
||||
else if ( util::streq(opt, "gen-standalone-C++") )
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "zeek/Desc.h"
|
||||
#include "zeek/script_opt/CPP/Func.h"
|
||||
#include "zeek/script_opt/CPP/HashMgr.h"
|
||||
#include "zeek/script_opt/CPP/InitsInfo.h"
|
||||
#include "zeek/script_opt/CPP/Tracker.h"
|
||||
#include "zeek/script_opt/CPP/Util.h"
|
||||
|
@ -15,10 +14,9 @@
|
|||
// functionality into a number of groups (see below), these interact with
|
||||
// one another, and in particular with various member variables, enough
|
||||
// so that it's not clear there's benefit to further splitting the
|
||||
// functionality into multiple classes. (Some splitting has already been
|
||||
// done for more self-contained functionality, resulting in the CPPTracker
|
||||
// and CPPHashManager classes, and initialization information in
|
||||
// InitsInfo.{h,cc} and RuntimeInits.{h,cc}.)
|
||||
// functionality into multiple classes. (Some splitting has already been done
|
||||
// for more self-contained functionality, resulting in the CPPTracker class
|
||||
// and initialization information in InitsInfo.{h,cc} and RuntimeInits.{h,cc}.)
|
||||
//
|
||||
// Most aspects of translating to C++ have a straightforward nature.
|
||||
// We can turn many Zeek script statements directly into the C++ that's
|
||||
|
@ -129,8 +127,7 @@ class CPPCompile
|
|||
{
|
||||
public:
|
||||
CPPCompile(std::vector<FuncInfo>& _funcs, ProfileFuncs& pfs, const std::string& gen_name,
|
||||
const std::string& addl_name, CPPHashManager& _hm, bool _standalone,
|
||||
bool report_uncompilable);
|
||||
bool add, bool _standalone, bool report_uncompilable);
|
||||
~CPPCompile();
|
||||
|
||||
// Constructing a CPPCompile object does all of the compilation.
|
||||
|
@ -317,10 +314,6 @@ private:
|
|||
// The global profile of all of the functions.
|
||||
ProfileFuncs& pfs;
|
||||
|
||||
// Hash-indexed information about previously compiled code (and used
|
||||
// to update it from this compilation run).
|
||||
CPPHashManager& hm;
|
||||
|
||||
// Script functions that we are able to compile. We compute
|
||||
// these ahead of time so that when compiling script function A
|
||||
// which makes a call to script function B, we know whether
|
||||
|
@ -369,11 +362,6 @@ private:
|
|||
// See Vars.cc for definitions.
|
||||
//
|
||||
|
||||
// Returns true if the current compilation context has collisions
|
||||
// with previously generated code (globals with conflicting types
|
||||
// or initialization values, or types with differing elements).
|
||||
bool CheckForCollisions();
|
||||
|
||||
// Generate declarations associated with the given global, and, if
|
||||
// it's used as a variable (not just as a function being called),
|
||||
// track it as such.
|
||||
|
@ -385,10 +373,8 @@ private:
|
|||
|
||||
// Register the given global name. "suffix" distinguishs particular
|
||||
// types of globals, such as the names of bifs, global (non-function)
|
||||
// variables, or compiled Zeek functions. If "track" is true then
|
||||
// if we're compiling incrementally, and this is a new global not
|
||||
// previously compiled, then we track its hash for future compilations.
|
||||
bool AddGlobal(const std::string& g, const char* suffix, bool track);
|
||||
// variables, or compiled Zeek functions.
|
||||
bool AddGlobal(const std::string& g, const char* suffix);
|
||||
|
||||
// Tracks that the body we're currently compiling refers to the
|
||||
// given event.
|
||||
|
@ -936,7 +922,7 @@ private:
|
|||
const char* IntrusiveVal(const TypePtr& t);
|
||||
|
||||
// Maps types to indices in the global "types__CPP" array.
|
||||
CPPTracker<Type> types = {"types", true, &compiled_items};
|
||||
CPPTracker<Type> types = {"types", true};
|
||||
|
||||
// Used to prevent analysis of mutually-referring types from
|
||||
// leading to infinite recursion. Maps types to their global
|
||||
|
@ -966,7 +952,7 @@ private:
|
|||
static const char* AttrName(AttrTag t);
|
||||
|
||||
// Similar for attributes, so we can reconstruct record types.
|
||||
CPPTracker<Attributes> attributes = {"attrs", false, &compiled_items};
|
||||
CPPTracker<Attributes> attributes = {"attrs", false};
|
||||
|
||||
// Maps Attributes and Attr's to their global initialization
|
||||
// information.
|
||||
|
@ -1036,7 +1022,7 @@ private:
|
|||
// Expressions for which we need to generate initialization-time
|
||||
// code. Currently, these are only expressions appearing in
|
||||
// attributes.
|
||||
CPPTracker<Expr> init_exprs = {"gen_init_expr", false, &compiled_items};
|
||||
CPPTracker<Expr> init_exprs = {"gen_init_expr", false};
|
||||
|
||||
//
|
||||
// End of methods related to run-time initialization.
|
||||
|
@ -1127,9 +1113,6 @@ private:
|
|||
// File to which we're generating code.
|
||||
FILE* write_file;
|
||||
|
||||
// Name of file holding potential "additional" code.
|
||||
std::string addl_name;
|
||||
|
||||
// Indentation level.
|
||||
int block_level = 0;
|
||||
|
||||
|
|
|
@ -12,13 +12,11 @@ namespace zeek::detail
|
|||
using namespace std;
|
||||
|
||||
CPPCompile::CPPCompile(vector<FuncInfo>& _funcs, ProfileFuncs& _pfs, const string& gen_name,
|
||||
const string& _addl_name, CPPHashManager& _hm, bool _standalone,
|
||||
bool report_uncompilable)
|
||||
: funcs(_funcs), pfs(_pfs), hm(_hm), standalone(_standalone)
|
||||
bool add, bool _standalone, bool report_uncompilable)
|
||||
: funcs(_funcs), pfs(_pfs), standalone(_standalone)
|
||||
{
|
||||
addl_name = _addl_name;
|
||||
auto target_name = gen_name.c_str();
|
||||
auto mode = "w";
|
||||
auto mode = add ? "a" : "w";
|
||||
|
||||
write_file = fopen(target_name, mode);
|
||||
if ( ! write_file )
|
||||
|
@ -26,19 +24,33 @@ CPPCompile::CPPCompile(vector<FuncInfo>& _funcs, ProfileFuncs& _pfs, const strin
|
|||
reporter->Error("can't open C++ target file %s", target_name);
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
|
||||
if ( add )
|
||||
{
|
||||
// Create an empty "additional" file.
|
||||
auto addl_f = fopen(addl_name.c_str(), "w");
|
||||
if ( ! addl_f )
|
||||
// We need a unique number to associate with the name
|
||||
// space for the code we're adding. A convenient way to
|
||||
// generate this safely is to use the present size of the
|
||||
// file we're appending to. That guarantees that every
|
||||
// incremental compilation will wind up with a different
|
||||
// number.
|
||||
struct stat st;
|
||||
if ( fstat(fileno(write_file), &st) != 0 )
|
||||
{
|
||||
reporter->Error("can't open C++ additional file %s", addl_name.c_str());
|
||||
char buf[256];
|
||||
util::zeek_strerror_r(errno, buf, sizeof(buf));
|
||||
reporter->Error("fstat failed on %s: %s", target_name, buf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fclose(addl_f);
|
||||
// We use a value of "0" to mean "we're not appending,
|
||||
// we're generating from scratch", so make sure we're
|
||||
// distinct from that.
|
||||
addl_tag = st.st_size + 1;
|
||||
}
|
||||
|
||||
else
|
||||
addl_tag = 0;
|
||||
|
||||
Compile(report_uncompilable);
|
||||
}
|
||||
|
||||
|
@ -84,15 +96,6 @@ void CPPCompile::Compile(bool report_uncompilable)
|
|||
reason);
|
||||
not_fully_compilable.insert(func.Func()->Name());
|
||||
}
|
||||
|
||||
auto h = func.Profile()->HashVal();
|
||||
if ( hm.HasHash(h) )
|
||||
{
|
||||
// Track the previously compiled instance
|
||||
// of this function.
|
||||
auto n = func.Func()->Name();
|
||||
hashed_funcs[n] = hm.FuncBodyName(h);
|
||||
}
|
||||
}
|
||||
|
||||
// Track all of the types we'll be using.
|
||||
|
@ -111,7 +114,7 @@ void CPPCompile::Compile(bool report_uncompilable)
|
|||
CreateGlobal(g);
|
||||
|
||||
for ( const auto& e : pfs.Events() )
|
||||
if ( AddGlobal(e, "gl", false) )
|
||||
if ( AddGlobal(e, "gl") )
|
||||
Emit("EventHandlerPtr %s_ev;", globals[string(e)]);
|
||||
|
||||
for ( const auto& t : pfs.RepTypes() )
|
||||
|
@ -177,9 +180,9 @@ void CPPCompile::GenProlog()
|
|||
if ( addl_tag == 0 )
|
||||
{
|
||||
Emit("#include \"zeek/script_opt/CPP/Runtime.h\"\n");
|
||||
Emit("namespace zeek::detail { //\n");
|
||||
}
|
||||
|
||||
Emit("namespace zeek::detail { //\n");
|
||||
Emit("namespace CPP_%s { // %s\n", Fmt(addl_tag), working_dir);
|
||||
|
||||
// The following might-or-might-not wind up being populated/used.
|
||||
|
@ -273,10 +276,7 @@ void CPPCompile::RegisterCompiledBody(const string& f)
|
|||
// Hash in the location associated with this compilation
|
||||
// pass, to get a final hash that avoids conflicts with
|
||||
// identical-but-in-a-different-context function bodies
|
||||
// when compiling potentially conflicting additional code
|
||||
// (which we want to support to enable quicker test suite
|
||||
// runs by enabling multiple tests to be compiled into the
|
||||
// same binary).
|
||||
// when compiling potentially conflicting additional code.
|
||||
h = merge_p_hashes(h, p_hash(cf_locs[f]));
|
||||
|
||||
auto fi = func_index.find(f);
|
||||
|
@ -418,11 +418,6 @@ void CPPCompile::GenEpilog()
|
|||
GenInitHook();
|
||||
|
||||
Emit("} // %s\n\n", scope_prefix(addl_tag));
|
||||
|
||||
if ( addl_tag > 0 )
|
||||
return;
|
||||
|
||||
Emit("#include \"" + addl_name + "\"\n");
|
||||
Emit("} // zeek::detail");
|
||||
}
|
||||
|
||||
|
@ -439,10 +434,6 @@ bool CPPCompile::IsCompilable(const FuncInfo& func, const char** reason)
|
|||
if ( func.ShouldSkip() )
|
||||
return false;
|
||||
|
||||
if ( hm.HasHash(func.Profile()->HashVal()) )
|
||||
// We've already compiled it.
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "zeek/script_opt/CPP/HashMgr.h"
|
||||
|
||||
#include "zeek/script_opt/CPP/Func.h"
|
||||
#include "zeek/script_opt/CPP/Util.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
VarMapper compiled_items;
|
||||
|
||||
CPPHashManager::CPPHashManager(const char* hash_name_base)
|
||||
{
|
||||
hash_name = string(hash_name_base) + ".dat";
|
||||
|
||||
hf_w = fopen(hash_name.c_str(), "w");
|
||||
if ( ! hf_w )
|
||||
{
|
||||
reporter->Error("can't open auxiliary C++ hash file %s for writing", hash_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
CPPHashManager::~CPPHashManager()
|
||||
{
|
||||
fclose(hf_w);
|
||||
|
||||
if ( hf_r )
|
||||
{
|
||||
unlock_file(hash_name, hf_r);
|
||||
fclose(hf_r);
|
||||
}
|
||||
}
|
||||
|
||||
void CPPHashManager::LoadHashes(FILE* f)
|
||||
{
|
||||
string key;
|
||||
|
||||
// The hash file format is inefficient but simple to scan.
|
||||
// It doesn't appear to pose a bottleneck, so until it does
|
||||
// it makes sense for maintainability to keep it dead simple.
|
||||
|
||||
while ( GetLine(f, key) )
|
||||
{
|
||||
string line;
|
||||
|
||||
RequireLine(f, line);
|
||||
|
||||
p_hash_type hash;
|
||||
|
||||
if ( key == "func" )
|
||||
{
|
||||
auto func = line;
|
||||
|
||||
RequireLine(f, line);
|
||||
|
||||
if ( sscanf(line.c_str(), "%llu", &hash) != 1 || hash == 0 )
|
||||
BadLine(line);
|
||||
|
||||
previously_compiled[hash] = func;
|
||||
}
|
||||
|
||||
else if ( key == "global" )
|
||||
{
|
||||
auto gl = line;
|
||||
|
||||
RequireLine(f, line);
|
||||
|
||||
p_hash_type gl_t_h, gl_v_h;
|
||||
if ( sscanf(line.c_str(), "%llu %llu", &gl_t_h, &gl_v_h) != 2 )
|
||||
BadLine(line);
|
||||
|
||||
gl_type_hashes[gl] = gl_t_h;
|
||||
gl_val_hashes[gl] = gl_v_h;
|
||||
|
||||
// Eat the location info. It's there just for
|
||||
// maintainers to be able to track down peculiarities
|
||||
// in the hash file.
|
||||
(void)RequireLine(f, line);
|
||||
}
|
||||
|
||||
else if ( key == "global-var" )
|
||||
{
|
||||
auto gl = line;
|
||||
|
||||
RequireLine(f, line);
|
||||
|
||||
int scope;
|
||||
if ( sscanf(line.c_str(), "%d", &scope) != 1 )
|
||||
BadLine(line);
|
||||
|
||||
gv_scopes[gl] = scope;
|
||||
}
|
||||
|
||||
else if ( key == "hash" )
|
||||
{
|
||||
int index;
|
||||
int scope;
|
||||
|
||||
if ( sscanf(line.c_str(), "%llu %d %d", &hash, &index, &scope) != 3 || hash == 0 )
|
||||
BadLine(line);
|
||||
|
||||
compiled_items[hash] = CompiledItemPair{index, scope};
|
||||
}
|
||||
|
||||
else if ( key == "record" )
|
||||
record_type_globals.insert(line);
|
||||
else if ( key == "enum" )
|
||||
enum_type_globals.insert(line);
|
||||
|
||||
else
|
||||
BadLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
void CPPHashManager::RequireLine(FILE* f, string& line)
|
||||
{
|
||||
if ( ! GetLine(f, line) )
|
||||
{
|
||||
reporter->Error("missing final %s hash file entry", hash_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool CPPHashManager::GetLine(FILE* f, string& line)
|
||||
{
|
||||
char buf[8192];
|
||||
if ( ! fgets(buf, sizeof buf, f) )
|
||||
return false;
|
||||
|
||||
int n = strlen(buf);
|
||||
if ( n > 0 && buf[n - 1] == '\n' )
|
||||
buf[n - 1] = '\0';
|
||||
|
||||
line = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPPHashManager::BadLine(string& line)
|
||||
{
|
||||
reporter->Error("bad %s hash file entry: %s", hash_name.c_str(), line.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
} // zeek::detail
|
|
@ -1,117 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
// C++ compiler support class for managing information about compiled
|
||||
// objects across compilations. The objects are identified via hashes,
|
||||
// hence the term "hash manager". Objects can exist in different scopes.
|
||||
// The information mapping hashes to objects and scopes is tracked
|
||||
// across multiple compilations using intermediary file(s).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "zeek/script_opt/ProfileFunc.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
class CPPHashManager
|
||||
{
|
||||
public:
|
||||
// Create a hash manager that uses the given name for
|
||||
// referring to hash file(s). It's a "base" rather than
|
||||
// a full name in case the manager winds up managing multiple
|
||||
// distinct files (not currently the case).
|
||||
//
|
||||
// If "append" is true then new hashes will be added to the
|
||||
// end of the file (and the hash file will be locked, to prevent
|
||||
// overlapping updates from concurrent compilation/appends).
|
||||
// Otherwise, the file will be generated afresh.
|
||||
CPPHashManager(const char* hash_name_base);
|
||||
~CPPHashManager();
|
||||
|
||||
// True if the given hash has already been generated.
|
||||
bool HasHash(p_hash_type h) const { return previously_compiled.count(h) > 0; }
|
||||
|
||||
// The internal (C++) name of a previously compiled function,
|
||||
// as identified by its hash.
|
||||
const std::string& FuncBodyName(p_hash_type h) { return previously_compiled[h]; }
|
||||
|
||||
// Whether the given global has already been generated;
|
||||
// and, if so, the hashes of its type and initialization
|
||||
// value (used for consistency checking). Here the name
|
||||
// is that used at the script level.
|
||||
bool HasGlobal(const std::string& gl) const { return gl_type_hashes.count(gl) > 0; }
|
||||
p_hash_type GlobalTypeHash(const std::string& gl) { return gl_type_hashes[gl]; }
|
||||
p_hash_type GlobalValHash(const std::string& gl) { return gl_val_hashes[gl]; }
|
||||
|
||||
// Whether the given C++ global already exists, and, if so,
|
||||
// in what scope.
|
||||
bool HasGlobalVar(const std::string& gv) const { return gv_scopes.count(gv) > 0; }
|
||||
int GlobalVarScope(const std::string& gv) { return gv_scopes[gv]; }
|
||||
|
||||
// True if the given global corresponds to a record type
|
||||
// or an enum type. Used to suppress complaints about
|
||||
// definitional inconsistencies for extensible types.
|
||||
bool HasRecordTypeGlobal(const std::string& rt) const
|
||||
{
|
||||
return record_type_globals.count(rt) > 0;
|
||||
}
|
||||
bool HasEnumTypeGlobal(const std::string& et) const { return enum_type_globals.count(et) > 0; }
|
||||
|
||||
// Access to the file we're writing hashes to, so that the
|
||||
// compiler can add new entries to it.
|
||||
FILE* HashFile() const { return hf_w; }
|
||||
|
||||
protected:
|
||||
// Parses an existing file with hash information.
|
||||
void LoadHashes(FILE* f);
|
||||
|
||||
// Helper routines to load lines from hash file.
|
||||
// The first complains if the line isn't present;
|
||||
// the second merely indicates whether it was.
|
||||
void RequireLine(FILE* f, std::string& line);
|
||||
bool GetLine(FILE* f, std::string& line);
|
||||
|
||||
// Generates an error message for a ill-formatted hash file line.
|
||||
void BadLine(std::string& line);
|
||||
|
||||
// Tracks previously compiled bodies based on hashes, mapping them
|
||||
// to fully qualified (in terms of scoping) C++ names.
|
||||
std::unordered_map<p_hash_type, std::string> previously_compiled;
|
||||
|
||||
// Tracks globals that are record or enum types, indexed using
|
||||
// script-level names.
|
||||
std::unordered_set<std::string> record_type_globals;
|
||||
std::unordered_set<std::string> enum_type_globals;
|
||||
|
||||
// Tracks globals seen in previously compiled bodies, mapping
|
||||
// script-level names to hashes of their types and their values.
|
||||
std::unordered_map<std::string, p_hash_type> gl_type_hashes;
|
||||
std::unordered_map<std::string, p_hash_type> gl_val_hashes;
|
||||
|
||||
// Information about globals in terms of their internal variable
|
||||
// names, rather than their script-level names.
|
||||
std::unordered_map<std::string, int> gv_scopes;
|
||||
|
||||
// Base for file names.
|
||||
std::string hash_name;
|
||||
|
||||
// Handles for reading from and writing to the hash file.
|
||||
// We lock on the first
|
||||
FILE* hf_r = nullptr;
|
||||
FILE* hf_w = nullptr;
|
||||
};
|
||||
|
||||
// Maps hashes to indices into C++ globals (like "types_N__CPP"), and
|
||||
// namespace scopes.
|
||||
struct CompiledItemPair
|
||||
{
|
||||
int index;
|
||||
int scope;
|
||||
};
|
||||
using VarMapper = std::unordered_map<p_hash_type, CompiledItemPair>;
|
||||
|
||||
extern VarMapper compiled_items;
|
||||
|
||||
} // zeek::detail
|
|
@ -73,9 +73,7 @@ The following workflow assumes you are in the `build/` subdirectory:
|
|||
|
||||
1. `./src/zeek -O gen-C++ target.zeek`
|
||||
The generated code is written to
|
||||
`CPP-gen.cc`. The compiler will also produce
|
||||
a file `CPP-hashes.dat`, for use by an advanced feature, and an
|
||||
empty `CPP-gen-addl.h` file (same).
|
||||
`CPP-gen.cc`.
|
||||
2. `ninja` or `make` to recompile Zeek
|
||||
3. `./src/zeek -O use-C++ target.zeek`
|
||||
Executes with each function/hook/event
|
||||
|
@ -110,6 +108,10 @@ On the other hand, it's possible (not yet established) that code created
|
|||
using `gen-C++` can be made to compile significantly faster than
|
||||
standalone code.
|
||||
|
||||
Another option, `-O add-C++`, instead _appends_ the generated code to existing C++ in `CPP-gen.cc`.
|
||||
You can use this option repeatedly for different scripts and then
|
||||
compile the collection _en masse_.
|
||||
|
||||
There are additional workflows relating to running the test suite, which
|
||||
we document only briefly here as they're likely going to change or go away
|
||||
, as it's not clear they're actually needed.
|
||||
|
|
|
@ -21,19 +21,8 @@ template <class T> void CPPTracker<T>::AddKey(IntrusivePtr<T> key, p_hash_type h
|
|||
|
||||
if ( map2.count(h) == 0 )
|
||||
{
|
||||
int index;
|
||||
if ( mapper && mapper->count(h) > 0 )
|
||||
{
|
||||
const auto& pair = (*mapper)[h];
|
||||
index = pair.index;
|
||||
scope2[h] = Fmt(pair.scope);
|
||||
inherited.insert(h);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = num_non_inherited++;
|
||||
auto index = keys.size();
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
map2[h] = index;
|
||||
reps[h] = key.get();
|
||||
|
@ -57,11 +46,6 @@ template <class T> string CPPTracker<T>::KeyName(const T* key)
|
|||
return gi->second->Name();
|
||||
|
||||
auto index = map2[hash];
|
||||
|
||||
string scope;
|
||||
if ( IsInherited(hash) )
|
||||
scope = scope_prefix(scope2[hash]);
|
||||
|
||||
string ind = Fmt(index);
|
||||
string full_name;
|
||||
|
||||
|
@ -70,18 +54,7 @@ template <class T> string CPPTracker<T>::KeyName(const T* key)
|
|||
else
|
||||
full_name = base_name + "_" + ind + "__CPP";
|
||||
|
||||
return scope + full_name;
|
||||
}
|
||||
|
||||
template <class T> void CPPTracker<T>::LogIfNew(IntrusivePtr<T> key, int scope, FILE* log_file)
|
||||
{
|
||||
if ( IsInherited(key) )
|
||||
return;
|
||||
|
||||
auto hash = map[key.get()];
|
||||
auto index = map2[hash];
|
||||
|
||||
fprintf(log_file, "hash\n%llu %d %d\n", hash, index, scope);
|
||||
return full_name;
|
||||
}
|
||||
|
||||
template <class T> p_hash_type CPPTracker<T>::Hash(IntrusivePtr<T> key) const
|
||||
|
|
|
@ -4,17 +4,15 @@
|
|||
// where the key can have any IntrusivePtr type. The properties of a
|
||||
// tracker are that it (1) supports a notion that two technically distinct
|
||||
// keys in fact reflect the same underlying object, (2) provides an
|
||||
// instance of such keys to consistently serve as their "representative",
|
||||
// instance of such keys to consistently serve as their "representative", and
|
||||
// (3) provides names (suitable for use as C++ variables) for representative
|
||||
// keys, and (4) has a notion of "inheritance" (the underlying object is
|
||||
// already available from a previously generated namespace).
|
||||
// keys.
|
||||
//
|
||||
// Notions of "same" are taken from hash values ala those provided by
|
||||
// ProfileFunc.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "zeek/script_opt/CPP/HashMgr.h"
|
||||
#include "zeek/script_opt/CPP/InitsInfo.h"
|
||||
|
||||
namespace zeek::detail
|
||||
|
@ -28,10 +26,8 @@ public:
|
|||
// The base name is used to construct key names. "single_global",
|
||||
// if true, specifies that the names should be constructed as
|
||||
// indexes into a single global, rather than as distinct globals.
|
||||
// The mapper, if present, maps hash values to information about
|
||||
// the previously generated scope in which the value appears.
|
||||
CPPTracker(const char* _base_name, bool _single_global, VarMapper* _mapper = nullptr)
|
||||
: base_name(_base_name), single_global(_single_global), mapper(_mapper)
|
||||
CPPTracker(const char* _base_name, bool _single_global)
|
||||
: base_name(_base_name), single_global(_single_global)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -50,8 +46,7 @@ public:
|
|||
std::string KeyName(IntrusivePtr<T> key) { return KeyName(key.get()); }
|
||||
|
||||
// Returns all of the distinct keys entered into the tracker.
|
||||
// A key is "distinct" if it's both (1) a representative and
|
||||
// (2) not inherited.
|
||||
// A key is "distinct" if it's a representative.
|
||||
const std::vector<IntrusivePtr<T>>& DistinctKeys() const { return keys; }
|
||||
|
||||
// For a given key, get its representative.
|
||||
|
@ -62,23 +57,6 @@ public:
|
|||
}
|
||||
const T* GetRep(IntrusivePtr<T> key) { return GetRep(key.get()); }
|
||||
|
||||
// True if the given key is represented by an inherited value.
|
||||
bool IsInherited(const T* key)
|
||||
{
|
||||
ASSERT(HasKey(key));
|
||||
return IsInherited(map[key]);
|
||||
}
|
||||
bool IsInherited(const IntrusivePtr<T>& key)
|
||||
{
|
||||
ASSERT(HasKey(key));
|
||||
return IsInherited(map[key.get()]);
|
||||
}
|
||||
bool IsInherited(p_hash_type h) { return inherited.count(h) > 0; }
|
||||
|
||||
// If the given key is not inherited, logs it and its associated
|
||||
// scope to the given file.
|
||||
void LogIfNew(IntrusivePtr<T> key, int scope, FILE* log_file);
|
||||
|
||||
private:
|
||||
// Compute a hash for the given key.
|
||||
p_hash_type Hash(IntrusivePtr<T> key) const;
|
||||
|
@ -88,12 +66,8 @@ private:
|
|||
|
||||
std::unordered_map<const T*, std::shared_ptr<CPP_InitInfo>> gi_s;
|
||||
|
||||
// Maps internal representations to distinct values. These
|
||||
// may-or-may-not be indices into an "inherited" namespace scope.
|
||||
// Maps internal representations to distinct values.
|
||||
std::unordered_map<p_hash_type, int> map2;
|
||||
std::unordered_map<p_hash_type, std::string> scope2; // only if inherited
|
||||
std::unordered_set<p_hash_type> inherited; // which are inherited
|
||||
int num_non_inherited = 0; // distinct non-inherited map2 entries
|
||||
|
||||
// Tracks the set of distinct keys, to facilitate iterating over them.
|
||||
// Each such key also has an entry in map2.
|
||||
|
@ -108,9 +82,6 @@ private:
|
|||
// Whether to base the names out of a single global, or distinct
|
||||
// globals.
|
||||
bool single_global;
|
||||
|
||||
// If non-nil, the mapper to consult for previous names.
|
||||
VarMapper* mapper;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
||||
|
|
|
@ -12,69 +12,6 @@ namespace zeek::detail
|
|||
|
||||
using namespace std;
|
||||
|
||||
bool CPPCompile::CheckForCollisions()
|
||||
{
|
||||
for ( auto& g : pfs.AllGlobals() )
|
||||
{
|
||||
auto gn = string(g->Name());
|
||||
|
||||
if ( hm.HasGlobal(gn) )
|
||||
{
|
||||
// Make sure the previous compilation used the
|
||||
// same type and initialization value for the global.
|
||||
auto ht_orig = hm.GlobalTypeHash(gn);
|
||||
auto hv_orig = hm.GlobalValHash(gn);
|
||||
|
||||
auto ht = pfs.HashType(g->GetType());
|
||||
p_hash_type hv = 0;
|
||||
if ( g->GetVal() )
|
||||
hv = p_hash(g->GetVal());
|
||||
|
||||
if ( ht != ht_orig || hv != hv_orig )
|
||||
{
|
||||
fprintf(stderr, "%s: hash clash for global %s (%llu/%llu vs. %llu/%llu)\n",
|
||||
working_dir.c_str(), gn.c_str(), ht, hv, ht_orig, hv_orig);
|
||||
fprintf(stderr, "val: %s\n",
|
||||
g->GetVal() ? obj_desc(g->GetVal().get()).c_str() : "<none>");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto& t : pfs.RepTypes() )
|
||||
{
|
||||
auto tag = t->Tag();
|
||||
|
||||
if ( tag != TYPE_ENUM && tag != TYPE_RECORD )
|
||||
// Other types, if inconsistent, will just not reuse
|
||||
// the previously compiled version of the type.
|
||||
continue;
|
||||
|
||||
// We identify enum's and record's by name. Make sure that
|
||||
// the name either (1) wasn't previously used, or (2) if it
|
||||
// was, it was likewise for an enum or a record.
|
||||
const auto& tn = t->GetName();
|
||||
if ( tn.empty() || ! hm.HasGlobal(tn) )
|
||||
// No concern of collision since the type name
|
||||
// wasn't previously compiled.
|
||||
continue;
|
||||
|
||||
if ( tag == TYPE_ENUM && hm.HasEnumTypeGlobal(tn) )
|
||||
// No inconsistency.
|
||||
continue;
|
||||
|
||||
if ( tag == TYPE_RECORD && hm.HasRecordTypeGlobal(tn) )
|
||||
// No inconsistency.
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "%s: type \"%s\" collides with compiled global\n", working_dir.c_str(),
|
||||
tn.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPPCompile::CreateGlobal(const ID* g)
|
||||
{
|
||||
auto gn = string(g->Name());
|
||||
|
@ -86,7 +23,7 @@ void CPPCompile::CreateGlobal(const ID* g)
|
|||
// then we'll call it directly.
|
||||
if ( compilable_funcs.count(gn) > 0 )
|
||||
{
|
||||
AddGlobal(gn, "zf", true);
|
||||
AddGlobal(gn, "zf");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -97,7 +34,7 @@ void CPPCompile::CreateGlobal(const ID* g)
|
|||
}
|
||||
}
|
||||
|
||||
if ( AddGlobal(gn, "gl", true) )
|
||||
if ( AddGlobal(gn, "gl") )
|
||||
{ // We'll be creating this global.
|
||||
Emit("IDPtr %s;", globals[gn]);
|
||||
|
||||
|
@ -146,30 +83,20 @@ void CPPCompile::AddBiF(const ID* b, bool is_var)
|
|||
if ( is_var )
|
||||
n = n + "_"; // make the name distinct
|
||||
|
||||
if ( AddGlobal(n, "bif", true) )
|
||||
if ( AddGlobal(n, "bif") )
|
||||
Emit("Func* %s;", globals[n]);
|
||||
|
||||
ASSERT(BiFs.count(globals[n]) == 0);
|
||||
BiFs[globals[n]] = bn;
|
||||
}
|
||||
|
||||
bool CPPCompile::AddGlobal(const string& g, const char* suffix, bool track)
|
||||
bool CPPCompile::AddGlobal(const string& g, const char* suffix)
|
||||
{
|
||||
bool new_var = false;
|
||||
if ( globals.count(g) > 0 )
|
||||
return false;
|
||||
|
||||
if ( globals.count(g) == 0 )
|
||||
{
|
||||
auto gn = GlobalName(g, suffix);
|
||||
|
||||
if ( hm.HasGlobalVar(gn) )
|
||||
gn = scope_prefix(hm.GlobalVarScope(gn)) + gn;
|
||||
else
|
||||
new_var = true;
|
||||
|
||||
globals.emplace(g, gn);
|
||||
}
|
||||
|
||||
return new_var;
|
||||
globals.emplace(g, GlobalName(g, suffix));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPPCompile::RegisterEvent(string ev_name)
|
||||
|
|
|
@ -33,7 +33,7 @@ static std::vector<FuncInfo> funcs;
|
|||
static ZAMCompiler* ZAM = nullptr;
|
||||
|
||||
static bool generating_CPP = false;
|
||||
static std::string hash_dir; // for storing hashes of previous compilations
|
||||
static std::string CPP_dir; // where to generate C++ code
|
||||
|
||||
static ScriptFuncPtr global_stmts;
|
||||
|
||||
|
@ -203,9 +203,9 @@ static void check_env_opt(const char* opt, bool& opt_flag)
|
|||
|
||||
static void init_options()
|
||||
{
|
||||
auto hd = getenv("ZEEK_HASH_DIR");
|
||||
if ( hd )
|
||||
hash_dir = std::string(hd) + "/";
|
||||
auto cppd = getenv("ZEEK_CPP_DIR");
|
||||
if ( cppd )
|
||||
CPP_dir = std::string(cppd) + "/";
|
||||
|
||||
// ZAM-related options.
|
||||
check_env_opt("ZEEK_DUMP_XFORM", analysis_options.dump_xform);
|
||||
|
@ -221,13 +221,14 @@ static void init_options()
|
|||
check_env_opt("ZEEK_PROFILE", analysis_options.profile_ZAM);
|
||||
|
||||
// Compile-to-C++-related options.
|
||||
check_env_opt("ZEEK_ADD_CPP", analysis_options.add_CPP);
|
||||
check_env_opt("ZEEK_GEN_CPP", analysis_options.gen_CPP);
|
||||
check_env_opt("ZEEK_GEN_STANDALONE_CPP", analysis_options.gen_standalone_CPP);
|
||||
check_env_opt("ZEEK_COMPILE_ALL", analysis_options.compile_all);
|
||||
check_env_opt("ZEEK_REPORT_CPP", analysis_options.report_CPP);
|
||||
check_env_opt("ZEEK_USE_CPP", analysis_options.use_CPP);
|
||||
|
||||
if ( analysis_options.gen_standalone_CPP )
|
||||
if ( analysis_options.gen_standalone_CPP || analysis_options.add_CPP )
|
||||
analysis_options.gen_CPP = true;
|
||||
|
||||
if ( analysis_options.gen_CPP )
|
||||
|
@ -378,15 +379,13 @@ static void use_CPP()
|
|||
|
||||
static void generate_CPP(std::unique_ptr<ProfileFuncs>& pfs)
|
||||
{
|
||||
const auto hash_name = hash_dir + "CPP-hashes";
|
||||
const auto gen_name = CPP_dir + "CPP-gen.cc";
|
||||
|
||||
auto hm = std::make_unique<CPPHashManager>(hash_name.c_str());
|
||||
const bool add = analysis_options.add_CPP;
|
||||
const bool standalone = analysis_options.gen_standalone_CPP;
|
||||
const bool report = analysis_options.report_uncompilable;
|
||||
|
||||
const auto gen_name = hash_dir + "CPP-gen.cc";
|
||||
const auto addl_name = hash_dir + "CPP-gen-addl.h";
|
||||
|
||||
CPPCompile cpp(funcs, *pfs, gen_name, addl_name, *hm, analysis_options.gen_standalone_CPP,
|
||||
analysis_options.report_uncompilable);
|
||||
CPPCompile cpp(funcs, *pfs, gen_name, add, standalone, report);
|
||||
}
|
||||
|
||||
static void find_when_funcs(std::unique_ptr<ProfileFuncs>& pfs,
|
||||
|
|
|
@ -96,6 +96,9 @@ struct AnalyOpt
|
|||
// of the corresponding script, and not activated by default).
|
||||
bool gen_standalone_CPP = false;
|
||||
|
||||
// Generate C++ that's added to existing generated code.
|
||||
bool add_CPP = false;
|
||||
|
||||
// If true, use C++ bodies if available.
|
||||
bool use_CPP = false;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue