Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

173 lines
7.7 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
#pragma once
#include <algorithm>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <hilti/ast/builder/builder.h>
#include <hilti/ast/expressions/resolved-operator.h>
#include <hilti/ast/function.h>
#include <hilti/ast/types/bitfield.h>
#include <hilti/base/cache.h>
#include <hilti/compiler/context.h>
#include <hilti/compiler/detail/cxx/elements.h>
#include <hilti/compiler/detail/cxx/unit.h>
namespace hilti {
namespace logging::debug {
inline const DebugStream CodeGen("codegen");
} // namespace logging::debug
class Node;
class Unit;
namespace detail {
namespace codegen {
enum class TypeUsage { Storage, CopyParameter, InParameter, InOutParameter, FunctionResult, Ctor, None };
struct CxxTypes {
std::optional<cxx::Type> base_type;
std::optional<cxx::Type> storage;
std::optional<cxx::Type> result;
std::optional<cxx::Type> param_copy;
std::optional<cxx::Type> param_in;
std::optional<cxx::Type> param_inout;
std::optional<cxx::Type> ctor;
std::optional<cxx::Expression> default_;
};
/** Structure capturing runtime type information for a specific type. */
struct CxxTypeInfo {
bool predefined; /**< True if the type information instances is being predefined statically by the runtime library
(vs. generated by the codegen) */
cxx::Expression reference; /**< ID to refer to this type information instance. */
std::optional<cxx::declaration::Constant> forward; /**< Forward declaration for type information. */
std::optional<cxx::declaration::Constant> declaration; /**< Actual declaration for type information. */
};
} // namespace codegen
/**
* HILTI's code generator. This is the main internal entry point for
* generating C++ code from HILTI source code.
*/
class CodeGen {
public:
CodeGen(const std::shared_ptr<Context>& context);
/** Entry point for code generation. */
Result<std::shared_ptr<cxx::Unit>> compileModule(declaration::Module* module);
/** Entry point for generating additional cross-unit C++ code through HILTI's linker. */
Result<std::shared_ptr<cxx::Unit>> linkUnits(const std::vector<cxx::linker::MetaData>& mds);
std::shared_ptr<Context> context() const { return _context.lock(); }
const Options& options() const { return context()->options(); }
auto* builder() const { return _builder.get(); }
// These must be called only while a module is being compiled.
std::optional<cxx::declaration::Type> typeDeclaration(QualifiedType* t);
std::list<cxx::declaration::Type> typeDependencies(QualifiedType* t);
cxx::Type compile(QualifiedType* t, codegen::TypeUsage usage);
cxx::Expression compile(hilti::Expression* e, bool lhs = false);
cxx::Expression compile(hilti::Ctor* c, bool lhs = false);
cxx::Expression compile(hilti::expression::ResolvedOperator* o, bool lhs = false);
cxx::Block compile(hilti::Statement* s, cxx::Block* b = nullptr);
cxx::declaration::Function compile(Declaration* decl, type::Function* ft, declaration::Linkage linkage,
function::CallingConvention cc = function::CallingConvention::Standard,
AttributeSet* fattrs = {}, std::optional<cxx::ID> namespace_ = {});
std::vector<cxx::Expression> compileCallArguments(const hilti::node::Range<Expression>& args,
const hilti::node::Set<declaration::Parameter>& params);
std::vector<cxx::Expression> compileCallArguments(const hilti::node::Range<Expression>& args,
const hilti::node::Range<declaration::Parameter>& params);
std::optional<cxx::Expression> typeDefaultValue(QualifiedType* t);
codegen::TypeUsage parameterKindToTypeUsage(parameter::Kind);
cxx::Expression typeInfo(QualifiedType* t);
void addTypeInfoDefinition(QualifiedType* t);
cxx::Expression coerce(const cxx::Expression& e, QualifiedType* src,
QualifiedType* dst); // only for supported coercions
cxx::Expression pack(Expression* data, const Expressions& args);
cxx::Expression pack(QualifiedType* t, const cxx::Expression& data, const std::vector<cxx::Expression>& args);
cxx::Expression unpack(QualifiedType* t, QualifiedType* data_type, Expression* data, const Expressions& args,
bool throw_on_error);
cxx::Expression unpack(QualifiedType* t, QualifiedType* data_type, const cxx::Expression& data,
const std::vector<cxx::Expression>& args, bool throw_on_error);
cxx::Expression addTmp(const std::string& prefix, const cxx::Type& t);
cxx::Expression addTmp(const std::string& prefix, const cxx::Expression& init);
cxx::Expression startProfiler(const std::string& name, cxx::Block* block = nullptr, bool insert_at_front = false);
void stopProfiler(const cxx::Expression& profiler, cxx::Block* block = nullptr);
cxx::Expression unsignedIntegerToBitfield(type::Bitfield* t, const cxx::Expression& value,
const cxx::Expression& bitorder);
// Helper factoring out common code to generate C++ types and default
// values for vector types and iterators.
std::pair<std::string, std::string> cxxTypeForVector(QualifiedType* element_type, bool want_iterator = false);
/**
* Returns an ID that's unique for a given node. The ID is derived from
* the node's location information, which must be present.
*
* @param prefix constant prefix that will be added to ID
* @param n node to generate the ID for
*
*/
cxx::ID uniqueID(const std::string& prefix, Node* n);
cxx::Expression self() const { return _self.back(); }
void pushSelf(detail::cxx::Expression e) { _self.push_back(std::move(e)); }
void popSelf() { _self.pop_back(); }
cxx::Expression dollardollar() const { return _dd.back(); }
void pushDollarDollar(cxx::Expression e) { _dd.push_back(std::move(e)); }
void popDollarDollar() { _dd.pop_back(); }
auto cxxBlock() const { return ! _cxx_blocks.empty() ? _cxx_blocks.back() : nullptr; }
void pushCxxBlock(cxx::Block* b) { _cxx_blocks.push_back(b); }
void popCxxBlock() { _cxx_blocks.pop_back(); }
cxx::Unit* unit() const; // will abort if not compiling a module.
hilti::declaration::Module* hiltiModule() const; // will abort if not compiling a module.
private:
const codegen::CxxTypeInfo& _getOrCreateTypeInfo(QualifiedType* t);
// Adapt expression so that it can be used as a LHS. If expr is already a
// LHS, it's returned directly. Otherwise it assigns it over into a
// temporary, which is then returned.
cxx::Expression _makeLhs(cxx::Expression expr, QualifiedType* type);
// Add all required C++ declarations to a unit.
void _addCxxDeclarations(cxx::Unit* unit);
std::weak_ptr<Context> _context;
std::unique_ptr<Builder> _builder;
std::shared_ptr<cxx::Unit> _cxx_unit;
hilti::declaration::Module* _hilti_module = nullptr;
std::vector<detail::cxx::Expression> _self = {{"__self", Side::LHS}};
std::vector<detail::cxx::Expression> _dd = {{"__dd", Side::LHS}};
std::vector<detail::cxx::Block*> _cxx_blocks;
std::vector<detail::cxx::declaration::Local> _tmps;
std::map<std::string, int> _tmp_counters;
hilti::util::Cache<cxx::ID, codegen::CxxTypes> _cache_types_storage;
hilti::util::Cache<cxx::ID, codegen::CxxTypeInfo> _cache_type_info;
hilti::util::Cache<cxx::ID, cxx::declaration::Type> _cache_types_declarations;
};
} // namespace detail
} // namespace hilti