Line data Source code
1 : // Copyright (C) 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 : #include "rust-intrinsic-handlers.h"
20 : #include "rust-compile-context.h"
21 : #include "rust-compile-type.h"
22 : #include "rust-compile-fnparam.h"
23 : #include "rust-builtins.h"
24 : #include "rust-diagnostics.h"
25 : #include "rust-location.h"
26 : #include "rust-constexpr.h"
27 : #include "rust-session-manager.h"
28 : #include "rust-tree.h"
29 : #include "tree-core.h"
30 : #include "rust-gcc.h"
31 : #include "fold-const.h"
32 : #include "rust-constexpr.h"
33 :
34 : // declaration taken from "stringpool.h"
35 : // the get_identifier macro causes compilation issues
36 : extern tree get_identifier (const char *);
37 :
38 : namespace Rust {
39 : namespace Compile {
40 : namespace handlers {
41 :
42 : static tree
43 126 : make_unsigned_long_tree (unsigned long value)
44 : {
45 14 : return build_int_cst (integer_type_node, value);
46 : }
47 :
48 : static bool
49 163 : is_basic_integer_type (TyTy::BaseType *type)
50 : {
51 163 : switch (type->get_kind ())
52 : {
53 : case TyTy::INT:
54 : case TyTy::UINT:
55 : case TyTy::USIZE:
56 : case TyTy::ISIZE:
57 : return true;
58 22 : default:
59 22 : return false;
60 : break;
61 : }
62 : }
63 :
64 : /**
65 : * Maybe override the Hir Lookups for the substituions in this context
66 : */
67 : static void
68 2191 : maybe_override_ctx (TyTy::FnType *fntype)
69 : {
70 2191 : if (fntype->has_substitutions_defined ())
71 2188 : fntype->override_context ();
72 2191 : }
73 :
74 : static bool
75 163 : check_for_basic_integer_type (const std::string &intrinsic_str,
76 : location_t locus, TyTy::BaseType *type)
77 : {
78 163 : auto is_basic_integer = is_basic_integer_type (type);
79 163 : if (!is_basic_integer)
80 : {
81 22 : rust_error_at (
82 : locus,
83 : "%s intrinsics can only be used with basic integer types (got %qs)",
84 44 : intrinsic_str.c_str (), type->get_name ().c_str ());
85 : }
86 :
87 163 : return is_basic_integer;
88 : }
89 :
90 : /**
91 : * Items can be forward compiled which means we may not need to invoke this
92 : * code. We might also have already compiled this generic function as well.
93 : */
94 : static bool
95 2119 : check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
96 : {
97 2119 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
98 2119 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
99 2119 : if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
100 : fntype->get_id (), fntype, asm_name))
101 : {
102 : return true;
103 : }
104 :
105 : return false;
106 2119 : }
107 :
108 : static tree
109 2191 : compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
110 : {
111 2191 : maybe_override_ctx (fntype);
112 :
113 2191 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
114 :
115 2191 : tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
116 2191 : std::string ir_symbol_name
117 2191 : = canonical_path.get () + fntype->subst_as_string ();
118 2191 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
119 :
120 2191 : unsigned int flags = 0;
121 2191 : tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
122 2191 : flags, fntype->get_ident ().locus);
123 :
124 2191 : TREE_PUBLIC (fndecl) = 0;
125 2191 : TREE_READONLY (fndecl) = 1;
126 2191 : DECL_ARTIFICIAL (fndecl) = 1;
127 2191 : DECL_EXTERNAL (fndecl) = 0;
128 2191 : DECL_DECLARED_INLINE_P (fndecl) = 1;
129 :
130 2191 : return fndecl;
131 2191 : }
132 :
133 : /**
134 : * Compile and setup a function's parameters
135 : */
136 : static void
137 1625 : compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
138 : std::vector<Bvariable *> *compiled_param_variables,
139 : std::vector<tree_node *> *compiled_param_types = nullptr)
140 : {
141 4775 : for (auto &parm : fntype->get_params ())
142 : {
143 3150 : auto &referenced_param = parm.get_pattern ();
144 3150 : auto param_tyty = parm.get_type ();
145 3150 : auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
146 :
147 3150 : location_t param_locus = referenced_param.get_locus ();
148 3150 : Bvariable *compiled_param_var
149 3150 : = CompileFnParam::compile (ctx, fndecl, referenced_param,
150 3150 : compiled_param_type, param_locus);
151 :
152 3150 : compiled_param_variables->push_back (compiled_param_var);
153 3150 : if (compiled_param_types)
154 359 : compiled_param_types->push_back (compiled_param_type);
155 : }
156 1625 : }
157 :
158 : static void
159 2191 : enter_intrinsic_block (Context *ctx, tree fndecl,
160 : const std::vector<Bvariable *> &vars = {})
161 : {
162 2191 : tree enclosing_scope = NULL_TREE;
163 2191 : location_t start_location = UNDEF_LOCATION;
164 2191 : location_t end_location = UNDEF_LOCATION;
165 :
166 2191 : auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
167 : end_location);
168 :
169 2191 : ctx->push_block (block);
170 2191 : }
171 :
172 : static void
173 2177 : finalize_intrinsic_block (Context *ctx, tree fndecl)
174 : {
175 2177 : tree bind_tree = ctx->pop_block ();
176 :
177 2177 : gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
178 :
179 2177 : DECL_SAVED_TREE (fndecl) = bind_tree;
180 :
181 2177 : ctx->push_function (fndecl);
182 :
183 2177 : DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
184 2177 : maybe_save_constexpr_fundef (fndecl);
185 2177 : }
186 :
187 : namespace inner {
188 :
189 : static std::string
190 98 : build_atomic_builtin_name (const std::string &prefix, location_t locus,
191 : TyTy::BaseType *operand_type)
192 : {
193 98 : static const std::map<std::string, std::string> allowed_types = {
194 : {"i8", "1"}, {"i16", "2"}, {"i32", "4"}, {"i64", "8"},
195 : {"i128", "16"}, {"isize", "8"}, {"u8", "1"}, {"u16", "2"},
196 : {"u32", "4"}, {"u64", "8"}, {"u128", "16"}, {"usize", "8"},
197 462 : };
198 :
199 : // TODO: Can we maybe get the generic version (atomic_store_n) to work... This
200 : // would be so much better
201 :
202 98 : std::string result = "__" + prefix; // + "n";
203 :
204 98 : auto type_name = operand_type->get_name ();
205 98 : if (type_name == "usize" || type_name == "isize")
206 : {
207 0 : rust_sorry_at (
208 : locus, "atomics are not yet available for size types (usize, isize)");
209 0 : return "";
210 : }
211 :
212 98 : if (type_name.at (0) == 'i')
213 : {
214 0 : rust_sorry_at (locus, "atomics are not yet supported for signed "
215 : "integer types (i8, i16, i32, i64, i128)");
216 0 : return "";
217 : }
218 :
219 98 : auto type_size_str = allowed_types.find (type_name);
220 :
221 98 : if (!check_for_basic_integer_type ("atomic", locus, operand_type))
222 14 : return "";
223 :
224 84 : result += type_size_str->second;
225 :
226 84 : return result;
227 98 : }
228 :
229 : inline tree
230 56 : unchecked_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
231 : {
232 56 : rust_assert (fntype->get_params ().size () == 2);
233 56 : rust_assert (fntype->get_num_substitutions () == 1);
234 :
235 56 : tree lookup = NULL_TREE;
236 56 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
237 0 : return lookup;
238 :
239 56 : auto fndecl = compile_intrinsic_function (ctx, fntype);
240 :
241 : // setup the params
242 56 : std::vector<Bvariable *> param_vars;
243 56 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
244 :
245 56 : if (!Backend::function_set_parameters (fndecl, param_vars))
246 0 : return error_mark_node;
247 :
248 56 : enter_intrinsic_block (ctx, fndecl);
249 :
250 : // BUILTIN unchecked_<op> BODY BEGIN
251 :
252 56 : auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
253 56 : auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
254 :
255 56 : auto *monomorphized_type
256 56 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
257 :
258 56 : check_for_basic_integer_type ("unchecked operation", fntype->get_locus (),
259 : monomorphized_type);
260 :
261 56 : auto expr = build2 (op, TREE_TYPE (x), x, y);
262 56 : auto return_statement
263 56 : = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
264 :
265 56 : ctx->add_statement (return_statement);
266 :
267 : // BUILTIN unchecked_<op> BODY END
268 :
269 56 : finalize_intrinsic_block (ctx, fndecl);
270 :
271 56 : return fndecl;
272 56 : }
273 :
274 : inline tree
275 0 : expect (Context *ctx, TyTy::FnType *fntype, bool likely)
276 : {
277 0 : rust_assert (fntype->get_params ().size () == 1);
278 :
279 0 : tree lookup = NULL_TREE;
280 0 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
281 0 : return lookup;
282 :
283 0 : auto fndecl = compile_intrinsic_function (ctx, fntype);
284 :
285 0 : enter_intrinsic_block (ctx, fndecl);
286 :
287 : // BUILTIN expect_handler_inner FN BODY BEGIN
288 : // setup the params
289 0 : std::vector<Bvariable *> param_vars;
290 0 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
291 0 : tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
292 0 : tree expect_fn_raw = nullptr;
293 0 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
294 : &expect_fn_raw);
295 0 : rust_assert (expect_fn_raw);
296 0 : auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
297 :
298 : // we need to convert the expression return type to long to match the expected
299 : // parameter type of __builtin_expect
300 0 : auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
301 0 : auto expect_value
302 0 : = make_unsigned_long_tree (static_cast<unsigned long> (likely));
303 :
304 0 : auto expect_call
305 0 : = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
306 : BUILTINS_LOCATION);
307 : // the return value also needs to be casted (to bool)
308 0 : auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, expect_call);
309 0 : auto return_statement
310 0 : = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
311 0 : ctx->add_statement (return_statement);
312 : // BUILTIN expect_handler_inner FN BODY END
313 :
314 0 : finalize_intrinsic_block (ctx, fndecl);
315 :
316 0 : return fndecl;
317 0 : }
318 :
319 : tree
320 2 : try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
321 : {
322 2 : rust_assert (fntype->get_params ().size () == 3);
323 :
324 2 : tree lookup = NULL_TREE;
325 2 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
326 0 : return lookup;
327 2 : auto fndecl = compile_intrinsic_function (ctx, fntype);
328 :
329 2 : enter_intrinsic_block (ctx, fndecl);
330 :
331 : // The following tricks are needed to make sure the try-catch blocks are not
332 : // optimized away
333 2 : TREE_READONLY (fndecl) = 0;
334 2 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
335 2 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
336 2 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
337 :
338 : // BUILTIN try_handler FN BODY BEGIN
339 : // setup the params
340 2 : std::vector<Bvariable *> param_vars;
341 2 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
342 2 : if (!Backend::function_set_parameters (fndecl, param_vars))
343 0 : return error_mark_node;
344 2 : tree enclosing_scope = NULL_TREE;
345 :
346 2 : bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
347 2 : == CompileOptions::PanicStrategy::Abort;
348 2 : tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
349 2 : tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
350 2 : tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
351 2 : tree normal_return_stmt = NULL_TREE;
352 2 : tree error_return_stmt = NULL_TREE;
353 2 : tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
354 : BUILTINS_LOCATION);
355 2 : tree catch_call = NULL_TREE;
356 2 : tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
357 : UNDEF_LOCATION);
358 :
359 2 : if (is_new_api)
360 : {
361 1 : auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
362 1 : auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
363 : UNDEF_LOCATION);
364 1 : normal_return_stmt
365 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
366 1 : error_return_stmt
367 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
368 : }
369 : else
370 : {
371 1 : normal_return_stmt = Backend::return_statement (fndecl, integer_zero_node,
372 : BUILTINS_LOCATION);
373 1 : error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
374 : BUILTINS_LOCATION);
375 : }
376 2 : Backend::block_add_statements (try_block,
377 2 : std::vector<tree>{try_call,
378 2 : normal_return_stmt});
379 2 : if (panic_is_abort)
380 : {
381 : // skip building the try-catch construct
382 0 : ctx->add_statement (try_block);
383 0 : finalize_intrinsic_block (ctx, fndecl);
384 0 : return fndecl;
385 : }
386 :
387 2 : tree eh_pointer
388 2 : = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
389 : integer_zero_node);
390 2 : catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
391 : NULL_TREE, BUILTINS_LOCATION);
392 :
393 2 : tree catch_block = Backend::block (fndecl, enclosing_scope, {},
394 : UNDEF_LOCATION, UNDEF_LOCATION);
395 2 : Backend::block_add_statements (catch_block,
396 2 : std::vector<tree>{catch_call,
397 2 : error_return_stmt});
398 : // emulate what cc1plus is doing for C++ try-catch
399 2 : tree inner_eh_construct
400 2 : = Backend::exception_handler_statement (catch_call, NULL_TREE,
401 : error_return_stmt,
402 : BUILTINS_LOCATION);
403 : // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
404 2 : auto eh_construct
405 2 : = Backend::exception_handler_statement (try_block, inner_eh_construct,
406 : NULL_TREE, BUILTINS_LOCATION);
407 2 : ctx->add_statement (eh_construct);
408 : // BUILTIN try_handler FN BODY END
409 2 : finalize_intrinsic_block (ctx, fndecl);
410 :
411 2 : return fndecl;
412 2 : }
413 :
414 : /**
415 : * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
416 : */
417 : tree
418 310 : wrapping_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
419 : {
420 : // wrapping_<op> intrinsics have two parameter
421 310 : rust_assert (fntype->get_params ().size () == 2);
422 :
423 310 : tree lookup = NULL_TREE;
424 310 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
425 0 : return lookup;
426 :
427 310 : auto fndecl = compile_intrinsic_function (ctx, fntype);
428 :
429 : // setup the params
430 310 : std::vector<Bvariable *> param_vars;
431 310 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
432 :
433 310 : auto &lhs_param = param_vars.at (0);
434 310 : auto &rhs_param = param_vars.at (1);
435 :
436 310 : if (!Backend::function_set_parameters (fndecl, param_vars))
437 0 : return error_mark_node;
438 :
439 310 : enter_intrinsic_block (ctx, fndecl);
440 :
441 : // BUILTIN wrapping_<op> FN BODY BEGIN
442 310 : auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
443 310 : auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
444 :
445 : // Operations are always wrapping in Rust, as we have -fwrapv enabled by
446 : // default. The difference between a wrapping_{add, sub, mul} and a regular
447 : // arithmetic operation is that these intrinsics do not panic - they always
448 : // carry over.
449 310 : auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
450 :
451 310 : auto return_statement
452 310 : = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
453 310 : ctx->add_statement (return_statement);
454 : // BUILTIN wrapping_<op> FN BODY END
455 :
456 310 : finalize_intrinsic_block (ctx, fndecl);
457 :
458 310 : return fndecl;
459 310 : }
460 :
461 : /**
462 : * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
463 : */
464 : tree
465 141 : op_with_overflow (Context *ctx, TyTy::FnType *fntype, tree_code op)
466 : {
467 : // wrapping_<op> intrinsics have two parameter
468 141 : rust_assert (fntype->get_params ().size () == 2);
469 :
470 141 : tree lookup = NULL_TREE;
471 141 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
472 0 : return lookup;
473 :
474 141 : auto fndecl = compile_intrinsic_function (ctx, fntype);
475 :
476 : // setup the params
477 141 : std::vector<Bvariable *> param_vars;
478 141 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
479 :
480 141 : auto &x_param = param_vars.at (0);
481 141 : auto &y_param = param_vars.at (1);
482 :
483 141 : if (!Backend::function_set_parameters (fndecl, param_vars))
484 0 : return error_mark_node;
485 :
486 141 : rust_assert (fntype->get_num_substitutions () == 1);
487 141 : auto ¶m_mapping = fntype->get_substs ().at (0);
488 141 : const auto param_tyty = param_mapping.get_param_ty ();
489 141 : auto resolved_tyty = param_tyty->resolve ();
490 141 : tree template_parameter_type
491 141 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
492 :
493 : // this should match y as well or we can take it from the TyTy structure
494 141 : tree tmp_stmt = error_mark_node;
495 141 : Bvariable *result_variable
496 141 : = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
497 : NULL_TREE, true /*address_is_taken*/,
498 : UNDEF_LOCATION, &tmp_stmt);
499 141 : Bvariable *bool_variable
500 141 : = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
501 : NULL_TREE, true /*address_is_taken*/,
502 : UNDEF_LOCATION, &tmp_stmt);
503 :
504 141 : enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
505 :
506 : // BUILTIN op_with_overflow FN BODY BEGIN
507 141 : auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
508 141 : auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
509 :
510 141 : tree overflow_builtin = error_mark_node;
511 141 : switch (op)
512 : {
513 127 : case PLUS_EXPR:
514 127 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
515 : &overflow_builtin);
516 127 : break;
517 :
518 7 : case MINUS_EXPR:
519 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
520 : &overflow_builtin);
521 7 : break;
522 :
523 7 : case MULT_EXPR:
524 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
525 : &overflow_builtin);
526 7 : break;
527 :
528 0 : default:
529 0 : rust_unreachable ();
530 141 : break;
531 : }
532 141 : rust_assert (overflow_builtin != error_mark_node);
533 :
534 141 : tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
535 141 : tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
536 141 : tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
537 :
538 141 : tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
539 : 3, x, y, result_ref);
540 :
541 141 : tree overflow_assignment
542 141 : = Backend::assignment_statement (bool_decl, builtin_call,
543 : BUILTINS_LOCATION);
544 :
545 141 : ctx->add_statement (overflow_assignment);
546 :
547 282 : std::vector<tree> vals = {result_decl, bool_decl};
548 141 : tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
549 141 : tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
550 : -1, UNDEF_LOCATION);
551 :
552 141 : auto return_statement
553 141 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
554 141 : ctx->add_statement (return_statement);
555 :
556 : // BUILTIN wrapping_<op> FN BODY END
557 :
558 141 : finalize_intrinsic_block (ctx, fndecl);
559 :
560 141 : return fndecl;
561 141 : }
562 :
563 : /**
564 : * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
565 : * fn copy<T>(src: *const T, dst: *mut T, count: usize);
566 : */
567 : tree
568 243 : copy (Context *ctx, TyTy::FnType *fntype, bool overlaps)
569 : {
570 243 : rust_assert (fntype->get_params ().size () == 3);
571 243 : rust_assert (fntype->get_num_substitutions () == 1);
572 :
573 243 : tree lookup = NULL_TREE;
574 243 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
575 0 : return lookup;
576 :
577 243 : auto fndecl = compile_intrinsic_function (ctx, fntype);
578 :
579 : // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
580 243 : TREE_READONLY (fndecl) = 0;
581 243 : TREE_SIDE_EFFECTS (fndecl) = 1;
582 :
583 : // setup the params
584 243 : std::vector<Bvariable *> param_vars;
585 243 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
586 :
587 243 : if (!Backend::function_set_parameters (fndecl, param_vars))
588 0 : return error_mark_node;
589 :
590 243 : enter_intrinsic_block (ctx, fndecl);
591 :
592 : // BUILTIN copy_nonoverlapping BODY BEGIN
593 :
594 243 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
595 243 : auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
596 243 : auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
597 :
598 : // We want to create the following statement
599 : // memcpy(dst, src, size_of::<T>());
600 : // so
601 : // memcpy(dst, src, size_expr);
602 :
603 243 : auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
604 243 : auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
605 :
606 243 : tree size_expr
607 243 : = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
608 :
609 243 : tree memcpy_raw = nullptr;
610 486 : BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
611 : : "__builtin_memcpy",
612 : &memcpy_raw);
613 243 : rust_assert (memcpy_raw);
614 243 : auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
615 :
616 243 : auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
617 : nullptr, UNDEF_LOCATION);
618 :
619 243 : ctx->add_statement (copy_call);
620 :
621 : // BUILTIN copy_nonoverlapping BODY END
622 :
623 243 : finalize_intrinsic_block (ctx, fndecl);
624 :
625 243 : return fndecl;
626 243 : }
627 :
628 : tree
629 70 : atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering)
630 : {
631 70 : rust_assert (fntype->get_params ().size () == 2);
632 70 : rust_assert (fntype->get_num_substitutions () == 1);
633 :
634 70 : tree lookup = NULL_TREE;
635 70 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
636 0 : return lookup;
637 :
638 70 : auto fndecl = compile_intrinsic_function (ctx, fntype);
639 :
640 : // Most intrinsic functions are pure but not the atomic ones
641 70 : TREE_READONLY (fndecl) = 0;
642 70 : TREE_SIDE_EFFECTS (fndecl) = 1;
643 :
644 : // setup the params
645 70 : std::vector<Bvariable *> param_vars;
646 70 : std::vector<tree> types;
647 70 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
648 :
649 70 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
650 70 : rust_assert (ok);
651 :
652 70 : enter_intrinsic_block (ctx, fndecl);
653 :
654 70 : auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
655 70 : TREE_READONLY (dst) = 0;
656 :
657 70 : auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
658 70 : auto memorder = make_unsigned_long_tree (ordering);
659 :
660 70 : auto monomorphized_type
661 70 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
662 :
663 70 : auto builtin_name
664 70 : = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (),
665 70 : monomorphized_type);
666 70 : if (builtin_name.empty ())
667 14 : return error_mark_node;
668 :
669 56 : tree atomic_store_raw = nullptr;
670 56 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
671 : &atomic_store_raw);
672 56 : rust_assert (atomic_store_raw);
673 :
674 56 : auto atomic_store
675 56 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
676 :
677 56 : auto store_call
678 56 : = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
679 : UNDEF_LOCATION);
680 56 : TREE_READONLY (store_call) = 0;
681 56 : TREE_SIDE_EFFECTS (store_call) = 1;
682 :
683 56 : ctx->add_statement (store_call);
684 56 : finalize_intrinsic_block (ctx, fndecl);
685 :
686 56 : return fndecl;
687 70 : }
688 :
689 : tree
690 28 : atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering)
691 : {
692 28 : rust_assert (fntype->get_params ().size () == 1);
693 28 : rust_assert (fntype->get_num_substitutions () == 1);
694 :
695 28 : tree lookup = NULL_TREE;
696 28 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
697 0 : return lookup;
698 :
699 28 : auto fndecl = compile_intrinsic_function (ctx, fntype);
700 :
701 : // Most intrinsic functions are pure but not the atomic ones
702 : // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
703 28 : TREE_READONLY (fndecl) = 0;
704 28 : TREE_SIDE_EFFECTS (fndecl) = 1;
705 :
706 : // setup the params
707 28 : std::vector<Bvariable *> param_vars;
708 28 : std::vector<tree> types;
709 28 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
710 :
711 28 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
712 28 : rust_assert (ok);
713 :
714 28 : enter_intrinsic_block (ctx, fndecl);
715 :
716 28 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
717 28 : auto memorder = make_unsigned_long_tree (ordering);
718 :
719 28 : auto monomorphized_type
720 28 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
721 :
722 28 : auto builtin_name
723 28 : = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
724 28 : monomorphized_type);
725 28 : if (builtin_name.empty ())
726 0 : return error_mark_node;
727 :
728 28 : tree atomic_load_raw = nullptr;
729 28 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
730 : &atomic_load_raw);
731 28 : rust_assert (atomic_load_raw);
732 :
733 28 : auto atomic_load
734 28 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
735 :
736 28 : auto load_call = Backend::call_expression (atomic_load, {src, memorder},
737 : nullptr, UNDEF_LOCATION);
738 28 : auto return_statement
739 28 : = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
740 :
741 28 : TREE_READONLY (load_call) = 0;
742 28 : TREE_SIDE_EFFECTS (load_call) = 1;
743 :
744 28 : ctx->add_statement (return_statement);
745 :
746 28 : finalize_intrinsic_block (ctx, fndecl);
747 :
748 28 : return fndecl;
749 28 : }
750 :
751 : } // namespace inner
752 :
753 : const HandlerBuilder
754 13959 : op_with_overflow (tree_code op)
755 : {
756 14100 : return [op] (Context *ctx, TyTy::FnType *fntype) {
757 141 : return inner::op_with_overflow (ctx, fntype, op);
758 13959 : };
759 : }
760 :
761 : tree
762 198 : rotate_left (Context *ctx, TyTy::FnType *fntype)
763 : {
764 198 : return handlers::rotate (ctx, fntype, LROTATE_EXPR);
765 : }
766 :
767 : tree
768 155 : rotate_right (Context *ctx, TyTy::FnType *fntype)
769 : {
770 155 : return handlers::rotate (ctx, fntype, RROTATE_EXPR);
771 : }
772 :
773 : const HandlerBuilder
774 13959 : wrapping_op (tree_code op)
775 : {
776 14269 : return [op] (Context *ctx, TyTy::FnType *fntype) {
777 310 : return inner::wrapping_op (ctx, fntype, op);
778 13959 : };
779 : }
780 :
781 : HandlerBuilder
782 18612 : atomic_store (int ordering)
783 : {
784 18682 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
785 70 : return inner::atomic_store (ctx, fntype, ordering);
786 18612 : };
787 : }
788 :
789 : HandlerBuilder
790 18612 : atomic_load (int ordering)
791 : {
792 18640 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
793 28 : return inner::atomic_load (ctx, fntype, ordering);
794 18612 : };
795 : }
796 :
797 : const HandlerBuilder
798 32571 : unchecked_op (tree_code op)
799 : {
800 32627 : return [op] (Context *ctx, TyTy::FnType *fntype) {
801 56 : return inner::unchecked_op (ctx, fntype, op);
802 32571 : };
803 : }
804 :
805 : const HandlerBuilder
806 9306 : copy (bool overlaps)
807 : {
808 9549 : return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
809 243 : return inner::copy (ctx, fntype, overlaps);
810 9306 : };
811 : }
812 :
813 : const HandlerBuilder
814 9306 : expect (bool likely)
815 : {
816 9306 : return [likely] (Context *ctx, TyTy::FnType *fntype) {
817 0 : return inner::expect (ctx, fntype, likely);
818 9306 : };
819 : }
820 :
821 : const HandlerBuilder
822 9306 : try_handler (bool is_new_api)
823 : {
824 9308 : return [is_new_api] (Context *ctx, TyTy::FnType *fntype) {
825 2 : return inner::try_handler (ctx, fntype, is_new_api);
826 9306 : };
827 : }
828 :
829 : tree
830 0 : sorry (Context *ctx, TyTy::FnType *fntype)
831 : {
832 0 : rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
833 : fntype->get_identifier ().c_str ());
834 :
835 0 : return error_mark_node;
836 : }
837 :
838 : tree
839 1 : assume (Context *ctx, TyTy::FnType *fntype)
840 : {
841 : // TODO: make sure this is actually helping the compiler optimize
842 :
843 1 : rust_assert (fntype->get_params ().size () == 1);
844 1 : rust_assert (fntype->param_at (0).get_type ()->get_kind ()
845 : == TyTy::TypeKind::BOOL);
846 :
847 1 : tree lookup = NULL_TREE;
848 1 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
849 0 : return lookup;
850 :
851 1 : auto fndecl = compile_intrinsic_function (ctx, fntype);
852 :
853 : // TODO: make sure these are necessary
854 1 : TREE_READONLY (fndecl) = 0;
855 1 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
856 1 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
857 1 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
858 :
859 1 : std::vector<Bvariable *> param_vars;
860 1 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
861 :
862 1 : if (!Backend::function_set_parameters (fndecl, param_vars))
863 0 : return error_mark_node;
864 :
865 1 : enter_intrinsic_block (ctx, fndecl);
866 :
867 : // BUILTIN assume FN BODY BEGIN
868 :
869 1 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
870 :
871 1 : tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
872 : void_type_node, 1, val);
873 1 : TREE_SIDE_EFFECTS (assume_expr) = 1;
874 :
875 1 : ctx->add_statement (assume_expr);
876 : // BUILTIN assume FN BODY END
877 :
878 1 : finalize_intrinsic_block (ctx, fndecl);
879 :
880 1 : return fndecl;
881 1 : }
882 :
883 : tree
884 102 : discriminant_value (Context *ctx, TyTy::FnType *fntype)
885 : {
886 102 : rust_assert (fntype->get_params ().size () == 1);
887 102 : rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
888 102 : rust_assert (fntype->has_substitutions ());
889 102 : rust_assert (fntype->get_num_type_params () == 1);
890 102 : auto &mapping = fntype->get_substs ().at (0);
891 102 : auto param_ty = mapping.get_param_ty ();
892 102 : rust_assert (param_ty->can_resolve ());
893 102 : auto resolved = param_ty->resolve ();
894 102 : auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
895 :
896 102 : TyTy::BaseType *return_type = nullptr;
897 102 : bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
898 102 : rust_assert (ok);
899 :
900 102 : bool is_adt = resolved->is<TyTy::ADTType> ();
901 102 : bool is_enum = false;
902 102 : if (is_adt)
903 : {
904 102 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
905 102 : return_type = adt.get_repr_options ().repr;
906 102 : rust_assert (return_type != nullptr);
907 102 : is_enum = adt.is_enum ();
908 : }
909 :
910 102 : p->set_associated_type (return_type->get_ref ());
911 :
912 102 : tree lookup = NULL_TREE;
913 102 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
914 0 : return lookup;
915 :
916 102 : auto fndecl = compile_intrinsic_function (ctx, fntype);
917 :
918 102 : std::vector<Bvariable *> param_vars;
919 102 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
920 :
921 102 : if (!Backend::function_set_parameters (fndecl, param_vars))
922 0 : return error_mark_node;
923 :
924 102 : enter_intrinsic_block (ctx, fndecl);
925 :
926 : // BUILTIN disriminant_value FN BODY BEGIN
927 :
928 102 : tree result = integer_zero_node;
929 102 : if (is_enum)
930 : {
931 102 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
932 102 : tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
933 102 : result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
934 : }
935 :
936 102 : auto return_statement
937 102 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
938 102 : ctx->add_statement (return_statement);
939 :
940 : // BUILTIN disriminant_value FN BODY END
941 :
942 102 : finalize_intrinsic_block (ctx, fndecl);
943 :
944 102 : return fndecl;
945 102 : }
946 :
947 : tree
948 7 : variant_count (Context *ctx, TyTy::FnType *fntype)
949 : {
950 7 : rust_assert (fntype->get_num_type_params () == 1);
951 7 : auto &mapping = fntype->get_substs ().at (0);
952 7 : auto param_ty = mapping.get_param_ty ();
953 7 : rust_assert (param_ty->can_resolve ());
954 7 : auto resolved = param_ty->resolve ();
955 :
956 7 : size_t variant_count = 0;
957 7 : bool is_adt = resolved->is<TyTy::ADTType> ();
958 7 : if (is_adt)
959 : {
960 7 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
961 7 : variant_count = adt.number_of_variants ();
962 : }
963 :
964 7 : tree lookup = NULL_TREE;
965 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
966 0 : return lookup;
967 :
968 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
969 :
970 7 : std::vector<Bvariable *> param_vars;
971 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
972 :
973 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
974 0 : return error_mark_node;
975 :
976 7 : enter_intrinsic_block (ctx, fndecl);
977 :
978 : // BUILTIN disriminant_value FN BODY BEGIN
979 7 : tree result_decl = DECL_RESULT (fndecl);
980 7 : tree type = TREE_TYPE (result_decl);
981 :
982 7 : mpz_t ival;
983 7 : mpz_init_set_ui (ival, variant_count);
984 7 : tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
985 7 : mpz_clear (ival);
986 :
987 7 : auto return_statement
988 7 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
989 7 : ctx->add_statement (return_statement);
990 :
991 : // BUILTIN disriminant_value FN BODY END
992 :
993 7 : finalize_intrinsic_block (ctx, fndecl);
994 :
995 7 : return fndecl;
996 7 : }
997 :
998 : tree
999 26 : move_val_init (Context *ctx, TyTy::FnType *fntype)
1000 : {
1001 26 : rust_assert (fntype->get_params ().size () == 2);
1002 :
1003 26 : tree lookup = NULL_TREE;
1004 26 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1005 0 : return lookup;
1006 :
1007 26 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1008 :
1009 : // Most intrinsic functions are pure - not `move_val_init`
1010 26 : TREE_READONLY (fndecl) = 0;
1011 26 : TREE_SIDE_EFFECTS (fndecl) = 1;
1012 :
1013 : // get the template parameter type tree fn size_of<T>();
1014 26 : rust_assert (fntype->get_num_substitutions () == 1);
1015 26 : auto ¶m_mapping = fntype->get_substs ().at (0);
1016 26 : auto param_tyty = param_mapping.get_param_ty ();
1017 26 : auto resolved_tyty = param_tyty->resolve ();
1018 26 : tree template_parameter_type
1019 26 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1020 :
1021 26 : std::vector<Bvariable *> param_vars;
1022 26 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1023 :
1024 26 : if (!Backend::function_set_parameters (fndecl, param_vars))
1025 0 : return error_mark_node;
1026 :
1027 26 : enter_intrinsic_block (ctx, fndecl);
1028 :
1029 : // BUILTIN size_of FN BODY BEGIN
1030 :
1031 26 : tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1032 26 : tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1033 26 : tree size = TYPE_SIZE_UNIT (template_parameter_type);
1034 :
1035 26 : tree memcpy_builtin = error_mark_node;
1036 26 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
1037 : &memcpy_builtin);
1038 26 : rust_assert (memcpy_builtin != error_mark_node);
1039 :
1040 26 : src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1041 26 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1042 : dst, src, size);
1043 :
1044 26 : ctx->add_statement (memset_call);
1045 : // BUILTIN size_of FN BODY END
1046 :
1047 26 : finalize_intrinsic_block (ctx, fndecl);
1048 :
1049 26 : return fndecl;
1050 26 : }
1051 :
1052 : tree
1053 92 : uninit (Context *ctx, TyTy::FnType *fntype)
1054 : {
1055 : // uninit has _zero_ parameters its parameter is the generic one
1056 92 : rust_assert (fntype->get_params ().size () == 0);
1057 :
1058 92 : tree lookup = NULL_TREE;
1059 92 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1060 0 : return lookup;
1061 :
1062 92 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1063 :
1064 : // Most intrinsic functions are pure - not `uninit_handler`
1065 92 : TREE_READONLY (fndecl) = 0;
1066 92 : TREE_SIDE_EFFECTS (fndecl) = 1;
1067 :
1068 : // get the template parameter type tree fn uninit<T>();
1069 92 : rust_assert (fntype->get_num_substitutions () == 1);
1070 92 : auto ¶m_mapping = fntype->get_substs ().at (0);
1071 92 : const auto param_tyty = param_mapping.get_param_ty ();
1072 92 : auto resolved_tyty = param_tyty->resolve ();
1073 92 : tree template_parameter_type
1074 92 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1075 :
1076 : // result temporary
1077 92 : tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
1078 92 : rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
1079 : == TYPE_SIZE_UNIT (dst_type));
1080 :
1081 92 : tree tmp_stmt = error_mark_node;
1082 92 : Bvariable *bvar
1083 92 : = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
1084 : true /*address_is_taken*/, UNDEF_LOCATION,
1085 : &tmp_stmt);
1086 :
1087 92 : enter_intrinsic_block (ctx, fndecl, {bvar});
1088 :
1089 : // BUILTIN size_of FN BODY BEGIN
1090 :
1091 92 : tree memset_builtin = error_mark_node;
1092 92 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
1093 : &memset_builtin);
1094 92 : rust_assert (memset_builtin != error_mark_node);
1095 :
1096 : // call memset with 0x01 and size of the thing see
1097 : // https://github.com/Rust-GCC/gccrs/issues/1899
1098 :
1099 92 : tree dst = bvar->get_tree (BUILTINS_LOCATION);
1100 92 : tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
1101 92 : tree constant_byte = build_int_cst (integer_type_node, 0x01);
1102 92 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1103 :
1104 92 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
1105 : dst_addr, constant_byte, size_expr);
1106 92 : ctx->add_statement (memset_call);
1107 :
1108 92 : auto return_statement
1109 92 : = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
1110 92 : ctx->add_statement (return_statement);
1111 : // BUILTIN size_of FN BODY END
1112 :
1113 92 : finalize_intrinsic_block (ctx, fndecl);
1114 :
1115 92 : return fndecl;
1116 : }
1117 :
1118 : tree
1119 7 : prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
1120 : {
1121 7 : return prefetch_data (ctx, fntype, Prefetch::Read);
1122 : }
1123 : tree
1124 7 : prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
1125 : {
1126 7 : return prefetch_data (ctx, fntype, Prefetch::Write);
1127 : }
1128 :
1129 : tree
1130 14 : prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
1131 : {
1132 14 : rust_assert (fntype->get_params ().size () == 2);
1133 :
1134 14 : tree lookup = NULL_TREE;
1135 14 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1136 0 : return lookup;
1137 :
1138 14 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1139 :
1140 : // prefetching isn't pure and shouldn't be discarded after GIMPLE
1141 14 : TREE_READONLY (fndecl) = 0;
1142 14 : TREE_SIDE_EFFECTS (fndecl) = 1;
1143 :
1144 14 : std::vector<Bvariable *> args;
1145 14 : compile_fn_params (ctx, fntype, fndecl, &args);
1146 :
1147 14 : if (!Backend::function_set_parameters (fndecl, args))
1148 0 : return error_mark_node;
1149 :
1150 14 : enter_intrinsic_block (ctx, fndecl);
1151 :
1152 14 : auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
1153 :
1154 : // The core library technically allows you to pass any i32 value as a
1155 : // locality, but LLVM will then complain if the value cannot be constant
1156 : // evaluated. For now, we ignore the locality argument and instead always
1157 : // pass `3` (the most restrictive value). This allows us to still have
1158 : // prefetch behavior, just not as granular as expected. In future Rust
1159 : // versions, we hope that prefetch intrinsics will be split up according to
1160 : // locality, similarly to atomic intrinsics.
1161 : // The solution is to try and perform constant folding for the locality
1162 : // argument, or instead of creating a new function definition, modify the call
1163 : // site directly This has the bad side-effect of creating warnings about
1164 : // `unused name - locality`, which we hack away here:
1165 : // TODO: Take care of handling locality properly
1166 14 : Backend::var_expression (args[1], UNDEF_LOCATION);
1167 :
1168 14 : auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
1169 :
1170 14 : auto prefetch_raw = NULL_TREE;
1171 14 : auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
1172 : &prefetch_raw);
1173 14 : rust_assert (ok);
1174 14 : auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
1175 :
1176 28 : auto prefetch_call = Backend::call_expression (prefetch,
1177 : {addr, rw_flag,
1178 : // locality arg
1179 14 : make_unsigned_long_tree (3)},
1180 : nullptr, UNDEF_LOCATION);
1181 :
1182 14 : TREE_READONLY (prefetch_call) = 0;
1183 14 : TREE_SIDE_EFFECTS (prefetch_call) = 1;
1184 :
1185 14 : ctx->add_statement (prefetch_call);
1186 :
1187 14 : finalize_intrinsic_block (ctx, fndecl);
1188 :
1189 14 : return fndecl;
1190 14 : }
1191 :
1192 : tree
1193 353 : rotate (Context *ctx, TyTy::FnType *fntype, tree_code op)
1194 : {
1195 : // rotate intrinsic has two parameter
1196 353 : rust_assert (fntype->get_params ().size () == 2);
1197 :
1198 353 : tree lookup = NULL_TREE;
1199 353 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1200 0 : return lookup;
1201 :
1202 353 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1203 :
1204 : // setup the params
1205 353 : std::vector<Bvariable *> param_vars;
1206 353 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1207 :
1208 353 : auto &x_param = param_vars.at (0);
1209 353 : auto &y_param = param_vars.at (1);
1210 353 : rust_assert (param_vars.size () == 2);
1211 353 : if (!Backend::function_set_parameters (fndecl, param_vars))
1212 0 : return error_mark_node;
1213 :
1214 353 : enter_intrinsic_block (ctx, fndecl);
1215 :
1216 : // BUILTIN rotate FN BODY BEGIN
1217 353 : tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
1218 353 : tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
1219 353 : tree rotate_expr
1220 353 : = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
1221 353 : auto return_statement
1222 353 : = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
1223 353 : ctx->add_statement (return_statement);
1224 : // BUILTIN rotate FN BODY END
1225 :
1226 353 : finalize_intrinsic_block (ctx, fndecl);
1227 :
1228 353 : return fndecl;
1229 353 : }
1230 :
1231 : tree
1232 191 : transmute (Context *ctx, TyTy::FnType *fntype)
1233 : {
1234 : // transmute intrinsic has one parameter
1235 191 : rust_assert (fntype->get_params ().size () == 1);
1236 :
1237 191 : tree lookup = NULL_TREE;
1238 191 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1239 0 : return lookup;
1240 :
1241 191 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1242 :
1243 191 : std::vector<Bvariable *> param_vars;
1244 191 : std::vector<tree_node *> compiled_types;
1245 191 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &compiled_types);
1246 :
1247 191 : if (!Backend::function_set_parameters (fndecl, param_vars))
1248 0 : return error_mark_node;
1249 :
1250 : // param to convert
1251 191 : Bvariable *convert_me_param = param_vars.at (0);
1252 191 : tree convert_me_expr
1253 191 : = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
1254 :
1255 : // check for transmute pre-conditions
1256 191 : tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
1257 191 : tree source_type_expr = compiled_types.at (0);
1258 191 : tree target_size_expr = TYPE_SIZE (target_type_expr);
1259 191 : tree source_size_expr = TYPE_SIZE (source_type_expr);
1260 : // for some reason, unit types and other zero-sized types return NULL for the
1261 : // size expressions
1262 191 : unsigned HOST_WIDE_INT target_size
1263 191 : = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
1264 191 : unsigned HOST_WIDE_INT source_size
1265 191 : = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
1266 :
1267 : // size check for concrete types
1268 : // TODO(liushuyu): check alignment for pointers; check for dependently-sized
1269 : // types
1270 191 : if (target_size != source_size)
1271 : {
1272 7 : rust_error_at (fntype->get_locus (),
1273 : "cannot transmute between types of different sizes, or "
1274 : "dependently-sized types");
1275 14 : rust_inform (
1276 7 : fntype->get_ident ().locus, "source type: %qs (%lu bits)",
1277 7 : fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
1278 : (unsigned long) source_size);
1279 7 : rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
1280 14 : fntype->get_return_type ()->as_string ().c_str (),
1281 : (unsigned long) target_size);
1282 : }
1283 :
1284 191 : enter_intrinsic_block (ctx, fndecl);
1285 :
1286 : // BUILTIN transmute FN BODY BEGIN
1287 :
1288 : // Return *((orig_type*)&decl) */
1289 :
1290 191 : tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
1291 191 : t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
1292 : build_pointer_type (target_type_expr), t);
1293 191 : tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
1294 :
1295 191 : auto return_statement
1296 191 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
1297 191 : ctx->add_statement (return_statement);
1298 : // BUILTIN transmute FN BODY END
1299 :
1300 191 : finalize_intrinsic_block (ctx, fndecl);
1301 :
1302 191 : return fndecl;
1303 191 : }
1304 :
1305 : tree
1306 474 : sizeof_handler (Context *ctx, TyTy::FnType *fntype)
1307 : {
1308 : // size_of has _zero_ parameters its parameter is the generic one
1309 474 : rust_assert (fntype->get_params ().size () == 0);
1310 :
1311 474 : tree lookup = NULL_TREE;
1312 474 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1313 0 : return lookup;
1314 :
1315 474 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1316 :
1317 : // get the template parameter type tree fn size_of<T>();
1318 474 : rust_assert (fntype->get_num_substitutions () == 1);
1319 474 : auto ¶m_mapping = fntype->get_substs ().at (0);
1320 474 : const auto param_tyty = param_mapping.get_param_ty ();
1321 474 : auto resolved_tyty = param_tyty->resolve ();
1322 474 : tree template_parameter_type
1323 474 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1324 :
1325 474 : enter_intrinsic_block (ctx, fndecl);
1326 :
1327 : // BUILTIN size_of FN BODY BEGIN
1328 474 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1329 474 : auto return_statement
1330 474 : = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
1331 474 : ctx->add_statement (return_statement);
1332 : // BUILTIN size_of FN BODY END
1333 :
1334 474 : finalize_intrinsic_block (ctx, fndecl);
1335 :
1336 474 : return fndecl;
1337 : }
1338 :
1339 : tree
1340 72 : offset (Context *ctx, TyTy::FnType *fntype)
1341 : {
1342 : // offset intrinsic has two params dst pointer and offset isize
1343 72 : rust_assert (fntype->get_params ().size () == 2);
1344 :
1345 72 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1346 :
1347 72 : std::vector<Bvariable *> param_vars;
1348 72 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1349 :
1350 72 : auto &dst_param = param_vars.at (0);
1351 72 : auto &size_param = param_vars.at (1);
1352 72 : rust_assert (param_vars.size () == 2);
1353 72 : if (!Backend::function_set_parameters (fndecl, param_vars))
1354 0 : return error_mark_node;
1355 :
1356 72 : enter_intrinsic_block (ctx, fndecl);
1357 :
1358 : // BUILTIN offset FN BODY BEGIN
1359 72 : tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
1360 72 : tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
1361 72 : tree pointer_offset_expr
1362 72 : = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
1363 72 : auto return_statement
1364 72 : = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
1365 72 : ctx->add_statement (return_statement);
1366 : // BUILTIN offset FN BODY END
1367 :
1368 72 : finalize_intrinsic_block (ctx, fndecl);
1369 :
1370 72 : return fndecl;
1371 72 : }
1372 :
1373 : /**
1374 : * pub const fn bswap<T: Copy>(x: T) -> T;
1375 : */
1376 : tree
1377 9 : bswap_handler (Context *ctx, TyTy::FnType *fntype)
1378 : {
1379 9 : rust_assert (fntype->get_params ().size () == 1);
1380 :
1381 9 : tree lookup = NULL_TREE;
1382 9 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1383 0 : return lookup;
1384 :
1385 9 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1386 :
1387 9 : auto locus = fntype->get_locus ();
1388 :
1389 9 : std::vector<Bvariable *> param_vars;
1390 9 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1391 :
1392 9 : auto &x_param = param_vars.at (0);
1393 9 : rust_assert (param_vars.size () == 1);
1394 9 : if (!Backend::function_set_parameters (fndecl, param_vars))
1395 0 : return error_mark_node;
1396 :
1397 9 : auto *monomorphized_type
1398 9 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
1399 :
1400 9 : check_for_basic_integer_type ("bswap", fntype->get_locus (),
1401 : monomorphized_type);
1402 :
1403 9 : tree template_parameter_type
1404 9 : = TyTyResolveCompile::compile (ctx, monomorphized_type);
1405 :
1406 9 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1407 9 : unsigned HOST_WIDE_INT size = TREE_INT_CST_LOW (size_expr);
1408 :
1409 9 : enter_intrinsic_block (ctx, fndecl);
1410 :
1411 : // BUILTIN bswap FN BODY BEGIN
1412 :
1413 9 : auto expr_x = Backend::var_expression (x_param, locus);
1414 9 : tree result = NULL_TREE;
1415 :
1416 9 : if (size == 1)
1417 : {
1418 : result = expr_x;
1419 : }
1420 : else
1421 : {
1422 8 : tree target_type = NULL_TREE;
1423 8 : const char *builtin_name = nullptr;
1424 8 : switch (size)
1425 : {
1426 2 : case 2:
1427 2 : builtin_name = "__builtin_bswap16";
1428 2 : target_type = uint16_type_node;
1429 2 : break;
1430 2 : case 4:
1431 2 : builtin_name = "__builtin_bswap32";
1432 2 : target_type = uint32_type_node;
1433 2 : break;
1434 2 : case 8:
1435 2 : builtin_name = "__builtin_bswap64";
1436 2 : target_type = uint64_type_node;
1437 2 : break;
1438 2 : case 16:
1439 2 : builtin_name = "__builtin_bswap128";
1440 2 : target_type = uint128_type_node;
1441 2 : break;
1442 0 : default:
1443 0 : return error_mark_node;
1444 : }
1445 :
1446 8 : tree bswap_raw = nullptr;
1447 8 : auto ok = BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
1448 : &bswap_raw);
1449 :
1450 8 : if (ok)
1451 : {
1452 8 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1453 :
1454 8 : auto bswap_x = build1 (CONVERT_EXPR, target_type, expr_x);
1455 :
1456 8 : auto bswap_call
1457 8 : = Backend::call_expression (bswap_fn, {bswap_x}, NULL_TREE, locus);
1458 :
1459 8 : result = build1 (CONVERT_EXPR, template_parameter_type, bswap_call);
1460 : }
1461 : else
1462 : {
1463 0 : auto ok2 = BuiltinsContext::get ().lookup_simple_builtin (
1464 0 : "__builtin_bswap64", &bswap_raw);
1465 0 : rust_assert (ok2);
1466 :
1467 0 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1468 :
1469 0 : tree tmp_in_stmt = error_mark_node;
1470 0 : Bvariable *in_var
1471 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1472 : template_parameter_type, expr_x,
1473 : true, locus, &tmp_in_stmt);
1474 0 : ctx->add_statement (tmp_in_stmt);
1475 :
1476 0 : tree addr_x
1477 0 : = build_fold_addr_expr_loc (locus, in_var->get_tree (locus));
1478 0 : tree u64_ptr_type = build_pointer_type (uint64_type_node);
1479 :
1480 0 : tree low_ptr = fold_convert (u64_ptr_type, addr_x);
1481 0 : tree low = build_fold_indirect_ref_loc (locus, low_ptr);
1482 :
1483 0 : tree high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type, low_ptr,
1484 : size_int (8));
1485 0 : tree high = build_fold_indirect_ref_loc (locus, high_ptr);
1486 :
1487 0 : auto new_high
1488 0 : = Backend::call_expression (bswap_fn, {low}, NULL_TREE, locus);
1489 0 : auto new_low
1490 0 : = Backend::call_expression (bswap_fn, {high}, NULL_TREE, locus);
1491 :
1492 0 : tree tmp_stmt = error_mark_node;
1493 0 : Bvariable *result_var
1494 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1495 : template_parameter_type, NULL_TREE,
1496 : true, locus, &tmp_stmt);
1497 0 : ctx->add_statement (tmp_stmt);
1498 :
1499 0 : tree addr_res
1500 0 : = build_fold_addr_expr_loc (locus, result_var->get_tree (locus));
1501 0 : tree res_ptr = fold_convert (u64_ptr_type, addr_res);
1502 :
1503 0 : tree store_low
1504 0 : = build2 (MODIFY_EXPR, void_type_node,
1505 : build_fold_indirect_ref_loc (locus, res_ptr), new_low);
1506 0 : ctx->add_statement (store_low);
1507 :
1508 0 : tree res_high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type,
1509 : res_ptr, size_int (8));
1510 0 : tree store_high
1511 0 : = build2 (MODIFY_EXPR, void_type_node,
1512 : build_fold_indirect_ref_loc (locus, res_high_ptr),
1513 : new_high);
1514 0 : ctx->add_statement (store_high);
1515 :
1516 0 : result = result_var->get_tree (locus);
1517 : }
1518 : }
1519 :
1520 9 : auto return_statement = Backend::return_statement (fndecl, result, locus);
1521 :
1522 9 : TREE_READONLY (result) = 1;
1523 :
1524 9 : ctx->add_statement (return_statement);
1525 :
1526 : // BUILTIN bswap FN BODY END
1527 :
1528 9 : finalize_intrinsic_block (ctx, fndecl);
1529 :
1530 9 : return fndecl;
1531 9 : }
1532 :
1533 : } // namespace handlers
1534 : } // namespace Compile
1535 : } // namespace Rust
|