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 867 : is_basic_integer_type (TyTy::BaseType *type)
50 : {
51 867 : 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 26 : default:
59 26 : 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 2921 : maybe_override_ctx (TyTy::FnType *fntype)
69 : {
70 2921 : if (fntype->has_substitutions_defined ())
71 2918 : fntype->override_context ();
72 2921 : }
73 :
74 : static bool
75 867 : check_for_basic_integer_type (const std::string &intrinsic_str,
76 : location_t locus, TyTy::BaseType *type)
77 : {
78 867 : auto is_basic_integer = is_basic_integer_type (type);
79 867 : if (!is_basic_integer)
80 : {
81 26 : rust_error_at (
82 : locus,
83 : "%s intrinsic can only be used with basic integer types (got %qs)",
84 52 : intrinsic_str.c_str (), type->get_name ().c_str ());
85 : }
86 :
87 867 : 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 2840 : check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
96 : {
97 2840 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
98 2840 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
99 2840 : 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 2840 : }
107 :
108 : static tree
109 2921 : compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
110 : {
111 2921 : maybe_override_ctx (fntype);
112 :
113 2921 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
114 :
115 2921 : tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
116 2921 : std::string ir_symbol_name
117 2921 : = canonical_path.get () + fntype->subst_as_string ();
118 2921 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
119 :
120 2921 : unsigned int flags = 0;
121 2921 : tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
122 2921 : flags, fntype->get_ident ().locus);
123 :
124 2921 : TREE_PUBLIC (fndecl) = 0;
125 2921 : TREE_READONLY (fndecl) = 1;
126 2921 : DECL_ARTIFICIAL (fndecl) = 1;
127 2921 : DECL_EXTERNAL (fndecl) = 0;
128 2921 : DECL_DECLARED_INLINE_P (fndecl) = 1;
129 :
130 2921 : return fndecl;
131 2921 : }
132 :
133 : /**
134 : * Compile and setup a function's parameters
135 : */
136 : static void
137 2353 : 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 6242 : for (auto &parm : fntype->get_params ())
142 : {
143 3889 : auto &referenced_param = parm.get_pattern ();
144 3889 : auto param_tyty = parm.get_type ();
145 3889 : auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
146 :
147 3889 : location_t param_locus = referenced_param.get_locus ();
148 3889 : Bvariable *compiled_param_var
149 3889 : = CompileFnParam::compile (ctx, fndecl, referenced_param,
150 3889 : compiled_param_type, param_locus);
151 :
152 3889 : compiled_param_variables->push_back (compiled_param_var);
153 3889 : if (compiled_param_types)
154 359 : compiled_param_types->push_back (compiled_param_type);
155 : }
156 2353 : }
157 :
158 : static void
159 2917 : enter_intrinsic_block (Context *ctx, tree fndecl,
160 : const std::vector<Bvariable *> &vars = {})
161 : {
162 2917 : tree enclosing_scope = NULL_TREE;
163 2917 : location_t start_location = UNDEF_LOCATION;
164 2917 : location_t end_location = UNDEF_LOCATION;
165 :
166 2917 : auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
167 : end_location);
168 :
169 2917 : ctx->push_block (block);
170 2917 : }
171 :
172 : static void
173 2903 : finalize_intrinsic_block (Context *ctx, tree fndecl)
174 : {
175 2903 : tree bind_tree = ctx->pop_block ();
176 :
177 2903 : gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
178 :
179 2903 : DECL_SAVED_TREE (fndecl) = bind_tree;
180 :
181 2903 : ctx->push_function (fndecl);
182 :
183 2903 : DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
184 2903 : maybe_save_constexpr_fundef (fndecl);
185 2903 : }
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 operation", 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 : auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
259 56 : check_for_basic_integer_type ("unchecked operation", call_locus,
260 : monomorphized_type);
261 :
262 56 : auto expr = build2 (op, TREE_TYPE (x), x, y);
263 56 : auto return_statement
264 56 : = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
265 :
266 56 : ctx->add_statement (return_statement);
267 :
268 : // BUILTIN unchecked_<op> BODY END
269 :
270 56 : finalize_intrinsic_block (ctx, fndecl);
271 :
272 56 : return fndecl;
273 56 : }
274 :
275 : inline tree
276 0 : expect (Context *ctx, TyTy::FnType *fntype, bool likely)
277 : {
278 0 : rust_assert (fntype->get_params ().size () == 1);
279 :
280 0 : tree lookup = NULL_TREE;
281 0 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
282 0 : return lookup;
283 :
284 0 : auto fndecl = compile_intrinsic_function (ctx, fntype);
285 :
286 0 : enter_intrinsic_block (ctx, fndecl);
287 :
288 : // BUILTIN expect_handler_inner FN BODY BEGIN
289 : // setup the params
290 0 : std::vector<Bvariable *> param_vars;
291 0 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
292 0 : tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
293 0 : tree expect_fn_raw = nullptr;
294 0 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
295 : &expect_fn_raw);
296 0 : rust_assert (expect_fn_raw);
297 0 : auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
298 :
299 : // we need to convert the expression return type to long to match the expected
300 : // parameter type of __builtin_expect
301 0 : auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
302 0 : auto expect_value
303 0 : = make_unsigned_long_tree (static_cast<unsigned long> (likely));
304 :
305 0 : auto expect_call
306 0 : = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
307 : BUILTINS_LOCATION);
308 : // the return value also needs to be casted (to bool)
309 0 : auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, expect_call);
310 0 : auto return_statement
311 0 : = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
312 0 : ctx->add_statement (return_statement);
313 : // BUILTIN expect_handler_inner FN BODY END
314 :
315 0 : finalize_intrinsic_block (ctx, fndecl);
316 :
317 0 : return fndecl;
318 0 : }
319 :
320 : tree
321 2 : try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
322 : {
323 2 : rust_assert (fntype->get_params ().size () == 3);
324 :
325 2 : tree lookup = NULL_TREE;
326 2 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
327 0 : return lookup;
328 2 : auto fndecl = compile_intrinsic_function (ctx, fntype);
329 :
330 2 : enter_intrinsic_block (ctx, fndecl);
331 :
332 : // The following tricks are needed to make sure the try-catch blocks are not
333 : // optimized away
334 2 : TREE_READONLY (fndecl) = 0;
335 2 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
336 2 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
337 2 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
338 :
339 : // BUILTIN try_handler FN BODY BEGIN
340 : // setup the params
341 2 : std::vector<Bvariable *> param_vars;
342 2 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
343 2 : if (!Backend::function_set_parameters (fndecl, param_vars))
344 0 : return error_mark_node;
345 2 : tree enclosing_scope = NULL_TREE;
346 :
347 2 : bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
348 2 : == CompileOptions::PanicStrategy::Abort;
349 2 : tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
350 2 : tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
351 2 : tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
352 2 : tree normal_return_stmt = NULL_TREE;
353 2 : tree error_return_stmt = NULL_TREE;
354 2 : tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
355 : BUILTINS_LOCATION);
356 2 : tree catch_call = NULL_TREE;
357 2 : tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
358 : UNDEF_LOCATION);
359 :
360 2 : if (is_new_api)
361 : {
362 1 : auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
363 1 : auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
364 : UNDEF_LOCATION);
365 1 : normal_return_stmt
366 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
367 1 : error_return_stmt
368 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
369 : }
370 : else
371 : {
372 1 : normal_return_stmt = Backend::return_statement (fndecl, integer_zero_node,
373 : BUILTINS_LOCATION);
374 1 : error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
375 : BUILTINS_LOCATION);
376 : }
377 2 : Backend::block_add_statements (try_block,
378 2 : std::vector<tree>{try_call,
379 2 : normal_return_stmt});
380 2 : if (panic_is_abort)
381 : {
382 : // skip building the try-catch construct
383 0 : ctx->add_statement (try_block);
384 0 : finalize_intrinsic_block (ctx, fndecl);
385 0 : return fndecl;
386 : }
387 :
388 2 : tree eh_pointer
389 2 : = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
390 : integer_zero_node);
391 2 : catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
392 : NULL_TREE, BUILTINS_LOCATION);
393 :
394 2 : tree catch_block = Backend::block (fndecl, enclosing_scope, {},
395 : UNDEF_LOCATION, UNDEF_LOCATION);
396 2 : Backend::block_add_statements (catch_block,
397 2 : std::vector<tree>{catch_call,
398 2 : error_return_stmt});
399 : // emulate what cc1plus is doing for C++ try-catch
400 2 : tree inner_eh_construct
401 2 : = Backend::exception_handler_statement (catch_call, NULL_TREE,
402 : error_return_stmt,
403 : BUILTINS_LOCATION);
404 : // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
405 2 : auto eh_construct
406 2 : = Backend::exception_handler_statement (try_block, inner_eh_construct,
407 : NULL_TREE, BUILTINS_LOCATION);
408 2 : ctx->add_statement (eh_construct);
409 : // BUILTIN try_handler FN BODY END
410 2 : finalize_intrinsic_block (ctx, fndecl);
411 :
412 2 : return fndecl;
413 2 : }
414 :
415 : /**
416 : * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
417 : */
418 : tree
419 310 : wrapping_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
420 : {
421 : // wrapping_<op> intrinsics have two parameter
422 310 : rust_assert (fntype->get_params ().size () == 2);
423 :
424 310 : tree lookup = NULL_TREE;
425 310 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
426 0 : return lookup;
427 :
428 310 : auto fndecl = compile_intrinsic_function (ctx, fntype);
429 :
430 : // setup the params
431 310 : std::vector<Bvariable *> param_vars;
432 310 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
433 :
434 310 : auto &lhs_param = param_vars.at (0);
435 310 : auto &rhs_param = param_vars.at (1);
436 :
437 310 : if (!Backend::function_set_parameters (fndecl, param_vars))
438 0 : return error_mark_node;
439 :
440 310 : enter_intrinsic_block (ctx, fndecl);
441 :
442 : // BUILTIN wrapping_<op> FN BODY BEGIN
443 310 : auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
444 310 : auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
445 :
446 : // Operations are always wrapping in Rust, as we have -fwrapv enabled by
447 : // default. The difference between a wrapping_{add, sub, mul} and a regular
448 : // arithmetic operation is that these intrinsics do not panic - they always
449 : // carry over.
450 310 : auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
451 :
452 310 : auto return_statement
453 310 : = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
454 310 : ctx->add_statement (return_statement);
455 : // BUILTIN wrapping_<op> FN BODY END
456 :
457 310 : finalize_intrinsic_block (ctx, fndecl);
458 :
459 310 : return fndecl;
460 310 : }
461 :
462 : /**
463 : * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
464 : */
465 : tree
466 141 : op_with_overflow (Context *ctx, TyTy::FnType *fntype, tree_code op)
467 : {
468 : // wrapping_<op> intrinsics have two parameter
469 141 : rust_assert (fntype->get_params ().size () == 2);
470 :
471 141 : tree lookup = NULL_TREE;
472 141 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
473 0 : return lookup;
474 :
475 141 : auto fndecl = compile_intrinsic_function (ctx, fntype);
476 :
477 : // setup the params
478 141 : std::vector<Bvariable *> param_vars;
479 141 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
480 :
481 141 : auto &x_param = param_vars.at (0);
482 141 : auto &y_param = param_vars.at (1);
483 :
484 141 : if (!Backend::function_set_parameters (fndecl, param_vars))
485 0 : return error_mark_node;
486 :
487 141 : rust_assert (fntype->get_num_substitutions () == 1);
488 141 : auto ¶m_mapping = fntype->get_substs ().at (0);
489 141 : const auto param_tyty = param_mapping.get_param_ty ();
490 141 : auto resolved_tyty = param_tyty->resolve ();
491 141 : tree template_parameter_type
492 141 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
493 :
494 : // this should match y as well or we can take it from the TyTy structure
495 141 : tree tmp_stmt = error_mark_node;
496 141 : Bvariable *result_variable
497 141 : = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
498 : NULL_TREE, true /*address_is_taken*/,
499 : UNDEF_LOCATION, &tmp_stmt);
500 141 : Bvariable *bool_variable
501 141 : = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
502 : NULL_TREE, true /*address_is_taken*/,
503 : UNDEF_LOCATION, &tmp_stmt);
504 :
505 141 : enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
506 :
507 : // BUILTIN op_with_overflow FN BODY BEGIN
508 141 : auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
509 141 : auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
510 :
511 141 : tree overflow_builtin = error_mark_node;
512 141 : switch (op)
513 : {
514 127 : case PLUS_EXPR:
515 127 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
516 : &overflow_builtin);
517 127 : break;
518 :
519 7 : case MINUS_EXPR:
520 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
521 : &overflow_builtin);
522 7 : break;
523 :
524 7 : case MULT_EXPR:
525 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
526 : &overflow_builtin);
527 7 : break;
528 :
529 0 : default:
530 0 : rust_unreachable ();
531 141 : break;
532 : }
533 141 : rust_assert (overflow_builtin != error_mark_node);
534 :
535 141 : tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
536 141 : tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
537 141 : tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
538 :
539 141 : tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
540 : 3, x, y, result_ref);
541 :
542 141 : tree overflow_assignment
543 141 : = Backend::assignment_statement (bool_decl, builtin_call,
544 : BUILTINS_LOCATION);
545 :
546 141 : ctx->add_statement (overflow_assignment);
547 :
548 282 : std::vector<tree> vals = {result_decl, bool_decl};
549 141 : tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
550 141 : tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
551 : -1, UNDEF_LOCATION);
552 :
553 141 : auto return_statement
554 141 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
555 141 : ctx->add_statement (return_statement);
556 :
557 : // BUILTIN wrapping_<op> FN BODY END
558 :
559 141 : finalize_intrinsic_block (ctx, fndecl);
560 :
561 141 : return fndecl;
562 141 : }
563 :
564 : /**
565 : * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
566 : * fn copy<T>(src: *const T, dst: *mut T, count: usize);
567 : */
568 : tree
569 243 : copy (Context *ctx, TyTy::FnType *fntype, bool overlaps)
570 : {
571 243 : rust_assert (fntype->get_params ().size () == 3);
572 243 : rust_assert (fntype->get_num_substitutions () == 1);
573 :
574 243 : tree lookup = NULL_TREE;
575 243 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
576 0 : return lookup;
577 :
578 243 : auto fndecl = compile_intrinsic_function (ctx, fntype);
579 :
580 : // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
581 243 : TREE_READONLY (fndecl) = 0;
582 243 : TREE_SIDE_EFFECTS (fndecl) = 1;
583 :
584 : // setup the params
585 243 : std::vector<Bvariable *> param_vars;
586 243 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
587 :
588 243 : if (!Backend::function_set_parameters (fndecl, param_vars))
589 0 : return error_mark_node;
590 :
591 243 : enter_intrinsic_block (ctx, fndecl);
592 :
593 : // BUILTIN copy_nonoverlapping BODY BEGIN
594 :
595 243 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
596 243 : auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
597 243 : auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
598 :
599 : // We want to create the following statement
600 : // memcpy(dst, src, size_of::<T>());
601 : // so
602 : // memcpy(dst, src, size_expr);
603 :
604 243 : auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
605 243 : auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
606 :
607 243 : tree size_expr
608 243 : = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
609 :
610 243 : tree memcpy_raw = nullptr;
611 486 : BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
612 : : "__builtin_memcpy",
613 : &memcpy_raw);
614 243 : rust_assert (memcpy_raw);
615 243 : auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
616 :
617 243 : auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
618 : nullptr, UNDEF_LOCATION);
619 :
620 243 : ctx->add_statement (copy_call);
621 :
622 : // BUILTIN copy_nonoverlapping BODY END
623 :
624 243 : finalize_intrinsic_block (ctx, fndecl);
625 :
626 243 : return fndecl;
627 243 : }
628 :
629 : tree
630 70 : atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering)
631 : {
632 70 : rust_assert (fntype->get_params ().size () == 2);
633 70 : rust_assert (fntype->get_num_substitutions () == 1);
634 :
635 70 : tree lookup = NULL_TREE;
636 70 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
637 0 : return lookup;
638 :
639 70 : auto fndecl = compile_intrinsic_function (ctx, fntype);
640 :
641 : // Most intrinsic functions are pure but not the atomic ones
642 70 : TREE_READONLY (fndecl) = 0;
643 70 : TREE_SIDE_EFFECTS (fndecl) = 1;
644 :
645 : // setup the params
646 70 : std::vector<Bvariable *> param_vars;
647 70 : std::vector<tree> types;
648 70 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
649 :
650 70 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
651 70 : rust_assert (ok);
652 :
653 70 : enter_intrinsic_block (ctx, fndecl);
654 :
655 70 : auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
656 70 : TREE_READONLY (dst) = 0;
657 :
658 70 : auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
659 70 : auto memorder = make_unsigned_long_tree (ordering);
660 :
661 70 : auto monomorphized_type
662 70 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
663 :
664 70 : auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
665 70 : auto builtin_name = build_atomic_builtin_name ("atomic_store_", call_locus,
666 70 : monomorphized_type);
667 70 : if (builtin_name.empty ())
668 14 : return error_mark_node;
669 :
670 56 : tree atomic_store_raw = nullptr;
671 56 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
672 : &atomic_store_raw);
673 56 : rust_assert (atomic_store_raw);
674 :
675 56 : auto atomic_store
676 56 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
677 :
678 56 : auto store_call
679 56 : = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
680 : UNDEF_LOCATION);
681 56 : TREE_READONLY (store_call) = 0;
682 56 : TREE_SIDE_EFFECTS (store_call) = 1;
683 :
684 56 : ctx->add_statement (store_call);
685 56 : finalize_intrinsic_block (ctx, fndecl);
686 :
687 56 : return fndecl;
688 70 : }
689 :
690 : tree
691 28 : atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering)
692 : {
693 28 : rust_assert (fntype->get_params ().size () == 1);
694 28 : rust_assert (fntype->get_num_substitutions () == 1);
695 :
696 28 : tree lookup = NULL_TREE;
697 28 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
698 0 : return lookup;
699 :
700 28 : auto fndecl = compile_intrinsic_function (ctx, fntype);
701 :
702 : // Most intrinsic functions are pure but not the atomic ones
703 : // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
704 28 : TREE_READONLY (fndecl) = 0;
705 28 : TREE_SIDE_EFFECTS (fndecl) = 1;
706 :
707 : // setup the params
708 28 : std::vector<Bvariable *> param_vars;
709 28 : std::vector<tree> types;
710 28 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
711 :
712 28 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
713 28 : rust_assert (ok);
714 :
715 28 : enter_intrinsic_block (ctx, fndecl);
716 :
717 28 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
718 28 : auto memorder = make_unsigned_long_tree (ordering);
719 :
720 28 : auto monomorphized_type
721 28 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
722 :
723 28 : auto builtin_name
724 28 : = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
725 28 : monomorphized_type);
726 28 : if (builtin_name.empty ())
727 0 : return error_mark_node;
728 :
729 28 : tree atomic_load_raw = nullptr;
730 28 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
731 : &atomic_load_raw);
732 28 : rust_assert (atomic_load_raw);
733 :
734 28 : auto atomic_load
735 28 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
736 :
737 28 : auto load_call = Backend::call_expression (atomic_load, {src, memorder},
738 : nullptr, UNDEF_LOCATION);
739 28 : auto return_statement
740 28 : = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
741 :
742 28 : TREE_READONLY (load_call) = 0;
743 28 : TREE_SIDE_EFFECTS (load_call) = 1;
744 :
745 28 : ctx->add_statement (return_statement);
746 :
747 28 : finalize_intrinsic_block (ctx, fndecl);
748 :
749 28 : return fndecl;
750 28 : }
751 :
752 : // Shared inner implementation for ctlz and ctlz_nonzero.
753 : //
754 : // nonzero=false → ctlz: ctlz(0) is well-defined in Rust and must return
755 : // bit_size, but __builtin_clz*(0) is undefined behaviour in C, so an
756 : // explicit arg==0 guard is emitted.
757 : //
758 : // nonzero=true → ctlz_nonzero: the caller guarantees arg != 0 (passing 0
759 : // is immediate UB in Rust), so the zero guard is omitted entirely.
760 : static tree
761 422 : ctlz_handler (Context *ctx, TyTy::FnType *fntype, bool nonzero)
762 : {
763 422 : rust_assert (fntype->get_params ().size () == 1);
764 :
765 422 : tree lookup = NULL_TREE;
766 422 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
767 0 : return lookup;
768 :
769 422 : auto fndecl = compile_intrinsic_function (ctx, fntype);
770 :
771 422 : std::vector<Bvariable *> param_vars;
772 422 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
773 :
774 422 : auto arg_param = param_vars.at (0);
775 422 : if (!Backend::function_set_parameters (fndecl, param_vars))
776 0 : return error_mark_node;
777 :
778 422 : rust_assert (fntype->get_num_substitutions () == 1);
779 422 : auto *monomorphized_type
780 422 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
781 422 : auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
782 422 : if (!check_for_basic_integer_type ("ctlz", call_locus, monomorphized_type))
783 2 : return error_mark_node;
784 :
785 420 : enter_intrinsic_block (ctx, fndecl);
786 :
787 : // BUILTIN ctlz FN BODY BEGIN
788 420 : auto locus = fntype->get_locus ();
789 420 : auto arg_expr = Backend::var_expression (arg_param, locus);
790 420 : tree arg_type = TREE_TYPE (arg_expr);
791 420 : unsigned bit_size = TYPE_PRECISION (arg_type);
792 :
793 : // Convert signed types to their same-width unsigned equivalent before
794 : // widening. Without this, widening a signed type sign-extends it.
795 : // For example, i8(-1) = 0xFF widened to u32 gives 0xFFFFFFFF, so
796 : // __builtin_clz(0xFFFFFFFF) = 0, then 0 - diff(24) = -24.
797 : // Converting to u8 first gives 0xFF → 0x000000FF (zero-extended), so
798 : // __builtin_clz(0x000000FF) = 24, then 24 - 24 = 0.
799 420 : tree unsigned_type
800 420 : = !TYPE_UNSIGNED (arg_type) ? unsigned_type_for (arg_type) : arg_type;
801 420 : tree unsigned_arg = fold_convert (unsigned_type, arg_expr);
802 :
803 : // Pick the narrowest GCC clz builtin whose operand type is wide enough to
804 : // hold bit_size bits. diff records how many extra leading zeros the builtin
805 : // will count due to the width difference and is subtracted from the result.
806 : //
807 : // Example: ctlz(1u8) bit_size=8, int_prec=32, diff=24.
808 : // __builtin_clz(1u) returns 31 (counts from bit 31 down to bit 0).
809 : // 31 - 24 = 7, which is the correct answer for an 8-bit value.
810 : //
811 : // TODO: 128-bit integers are not yet handled.
812 420 : unsigned int_prec = TYPE_PRECISION (unsigned_type_node);
813 420 : unsigned long_prec = TYPE_PRECISION (long_unsigned_type_node);
814 420 : unsigned longlong_prec = TYPE_PRECISION (long_long_unsigned_type_node);
815 :
816 420 : const char *builtin_name = nullptr;
817 420 : tree cast_type = NULL_TREE;
818 420 : int diff = 0;
819 :
820 420 : if (bit_size <= int_prec)
821 : {
822 : // Fits in unsigned int: covers 8/16/32-bit integers on most targets.
823 315 : builtin_name = "__builtin_clz";
824 315 : cast_type = unsigned_type_node;
825 315 : diff = static_cast<int> (int_prec - bit_size);
826 : }
827 105 : else if (bit_size <= long_prec)
828 : {
829 : // Fits in unsigned long but not unsigned int.
830 105 : builtin_name = "__builtin_clzl";
831 105 : cast_type = long_unsigned_type_node;
832 105 : diff = static_cast<int> (long_prec - bit_size);
833 : }
834 0 : else if (bit_size <= longlong_prec)
835 : {
836 : // Fits in unsigned long long but not unsigned long.
837 0 : builtin_name = "__builtin_clzll";
838 0 : cast_type = long_long_unsigned_type_node;
839 0 : diff = static_cast<int> (longlong_prec - bit_size);
840 : }
841 : else
842 : {
843 0 : rust_sorry_at (locus, "ctlz for %u-bit integers is not yet implemented",
844 : bit_size);
845 0 : return error_mark_node;
846 : }
847 :
848 : // Widen the unsigned arg to the chosen builtin's operand type, call it,
849 : // then subtract the padding bits. diff == 0 means the Rust type exactly
850 : // matches the builtin's operand width, so the subtraction is skipped.
851 420 : tree call_arg = fold_convert (cast_type, unsigned_arg);
852 :
853 420 : tree builtin_decl = error_mark_node;
854 420 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &builtin_decl);
855 420 : rust_assert (builtin_decl != error_mark_node);
856 :
857 420 : tree builtin_fn = build_fold_addr_expr_loc (locus, builtin_decl);
858 420 : tree clz_expr
859 420 : = Backend::call_expression (builtin_fn, {call_arg}, nullptr, locus);
860 :
861 420 : if (diff > 0)
862 : {
863 210 : tree diff_cst = build_int_cst (integer_type_node, diff);
864 210 : clz_expr
865 210 : = fold_build2 (MINUS_EXPR, integer_type_node, clz_expr, diff_cst);
866 : }
867 :
868 420 : clz_expr = fold_convert (uint32_type_node, clz_expr);
869 :
870 420 : tree final_expr;
871 420 : if (!nonzero)
872 : {
873 : // ctlz(0) must return bit_size per the Rust reference.
874 : // We cannot pass 0 to __builtin_clz* (UB), so emit:
875 : // arg == 0 ? bit_size : clz_expr
876 252 : tree zero = build_int_cst (arg_type, 0);
877 252 : tree cmp = fold_build2 (EQ_EXPR, boolean_type_node, arg_expr, zero);
878 252 : tree width_cst = build_int_cst (uint32_type_node, bit_size);
879 252 : final_expr
880 252 : = fold_build3 (COND_EXPR, uint32_type_node, cmp, width_cst, clz_expr);
881 : }
882 : else
883 : {
884 : // ctlz_nonzero: arg != 0 is guaranteed by the caller, no guard needed.
885 : final_expr = clz_expr;
886 : }
887 :
888 420 : tree result = fold_convert (TREE_TYPE (DECL_RESULT (fndecl)), final_expr);
889 420 : auto return_stmt = Backend::return_statement (fndecl, result, locus);
890 420 : ctx->add_statement (return_stmt);
891 : // BUILTIN ctlz FN BODY END
892 :
893 420 : finalize_intrinsic_block (ctx, fndecl);
894 420 : return fndecl;
895 422 : }
896 :
897 : // Shared inner implementation for cttz and cttz_nonzero.
898 : //
899 : // nonzero=false → cttz: cttz(0) is well-defined in Rust and must return
900 : // bit_size, but __builtin_ctz*(0) is undefined behaviour in C, so an
901 : // explicit arg==0 guard is emitted.
902 : //
903 : // nonzero=true → cttz_nonzero: the caller guarantees arg != 0 (passing 0
904 : // is immediate UB in Rust), so the zero guard is omitted entirely.
905 : static tree
906 282 : cttz_handler (Context *ctx, TyTy::FnType *fntype, bool nonzero)
907 : {
908 282 : rust_assert (fntype->get_params ().size () == 1);
909 :
910 282 : tree lookup = NULL_TREE;
911 282 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
912 0 : return lookup;
913 :
914 282 : auto fndecl = compile_intrinsic_function (ctx, fntype);
915 :
916 282 : std::vector<Bvariable *> param_vars;
917 282 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
918 :
919 282 : auto arg_param = param_vars.at (0);
920 282 : if (!Backend::function_set_parameters (fndecl, param_vars))
921 0 : return error_mark_node;
922 :
923 282 : rust_assert (fntype->get_num_substitutions () == 1);
924 282 : auto *monomorphized_type
925 282 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
926 282 : auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
927 282 : if (!check_for_basic_integer_type ("cttz", call_locus, monomorphized_type))
928 2 : return error_mark_node;
929 :
930 280 : enter_intrinsic_block (ctx, fndecl);
931 :
932 : // BUILTIN cttz FN BODY BEGIN
933 280 : auto locus = fntype->get_locus ();
934 280 : auto arg_expr = Backend::var_expression (arg_param, locus);
935 280 : tree arg_type = TREE_TYPE (arg_expr);
936 280 : unsigned bit_size = TYPE_PRECISION (arg_type);
937 :
938 : // Convert signed types to their same-width unsigned equivalent before
939 : // widening. For cttz this is not strictly required for correctness (sign
940 : // extension fills high bits with 1s, which does not alter the trailing-zero
941 : // count at the low end), but it avoids relying on signed-integer
942 : // representations and keeps the approach consistent with ctlz.
943 280 : tree unsigned_type
944 280 : = !TYPE_UNSIGNED (arg_type) ? unsigned_type_for (arg_type) : arg_type;
945 280 : tree unsigned_arg = fold_convert (unsigned_type, arg_expr);
946 :
947 : // Pick the narrowest GCC ctz builtin whose operand type is wide enough to
948 : // hold bit_size bits. Unlike ctlz, no diff adjustment is needed: widening
949 : // a value zero-extends it (fills the added high bits with 0s), which does
950 : // not introduce new trailing zeros at the low end.
951 : //
952 : // Example: cttz(0b00001000_u8) = 3
953 : // Widened to u32: 0x00000008. __builtin_ctz(0x00000008) = 3.
954 : //
955 : // TODO: 128-bit integers are not yet handled.
956 280 : unsigned int_prec = TYPE_PRECISION (unsigned_type_node);
957 280 : unsigned long_prec = TYPE_PRECISION (long_unsigned_type_node);
958 280 : unsigned longlong_prec = TYPE_PRECISION (long_long_unsigned_type_node);
959 :
960 280 : const char *builtin_name = nullptr;
961 280 : tree cast_type = NULL_TREE;
962 :
963 280 : if (bit_size <= int_prec)
964 : {
965 : // Fits in unsigned int: covers 8/16/32-bit integers on most targets.
966 : builtin_name = "__builtin_ctz";
967 : cast_type = unsigned_type_node;
968 : }
969 70 : else if (bit_size <= long_prec)
970 : {
971 : // Fits in unsigned long but not unsigned int.
972 : builtin_name = "__builtin_ctzl";
973 : cast_type = long_unsigned_type_node;
974 : }
975 0 : else if (bit_size <= longlong_prec)
976 : {
977 : // Fits in unsigned long long but not unsigned long.
978 : builtin_name = "__builtin_ctzll";
979 : cast_type = long_long_unsigned_type_node;
980 : }
981 : else
982 : {
983 0 : rust_sorry_at (locus, "cttz for %u-bit integers is not yet implemented",
984 : bit_size);
985 0 : return error_mark_node;
986 : }
987 :
988 280 : tree call_arg = fold_convert (cast_type, unsigned_arg);
989 :
990 280 : tree builtin_decl = error_mark_node;
991 280 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &builtin_decl);
992 280 : rust_assert (builtin_decl != error_mark_node);
993 :
994 280 : tree builtin_fn = build_fold_addr_expr_loc (locus, builtin_decl);
995 280 : tree ctz_expr
996 280 : = Backend::call_expression (builtin_fn, {call_arg}, nullptr, locus);
997 :
998 280 : ctz_expr = fold_convert (uint32_type_node, ctz_expr);
999 :
1000 280 : tree final_expr;
1001 280 : if (!nonzero)
1002 : {
1003 : // cttz(0) must return bit_size per the Rust reference.
1004 : // We cannot pass 0 to __builtin_ctz* (UB), so emit:
1005 : // arg == 0 ? bit_size : ctz_expr
1006 168 : tree zero = build_int_cst (arg_type, 0);
1007 168 : tree cmp = fold_build2 (EQ_EXPR, boolean_type_node, arg_expr, zero);
1008 168 : tree width_cst = build_int_cst (uint32_type_node, bit_size);
1009 168 : final_expr
1010 168 : = fold_build3 (COND_EXPR, uint32_type_node, cmp, width_cst, ctz_expr);
1011 : }
1012 : else
1013 : {
1014 : // cttz_nonzero: arg != 0 is guaranteed by the caller, no guard needed.
1015 : final_expr = ctz_expr;
1016 : }
1017 :
1018 280 : tree result = fold_convert (TREE_TYPE (DECL_RESULT (fndecl)), final_expr);
1019 280 : auto return_stmt = Backend::return_statement (fndecl, result, locus);
1020 280 : ctx->add_statement (return_stmt);
1021 : // BUILTIN cttz FN BODY END
1022 :
1023 280 : finalize_intrinsic_block (ctx, fndecl);
1024 280 : return fndecl;
1025 282 : }
1026 :
1027 : } // namespace inner
1028 :
1029 : const HandlerBuilder
1030 14730 : op_with_overflow (tree_code op)
1031 : {
1032 14871 : return [op] (Context *ctx, TyTy::FnType *fntype, location_t) {
1033 141 : return inner::op_with_overflow (ctx, fntype, op);
1034 14730 : };
1035 : }
1036 :
1037 : tree
1038 198 : rotate_left (Context *ctx, TyTy::FnType *fntype, location_t)
1039 : {
1040 198 : return handlers::rotate (ctx, fntype, LROTATE_EXPR);
1041 : }
1042 :
1043 : tree
1044 155 : rotate_right (Context *ctx, TyTy::FnType *fntype, location_t)
1045 : {
1046 155 : return handlers::rotate (ctx, fntype, RROTATE_EXPR);
1047 : }
1048 :
1049 : const HandlerBuilder
1050 14730 : wrapping_op (tree_code op)
1051 : {
1052 15040 : return [op] (Context *ctx, TyTy::FnType *fntype, location_t) {
1053 310 : return inner::wrapping_op (ctx, fntype, op);
1054 14730 : };
1055 : }
1056 :
1057 : HandlerBuilder
1058 19640 : atomic_store (int ordering)
1059 : {
1060 19710 : return [ordering] (Context *ctx, TyTy::FnType *fntype, location_t) {
1061 70 : return inner::atomic_store (ctx, fntype, ordering);
1062 19640 : };
1063 : }
1064 :
1065 : HandlerBuilder
1066 19640 : atomic_load (int ordering)
1067 : {
1068 19668 : return [ordering] (Context *ctx, TyTy::FnType *fntype, location_t) {
1069 28 : return inner::atomic_load (ctx, fntype, ordering);
1070 19640 : };
1071 : }
1072 :
1073 : const HandlerBuilder
1074 34370 : unchecked_op (tree_code op)
1075 : {
1076 34426 : return [op] (Context *ctx, TyTy::FnType *fntype, location_t) {
1077 56 : return inner::unchecked_op (ctx, fntype, op);
1078 34370 : };
1079 : }
1080 :
1081 : const HandlerBuilder
1082 9820 : copy (bool overlaps)
1083 : {
1084 10063 : return [overlaps] (Context *ctx, TyTy::FnType *fntype, location_t) {
1085 243 : return inner::copy (ctx, fntype, overlaps);
1086 9820 : };
1087 : }
1088 :
1089 : const HandlerBuilder
1090 9820 : expect (bool likely)
1091 : {
1092 9820 : return [likely] (Context *ctx, TyTy::FnType *fntype, location_t) {
1093 0 : return inner::expect (ctx, fntype, likely);
1094 9820 : };
1095 : }
1096 :
1097 : const HandlerBuilder
1098 9820 : try_handler (bool is_new_api)
1099 : {
1100 9822 : return [is_new_api] (Context *ctx, TyTy::FnType *fntype, location_t) {
1101 2 : return inner::try_handler (ctx, fntype, is_new_api);
1102 9820 : };
1103 : }
1104 :
1105 : tree
1106 0 : sorry (Context *ctx, TyTy::FnType *fntype, location_t)
1107 : {
1108 0 : rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
1109 : fntype->get_identifier ().c_str ());
1110 :
1111 0 : return error_mark_node;
1112 : }
1113 :
1114 : tree
1115 1 : assume (Context *ctx, TyTy::FnType *fntype, location_t)
1116 : {
1117 : // TODO: make sure this is actually helping the compiler optimize
1118 :
1119 1 : rust_assert (fntype->get_params ().size () == 1);
1120 1 : rust_assert (fntype->param_at (0).get_type ()->get_kind ()
1121 : == TyTy::TypeKind::BOOL);
1122 :
1123 1 : tree lookup = NULL_TREE;
1124 1 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1125 0 : return lookup;
1126 :
1127 1 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1128 :
1129 : // TODO: make sure these are necessary
1130 1 : TREE_READONLY (fndecl) = 0;
1131 1 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
1132 1 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
1133 1 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
1134 :
1135 1 : std::vector<Bvariable *> param_vars;
1136 1 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1137 :
1138 1 : if (!Backend::function_set_parameters (fndecl, param_vars))
1139 0 : return error_mark_node;
1140 :
1141 1 : enter_intrinsic_block (ctx, fndecl);
1142 :
1143 : // BUILTIN assume FN BODY BEGIN
1144 :
1145 1 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1146 :
1147 1 : tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
1148 : void_type_node, 1, val);
1149 1 : TREE_SIDE_EFFECTS (assume_expr) = 1;
1150 :
1151 1 : ctx->add_statement (assume_expr);
1152 : // BUILTIN assume FN BODY END
1153 :
1154 1 : finalize_intrinsic_block (ctx, fndecl);
1155 :
1156 1 : return fndecl;
1157 1 : }
1158 :
1159 : tree
1160 102 : discriminant_value (Context *ctx, TyTy::FnType *fntype, location_t)
1161 : {
1162 102 : rust_assert (fntype->get_params ().size () == 1);
1163 102 : rust_assert (fntype->has_substitutions ());
1164 102 : rust_assert (fntype->get_num_type_params () == 1);
1165 102 : auto &mapping = fntype->get_substs ().at (0);
1166 102 : auto param_ty = mapping.get_param_ty ();
1167 102 : rust_assert (param_ty->can_resolve ());
1168 102 : auto resolved = param_ty->resolve ();
1169 :
1170 102 : TyTy::BaseType *return_type = nullptr;
1171 102 : bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
1172 102 : rust_assert (ok);
1173 :
1174 102 : bool is_adt = resolved->is<TyTy::ADTType> ();
1175 102 : bool is_enum = false;
1176 102 : if (is_adt)
1177 : {
1178 102 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1179 102 : auto *repr = adt.get_repr_options ().repr;
1180 102 : if (repr != nullptr)
1181 102 : return_type = repr;
1182 102 : is_enum = adt.is_enum ();
1183 : }
1184 :
1185 102 : tree lookup = NULL_TREE;
1186 102 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1187 0 : return lookup;
1188 :
1189 102 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1190 :
1191 102 : std::vector<Bvariable *> param_vars;
1192 102 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1193 :
1194 102 : if (!Backend::function_set_parameters (fndecl, param_vars))
1195 0 : return error_mark_node;
1196 :
1197 102 : enter_intrinsic_block (ctx, fndecl);
1198 :
1199 : // BUILTIN disriminant_value FN BODY BEGIN
1200 :
1201 102 : tree result = integer_zero_node;
1202 102 : if (is_enum)
1203 : {
1204 102 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1205 102 : tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
1206 102 : result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
1207 : }
1208 :
1209 102 : auto return_statement
1210 102 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1211 102 : ctx->add_statement (return_statement);
1212 :
1213 : // BUILTIN disriminant_value FN BODY END
1214 :
1215 102 : finalize_intrinsic_block (ctx, fndecl);
1216 :
1217 102 : return fndecl;
1218 102 : }
1219 :
1220 : tree
1221 7 : variant_count (Context *ctx, TyTy::FnType *fntype, location_t)
1222 : {
1223 7 : rust_assert (fntype->get_num_type_params () == 1);
1224 7 : auto &mapping = fntype->get_substs ().at (0);
1225 7 : auto param_ty = mapping.get_param_ty ();
1226 7 : rust_assert (param_ty->can_resolve ());
1227 7 : auto resolved = param_ty->resolve ();
1228 :
1229 7 : size_t variant_count = 0;
1230 7 : bool is_adt = resolved->is<TyTy::ADTType> ();
1231 7 : if (is_adt)
1232 : {
1233 7 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1234 7 : variant_count = adt.number_of_variants ();
1235 : }
1236 :
1237 7 : tree lookup = NULL_TREE;
1238 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1239 0 : return lookup;
1240 :
1241 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1242 :
1243 7 : std::vector<Bvariable *> param_vars;
1244 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1245 :
1246 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
1247 0 : return error_mark_node;
1248 :
1249 7 : enter_intrinsic_block (ctx, fndecl);
1250 :
1251 : // BUILTIN disriminant_value FN BODY BEGIN
1252 7 : tree result_decl = DECL_RESULT (fndecl);
1253 7 : tree type = TREE_TYPE (result_decl);
1254 :
1255 7 : mpz_t ival;
1256 7 : mpz_init_set_ui (ival, variant_count);
1257 7 : tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
1258 7 : mpz_clear (ival);
1259 :
1260 7 : auto return_statement
1261 7 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1262 7 : ctx->add_statement (return_statement);
1263 :
1264 : // BUILTIN disriminant_value FN BODY END
1265 :
1266 7 : finalize_intrinsic_block (ctx, fndecl);
1267 :
1268 7 : return fndecl;
1269 7 : }
1270 :
1271 : tree
1272 26 : move_val_init (Context *ctx, TyTy::FnType *fntype, location_t)
1273 : {
1274 26 : rust_assert (fntype->get_params ().size () == 2);
1275 :
1276 26 : tree lookup = NULL_TREE;
1277 26 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1278 0 : return lookup;
1279 :
1280 26 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1281 :
1282 : // Most intrinsic functions are pure - not `move_val_init`
1283 26 : TREE_READONLY (fndecl) = 0;
1284 26 : TREE_SIDE_EFFECTS (fndecl) = 1;
1285 :
1286 : // get the template parameter type tree fn size_of<T>();
1287 26 : rust_assert (fntype->get_num_substitutions () == 1);
1288 26 : auto ¶m_mapping = fntype->get_substs ().at (0);
1289 26 : auto param_tyty = param_mapping.get_param_ty ();
1290 26 : auto resolved_tyty = param_tyty->resolve ();
1291 26 : tree template_parameter_type
1292 26 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1293 :
1294 26 : std::vector<Bvariable *> param_vars;
1295 26 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1296 :
1297 26 : if (!Backend::function_set_parameters (fndecl, param_vars))
1298 0 : return error_mark_node;
1299 :
1300 26 : enter_intrinsic_block (ctx, fndecl);
1301 :
1302 : // BUILTIN size_of FN BODY BEGIN
1303 :
1304 26 : tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1305 26 : tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1306 26 : tree size = TYPE_SIZE_UNIT (template_parameter_type);
1307 :
1308 26 : tree memcpy_builtin = error_mark_node;
1309 26 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
1310 : &memcpy_builtin);
1311 26 : rust_assert (memcpy_builtin != error_mark_node);
1312 :
1313 26 : src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1314 26 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1315 : dst, src, size);
1316 :
1317 26 : ctx->add_statement (memset_call);
1318 : // BUILTIN size_of FN BODY END
1319 :
1320 26 : finalize_intrinsic_block (ctx, fndecl);
1321 :
1322 26 : return fndecl;
1323 26 : }
1324 :
1325 : tree
1326 92 : uninit (Context *ctx, TyTy::FnType *fntype, location_t)
1327 : {
1328 : // uninit has _zero_ parameters its parameter is the generic one
1329 92 : rust_assert (fntype->get_params ().size () == 0);
1330 :
1331 92 : tree lookup = NULL_TREE;
1332 92 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1333 0 : return lookup;
1334 :
1335 92 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1336 :
1337 : // Most intrinsic functions are pure - not `uninit_handler`
1338 92 : TREE_READONLY (fndecl) = 0;
1339 92 : TREE_SIDE_EFFECTS (fndecl) = 1;
1340 :
1341 : // get the template parameter type tree fn uninit<T>();
1342 92 : rust_assert (fntype->get_num_substitutions () == 1);
1343 92 : auto ¶m_mapping = fntype->get_substs ().at (0);
1344 92 : const auto param_tyty = param_mapping.get_param_ty ();
1345 92 : auto resolved_tyty = param_tyty->resolve ();
1346 92 : tree template_parameter_type
1347 92 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1348 :
1349 : // result temporary
1350 92 : tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
1351 92 : rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
1352 : == TYPE_SIZE_UNIT (dst_type));
1353 :
1354 92 : tree tmp_stmt = error_mark_node;
1355 92 : Bvariable *bvar
1356 92 : = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
1357 : true /*address_is_taken*/, UNDEF_LOCATION,
1358 : &tmp_stmt);
1359 :
1360 92 : enter_intrinsic_block (ctx, fndecl, {bvar});
1361 :
1362 : // BUILTIN size_of FN BODY BEGIN
1363 :
1364 92 : tree memset_builtin = error_mark_node;
1365 92 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
1366 : &memset_builtin);
1367 92 : rust_assert (memset_builtin != error_mark_node);
1368 :
1369 : // call memset with 0x01 and size of the thing see
1370 : // https://github.com/Rust-GCC/gccrs/issues/1899
1371 :
1372 92 : tree dst = bvar->get_tree (BUILTINS_LOCATION);
1373 92 : tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
1374 92 : tree constant_byte = build_int_cst (integer_type_node, 0x01);
1375 92 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1376 :
1377 92 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
1378 : dst_addr, constant_byte, size_expr);
1379 92 : ctx->add_statement (memset_call);
1380 :
1381 92 : auto return_statement
1382 92 : = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
1383 92 : ctx->add_statement (return_statement);
1384 : // BUILTIN size_of FN BODY END
1385 :
1386 92 : finalize_intrinsic_block (ctx, fndecl);
1387 :
1388 92 : return fndecl;
1389 : }
1390 :
1391 : tree
1392 7 : prefetch_read_data (Context *ctx, TyTy::FnType *fntype, location_t)
1393 : {
1394 7 : return prefetch_data (ctx, fntype, Prefetch::Read);
1395 : }
1396 : tree
1397 7 : prefetch_write_data (Context *ctx, TyTy::FnType *fntype, location_t)
1398 : {
1399 7 : return prefetch_data (ctx, fntype, Prefetch::Write);
1400 : }
1401 :
1402 : tree
1403 14 : prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
1404 : {
1405 14 : rust_assert (fntype->get_params ().size () == 2);
1406 :
1407 14 : tree lookup = NULL_TREE;
1408 14 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1409 0 : return lookup;
1410 :
1411 14 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1412 :
1413 : // prefetching isn't pure and shouldn't be discarded after GIMPLE
1414 14 : TREE_READONLY (fndecl) = 0;
1415 14 : TREE_SIDE_EFFECTS (fndecl) = 1;
1416 :
1417 14 : std::vector<Bvariable *> args;
1418 14 : compile_fn_params (ctx, fntype, fndecl, &args);
1419 :
1420 14 : if (!Backend::function_set_parameters (fndecl, args))
1421 0 : return error_mark_node;
1422 :
1423 14 : enter_intrinsic_block (ctx, fndecl);
1424 :
1425 14 : auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
1426 :
1427 : // The core library technically allows you to pass any i32 value as a
1428 : // locality, but LLVM will then complain if the value cannot be constant
1429 : // evaluated. For now, we ignore the locality argument and instead always
1430 : // pass `3` (the most restrictive value). This allows us to still have
1431 : // prefetch behavior, just not as granular as expected. In future Rust
1432 : // versions, we hope that prefetch intrinsics will be split up according to
1433 : // locality, similarly to atomic intrinsics.
1434 : // The solution is to try and perform constant folding for the locality
1435 : // argument, or instead of creating a new function definition, modify the call
1436 : // site directly This has the bad side-effect of creating warnings about
1437 : // `unused name - locality`, which we hack away here:
1438 : // TODO: Take care of handling locality properly
1439 14 : Backend::var_expression (args[1], UNDEF_LOCATION);
1440 :
1441 14 : auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
1442 :
1443 14 : auto prefetch_raw = NULL_TREE;
1444 14 : auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
1445 : &prefetch_raw);
1446 14 : rust_assert (ok);
1447 14 : auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
1448 :
1449 28 : auto prefetch_call = Backend::call_expression (prefetch,
1450 : {addr, rw_flag,
1451 : // locality arg
1452 14 : make_unsigned_long_tree (3)},
1453 : nullptr, UNDEF_LOCATION);
1454 :
1455 14 : TREE_READONLY (prefetch_call) = 0;
1456 14 : TREE_SIDE_EFFECTS (prefetch_call) = 1;
1457 :
1458 14 : ctx->add_statement (prefetch_call);
1459 :
1460 14 : finalize_intrinsic_block (ctx, fndecl);
1461 :
1462 14 : return fndecl;
1463 14 : }
1464 :
1465 : tree
1466 353 : rotate (Context *ctx, TyTy::FnType *fntype, tree_code op)
1467 : {
1468 : // rotate intrinsic has two parameter
1469 353 : rust_assert (fntype->get_params ().size () == 2);
1470 :
1471 353 : tree lookup = NULL_TREE;
1472 353 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1473 0 : return lookup;
1474 :
1475 353 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1476 :
1477 : // setup the params
1478 353 : std::vector<Bvariable *> param_vars;
1479 353 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1480 :
1481 353 : auto &x_param = param_vars.at (0);
1482 353 : auto &y_param = param_vars.at (1);
1483 353 : rust_assert (param_vars.size () == 2);
1484 353 : if (!Backend::function_set_parameters (fndecl, param_vars))
1485 0 : return error_mark_node;
1486 :
1487 353 : enter_intrinsic_block (ctx, fndecl);
1488 :
1489 : // BUILTIN rotate FN BODY BEGIN
1490 353 : tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
1491 353 : tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
1492 353 : tree rotate_expr
1493 353 : = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
1494 353 : auto return_statement
1495 353 : = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
1496 353 : ctx->add_statement (return_statement);
1497 : // BUILTIN rotate FN BODY END
1498 :
1499 353 : finalize_intrinsic_block (ctx, fndecl);
1500 :
1501 353 : return fndecl;
1502 353 : }
1503 :
1504 : tree
1505 191 : transmute (Context *ctx, TyTy::FnType *fntype, location_t)
1506 : {
1507 : // transmute intrinsic has one parameter
1508 191 : rust_assert (fntype->get_params ().size () == 1);
1509 :
1510 191 : tree lookup = NULL_TREE;
1511 191 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1512 0 : return lookup;
1513 :
1514 191 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1515 :
1516 191 : std::vector<Bvariable *> param_vars;
1517 191 : std::vector<tree_node *> compiled_types;
1518 191 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &compiled_types);
1519 :
1520 191 : if (!Backend::function_set_parameters (fndecl, param_vars))
1521 0 : return error_mark_node;
1522 :
1523 : // param to convert
1524 191 : Bvariable *convert_me_param = param_vars.at (0);
1525 191 : tree convert_me_expr
1526 191 : = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
1527 :
1528 : // check for transmute pre-conditions
1529 191 : tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
1530 191 : tree source_type_expr = compiled_types.at (0);
1531 191 : tree target_size_expr = TYPE_SIZE (target_type_expr);
1532 191 : tree source_size_expr = TYPE_SIZE (source_type_expr);
1533 : // for some reason, unit types and other zero-sized types return NULL for the
1534 : // size expressions
1535 191 : unsigned HOST_WIDE_INT target_size
1536 191 : = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
1537 191 : unsigned HOST_WIDE_INT source_size
1538 191 : = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
1539 :
1540 : // size check for concrete types
1541 : // TODO(liushuyu): check alignment for pointers; check for dependently-sized
1542 : // types
1543 191 : if (target_size != source_size)
1544 : {
1545 7 : rust_error_at (fntype->get_locus (),
1546 : "cannot transmute between types of different sizes, or "
1547 : "dependently-sized types");
1548 14 : rust_inform (
1549 7 : fntype->get_ident ().locus, "source type: %qs (%lu bits)",
1550 7 : fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
1551 : (unsigned long) source_size);
1552 7 : rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
1553 14 : fntype->get_return_type ()->as_string ().c_str (),
1554 : (unsigned long) target_size);
1555 : }
1556 :
1557 191 : enter_intrinsic_block (ctx, fndecl);
1558 :
1559 : // BUILTIN transmute FN BODY BEGIN
1560 :
1561 : // Return *((orig_type*)&decl) */
1562 :
1563 191 : tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
1564 191 : t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
1565 : build_pointer_type (target_type_expr), t);
1566 191 : tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
1567 :
1568 191 : auto return_statement
1569 191 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
1570 191 : ctx->add_statement (return_statement);
1571 : // BUILTIN transmute FN BODY END
1572 :
1573 191 : finalize_intrinsic_block (ctx, fndecl);
1574 :
1575 191 : return fndecl;
1576 191 : }
1577 :
1578 : tree
1579 474 : sizeof_handler (Context *ctx, TyTy::FnType *fntype, location_t)
1580 : {
1581 : // size_of has _zero_ parameters its parameter is the generic one
1582 474 : rust_assert (fntype->get_params ().size () == 0);
1583 :
1584 474 : tree lookup = NULL_TREE;
1585 474 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1586 0 : return lookup;
1587 :
1588 474 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1589 :
1590 : // get the template parameter type tree fn size_of<T>();
1591 474 : rust_assert (fntype->get_num_substitutions () == 1);
1592 474 : auto ¶m_mapping = fntype->get_substs ().at (0);
1593 474 : const auto param_tyty = param_mapping.get_param_ty ();
1594 474 : auto resolved_tyty = param_tyty->resolve ();
1595 474 : tree template_parameter_type
1596 474 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1597 :
1598 474 : enter_intrinsic_block (ctx, fndecl);
1599 :
1600 : // BUILTIN size_of FN BODY BEGIN
1601 474 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1602 474 : auto return_statement
1603 474 : = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
1604 474 : ctx->add_statement (return_statement);
1605 : // BUILTIN size_of FN BODY END
1606 :
1607 474 : finalize_intrinsic_block (ctx, fndecl);
1608 :
1609 474 : return fndecl;
1610 : }
1611 :
1612 : /**
1613 : * pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
1614 : */
1615 : tree
1616 7 : size_of_val_handler (Context *ctx, TyTy::FnType *fntype, location_t)
1617 : {
1618 7 : rust_assert (fntype->get_params ().size () == 1);
1619 :
1620 7 : tree lookup = NULL_TREE;
1621 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1622 0 : return lookup;
1623 :
1624 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1625 :
1626 7 : auto locus = fntype->get_locus ();
1627 :
1628 7 : std::vector<Bvariable *> param_vars;
1629 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1630 :
1631 7 : auto &__param = param_vars.at (0);
1632 7 : rust_assert (param_vars.size () == 1);
1633 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
1634 0 : return error_mark_node;
1635 :
1636 7 : rust_assert (fntype->get_num_substitutions () == 1);
1637 7 : auto ¶m_mapping = fntype->get_substs ().at (0);
1638 7 : const auto param_tyty = param_mapping.get_param_ty ();
1639 7 : auto resolved_tyty = param_tyty->resolve ();
1640 7 : tree template_parameter_type
1641 7 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1642 :
1643 7 : enter_intrinsic_block (ctx, fndecl);
1644 :
1645 : // BUILTIN size_of FN BODY BEGIN
1646 :
1647 7 : tree size_expr = NULL_TREE;
1648 7 : if (RS_DST_FLAG_P (template_parameter_type))
1649 : {
1650 4 : tree param = Backend::var_expression (__param, UNDEF_LOCATION);
1651 4 : tree param_ty = TREE_TYPE (param);
1652 4 : tree data_field = TYPE_FIELDS (param_ty);
1653 4 : tree meta_field = DECL_CHAIN (data_field);
1654 4 : tree meta_field_expr
1655 4 : = build3_loc (locus, COMPONENT_REF, TREE_TYPE (meta_field), param,
1656 : meta_field, NULL_TREE);
1657 :
1658 4 : if (resolved_tyty->get_kind () == TyTy::TypeKind::SLICE
1659 4 : || resolved_tyty->get_kind () == TyTy::TypeKind::STR)
1660 : {
1661 2 : tree elem_type = NULL_TREE;
1662 2 : if (resolved_tyty->get_kind () == TyTy::TypeKind::SLICE)
1663 : {
1664 1 : auto slice_tyty = static_cast<TyTy::SliceType *> (resolved_tyty);
1665 1 : elem_type
1666 1 : = TyTyResolveCompile::compile (ctx,
1667 1 : slice_tyty->get_element_type ());
1668 : }
1669 : else
1670 1 : elem_type = char_type_node;
1671 :
1672 2 : tree elem_size = TYPE_SIZE_UNIT (elem_type);
1673 2 : size_expr
1674 2 : = build2_loc (locus, MULT_EXPR, size_type_node,
1675 : fold_convert_loc (locus, size_type_node,
1676 : meta_field_expr),
1677 : fold_convert_loc (locus, size_type_node, elem_size));
1678 : }
1679 2 : else if (resolved_tyty->get_kind () == TyTy::TypeKind::DYNAMIC)
1680 : {
1681 2 : tree vtable_ptr_ty = TREE_TYPE (meta_field_expr);
1682 2 : tree vtable_ty = TREE_TYPE (vtable_ptr_ty);
1683 :
1684 2 : tree vtable_ref
1685 2 : = build1_loc (locus, INDIRECT_REF, vtable_ty, meta_field_expr);
1686 :
1687 2 : tree vtable_field_0 = TYPE_FIELDS (vtable_ty);
1688 2 : tree vtable_field_size = DECL_CHAIN (vtable_field_0);
1689 2 : rust_assert (vtable_field_size != NULL_TREE);
1690 :
1691 2 : size_expr
1692 2 : = build3_loc (locus, COMPONENT_REF, TREE_TYPE (vtable_field_size),
1693 : vtable_ref, vtable_field_size, NULL_TREE);
1694 : }
1695 : else
1696 : {
1697 0 : rust_unreachable ();
1698 : }
1699 : }
1700 : else
1701 3 : size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1702 :
1703 7 : auto return_statement
1704 7 : = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
1705 7 : ctx->add_statement (return_statement);
1706 :
1707 : // BUILTIN size_of FN BODY END
1708 :
1709 7 : finalize_intrinsic_block (ctx, fndecl);
1710 :
1711 7 : return fndecl;
1712 7 : }
1713 :
1714 : /**
1715 : * pub fn min_align_of<T>() -> usize;
1716 : */
1717 : tree
1718 2 : min_align_of_handler (Context *ctx, TyTy::FnType *fntype, location_t)
1719 : {
1720 : // min_align_of has _zero_ parameters its parameter is the generic one
1721 2 : rust_assert (fntype->get_params ().size () == 0);
1722 :
1723 2 : tree lookup = NULL_TREE;
1724 2 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1725 0 : return lookup;
1726 :
1727 2 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1728 :
1729 : // get the template parameter type tree fn min_align_of<T>();
1730 2 : rust_assert (fntype->get_num_substitutions () == 1);
1731 2 : auto ¶m_mapping = fntype->get_substs ().at (0);
1732 2 : const auto param_tyty = param_mapping.get_param_ty ();
1733 2 : auto resolved_tyty = param_tyty->resolve ();
1734 2 : tree template_parameter_type
1735 2 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1736 :
1737 2 : enter_intrinsic_block (ctx, fndecl);
1738 :
1739 : // BUILTIN min_align_of FN BODY BEGIN
1740 2 : tree align_expr
1741 2 : = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (template_parameter_type));
1742 :
1743 2 : auto return_statement
1744 2 : = Backend::return_statement (fndecl, align_expr, UNDEF_LOCATION);
1745 2 : ctx->add_statement (return_statement);
1746 : // BUILTIN min_align_of FN BODY END
1747 :
1748 2 : finalize_intrinsic_block (ctx, fndecl);
1749 :
1750 2 : return fndecl;
1751 : }
1752 :
1753 : /**
1754 : * pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
1755 : */
1756 : tree
1757 7 : min_align_of_val_handler (Context *ctx, TyTy::FnType *fntype, location_t)
1758 : {
1759 7 : rust_assert (fntype->get_params ().size () == 1);
1760 :
1761 7 : tree lookup = NULL_TREE;
1762 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1763 0 : return lookup;
1764 :
1765 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1766 :
1767 7 : auto locus = fntype->get_locus ();
1768 :
1769 7 : std::vector<Bvariable *> param_vars;
1770 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1771 :
1772 7 : auto &__param = param_vars.at (0);
1773 7 : rust_assert (param_vars.size () == 1);
1774 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
1775 0 : return error_mark_node;
1776 :
1777 7 : rust_assert (fntype->get_num_substitutions () == 1);
1778 7 : auto ¶m_mapping = fntype->get_substs ().at (0);
1779 7 : const auto param_tyty = param_mapping.get_param_ty ();
1780 7 : auto resolved_tyty = param_tyty->resolve ();
1781 7 : tree template_parameter_type
1782 7 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1783 :
1784 7 : enter_intrinsic_block (ctx, fndecl);
1785 :
1786 : // BUILTIN size_of FN BODY BEGIN
1787 :
1788 7 : tree align_expr = NULL_TREE;
1789 7 : if (RS_DST_FLAG_P (template_parameter_type))
1790 : {
1791 4 : tree param = Backend::var_expression (__param, UNDEF_LOCATION);
1792 4 : tree param_ty = TREE_TYPE (param);
1793 4 : tree data_field = TYPE_FIELDS (param_ty);
1794 4 : tree meta_field = DECL_CHAIN (data_field);
1795 4 : tree meta_field_expr
1796 4 : = build3_loc (locus, COMPONENT_REF, TREE_TYPE (meta_field), param,
1797 : meta_field, NULL_TREE);
1798 :
1799 4 : if (resolved_tyty->get_kind () == TyTy::TypeKind::SLICE
1800 4 : || resolved_tyty->get_kind () == TyTy::TypeKind::STR)
1801 : {
1802 2 : tree elem_type = NULL_TREE;
1803 2 : if (resolved_tyty->get_kind () == TyTy::TypeKind::SLICE)
1804 : {
1805 1 : auto slice_tyty = static_cast<TyTy::SliceType *> (resolved_tyty);
1806 1 : elem_type
1807 1 : = TyTyResolveCompile::compile (ctx,
1808 1 : slice_tyty->get_element_type ());
1809 : }
1810 : else
1811 1 : elem_type = char_type_node;
1812 :
1813 2 : align_expr
1814 2 : = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (elem_type));
1815 : }
1816 2 : else if (resolved_tyty->get_kind () == TyTy::TypeKind::DYNAMIC)
1817 : {
1818 2 : tree vtable_ptr_ty = TREE_TYPE (meta_field_expr);
1819 2 : tree vtable_ty = TREE_TYPE (vtable_ptr_ty);
1820 :
1821 2 : tree vtable_ref
1822 2 : = build1_loc (locus, INDIRECT_REF, vtable_ty, meta_field_expr);
1823 :
1824 2 : tree vtable_field_0 = TYPE_FIELDS (vtable_ty);
1825 2 : tree vtable_field_1 = DECL_CHAIN (vtable_field_0);
1826 2 : tree vtable_field_align = DECL_CHAIN (vtable_field_1);
1827 2 : rust_assert (vtable_field_align != NULL_TREE);
1828 :
1829 2 : align_expr
1830 2 : = build3_loc (locus, COMPONENT_REF, TREE_TYPE (vtable_field_align),
1831 : vtable_ref, vtable_field_align, NULL_TREE);
1832 : }
1833 : else
1834 : {
1835 0 : rust_unreachable ();
1836 : }
1837 : }
1838 : else
1839 3 : align_expr = build_int_cst (size_type_node,
1840 3 : TYPE_ALIGN_UNIT (template_parameter_type));
1841 :
1842 7 : auto return_statement
1843 7 : = Backend::return_statement (fndecl, align_expr, UNDEF_LOCATION);
1844 7 : ctx->add_statement (return_statement);
1845 :
1846 : // BUILTIN size_of FN BODY END
1847 :
1848 7 : finalize_intrinsic_block (ctx, fndecl);
1849 :
1850 7 : return fndecl;
1851 7 : }
1852 :
1853 : tree
1854 80 : offset (Context *ctx, TyTy::FnType *fntype, location_t expr_locus)
1855 : {
1856 : // offset intrinsic has two params dst pointer and offset isize
1857 80 : rust_assert (fntype->get_params ().size () == 2);
1858 :
1859 80 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1860 :
1861 80 : std::vector<Bvariable *> param_vars;
1862 80 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1863 :
1864 80 : auto &dst_param = param_vars.at (0);
1865 80 : auto &size_param = param_vars.at (1);
1866 80 : rust_assert (param_vars.size () == 2);
1867 80 : if (!Backend::function_set_parameters (fndecl, param_vars))
1868 0 : return error_mark_node;
1869 :
1870 80 : enter_intrinsic_block (ctx, fndecl);
1871 :
1872 : // BUILTIN offset FN BODY BEGIN
1873 80 : tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
1874 80 : tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
1875 80 : tree pointer_offset_expr = pointer_offset_expression (dst, size, expr_locus);
1876 80 : auto return_statement
1877 80 : = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
1878 80 : ctx->add_statement (return_statement);
1879 : // BUILTIN offset FN BODY END
1880 :
1881 80 : finalize_intrinsic_block (ctx, fndecl);
1882 :
1883 80 : return fndecl;
1884 80 : }
1885 :
1886 : /**
1887 : * pub const fn bswap<T: Copy>(x: T) -> T;
1888 : */
1889 : tree
1890 9 : bswap_handler (Context *ctx, TyTy::FnType *fntype, location_t)
1891 : {
1892 9 : rust_assert (fntype->get_params ().size () == 1);
1893 :
1894 9 : tree lookup = NULL_TREE;
1895 9 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1896 0 : return lookup;
1897 :
1898 9 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1899 :
1900 9 : auto locus = fntype->get_locus ();
1901 :
1902 9 : std::vector<Bvariable *> param_vars;
1903 9 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1904 :
1905 9 : auto &x_param = param_vars.at (0);
1906 9 : rust_assert (param_vars.size () == 1);
1907 9 : if (!Backend::function_set_parameters (fndecl, param_vars))
1908 0 : return error_mark_node;
1909 :
1910 9 : auto *monomorphized_type
1911 9 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
1912 :
1913 9 : auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
1914 9 : check_for_basic_integer_type ("bswap", call_locus, monomorphized_type);
1915 :
1916 9 : tree template_parameter_type
1917 9 : = TyTyResolveCompile::compile (ctx, monomorphized_type);
1918 :
1919 9 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1920 9 : unsigned HOST_WIDE_INT size = TREE_INT_CST_LOW (size_expr);
1921 :
1922 9 : enter_intrinsic_block (ctx, fndecl);
1923 :
1924 : // BUILTIN bswap FN BODY BEGIN
1925 :
1926 9 : auto expr_x = Backend::var_expression (x_param, locus);
1927 9 : tree result = NULL_TREE;
1928 :
1929 9 : if (size == 1)
1930 : {
1931 : result = expr_x;
1932 : }
1933 : else
1934 : {
1935 8 : tree target_type = NULL_TREE;
1936 8 : const char *builtin_name = nullptr;
1937 8 : switch (size)
1938 : {
1939 2 : case 2:
1940 2 : builtin_name = "__builtin_bswap16";
1941 2 : target_type = uint16_type_node;
1942 2 : break;
1943 2 : case 4:
1944 2 : builtin_name = "__builtin_bswap32";
1945 2 : target_type = uint32_type_node;
1946 2 : break;
1947 2 : case 8:
1948 2 : builtin_name = "__builtin_bswap64";
1949 2 : target_type = uint64_type_node;
1950 2 : break;
1951 2 : case 16:
1952 2 : builtin_name = "__builtin_bswap128";
1953 2 : target_type = uint128_type_node;
1954 2 : break;
1955 0 : default:
1956 0 : return error_mark_node;
1957 : }
1958 :
1959 8 : tree bswap_raw = nullptr;
1960 8 : auto ok = BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
1961 : &bswap_raw);
1962 :
1963 8 : if (ok)
1964 : {
1965 8 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1966 :
1967 8 : auto bswap_x = build1 (CONVERT_EXPR, target_type, expr_x);
1968 :
1969 8 : auto bswap_call
1970 8 : = Backend::call_expression (bswap_fn, {bswap_x}, NULL_TREE, locus);
1971 :
1972 8 : result = build1 (CONVERT_EXPR, template_parameter_type, bswap_call);
1973 : }
1974 : else
1975 : {
1976 0 : auto ok2 = BuiltinsContext::get ().lookup_simple_builtin (
1977 0 : "__builtin_bswap64", &bswap_raw);
1978 0 : rust_assert (ok2);
1979 :
1980 0 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1981 :
1982 0 : tree tmp_in_stmt = error_mark_node;
1983 0 : Bvariable *in_var
1984 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1985 : template_parameter_type, expr_x,
1986 : true, locus, &tmp_in_stmt);
1987 0 : ctx->add_statement (tmp_in_stmt);
1988 :
1989 0 : tree addr_x
1990 0 : = build_fold_addr_expr_loc (locus, in_var->get_tree (locus));
1991 0 : tree u64_ptr_type = build_pointer_type (uint64_type_node);
1992 :
1993 0 : tree low_ptr = fold_convert (u64_ptr_type, addr_x);
1994 0 : tree low = build_fold_indirect_ref_loc (locus, low_ptr);
1995 :
1996 0 : tree high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type, low_ptr,
1997 : size_int (8));
1998 0 : tree high = build_fold_indirect_ref_loc (locus, high_ptr);
1999 :
2000 0 : auto new_high
2001 0 : = Backend::call_expression (bswap_fn, {low}, NULL_TREE, locus);
2002 0 : auto new_low
2003 0 : = Backend::call_expression (bswap_fn, {high}, NULL_TREE, locus);
2004 :
2005 0 : tree tmp_stmt = error_mark_node;
2006 0 : Bvariable *result_var
2007 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
2008 : template_parameter_type, NULL_TREE,
2009 : true, locus, &tmp_stmt);
2010 0 : ctx->add_statement (tmp_stmt);
2011 :
2012 0 : tree addr_res
2013 0 : = build_fold_addr_expr_loc (locus, result_var->get_tree (locus));
2014 0 : tree res_ptr = fold_convert (u64_ptr_type, addr_res);
2015 :
2016 0 : tree store_low
2017 0 : = build2 (MODIFY_EXPR, void_type_node,
2018 : build_fold_indirect_ref_loc (locus, res_ptr), new_low);
2019 0 : ctx->add_statement (store_low);
2020 :
2021 0 : tree res_high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type,
2022 : res_ptr, size_int (8));
2023 0 : tree store_high
2024 0 : = build2 (MODIFY_EXPR, void_type_node,
2025 : build_fold_indirect_ref_loc (locus, res_high_ptr),
2026 : new_high);
2027 0 : ctx->add_statement (store_high);
2028 :
2029 0 : result = result_var->get_tree (locus);
2030 : }
2031 : }
2032 :
2033 9 : auto return_statement = Backend::return_statement (fndecl, result, locus);
2034 :
2035 9 : TREE_READONLY (result) = 1;
2036 :
2037 9 : ctx->add_statement (return_statement);
2038 :
2039 : // BUILTIN bswap FN BODY END
2040 :
2041 9 : finalize_intrinsic_block (ctx, fndecl);
2042 :
2043 9 : return fndecl;
2044 9 : }
2045 :
2046 : tree
2047 253 : ctlz_handler (Context *ctx, TyTy::FnType *fntype, location_t)
2048 : {
2049 253 : return inner::ctlz_handler (ctx, fntype, false);
2050 : }
2051 :
2052 : tree
2053 169 : ctlz_nonzero_handler (Context *ctx, TyTy::FnType *fntype, location_t)
2054 : {
2055 169 : return inner::ctlz_handler (ctx, fntype, true);
2056 : }
2057 :
2058 : tree
2059 169 : cttz_handler (Context *ctx, TyTy::FnType *fntype, location_t)
2060 : {
2061 169 : return inner::cttz_handler (ctx, fntype, false);
2062 : }
2063 :
2064 : tree
2065 113 : cttz_nonzero_handler (Context *ctx, TyTy::FnType *fntype, location_t)
2066 : {
2067 113 : return inner::cttz_handler (ctx, fntype, true);
2068 : }
2069 :
2070 : /**
2071 : * pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
2072 : */
2073 : tree
2074 1 : write_bytes_handler (Context *ctx, TyTy::FnType *fntype, location_t)
2075 : {
2076 1 : rust_assert (fntype->get_params ().size () == 3);
2077 :
2078 1 : tree lookup = NULL_TREE;
2079 1 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
2080 0 : return lookup;
2081 :
2082 1 : tree fndecl = compile_intrinsic_function (ctx, fntype);
2083 :
2084 1 : auto locus = fntype->get_locus ();
2085 :
2086 1 : std::vector<Bvariable *> param_vars;
2087 1 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
2088 :
2089 1 : auto &dst_param = param_vars.at (0);
2090 1 : auto &val_param = param_vars.at (1);
2091 1 : auto &count_param = param_vars.at (2);
2092 1 : rust_assert (param_vars.size () == 3);
2093 1 : if (!Backend::function_set_parameters (fndecl, param_vars))
2094 0 : return error_mark_node;
2095 :
2096 1 : auto *monomorphized_type
2097 1 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
2098 :
2099 1 : tree template_parameter_type
2100 1 : = TyTyResolveCompile::compile (ctx, monomorphized_type);
2101 1 : tree dst_size_expr = TYPE_SIZE_UNIT (template_parameter_type);
2102 1 : rust_assert (dst_size_expr != NULL_TREE);
2103 :
2104 1 : enter_intrinsic_block (ctx, fndecl);
2105 :
2106 : // BUILTIN WRITE_BYTES FN BODY START
2107 :
2108 1 : tree expr_dst = Backend::var_expression (dst_param, locus);
2109 1 : tree expr_val = Backend::var_expression (val_param, locus);
2110 1 : tree expr_count = Backend::var_expression (count_param, locus);
2111 :
2112 1 : tree expr_count_final
2113 1 : = fold_build2_loc (locus, MULT_EXPR, size_type_node,
2114 : fold_convert_loc (locus, size_type_node, expr_count),
2115 : fold_convert_loc (locus, size_type_node, dst_size_expr));
2116 :
2117 1 : tree write_bytes_raw = nullptr;
2118 : // void* memset( void* dest, int ch, size_t count );
2119 1 : bool ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
2120 : &write_bytes_raw);
2121 1 : rust_assert (ok);
2122 :
2123 1 : tree write_bytes_fn = build_fold_addr_expr_loc (locus, write_bytes_raw);
2124 1 : tree write_bytes_call
2125 1 : = Backend::call_expression (write_bytes_fn,
2126 : {expr_dst, expr_val, expr_count_final},
2127 : NULL_TREE, locus);
2128 :
2129 1 : ctx->add_statement (write_bytes_call);
2130 :
2131 : // BUILTIN WRITE_BYTES FN BODY END
2132 :
2133 1 : finalize_intrinsic_block (ctx, fndecl);
2134 :
2135 1 : TREE_READONLY (fndecl) = 0;
2136 :
2137 1 : return fndecl;
2138 1 : }
2139 :
2140 : /**
2141 : * pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
2142 : */
2143 : tree
2144 1 : arith_offset_handler (Context *ctx, TyTy::FnType *fntype, location_t expr_locus)
2145 : {
2146 1 : rust_assert (fntype->get_params ().size () == 2);
2147 :
2148 1 : auto fndecl = compile_intrinsic_function (ctx, fntype);
2149 :
2150 1 : auto locus = fntype->get_locus ();
2151 :
2152 1 : std::vector<Bvariable *> param_vars;
2153 1 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
2154 :
2155 1 : auto &dst_param = param_vars.at (0);
2156 1 : auto &size_param = param_vars.at (1);
2157 1 : rust_assert (param_vars.size () == 2);
2158 1 : if (!Backend::function_set_parameters (fndecl, param_vars))
2159 0 : return error_mark_node;
2160 :
2161 1 : enter_intrinsic_block (ctx, fndecl);
2162 :
2163 : // BUILTIN arith_offset FN BODY BEGIN
2164 :
2165 1 : tree dst = Backend::var_expression (dst_param, locus);
2166 1 : tree size = Backend::var_expression (size_param, locus);
2167 1 : tree pointer_offset_expr = pointer_offset_expression (dst, size, expr_locus);
2168 1 : auto return_statement
2169 1 : = Backend::return_statement (fndecl, pointer_offset_expr, locus);
2170 1 : ctx->add_statement (return_statement);
2171 :
2172 : // BUILTIN arith_offset FN BODY END
2173 :
2174 1 : finalize_intrinsic_block (ctx, fndecl);
2175 :
2176 1 : return fndecl;
2177 1 : }
2178 :
2179 : } // namespace handlers
2180 : } // namespace Compile
2181 : } // namespace Rust
|