// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 base_type; std::optional storage; std::optional result; std::optional param_copy; std::optional param_in; std::optional param_inout; std::optional ctor; std::optional 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 forward; /**< Forward declaration for type information. */ std::optional 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); /** Entry point for code generation. */ Result> compileModule(declaration::Module* module); /** Entry point for generating additional cross-unit C++ code through HILTI's linker. */ Result> linkUnits(const std::vector& mds); std::shared_ptr 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 typeDeclaration(QualifiedType* t); std::list 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 namespace_ = {}); std::vector compileCallArguments(const hilti::node::Range& args, const hilti::node::Set& params); std::vector compileCallArguments(const hilti::node::Range& args, const hilti::node::Range& params); std::optional 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& 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& 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 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; std::unique_ptr _builder; std::shared_ptr _cxx_unit; hilti::declaration::Module* _hilti_module = nullptr; std::vector _self = {{"__self", Side::LHS}}; std::vector _dd = {{"__dd", Side::LHS}}; std::vector _cxx_blocks; std::vector _tmps; std::map _tmp_counters; hilti::util::Cache _cache_types_storage; hilti::util::Cache _cache_type_info; hilti::util::Cache _cache_types_declarations; }; } // namespace detail } // namespace hilti