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