LCOV - code coverage report
Current view: top level - gcc/rust/backend - rust-compile-extern.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 82.4 % 233 192
Test Date: 2026-09-19 16:22:48 Functions: 75.0 % 8 6
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #ifndef RUST_COMPILE_EXTERN_ITEM
      20              : #define RUST_COMPILE_EXTERN_ITEM
      21              : 
      22              : #include "rust-compile-base.h"
      23              : #include "rust-compile-intrinsic.h"
      24              : #include "rust-compile-type.h"
      25              : #include "rust-diagnostics.h"
      26              : #include "rust-hir-full-decls.h"
      27              : #include "rust-attributes.h"
      28              : #include "rust-attribute-values.h"
      29              : #include "rust-builtins.h"
      30              : #include "rust-compile-fnparam.h"
      31              : #include "fold-const.h"
      32              : 
      33              : namespace Rust {
      34              : namespace Compile {
      35              : 
      36         2666 : class CompileExternItem : public HIRCompileBase,
      37              :                           public HIR::HIRExternalItemVisitor
      38              : {
      39              : public:
      40         2666 :   static tree compile (HIR::ExternalItem *item, Context *ctx,
      41              :                        TyTy::BaseType *concrete = nullptr,
      42              :                        location_t ref_locus = UNDEF_LOCATION)
      43              :   {
      44         2666 :     CompileExternItem compiler (ctx, concrete, ref_locus);
      45         2666 :     item->accept_vis (compiler);
      46         2666 :     return compiler.reference;
      47         2666 :   }
      48              : 
      49            0 :   void visit (HIR::ExternalStaticItem &item) override
      50              :   {
      51              :     // check if its already been compiled
      52            0 :     Bvariable *lookup = Bvariable::error_variable ();
      53            0 :     if (ctx->lookup_var_decl (item.get_mappings ().get_hirid (), &lookup))
      54              :       {
      55            0 :         reference = Backend::var_expression (lookup, ref_locus);
      56            0 :         return;
      57              :       }
      58              : 
      59            0 :     TyTy::BaseType *resolved_type = nullptr;
      60            0 :     bool ok = ctx->get_tyctx ()->lookup_type (item.get_mappings ().get_hirid (),
      61              :                                               &resolved_type);
      62            0 :     rust_assert (ok);
      63              : 
      64            0 :     std::string name = item.get_item_name ().as_string ();
      65            0 :     GGC::Ident asm_name = get_link_name (item);
      66              : 
      67            0 :     tree type = TyTyResolveCompile::compile (ctx, resolved_type);
      68            0 :     bool is_external = true;
      69            0 :     bool is_hidden = false;
      70            0 :     bool in_unique_section = false;
      71              : 
      72            0 :     Bvariable *static_global
      73            0 :       = Backend::global_variable (name, asm_name, type, is_external, is_hidden,
      74              :                                   in_unique_section, item.get_locus ());
      75            0 :     ctx->insert_var_decl (item.get_mappings ().get_hirid (), static_global);
      76            0 :     ctx->push_var (static_global);
      77              : 
      78            0 :     reference = Backend::var_expression (static_global, ref_locus);
      79            0 :   }
      80              : 
      81         2664 :   void visit (HIR::ExternalFunctionItem &function) override
      82              :   {
      83         2664 :     TyTy::BaseType *fntype_tyty;
      84         2664 :     if (!ctx->get_tyctx ()->lookup_type (function.get_mappings ().get_hirid (),
      85              :                                          &fntype_tyty))
      86              :       {
      87            0 :         rust_fatal_error (function.get_locus (),
      88              :                           "failed to lookup function type");
      89         1524 :         return;
      90              :       }
      91              : 
      92         2664 :     rust_assert (fntype_tyty->get_kind () == TyTy::TypeKind::FNDEF);
      93         2664 :     TyTy::FnType *fntype = static_cast<TyTy::FnType *> (fntype_tyty);
      94         2664 :     if (fntype->has_substitutions_defined ())
      95              :       {
      96              :         // we cant do anything for this only when it is used and a concrete type
      97              :         // is given
      98         1051 :         if (concrete == nullptr)
      99              :           return;
     100              :         else
     101              :           {
     102            0 :             rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF);
     103            0 :             fntype = static_cast<TyTy::FnType *> (concrete);
     104              :           }
     105              :       }
     106              : 
     107              :     // items can be forward compiled which means we may not need to invoke this
     108              :     // code. We might also have already compiled this generic function as well.
     109         1613 :     tree lookup = NULL_TREE;
     110         1613 :     if (ctx->lookup_function_decl (fntype->get_ty_ref (), &lookup,
     111              :                                    fntype->get_id (), fntype))
     112              :       {
     113           16 :         reference = address_expression (lookup, ref_locus);
     114           16 :         return;
     115              :       }
     116              : 
     117         1597 :     if (fntype->has_substitutions_defined ())
     118              :       // override the HIR lookups for the substitutions in this context
     119            0 :       fntype->override_context ();
     120              : 
     121         1597 :     if (fntype->get_abi () == ABI::INTRINSIC)
     122              :       {
     123          452 :         Intrinsics compile (ctx);
     124          452 :         tree fndecl = compile.compile (fntype, ref_locus);
     125          452 :         ctx->insert_function_decl (fntype, fndecl);
     126          452 :         return;
     127              :       }
     128              : 
     129         1145 :     tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
     130         2290 :     std::string ir_symbol_name = function.get_item_name ().as_string ();
     131         1145 :     GGC::Ident asm_name = get_link_name (function);
     132              : 
     133         1145 :     if (fntype->get_abi () == ABI::UNADJUSTED)
     134              :       {
     135            5 :         tree resolved;
     136            5 :         LlvmBuiltinAdapter adapter;
     137           10 :         auto mapping_res = BuiltinsContext::get ().map_llvm_to_gcc_builtin (
     138            5 :           asm_name.as_string (), &resolved, &adapter);
     139              : 
     140            5 :         switch (mapping_res)
     141              :           {
     142            0 :           case LlvmBuiltinMappingResult::NOT_MAPPED:
     143            0 :             rust_error_at (function.get_locus (),
     144              :                            "LLVM intrinsic %qs is not supported at the moment",
     145              :                            asm_name.c_str ());
     146            0 :             reference = error_mark_node;
     147            0 :             return;
     148            0 :           case LlvmBuiltinMappingResult::TARGET_UNAVAILABLE:
     149            0 :             rust_error_at (
     150              :               function.get_locus (),
     151              :               "LLVM intrinsic %qs is not available for this target",
     152              :               asm_name.c_str ());
     153            0 :             reference = error_mark_node;
     154            0 :             return;
     155              :           case LlvmBuiltinMappingResult::RESOLVED:
     156              :             break;
     157              :           }
     158              : 
     159            5 :         tree adapter_tree = error_mark_node;
     160              : 
     161            5 :         switch (adapter)
     162              :           {
     163            2 :           case LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS:
     164            2 :             adapter_tree = compile_x86_output_pointer_adapter (
     165              :               ctx, fntype, resolved, OutputTupleOrder::VALUE_STATUS,
     166              :               function.get_locus ());
     167            2 :             break;
     168            2 :           case LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE:
     169            2 :             adapter_tree = compile_x86_output_pointer_adapter (
     170              :               ctx, fntype, resolved, OutputTupleOrder::STATUS_VALUE,
     171              :               function.get_locus ());
     172            2 :             break;
     173            1 :           case LlvmBuiltinAdapter::FORWARD_ARGUMENTS:
     174            1 :             adapter_tree
     175            1 :               = compile_x86_forwarding_adapter (ctx, fntype, resolved,
     176              :                                                 function.get_locus ());
     177            1 :             break;
     178              :           }
     179              : 
     180            5 :         if (adapter_tree == error_mark_node)
     181              :           {
     182            0 :             rust_error_at (function.get_locus (),
     183              :                            "Invalid signature for LLVM intrinsic %qs",
     184              :                            asm_name.c_str ());
     185            0 :             reference = error_mark_node;
     186            0 :             return;
     187              :           }
     188              : 
     189            5 :         ctx->insert_function_decl (fntype, adapter_tree);
     190            5 :         reference = address_expression (adapter_tree, ref_locus);
     191            5 :         return;
     192              :       }
     193              : 
     194         1140 :     const unsigned int flags = Backend::function_is_declaration;
     195         1140 :     tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
     196              :                                      flags, function.get_locus ());
     197         1140 :     TREE_PUBLIC (fndecl) = 1;
     198         1140 :     setup_abi_options (fndecl, fntype->get_abi ());
     199              : 
     200         1140 :     ctx->insert_function_decl (fntype, fndecl);
     201              : 
     202         1140 :     reference = address_expression (fndecl, ref_locus);
     203         1145 :   }
     204              : 
     205            2 :   void visit (HIR::ExternalTypeItem &type) override
     206              :   {
     207            2 :     TyTy::BaseType *lookup = nullptr;
     208            2 :     if (!ctx->get_tyctx ()->lookup_type (type.get_mappings ().get_hirid (),
     209              :                                          &lookup))
     210              :       {
     211            0 :         rust_error_at (type.get_locus (), "failed to resolve type");
     212            0 :         return;
     213              :       }
     214              : 
     215            2 :     TyTyResolveCompile::compile (ctx, lookup);
     216              :   }
     217              : 
     218              : private:
     219              :   enum class OutputTupleOrder
     220              :   {
     221              :     VALUE_STATUS,
     222              :     STATUS_VALUE,
     223              :   };
     224              : 
     225         2666 :   CompileExternItem (Context *ctx, TyTy::BaseType *concrete,
     226              :                      location_t ref_locus)
     227         2666 :     : HIRCompileBase (ctx), concrete (concrete), reference (error_mark_node),
     228         2666 :       ref_locus (ref_locus)
     229              :   {}
     230              : 
     231         1145 :   template <typename T> static GGC::Ident get_link_name (T &obj)
     232              :   {
     233         1145 :     AST::Attribute *use_attr = nullptr;
     234              : 
     235         1157 :     for (auto &attr : obj.get_outer_attrs ())
     236              :       {
     237           12 :         if (attr.get_path ().as_string () == Values::Attributes::LINK_NAME)
     238              :           {
     239              :             // later attributes override earlier ones
     240              :             // TODO: add warning -- should duplicate
     241              :             //       attributes be folded elsewhere?
     242           12 :             use_attr = &attr;
     243              :           }
     244              :       }
     245              : 
     246         1145 :     if (use_attr)
     247              :       {
     248           12 :         auto link_name
     249              :           = Analysis::Attributes::extract_string_literal (*use_attr);
     250              : 
     251           12 :         if (!link_name.has_value ())
     252            0 :           rust_error_at (use_attr->get_locus (),
     253              :                          "malformed %<link_name%> attribute input");
     254              :         else
     255           12 :           return *link_name;
     256           12 :       }
     257              : 
     258         1133 :     return obj.get_item_name ();
     259              :   }
     260              : 
     261              :   /**
     262              :    * Compiles a wrapper that wraps around GCC built-ins for compatibility
     263              :    * with LLVM built-ins function signatures returning a tuple with value +
     264              :    * status.
     265              :    *
     266              :    * @param ctx
     267              :    * @param fntype the LLVM built-in function type
     268              :    * @param gcc_builtin the GCC built-in function
     269              :    * @param order whether the LLVM built-in returns (value, status) or (status,
     270              :    * value)
     271              :    * @param locus
     272              :    * @return tree the resultant wrapper function
     273              :    */
     274            4 :   static tree compile_x86_output_pointer_adapter (Context *ctx,
     275              :                                                   TyTy::FnType *fntype,
     276              :                                                   tree gcc_builtin,
     277              :                                                   OutputTupleOrder order,
     278              :                                                   location_t locus)
     279              :   {
     280              :     // expect to be a 2-field tuple return type
     281            4 :     TyTy::TupleType *tuple
     282            4 :       = fntype->get_return_type ()->try_as<TyTy::TupleType> ();
     283              : 
     284            4 :     if (tuple == nullptr || tuple->num_fields () != 2)
     285              :       // TODO probably add error here
     286            0 :       return error_mark_node;
     287              : 
     288            4 :     tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
     289              : 
     290            4 :     const auto &path = fntype->get_ident ().path;
     291            4 :     std::string ir_name = path.get () + fntype->subst_as_string ();
     292            4 :     std::string asm_name = ctx->mangle_item (fntype, path);
     293              : 
     294              :     // start building the wrapper function
     295            4 :     tree fndecl
     296            4 :       = Backend::function (compiled_fn_type, ir_name, asm_name, 0, locus);
     297              : 
     298            4 :     TREE_PUBLIC (fndecl) = 0;
     299            4 :     DECL_ARTIFICIAL (fndecl) = 1;
     300            4 :     DECL_EXTERNAL (fndecl) = 0;
     301            4 :     DECL_DECLARED_INLINE_P (fndecl) = 1;
     302              : 
     303              :     // compile params for the rust wrapper
     304            4 :     std::vector<Bvariable *> param_vars;
     305            4 :     param_vars.reserve (fntype->get_params ().size ());
     306           10 :     for (auto &param : fntype->get_params ())
     307              :       {
     308            6 :         auto &pattern = param.get_pattern ();
     309            6 :         tree type = TyTyResolveCompile::compile (ctx, param.get_type ());
     310            6 :         Bvariable *variable
     311            6 :           = CompileFnParam::compile (ctx, fndecl, pattern, type,
     312            6 :                                      pattern.get_locus ());
     313            6 :         param_vars.emplace_back (variable);
     314              :       }
     315              : 
     316            4 :     if (!Backend::function_set_parameters (fndecl, param_vars))
     317            0 :       return error_mark_node;
     318              : 
     319              :     // forward the rust params, convert each into the corresponding gcc
     320              :     // built-in param type
     321            4 :     std::vector<tree> call_arguments;
     322              :     // +1 here because gcc built-in expects output pointer as param
     323            4 :     call_arguments.reserve (param_vars.size () + 1);
     324            4 :     tree gcc_argument_types = TYPE_ARG_TYPES (TREE_TYPE (gcc_builtin));
     325           10 :     for (Bvariable *param : param_vars)
     326              :       {
     327            6 :         tree argument = param->get_tree (locus);
     328            6 :         tree expected_type = TREE_VALUE (gcc_argument_types);
     329            6 :         argument = Backend::convert_expression (expected_type, argument, locus);
     330            6 :         call_arguments.emplace_back (argument);
     331            6 :         gcc_argument_types = TREE_CHAIN (gcc_argument_types);
     332              :       }
     333              : 
     334              :     // the order of llvm built-ins' tuple return types' fields is not
     335              :     // consistent, some are (value, status) and others are (status, value), so
     336              :     // we need to reorder it...
     337            4 :     tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
     338            4 :     tree first_field = TYPE_FIELDS (tuple_type);
     339            4 :     tree second_field = DECL_CHAIN (first_field);
     340              : 
     341            4 :     tree value_field
     342            4 :       = order == OutputTupleOrder::VALUE_STATUS ? first_field : second_field;
     343            2 :     tree status_field
     344              :       = order == OutputTupleOrder::VALUE_STATUS ? second_field : first_field;
     345              : 
     346            4 :     tree value_type = TREE_TYPE (value_field);
     347            4 :     tree status_type = TREE_TYPE (status_field);
     348              : 
     349            4 :     tree temporary_stmt = NULL_TREE;
     350            4 :     Bvariable *value
     351            4 :       = Backend::temporary_variable (fndecl, NULL_TREE, value_type, NULL_TREE,
     352              :                                      true, locus, &temporary_stmt);
     353            4 :     Bvariable *status
     354            4 :       = Backend::temporary_variable (fndecl, NULL_TREE, status_type, NULL_TREE,
     355              :                                      false, locus, &temporary_stmt);
     356              : 
     357              :     // compile the final gcc built-in param which is the output value pointer,
     358              :     // which llvm returns as part of the tuple return type
     359            4 :     tree gcc_pointer_type = TREE_VALUE (gcc_argument_types);
     360            4 :     tree block
     361            4 :       = Backend::block (fndecl, NULL_TREE, {value, status}, locus, locus);
     362            4 :     ctx->push_block (block);
     363              : 
     364            4 :     tree value_decl = value->get_tree (locus);
     365            4 :     tree status_decl = status->get_tree (locus);
     366              : 
     367            4 :     tree value_address = build_fold_addr_expr_loc (locus, value_decl);
     368            4 :     value_address
     369            4 :       = Backend::convert_expression (gcc_pointer_type, value_address, locus);
     370            4 :     call_arguments.emplace_back (value_address);
     371              : 
     372              :     // call the gcc built-in with the params that were built and assign the
     373              :     // returned val to the status temp var
     374            4 :     tree builtin_call
     375            4 :       = build_call_expr_loc_array (locus, gcc_builtin,
     376            4 :                                    static_cast<int> (call_arguments.size ()),
     377              :                                    call_arguments.data ());
     378            4 :     builtin_call
     379            4 :       = Backend::convert_expression (status_type, builtin_call, locus);
     380            4 :     ctx->add_statement (
     381              :       Backend::assignment_statement (status_decl, builtin_call, locus));
     382              : 
     383              :     // finally compile and add the return statement
     384            4 :     std::vector<tree> tuple_values;
     385            4 :     if (order == OutputTupleOrder::VALUE_STATUS)
     386            2 :       tuple_values = {value_decl, status_decl};
     387              :     else
     388            2 :       tuple_values = {status_decl, value_decl};
     389              : 
     390            4 :     tree tuple_result
     391            4 :       = Backend::constructor_expression (tuple_type, false, tuple_values, -1,
     392              :                                          locus);
     393            4 :     ctx->add_statement (
     394              :       Backend::return_statement (fndecl, tuple_result, locus));
     395              : 
     396            4 :     tree body = ctx->pop_block ();
     397            4 :     DECL_SAVED_TREE (fndecl) = body;
     398              : 
     399            4 :     ctx->push_function (fndecl);
     400            4 :     return fndecl;
     401            8 :   }
     402              : 
     403              :   /**
     404              :    * Compiles a 1-to-1 wrapper for LLVM built-ins that wraps around GCC
     405              :    * built-ins.
     406              :    *
     407              :    * @param ctx
     408              :    * @param fntype the LLVM built-in function type
     409              :    * @param gcc_builtin the GCC built-in function
     410              :    * @param locus
     411              :    * @return tree the resultant wrapper function
     412              :    */
     413            1 :   static tree compile_x86_forwarding_adapter (Context *ctx,
     414              :                                               TyTy::FnType *fntype,
     415              :                                               tree gcc_builtin,
     416              :                                               location_t locus)
     417              :   {
     418            1 :     tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
     419              : 
     420            1 :     const auto &path = fntype->get_ident ().path;
     421            1 :     std::string ir_name = path.get () + fntype->subst_as_string ();
     422            1 :     std::string asm_name = ctx->mangle_item (fntype, path);
     423              : 
     424              :     // start building the wrapper function
     425            1 :     tree fndecl
     426            1 :       = Backend::function (compiled_fn_type, ir_name, asm_name, 0, locus);
     427              : 
     428            1 :     TREE_PUBLIC (fndecl) = 0;
     429            1 :     DECL_ARTIFICIAL (fndecl) = 1;
     430            1 :     DECL_EXTERNAL (fndecl) = 0;
     431            1 :     DECL_DECLARED_INLINE_P (fndecl) = 1;
     432              : 
     433              :     // compile params for the rust wrapper
     434            1 :     std::vector<Bvariable *> param_vars;
     435            1 :     param_vars.reserve (fntype->get_params ().size ());
     436            5 :     for (auto &param : fntype->get_params ())
     437              :       {
     438            4 :         auto &pattern = param.get_pattern ();
     439            4 :         tree type = TyTyResolveCompile::compile (ctx, param.get_type ());
     440            4 :         Bvariable *variable
     441            4 :           = CompileFnParam::compile (ctx, fndecl, pattern, type,
     442            4 :                                      pattern.get_locus ());
     443            4 :         param_vars.emplace_back (variable);
     444              :       }
     445              : 
     446            1 :     if (!Backend::function_set_parameters (fndecl, param_vars))
     447            0 :       return error_mark_node;
     448              : 
     449              :     // forward the rust params, convert each into the corresponding gcc
     450              :     // built-in param type
     451            1 :     std::vector<tree> call_arguments;
     452            1 :     call_arguments.reserve (param_vars.size ());
     453            1 :     tree gcc_argument_types = TYPE_ARG_TYPES (TREE_TYPE (gcc_builtin));
     454            5 :     for (Bvariable *param : param_vars)
     455              :       {
     456            4 :         tree argument = param->get_tree (locus);
     457            4 :         tree expected_type = TREE_VALUE (gcc_argument_types);
     458            4 :         argument = Backend::convert_expression (expected_type, argument, locus);
     459            4 :         call_arguments.emplace_back (argument);
     460            4 :         gcc_argument_types = TREE_CHAIN (gcc_argument_types);
     461              :       }
     462              : 
     463            1 :     tree builtin_call
     464            1 :       = build_call_expr_loc_array (locus, gcc_builtin,
     465            1 :                                    static_cast<int> (call_arguments.size ()),
     466              :                                    call_arguments.data ());
     467            1 :     tree wrapper_ret_type = TREE_TYPE (DECL_RESULT (fndecl));
     468              : 
     469            1 :     builtin_call
     470            1 :       = Backend::convert_expression (wrapper_ret_type, builtin_call, locus);
     471            1 :     tree block = Backend::block (fndecl, NULL_TREE, {}, locus, locus);
     472            1 :     ctx->push_block (block);
     473            1 :     ctx->add_statement (
     474              :       Backend::return_statement (fndecl, builtin_call, locus));
     475            1 :     tree body = ctx->pop_block ();
     476            1 :     DECL_SAVED_TREE (fndecl) = body;
     477              : 
     478            1 :     ctx->push_function (fndecl);
     479            1 :     return fndecl;
     480            2 :   }
     481              : 
     482              :   TyTy::BaseType *concrete;
     483              :   tree reference;
     484              :   location_t ref_locus;
     485              : };
     486              : 
     487              : } // namespace Compile
     488              : } // namespace Rust
     489              : 
     490              : #endif // RUST_COMPILE_EXTERN_ITEM
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.