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 ¶m : 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 ¶m : 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
|