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 2895 : maybe_override_ctx (TyTy::FnType *fntype)
69 : {
70 2895 : if (fntype->has_substitutions_defined ())
71 2892 : fntype->override_context ();
72 2895 : }
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 2823 : check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
96 : {
97 2823 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
98 2823 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
99 2823 : 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 2823 : }
107 :
108 : static tree
109 2895 : compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
110 : {
111 2895 : maybe_override_ctx (fntype);
112 :
113 2895 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
114 :
115 2895 : tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
116 2895 : std::string ir_symbol_name
117 2895 : = canonical_path.get () + fntype->subst_as_string ();
118 2895 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
119 :
120 2895 : unsigned int flags = 0;
121 2895 : tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
122 2895 : flags, fntype->get_ident ().locus);
123 :
124 2895 : TREE_PUBLIC (fndecl) = 0;
125 2895 : TREE_READONLY (fndecl) = 1;
126 2895 : DECL_ARTIFICIAL (fndecl) = 1;
127 2895 : DECL_EXTERNAL (fndecl) = 0;
128 2895 : DECL_DECLARED_INLINE_P (fndecl) = 1;
129 :
130 2895 : return fndecl;
131 2895 : }
132 :
133 : /**
134 : * Compile and setup a function's parameters
135 : */
136 : static void
137 2329 : 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 6183 : for (auto &parm : fntype->get_params ())
142 : {
143 3854 : auto &referenced_param = parm.get_pattern ();
144 3854 : auto param_tyty = parm.get_type ();
145 3854 : auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
146 :
147 3854 : location_t param_locus = referenced_param.get_locus ();
148 3854 : Bvariable *compiled_param_var
149 3854 : = CompileFnParam::compile (ctx, fndecl, referenced_param,
150 3854 : compiled_param_type, param_locus);
151 :
152 3854 : compiled_param_variables->push_back (compiled_param_var);
153 3854 : if (compiled_param_types)
154 359 : compiled_param_types->push_back (compiled_param_type);
155 : }
156 2329 : }
157 :
158 : static void
159 2891 : enter_intrinsic_block (Context *ctx, tree fndecl,
160 : const std::vector<Bvariable *> &vars = {})
161 : {
162 2891 : tree enclosing_scope = NULL_TREE;
163 2891 : location_t start_location = UNDEF_LOCATION;
164 2891 : location_t end_location = UNDEF_LOCATION;
165 :
166 2891 : auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
167 : end_location);
168 :
169 2891 : ctx->push_block (block);
170 2891 : }
171 :
172 : static void
173 2877 : finalize_intrinsic_block (Context *ctx, tree fndecl)
174 : {
175 2877 : tree bind_tree = ctx->pop_block ();
176 :
177 2877 : gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
178 :
179 2877 : DECL_SAVED_TREE (fndecl) = bind_tree;
180 :
181 2877 : ctx->push_function (fndecl);
182 :
183 2877 : DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
184 2877 : maybe_save_constexpr_fundef (fndecl);
185 2877 : }
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 14502 : op_with_overflow (tree_code op)
1031 : {
1032 14643 : return [op] (Context *ctx, TyTy::FnType *fntype) {
1033 141 : return inner::op_with_overflow (ctx, fntype, op);
1034 14502 : };
1035 : }
1036 :
1037 : tree
1038 198 : rotate_left (Context *ctx, TyTy::FnType *fntype)
1039 : {
1040 198 : return handlers::rotate (ctx, fntype, LROTATE_EXPR);
1041 : }
1042 :
1043 : tree
1044 155 : rotate_right (Context *ctx, TyTy::FnType *fntype)
1045 : {
1046 155 : return handlers::rotate (ctx, fntype, RROTATE_EXPR);
1047 : }
1048 :
1049 : const HandlerBuilder
1050 14502 : wrapping_op (tree_code op)
1051 : {
1052 14812 : return [op] (Context *ctx, TyTy::FnType *fntype) {
1053 310 : return inner::wrapping_op (ctx, fntype, op);
1054 14502 : };
1055 : }
1056 :
1057 : HandlerBuilder
1058 19336 : atomic_store (int ordering)
1059 : {
1060 19406 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
1061 70 : return inner::atomic_store (ctx, fntype, ordering);
1062 19336 : };
1063 : }
1064 :
1065 : HandlerBuilder
1066 19336 : atomic_load (int ordering)
1067 : {
1068 19364 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
1069 28 : return inner::atomic_load (ctx, fntype, ordering);
1070 19336 : };
1071 : }
1072 :
1073 : const HandlerBuilder
1074 33838 : unchecked_op (tree_code op)
1075 : {
1076 33894 : return [op] (Context *ctx, TyTy::FnType *fntype) {
1077 56 : return inner::unchecked_op (ctx, fntype, op);
1078 33838 : };
1079 : }
1080 :
1081 : const HandlerBuilder
1082 9668 : copy (bool overlaps)
1083 : {
1084 9911 : return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
1085 243 : return inner::copy (ctx, fntype, overlaps);
1086 9668 : };
1087 : }
1088 :
1089 : const HandlerBuilder
1090 9668 : expect (bool likely)
1091 : {
1092 9668 : return [likely] (Context *ctx, TyTy::FnType *fntype) {
1093 0 : return inner::expect (ctx, fntype, likely);
1094 9668 : };
1095 : }
1096 :
1097 : const HandlerBuilder
1098 9668 : try_handler (bool is_new_api)
1099 : {
1100 9670 : return [is_new_api] (Context *ctx, TyTy::FnType *fntype) {
1101 2 : return inner::try_handler (ctx, fntype, is_new_api);
1102 9668 : };
1103 : }
1104 :
1105 : tree
1106 0 : sorry (Context *ctx, TyTy::FnType *fntype)
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)
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)
1161 : {
1162 102 : rust_assert (fntype->get_params ().size () == 1);
1163 102 : rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
1164 102 : rust_assert (fntype->has_substitutions ());
1165 102 : rust_assert (fntype->get_num_type_params () == 1);
1166 102 : auto &mapping = fntype->get_substs ().at (0);
1167 102 : auto param_ty = mapping.get_param_ty ();
1168 102 : rust_assert (param_ty->can_resolve ());
1169 102 : auto resolved = param_ty->resolve ();
1170 102 : auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
1171 :
1172 102 : TyTy::BaseType *return_type = nullptr;
1173 102 : bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
1174 102 : rust_assert (ok);
1175 :
1176 102 : bool is_adt = resolved->is<TyTy::ADTType> ();
1177 102 : bool is_enum = false;
1178 102 : if (is_adt)
1179 : {
1180 102 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1181 102 : return_type = adt.get_repr_options ().repr;
1182 102 : rust_assert (return_type != nullptr);
1183 102 : is_enum = adt.is_enum ();
1184 : }
1185 :
1186 102 : p->set_associated_type (return_type->get_ref ());
1187 :
1188 102 : tree lookup = NULL_TREE;
1189 102 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1190 0 : return lookup;
1191 :
1192 102 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1193 :
1194 102 : std::vector<Bvariable *> param_vars;
1195 102 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1196 :
1197 102 : if (!Backend::function_set_parameters (fndecl, param_vars))
1198 0 : return error_mark_node;
1199 :
1200 102 : enter_intrinsic_block (ctx, fndecl);
1201 :
1202 : // BUILTIN disriminant_value FN BODY BEGIN
1203 :
1204 102 : tree result = integer_zero_node;
1205 102 : if (is_enum)
1206 : {
1207 102 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1208 102 : tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
1209 102 : result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
1210 : }
1211 :
1212 102 : auto return_statement
1213 102 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1214 102 : ctx->add_statement (return_statement);
1215 :
1216 : // BUILTIN disriminant_value FN BODY END
1217 :
1218 102 : finalize_intrinsic_block (ctx, fndecl);
1219 :
1220 102 : return fndecl;
1221 102 : }
1222 :
1223 : tree
1224 7 : variant_count (Context *ctx, TyTy::FnType *fntype)
1225 : {
1226 7 : rust_assert (fntype->get_num_type_params () == 1);
1227 7 : auto &mapping = fntype->get_substs ().at (0);
1228 7 : auto param_ty = mapping.get_param_ty ();
1229 7 : rust_assert (param_ty->can_resolve ());
1230 7 : auto resolved = param_ty->resolve ();
1231 :
1232 7 : size_t variant_count = 0;
1233 7 : bool is_adt = resolved->is<TyTy::ADTType> ();
1234 7 : if (is_adt)
1235 : {
1236 7 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1237 7 : variant_count = adt.number_of_variants ();
1238 : }
1239 :
1240 7 : tree lookup = NULL_TREE;
1241 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1242 0 : return lookup;
1243 :
1244 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1245 :
1246 7 : std::vector<Bvariable *> param_vars;
1247 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1248 :
1249 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
1250 0 : return error_mark_node;
1251 :
1252 7 : enter_intrinsic_block (ctx, fndecl);
1253 :
1254 : // BUILTIN disriminant_value FN BODY BEGIN
1255 7 : tree result_decl = DECL_RESULT (fndecl);
1256 7 : tree type = TREE_TYPE (result_decl);
1257 :
1258 7 : mpz_t ival;
1259 7 : mpz_init_set_ui (ival, variant_count);
1260 7 : tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
1261 7 : mpz_clear (ival);
1262 :
1263 7 : auto return_statement
1264 7 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1265 7 : ctx->add_statement (return_statement);
1266 :
1267 : // BUILTIN disriminant_value FN BODY END
1268 :
1269 7 : finalize_intrinsic_block (ctx, fndecl);
1270 :
1271 7 : return fndecl;
1272 7 : }
1273 :
1274 : tree
1275 26 : move_val_init (Context *ctx, TyTy::FnType *fntype)
1276 : {
1277 26 : rust_assert (fntype->get_params ().size () == 2);
1278 :
1279 26 : tree lookup = NULL_TREE;
1280 26 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1281 0 : return lookup;
1282 :
1283 26 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1284 :
1285 : // Most intrinsic functions are pure - not `move_val_init`
1286 26 : TREE_READONLY (fndecl) = 0;
1287 26 : TREE_SIDE_EFFECTS (fndecl) = 1;
1288 :
1289 : // get the template parameter type tree fn size_of<T>();
1290 26 : rust_assert (fntype->get_num_substitutions () == 1);
1291 26 : auto ¶m_mapping = fntype->get_substs ().at (0);
1292 26 : auto param_tyty = param_mapping.get_param_ty ();
1293 26 : auto resolved_tyty = param_tyty->resolve ();
1294 26 : tree template_parameter_type
1295 26 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1296 :
1297 26 : std::vector<Bvariable *> param_vars;
1298 26 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1299 :
1300 26 : if (!Backend::function_set_parameters (fndecl, param_vars))
1301 0 : return error_mark_node;
1302 :
1303 26 : enter_intrinsic_block (ctx, fndecl);
1304 :
1305 : // BUILTIN size_of FN BODY BEGIN
1306 :
1307 26 : tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1308 26 : tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1309 26 : tree size = TYPE_SIZE_UNIT (template_parameter_type);
1310 :
1311 26 : tree memcpy_builtin = error_mark_node;
1312 26 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
1313 : &memcpy_builtin);
1314 26 : rust_assert (memcpy_builtin != error_mark_node);
1315 :
1316 26 : src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1317 26 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1318 : dst, src, size);
1319 :
1320 26 : ctx->add_statement (memset_call);
1321 : // BUILTIN size_of FN BODY END
1322 :
1323 26 : finalize_intrinsic_block (ctx, fndecl);
1324 :
1325 26 : return fndecl;
1326 26 : }
1327 :
1328 : tree
1329 92 : uninit (Context *ctx, TyTy::FnType *fntype)
1330 : {
1331 : // uninit has _zero_ parameters its parameter is the generic one
1332 92 : rust_assert (fntype->get_params ().size () == 0);
1333 :
1334 92 : tree lookup = NULL_TREE;
1335 92 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1336 0 : return lookup;
1337 :
1338 92 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1339 :
1340 : // Most intrinsic functions are pure - not `uninit_handler`
1341 92 : TREE_READONLY (fndecl) = 0;
1342 92 : TREE_SIDE_EFFECTS (fndecl) = 1;
1343 :
1344 : // get the template parameter type tree fn uninit<T>();
1345 92 : rust_assert (fntype->get_num_substitutions () == 1);
1346 92 : auto ¶m_mapping = fntype->get_substs ().at (0);
1347 92 : const auto param_tyty = param_mapping.get_param_ty ();
1348 92 : auto resolved_tyty = param_tyty->resolve ();
1349 92 : tree template_parameter_type
1350 92 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1351 :
1352 : // result temporary
1353 92 : tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
1354 92 : rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
1355 : == TYPE_SIZE_UNIT (dst_type));
1356 :
1357 92 : tree tmp_stmt = error_mark_node;
1358 92 : Bvariable *bvar
1359 92 : = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
1360 : true /*address_is_taken*/, UNDEF_LOCATION,
1361 : &tmp_stmt);
1362 :
1363 92 : enter_intrinsic_block (ctx, fndecl, {bvar});
1364 :
1365 : // BUILTIN size_of FN BODY BEGIN
1366 :
1367 92 : tree memset_builtin = error_mark_node;
1368 92 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
1369 : &memset_builtin);
1370 92 : rust_assert (memset_builtin != error_mark_node);
1371 :
1372 : // call memset with 0x01 and size of the thing see
1373 : // https://github.com/Rust-GCC/gccrs/issues/1899
1374 :
1375 92 : tree dst = bvar->get_tree (BUILTINS_LOCATION);
1376 92 : tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
1377 92 : tree constant_byte = build_int_cst (integer_type_node, 0x01);
1378 92 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1379 :
1380 92 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
1381 : dst_addr, constant_byte, size_expr);
1382 92 : ctx->add_statement (memset_call);
1383 :
1384 92 : auto return_statement
1385 92 : = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
1386 92 : ctx->add_statement (return_statement);
1387 : // BUILTIN size_of FN BODY END
1388 :
1389 92 : finalize_intrinsic_block (ctx, fndecl);
1390 :
1391 92 : return fndecl;
1392 : }
1393 :
1394 : tree
1395 7 : prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
1396 : {
1397 7 : return prefetch_data (ctx, fntype, Prefetch::Read);
1398 : }
1399 : tree
1400 7 : prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
1401 : {
1402 7 : return prefetch_data (ctx, fntype, Prefetch::Write);
1403 : }
1404 :
1405 : tree
1406 14 : prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
1407 : {
1408 14 : rust_assert (fntype->get_params ().size () == 2);
1409 :
1410 14 : tree lookup = NULL_TREE;
1411 14 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1412 0 : return lookup;
1413 :
1414 14 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1415 :
1416 : // prefetching isn't pure and shouldn't be discarded after GIMPLE
1417 14 : TREE_READONLY (fndecl) = 0;
1418 14 : TREE_SIDE_EFFECTS (fndecl) = 1;
1419 :
1420 14 : std::vector<Bvariable *> args;
1421 14 : compile_fn_params (ctx, fntype, fndecl, &args);
1422 :
1423 14 : if (!Backend::function_set_parameters (fndecl, args))
1424 0 : return error_mark_node;
1425 :
1426 14 : enter_intrinsic_block (ctx, fndecl);
1427 :
1428 14 : auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
1429 :
1430 : // The core library technically allows you to pass any i32 value as a
1431 : // locality, but LLVM will then complain if the value cannot be constant
1432 : // evaluated. For now, we ignore the locality argument and instead always
1433 : // pass `3` (the most restrictive value). This allows us to still have
1434 : // prefetch behavior, just not as granular as expected. In future Rust
1435 : // versions, we hope that prefetch intrinsics will be split up according to
1436 : // locality, similarly to atomic intrinsics.
1437 : // The solution is to try and perform constant folding for the locality
1438 : // argument, or instead of creating a new function definition, modify the call
1439 : // site directly This has the bad side-effect of creating warnings about
1440 : // `unused name - locality`, which we hack away here:
1441 : // TODO: Take care of handling locality properly
1442 14 : Backend::var_expression (args[1], UNDEF_LOCATION);
1443 :
1444 14 : auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
1445 :
1446 14 : auto prefetch_raw = NULL_TREE;
1447 14 : auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
1448 : &prefetch_raw);
1449 14 : rust_assert (ok);
1450 14 : auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
1451 :
1452 28 : auto prefetch_call = Backend::call_expression (prefetch,
1453 : {addr, rw_flag,
1454 : // locality arg
1455 14 : make_unsigned_long_tree (3)},
1456 : nullptr, UNDEF_LOCATION);
1457 :
1458 14 : TREE_READONLY (prefetch_call) = 0;
1459 14 : TREE_SIDE_EFFECTS (prefetch_call) = 1;
1460 :
1461 14 : ctx->add_statement (prefetch_call);
1462 :
1463 14 : finalize_intrinsic_block (ctx, fndecl);
1464 :
1465 14 : return fndecl;
1466 14 : }
1467 :
1468 : tree
1469 353 : rotate (Context *ctx, TyTy::FnType *fntype, tree_code op)
1470 : {
1471 : // rotate intrinsic has two parameter
1472 353 : rust_assert (fntype->get_params ().size () == 2);
1473 :
1474 353 : tree lookup = NULL_TREE;
1475 353 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1476 0 : return lookup;
1477 :
1478 353 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1479 :
1480 : // setup the params
1481 353 : std::vector<Bvariable *> param_vars;
1482 353 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1483 :
1484 353 : auto &x_param = param_vars.at (0);
1485 353 : auto &y_param = param_vars.at (1);
1486 353 : rust_assert (param_vars.size () == 2);
1487 353 : if (!Backend::function_set_parameters (fndecl, param_vars))
1488 0 : return error_mark_node;
1489 :
1490 353 : enter_intrinsic_block (ctx, fndecl);
1491 :
1492 : // BUILTIN rotate FN BODY BEGIN
1493 353 : tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
1494 353 : tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
1495 353 : tree rotate_expr
1496 353 : = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
1497 353 : auto return_statement
1498 353 : = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
1499 353 : ctx->add_statement (return_statement);
1500 : // BUILTIN rotate FN BODY END
1501 :
1502 353 : finalize_intrinsic_block (ctx, fndecl);
1503 :
1504 353 : return fndecl;
1505 353 : }
1506 :
1507 : tree
1508 191 : transmute (Context *ctx, TyTy::FnType *fntype)
1509 : {
1510 : // transmute intrinsic has one parameter
1511 191 : rust_assert (fntype->get_params ().size () == 1);
1512 :
1513 191 : tree lookup = NULL_TREE;
1514 191 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1515 0 : return lookup;
1516 :
1517 191 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1518 :
1519 191 : std::vector<Bvariable *> param_vars;
1520 191 : std::vector<tree_node *> compiled_types;
1521 191 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &compiled_types);
1522 :
1523 191 : if (!Backend::function_set_parameters (fndecl, param_vars))
1524 0 : return error_mark_node;
1525 :
1526 : // param to convert
1527 191 : Bvariable *convert_me_param = param_vars.at (0);
1528 191 : tree convert_me_expr
1529 191 : = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
1530 :
1531 : // check for transmute pre-conditions
1532 191 : tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
1533 191 : tree source_type_expr = compiled_types.at (0);
1534 191 : tree target_size_expr = TYPE_SIZE (target_type_expr);
1535 191 : tree source_size_expr = TYPE_SIZE (source_type_expr);
1536 : // for some reason, unit types and other zero-sized types return NULL for the
1537 : // size expressions
1538 191 : unsigned HOST_WIDE_INT target_size
1539 191 : = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
1540 191 : unsigned HOST_WIDE_INT source_size
1541 191 : = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
1542 :
1543 : // size check for concrete types
1544 : // TODO(liushuyu): check alignment for pointers; check for dependently-sized
1545 : // types
1546 191 : if (target_size != source_size)
1547 : {
1548 7 : rust_error_at (fntype->get_locus (),
1549 : "cannot transmute between types of different sizes, or "
1550 : "dependently-sized types");
1551 14 : rust_inform (
1552 7 : fntype->get_ident ().locus, "source type: %qs (%lu bits)",
1553 7 : fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
1554 : (unsigned long) source_size);
1555 7 : rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
1556 14 : fntype->get_return_type ()->as_string ().c_str (),
1557 : (unsigned long) target_size);
1558 : }
1559 :
1560 191 : enter_intrinsic_block (ctx, fndecl);
1561 :
1562 : // BUILTIN transmute FN BODY BEGIN
1563 :
1564 : // Return *((orig_type*)&decl) */
1565 :
1566 191 : tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
1567 191 : t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
1568 : build_pointer_type (target_type_expr), t);
1569 191 : tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
1570 :
1571 191 : auto return_statement
1572 191 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
1573 191 : ctx->add_statement (return_statement);
1574 : // BUILTIN transmute FN BODY END
1575 :
1576 191 : finalize_intrinsic_block (ctx, fndecl);
1577 :
1578 191 : return fndecl;
1579 191 : }
1580 :
1581 : tree
1582 474 : sizeof_handler (Context *ctx, TyTy::FnType *fntype)
1583 : {
1584 : // size_of has _zero_ parameters its parameter is the generic one
1585 474 : rust_assert (fntype->get_params ().size () == 0);
1586 :
1587 474 : tree lookup = NULL_TREE;
1588 474 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1589 0 : return lookup;
1590 :
1591 474 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1592 :
1593 : // get the template parameter type tree fn size_of<T>();
1594 474 : rust_assert (fntype->get_num_substitutions () == 1);
1595 474 : auto ¶m_mapping = fntype->get_substs ().at (0);
1596 474 : const auto param_tyty = param_mapping.get_param_ty ();
1597 474 : auto resolved_tyty = param_tyty->resolve ();
1598 474 : tree template_parameter_type
1599 474 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1600 :
1601 474 : enter_intrinsic_block (ctx, fndecl);
1602 :
1603 : // BUILTIN size_of FN BODY BEGIN
1604 474 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1605 474 : auto return_statement
1606 474 : = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
1607 474 : ctx->add_statement (return_statement);
1608 : // BUILTIN size_of FN BODY END
1609 :
1610 474 : finalize_intrinsic_block (ctx, fndecl);
1611 :
1612 474 : return fndecl;
1613 : }
1614 :
1615 : tree
1616 72 : offset (Context *ctx, TyTy::FnType *fntype)
1617 : {
1618 : // offset intrinsic has two params dst pointer and offset isize
1619 72 : rust_assert (fntype->get_params ().size () == 2);
1620 :
1621 72 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1622 :
1623 72 : std::vector<Bvariable *> param_vars;
1624 72 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1625 :
1626 72 : auto &dst_param = param_vars.at (0);
1627 72 : auto &size_param = param_vars.at (1);
1628 72 : rust_assert (param_vars.size () == 2);
1629 72 : if (!Backend::function_set_parameters (fndecl, param_vars))
1630 0 : return error_mark_node;
1631 :
1632 72 : enter_intrinsic_block (ctx, fndecl);
1633 :
1634 : // BUILTIN offset FN BODY BEGIN
1635 72 : tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
1636 72 : tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
1637 72 : tree pointer_offset_expr
1638 72 : = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
1639 72 : auto return_statement
1640 72 : = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
1641 72 : ctx->add_statement (return_statement);
1642 : // BUILTIN offset FN BODY END
1643 :
1644 72 : finalize_intrinsic_block (ctx, fndecl);
1645 :
1646 72 : return fndecl;
1647 72 : }
1648 :
1649 : /**
1650 : * pub const fn bswap<T: Copy>(x: T) -> T;
1651 : */
1652 : tree
1653 9 : bswap_handler (Context *ctx, TyTy::FnType *fntype)
1654 : {
1655 9 : rust_assert (fntype->get_params ().size () == 1);
1656 :
1657 9 : tree lookup = NULL_TREE;
1658 9 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1659 0 : return lookup;
1660 :
1661 9 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1662 :
1663 9 : auto locus = fntype->get_locus ();
1664 :
1665 9 : std::vector<Bvariable *> param_vars;
1666 9 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1667 :
1668 9 : auto &x_param = param_vars.at (0);
1669 9 : rust_assert (param_vars.size () == 1);
1670 9 : if (!Backend::function_set_parameters (fndecl, param_vars))
1671 0 : return error_mark_node;
1672 :
1673 9 : auto *monomorphized_type
1674 9 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
1675 :
1676 9 : auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
1677 9 : check_for_basic_integer_type ("bswap", call_locus, monomorphized_type);
1678 :
1679 9 : tree template_parameter_type
1680 9 : = TyTyResolveCompile::compile (ctx, monomorphized_type);
1681 :
1682 9 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1683 9 : unsigned HOST_WIDE_INT size = TREE_INT_CST_LOW (size_expr);
1684 :
1685 9 : enter_intrinsic_block (ctx, fndecl);
1686 :
1687 : // BUILTIN bswap FN BODY BEGIN
1688 :
1689 9 : auto expr_x = Backend::var_expression (x_param, locus);
1690 9 : tree result = NULL_TREE;
1691 :
1692 9 : if (size == 1)
1693 : {
1694 : result = expr_x;
1695 : }
1696 : else
1697 : {
1698 8 : tree target_type = NULL_TREE;
1699 8 : const char *builtin_name = nullptr;
1700 8 : switch (size)
1701 : {
1702 2 : case 2:
1703 2 : builtin_name = "__builtin_bswap16";
1704 2 : target_type = uint16_type_node;
1705 2 : break;
1706 2 : case 4:
1707 2 : builtin_name = "__builtin_bswap32";
1708 2 : target_type = uint32_type_node;
1709 2 : break;
1710 2 : case 8:
1711 2 : builtin_name = "__builtin_bswap64";
1712 2 : target_type = uint64_type_node;
1713 2 : break;
1714 2 : case 16:
1715 2 : builtin_name = "__builtin_bswap128";
1716 2 : target_type = uint128_type_node;
1717 2 : break;
1718 0 : default:
1719 0 : return error_mark_node;
1720 : }
1721 :
1722 8 : tree bswap_raw = nullptr;
1723 8 : auto ok = BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
1724 : &bswap_raw);
1725 :
1726 8 : if (ok)
1727 : {
1728 8 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1729 :
1730 8 : auto bswap_x = build1 (CONVERT_EXPR, target_type, expr_x);
1731 :
1732 8 : auto bswap_call
1733 8 : = Backend::call_expression (bswap_fn, {bswap_x}, NULL_TREE, locus);
1734 :
1735 8 : result = build1 (CONVERT_EXPR, template_parameter_type, bswap_call);
1736 : }
1737 : else
1738 : {
1739 0 : auto ok2 = BuiltinsContext::get ().lookup_simple_builtin (
1740 0 : "__builtin_bswap64", &bswap_raw);
1741 0 : rust_assert (ok2);
1742 :
1743 0 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1744 :
1745 0 : tree tmp_in_stmt = error_mark_node;
1746 0 : Bvariable *in_var
1747 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1748 : template_parameter_type, expr_x,
1749 : true, locus, &tmp_in_stmt);
1750 0 : ctx->add_statement (tmp_in_stmt);
1751 :
1752 0 : tree addr_x
1753 0 : = build_fold_addr_expr_loc (locus, in_var->get_tree (locus));
1754 0 : tree u64_ptr_type = build_pointer_type (uint64_type_node);
1755 :
1756 0 : tree low_ptr = fold_convert (u64_ptr_type, addr_x);
1757 0 : tree low = build_fold_indirect_ref_loc (locus, low_ptr);
1758 :
1759 0 : tree high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type, low_ptr,
1760 : size_int (8));
1761 0 : tree high = build_fold_indirect_ref_loc (locus, high_ptr);
1762 :
1763 0 : auto new_high
1764 0 : = Backend::call_expression (bswap_fn, {low}, NULL_TREE, locus);
1765 0 : auto new_low
1766 0 : = Backend::call_expression (bswap_fn, {high}, NULL_TREE, locus);
1767 :
1768 0 : tree tmp_stmt = error_mark_node;
1769 0 : Bvariable *result_var
1770 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1771 : template_parameter_type, NULL_TREE,
1772 : true, locus, &tmp_stmt);
1773 0 : ctx->add_statement (tmp_stmt);
1774 :
1775 0 : tree addr_res
1776 0 : = build_fold_addr_expr_loc (locus, result_var->get_tree (locus));
1777 0 : tree res_ptr = fold_convert (u64_ptr_type, addr_res);
1778 :
1779 0 : tree store_low
1780 0 : = build2 (MODIFY_EXPR, void_type_node,
1781 : build_fold_indirect_ref_loc (locus, res_ptr), new_low);
1782 0 : ctx->add_statement (store_low);
1783 :
1784 0 : tree res_high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type,
1785 : res_ptr, size_int (8));
1786 0 : tree store_high
1787 0 : = build2 (MODIFY_EXPR, void_type_node,
1788 : build_fold_indirect_ref_loc (locus, res_high_ptr),
1789 : new_high);
1790 0 : ctx->add_statement (store_high);
1791 :
1792 0 : result = result_var->get_tree (locus);
1793 : }
1794 : }
1795 :
1796 9 : auto return_statement = Backend::return_statement (fndecl, result, locus);
1797 :
1798 9 : TREE_READONLY (result) = 1;
1799 :
1800 9 : ctx->add_statement (return_statement);
1801 :
1802 : // BUILTIN bswap FN BODY END
1803 :
1804 9 : finalize_intrinsic_block (ctx, fndecl);
1805 :
1806 9 : return fndecl;
1807 9 : }
1808 :
1809 : tree
1810 253 : ctlz_handler (Context *ctx, TyTy::FnType *fntype)
1811 : {
1812 253 : return inner::ctlz_handler (ctx, fntype, false);
1813 : }
1814 :
1815 : tree
1816 169 : ctlz_nonzero_handler (Context *ctx, TyTy::FnType *fntype)
1817 : {
1818 169 : return inner::ctlz_handler (ctx, fntype, true);
1819 : }
1820 :
1821 : tree
1822 169 : cttz_handler (Context *ctx, TyTy::FnType *fntype)
1823 : {
1824 169 : return inner::cttz_handler (ctx, fntype, false);
1825 : }
1826 :
1827 : tree
1828 113 : cttz_nonzero_handler (Context *ctx, TyTy::FnType *fntype)
1829 : {
1830 113 : return inner::cttz_handler (ctx, fntype, true);
1831 : }
1832 :
1833 : } // namespace handlers
1834 : } // namespace Compile
1835 : } // namespace Rust
|