// Copyright (C) 2020, 2021 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #ifndef RUST_COMPILE_CONTEXT #define RUST_COMPILE_CONTEXT #include "rust-system.h" #include "rust-hir-map.h" #include "rust-name-resolver.h" #include "rust-hir-type-check.h" #include "rust-backend.h" #include "rust-compile-tyty.h" #include "rust-ast-full.h" #include "rust-hir-full.h" #include "rust-hir-const-fold-ctx.h" #include "rust-mangle.h" namespace Rust { namespace Compile { struct fncontext { ::Bfunction *fndecl; ::Bvariable *ret_addr; }; class Context { public: Context (::Backend *backend) : backend (backend), resolver (Resolver::Resolver::get ()), tyctx (Resolver::TypeCheckContext::get ()), mappings (Analysis::Mappings::get ()), const_ctx (ConstFold::Context::get ()), mangler (Mangler ()) { // insert the builtins auto builtins = resolver->get_builtin_types (); for (auto it = builtins.begin (); it != builtins.end (); it++) { HirId ref; rust_assert ( tyctx->lookup_type_by_node_id ((*it)->get_node_id (), &ref)); TyTy::BaseType *lookup; rust_assert (tyctx->lookup_type (ref, &lookup)); Btype *compiled = TyTyCompile::compile (backend, lookup); compiled_type_map.insert (std::pair (ref, compiled)); builtin_range.insert (ref); } } bool lookup_compiled_types (HirId id, ::Btype **type, const TyTy::BaseType *ref = nullptr) { if (ref != nullptr) { for (auto it = mono.begin (); it != mono.end (); it++) { std::pair &val = it->second; const TyTy::BaseType *r = it->first; if (ref->is_equal (*r)) { *type = val.second; return true; } } return false; } auto it = compiled_type_map.find (id); if (it == compiled_type_map.end ()) return false; *type = it->second; return true; } void insert_compiled_type (HirId id, ::Btype *type, const TyTy::BaseType *ref = nullptr) { rust_assert (builtin_range.find (id) == builtin_range.end ()); compiled_type_map.insert (std::pair (id, type)); if (ref != nullptr) { std::pair elem (id, type); mono[ref] = std::move (elem); } } ::Backend *get_backend () { return backend; } Resolver::Resolver *get_resolver () { return resolver; } Resolver::TypeCheckContext *get_tyctx () { return tyctx; } Analysis::Mappings *get_mappings () { return mappings; } ConstFold::Context *get_const_ctx () { return const_ctx; } void push_block (Bblock *scope) { scope_stack.push_back (scope); statements.push_back ({}); } Bblock *pop_block () { auto block = scope_stack.back (); scope_stack.pop_back (); auto stmts = statements.back (); statements.pop_back (); backend->block_add_statements (block, stmts); return block; } Bblock *peek_enclosing_scope () { if (scope_stack.size () == 0) return nullptr; return scope_stack.back (); } void add_statement_to_enclosing_scope (Bstatement *stmt) { statements.at (statements.size () - 2).push_back (stmt); } void add_statement (Bstatement *stmt) { statements.back ().push_back (stmt); } void insert_var_decl (HirId id, ::Bvariable *decl) { compiled_var_decls[id] = decl; } bool lookup_var_decl (HirId id, ::Bvariable **decl) { auto it = compiled_var_decls.find (id); if (it == compiled_var_decls.end ()) return false; *decl = it->second; return true; } void insert_function_decl (const TyTy::FnType *ref, ::Bfunction *fn) { auto id = ref->get_ty_ref (); auto dId = ref->get_id (); rust_assert (compiled_fn_map.find (id) == compiled_fn_map.end ()); compiled_fn_map[id] = fn; auto it = mono_fns.find (dId); if (it == mono_fns.end ()) mono_fns[dId] = {}; mono_fns[dId].push_back ({ref, fn}); } bool lookup_function_decl (HirId id, ::Bfunction **fn, DefId dId = UNKNOWN_DEFID, const TyTy::BaseType *ref = nullptr) { // for for any monomorphized fns if (ref != nullptr) { rust_assert (dId != UNKNOWN_DEFID); auto it = mono_fns.find (dId); if (it == mono_fns.end ()) return false; for (auto &e : mono_fns[dId]) { const TyTy::BaseType *r = e.first; ::Bfunction *f = e.second; if (ref->is_equal (*r)) { *fn = f; return true; } } return false; } auto it = compiled_fn_map.find (id); if (it == compiled_fn_map.end ()) return false; *fn = it->second; return true; } void insert_const_decl (HirId id, ::Bexpression *expr) { compiled_consts[id] = expr; } bool lookup_const_decl (HirId id, ::Bexpression **expr) { auto it = compiled_consts.find (id); if (it == compiled_consts.end ()) return false; *expr = it->second; return true; } void insert_label_decl (HirId id, ::Blabel *label) { compiled_labels[id] = label; } bool lookup_label_decl (HirId id, ::Blabel **label) { auto it = compiled_labels.find (id); if (it == compiled_labels.end ()) return false; *label = it->second; return true; } void push_fn (::Bfunction *fn, ::Bvariable *ret_addr) { fn_stack.push_back (fncontext{fn, ret_addr}); } void pop_fn () { fn_stack.pop_back (); } fncontext peek_fn () { return fn_stack.back (); } void push_type (::Btype *t) { type_decls.push_back (t); } void push_var (::Bvariable *v) { var_decls.push_back (v); } void push_const (::Bexpression *c) { const_decls.push_back (c); } void push_function (::Bfunction *f) { func_decls.push_back (f); } void write_to_backend () { backend->write_global_definitions (type_decls, const_decls, func_decls, var_decls); } bool function_completed (Bfunction *fn) { for (auto it = func_decls.begin (); it != func_decls.end (); it++) { Bfunction *i = (*it); if (i == fn) { return true; } } return false; } void push_loop_context (Bvariable *var) { loop_value_stack.push_back (var); } Bvariable *peek_loop_context () { return loop_value_stack.back (); } Bvariable *pop_loop_context () { auto back = loop_value_stack.back (); loop_value_stack.pop_back (); return back; } void push_loop_begin_label (Blabel *label) { loop_begin_labels.push_back (label); } Blabel *peek_loop_begin_label () { return loop_begin_labels.back (); } Blabel *pop_loop_begin_label () { Blabel *pop = loop_begin_labels.back (); loop_begin_labels.pop_back (); return pop; } std::string mangle_item (const TyTy::BaseType *ty, const Resolver::CanonicalPath &path) const { return mangler.mangle_item (ty, path, mappings->get_current_crate_name ()); } std::string mangle_impl_item (const TyTy::BaseType *self, const TyTy::BaseType *ty, const std::string &name) const { return mangler.mangle_impl_item (self, ty, name, mappings->get_current_crate_name ()); } private: ::Backend *backend; Resolver::Resolver *resolver; Resolver::TypeCheckContext *tyctx; Analysis::Mappings *mappings; ConstFold::Context *const_ctx; std::set builtin_range; Mangler mangler; // state std::vector fn_stack; std::map compiled_var_decls; std::map compiled_type_map; std::map compiled_fn_map; std::map compiled_consts; std::map compiled_labels; std::vector<::std::vector> statements; std::vector<::Bblock *> scope_stack; std::vector<::Bvariable *> loop_value_stack; std::vector<::Blabel *> loop_begin_labels; std::map> mono; std::map>> mono_fns; // To GCC middle-end std::vector<::Btype *> type_decls; std::vector<::Bvariable *> var_decls; std::vector<::Bexpression *> const_decls; std::vector<::Bfunction *> func_decls; }; class TyTyResolveCompile : public TyTy::TyVisitor { public: static ::Btype *compile (Context *ctx, TyTy::BaseType *ty, bool trait_object_mode = false) { TyTyResolveCompile compiler (ctx, trait_object_mode); ty->accept_vis (compiler); return compiler.translated; } void visit (TyTy::ErrorType &) override { gcc_unreachable (); } void visit (TyTy::InferType &) override { gcc_unreachable (); } void visit (TyTy::ProjectionType &type) override { type.get ()->accept_vis (*this); } void visit (TyTy::PlaceholderType &type) override { type.resolve ()->accept_vis (*this); } void visit (TyTy::ParamType ¶m) override { param.resolve ()->accept_vis (*this); } void visit (TyTy::FnType &type) override { Backend::Btyped_identifier receiver; std::vector parameters; std::vector results; if (!type.get_return_type ()->is_unit ()) { auto hir_type = type.get_return_type (); auto ret = TyTyResolveCompile::compile (ctx, hir_type, trait_object_mode); results.push_back (Backend::Btyped_identifier ( "_", ret, ctx->get_mappings ()->lookup_location (hir_type->get_ref ()))); } for (auto ¶m_pair : type.get_params ()) { auto param_tyty = param_pair.second; auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty, trait_object_mode); auto compiled_param = Backend::Btyped_identifier ( param_pair.first->as_string (), compiled_param_type, ctx->get_mappings ()->lookup_location (param_tyty->get_ref ())); parameters.push_back (compiled_param); } if (!type.is_varadic ()) translated = ctx->get_backend ()->function_type ( receiver, parameters, results, NULL, ctx->get_mappings ()->lookup_location (type.get_ref ())); else translated = ctx->get_backend ()->function_type_varadic ( receiver, parameters, results, NULL, ctx->get_mappings ()->lookup_location (type.get_ref ())); } void visit (TyTy::FnPtr &type) override { Btype *result_type = TyTyResolveCompile::compile (ctx, type.get_return_type ()); std::vector parameters; type.iterate_params ([&] (TyTy::BaseType *p) mutable -> bool { Btype *pty = TyTyResolveCompile::compile (ctx, p); parameters.push_back (pty); return true; }); translated = ctx->get_backend ()->function_ptr_type ( result_type, parameters, ctx->get_mappings ()->lookup_location (type.get_ref ())); } void visit (TyTy::ADTType &type) override { if (ctx->lookup_compiled_types (type.get_ty_ref (), &translated, &type)) return; std::vector fields; for (size_t i = 0; i < type.num_fields (); i++) { TyTy::StructFieldType *field = type.get_field (i); Btype *compiled_field_ty = TyTyResolveCompile::compile (ctx, field->get_field_type ()); Backend::Btyped_identifier f (field->get_name (), compiled_field_ty, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); fields.push_back (std::move (f)); } Btype *type_record; if (type.is_union ()) type_record = ctx->get_backend ()->union_type (fields); else type_record = ctx->get_backend ()->struct_type (fields); Btype *named_struct = ctx->get_backend ()->named_type (type.get_name (), type_record, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); ctx->push_type (named_struct); translated = named_struct; ctx->insert_compiled_type (type.get_ty_ref (), named_struct, &type); } void visit (TyTy::TupleType &type) override { if (type.num_fields () == 0) { translated = ctx->get_backend ()->unit_type (); return; } bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &translated, &type); if (ok) return; // create implicit struct std::vector fields; for (size_t i = 0; i < type.num_fields (); i++) { TyTy::BaseType *field = type.get_field (i); Btype *compiled_field_ty = TyTyResolveCompile::compile (ctx, field); // rustc uses the convention __N, where N is an integer, to // name the fields of a tuple. We follow this as well, // because this is used by GDB. One further reason to prefer // this, rather than simply emitting the integer, is that this // approach makes it simpler to use a C-only debugger, or // GDB's C mode, when debugging Rust. Backend::Btyped_identifier f ("__" + std::to_string (i), compiled_field_ty, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); fields.push_back (std::move (f)); } Btype *struct_type_record = ctx->get_backend ()->struct_type (fields); Btype *named_struct = ctx->get_backend ()->named_type (type.as_string (), struct_type_record, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); ctx->push_type (named_struct); ctx->insert_compiled_type (type.get_ty_ref (), named_struct, &type); translated = named_struct; } void visit (TyTy::ArrayType &type) override { Btype *element_type = TyTyResolveCompile::compile (ctx, type.get_element_type ()); translated = ctx->get_backend ()->array_type (element_type, type.get_capacity ()); } void visit (TyTy::BoolType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::IntType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::UintType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::FloatType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::USizeType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::ISizeType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::CharType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::ReferenceType &type) override { Btype *base_compiled_type = TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode); if (type.is_mutable ()) { translated = ctx->get_backend ()->reference_type (base_compiled_type); } else { auto base = ctx->get_backend ()->immutable_type (base_compiled_type); translated = ctx->get_backend ()->reference_type (base); } } void visit (TyTy::PointerType &type) override { Btype *base_compiled_type = TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode); if (type.is_mutable ()) { translated = ctx->get_backend ()->pointer_type (base_compiled_type); } else { auto base = ctx->get_backend ()->immutable_type (base_compiled_type); translated = ctx->get_backend ()->pointer_type (base); } } void visit (TyTy::StrType &type) override { ::Btype *compiled_type = nullptr; bool ok = ctx->lookup_compiled_types (type.get_ty_ref (), &compiled_type); rust_assert (ok); translated = compiled_type; } void visit (TyTy::NeverType &) override { translated = ctx->get_backend ()->unit_type (); } void visit (TyTy::DynamicObjectType &type) override { if (trait_object_mode) { translated = ctx->get_backend ()->integer_type ( true, ctx->get_backend ()->get_pointer_size ()); return; } if (ctx->lookup_compiled_types (type.get_ty_ref (), &translated, &type)) return; // create implicit struct auto items = type.get_object_items (); std::vector fields; Btype *uint = ctx->get_backend ()->integer_type ( true, ctx->get_backend ()->get_pointer_size ()); Btype *uintptr_ty = ctx->get_backend ()->pointer_type (uint); Backend::Btyped_identifier f ("__receiver_trait_obj_ptr", uintptr_ty, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); fields.push_back (std::move (f)); for (size_t i = 0; i < items.size (); i++) { // mrustc seems to make a vtable consisting of uintptr's Btype *uint = ctx->get_backend ()->integer_type ( true, ctx->get_backend ()->get_pointer_size ()); Btype *uintptr_ty = ctx->get_backend ()->pointer_type (uint); Backend::Btyped_identifier f ("__" + std::to_string (i), uintptr_ty, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); fields.push_back (std::move (f)); } Btype *type_record = ctx->get_backend ()->struct_type (fields); Btype *named_struct = ctx->get_backend ()->named_type (type.get_name (), type_record, ctx->get_mappings ()->lookup_location ( type.get_ty_ref ())); ctx->push_type (named_struct); translated = named_struct; ctx->insert_compiled_type (type.get_ty_ref (), named_struct, &type); } void visit (TyTy::ClosureType &type) override { gcc_unreachable (); } private: TyTyResolveCompile (Context *ctx, bool trait_object_mode) : ctx (ctx), trait_object_mode (trait_object_mode), translated (nullptr) {} Context *ctx; bool trait_object_mode; ::Btype *translated; }; } // namespace Compile } // namespace Rust #endif // RUST_COMPILE_CONTEXT