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 intrinsics 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", locus, operand_type))
222 14 : return "";
223 :
224 84 : result += type_size_str->second;
225 :
226 84 : return result;
227 98 : }
228 :
229 : inline tree
230 56 : unchecked_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
231 : {
232 56 : rust_assert (fntype->get_params ().size () == 2);
233 56 : rust_assert (fntype->get_num_substitutions () == 1);
234 :
235 56 : tree lookup = NULL_TREE;
236 56 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
237 0 : return lookup;
238 :
239 56 : auto fndecl = compile_intrinsic_function (ctx, fntype);
240 :
241 : // setup the params
242 56 : std::vector<Bvariable *> param_vars;
243 56 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
244 :
245 56 : if (!Backend::function_set_parameters (fndecl, param_vars))
246 0 : return error_mark_node;
247 :
248 56 : enter_intrinsic_block (ctx, fndecl);
249 :
250 : // BUILTIN unchecked_<op> BODY BEGIN
251 :
252 56 : auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
253 56 : auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
254 :
255 56 : auto *monomorphized_type
256 56 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
257 :
258 56 : check_for_basic_integer_type ("unchecked operation", fntype->get_locus (),
259 : monomorphized_type);
260 :
261 56 : auto expr = build2 (op, TREE_TYPE (x), x, y);
262 56 : auto return_statement
263 56 : = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
264 :
265 56 : ctx->add_statement (return_statement);
266 :
267 : // BUILTIN unchecked_<op> BODY END
268 :
269 56 : finalize_intrinsic_block (ctx, fndecl);
270 :
271 56 : return fndecl;
272 56 : }
273 :
274 : inline tree
275 0 : expect (Context *ctx, TyTy::FnType *fntype, bool likely)
276 : {
277 0 : rust_assert (fntype->get_params ().size () == 1);
278 :
279 0 : tree lookup = NULL_TREE;
280 0 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
281 0 : return lookup;
282 :
283 0 : auto fndecl = compile_intrinsic_function (ctx, fntype);
284 :
285 0 : enter_intrinsic_block (ctx, fndecl);
286 :
287 : // BUILTIN expect_handler_inner FN BODY BEGIN
288 : // setup the params
289 0 : std::vector<Bvariable *> param_vars;
290 0 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
291 0 : tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
292 0 : tree expect_fn_raw = nullptr;
293 0 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
294 : &expect_fn_raw);
295 0 : rust_assert (expect_fn_raw);
296 0 : auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
297 :
298 : // we need to convert the expression return type to long to match the expected
299 : // parameter type of __builtin_expect
300 0 : auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
301 0 : auto expect_value
302 0 : = make_unsigned_long_tree (static_cast<unsigned long> (likely));
303 :
304 0 : auto expect_call
305 0 : = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
306 : BUILTINS_LOCATION);
307 : // the return value also needs to be casted (to bool)
308 0 : auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, expect_call);
309 0 : auto return_statement
310 0 : = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
311 0 : ctx->add_statement (return_statement);
312 : // BUILTIN expect_handler_inner FN BODY END
313 :
314 0 : finalize_intrinsic_block (ctx, fndecl);
315 :
316 0 : return fndecl;
317 0 : }
318 :
319 : tree
320 2 : try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
321 : {
322 2 : rust_assert (fntype->get_params ().size () == 3);
323 :
324 2 : tree lookup = NULL_TREE;
325 2 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
326 0 : return lookup;
327 2 : auto fndecl = compile_intrinsic_function (ctx, fntype);
328 :
329 2 : enter_intrinsic_block (ctx, fndecl);
330 :
331 : // The following tricks are needed to make sure the try-catch blocks are not
332 : // optimized away
333 2 : TREE_READONLY (fndecl) = 0;
334 2 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
335 2 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
336 2 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
337 :
338 : // BUILTIN try_handler FN BODY BEGIN
339 : // setup the params
340 2 : std::vector<Bvariable *> param_vars;
341 2 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
342 2 : if (!Backend::function_set_parameters (fndecl, param_vars))
343 0 : return error_mark_node;
344 2 : tree enclosing_scope = NULL_TREE;
345 :
346 2 : bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
347 2 : == CompileOptions::PanicStrategy::Abort;
348 2 : tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
349 2 : tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
350 2 : tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
351 2 : tree normal_return_stmt = NULL_TREE;
352 2 : tree error_return_stmt = NULL_TREE;
353 2 : tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
354 : BUILTINS_LOCATION);
355 2 : tree catch_call = NULL_TREE;
356 2 : tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
357 : UNDEF_LOCATION);
358 :
359 2 : if (is_new_api)
360 : {
361 1 : auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
362 1 : auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
363 : UNDEF_LOCATION);
364 1 : normal_return_stmt
365 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
366 1 : error_return_stmt
367 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
368 : }
369 : else
370 : {
371 1 : normal_return_stmt = Backend::return_statement (fndecl, integer_zero_node,
372 : BUILTINS_LOCATION);
373 1 : error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
374 : BUILTINS_LOCATION);
375 : }
376 2 : Backend::block_add_statements (try_block,
377 2 : std::vector<tree>{try_call,
378 2 : normal_return_stmt});
379 2 : if (panic_is_abort)
380 : {
381 : // skip building the try-catch construct
382 0 : ctx->add_statement (try_block);
383 0 : finalize_intrinsic_block (ctx, fndecl);
384 0 : return fndecl;
385 : }
386 :
387 2 : tree eh_pointer
388 2 : = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
389 : integer_zero_node);
390 2 : catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
391 : NULL_TREE, BUILTINS_LOCATION);
392 :
393 2 : tree catch_block = Backend::block (fndecl, enclosing_scope, {},
394 : UNDEF_LOCATION, UNDEF_LOCATION);
395 2 : Backend::block_add_statements (catch_block,
396 2 : std::vector<tree>{catch_call,
397 2 : error_return_stmt});
398 : // emulate what cc1plus is doing for C++ try-catch
399 2 : tree inner_eh_construct
400 2 : = Backend::exception_handler_statement (catch_call, NULL_TREE,
401 : error_return_stmt,
402 : BUILTINS_LOCATION);
403 : // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
404 2 : auto eh_construct
405 2 : = Backend::exception_handler_statement (try_block, inner_eh_construct,
406 : NULL_TREE, BUILTINS_LOCATION);
407 2 : ctx->add_statement (eh_construct);
408 : // BUILTIN try_handler FN BODY END
409 2 : finalize_intrinsic_block (ctx, fndecl);
410 :
411 2 : return fndecl;
412 2 : }
413 :
414 : /**
415 : * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
416 : */
417 : tree
418 310 : wrapping_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
419 : {
420 : // wrapping_<op> intrinsics have two parameter
421 310 : rust_assert (fntype->get_params ().size () == 2);
422 :
423 310 : tree lookup = NULL_TREE;
424 310 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
425 0 : return lookup;
426 :
427 310 : auto fndecl = compile_intrinsic_function (ctx, fntype);
428 :
429 : // setup the params
430 310 : std::vector<Bvariable *> param_vars;
431 310 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
432 :
433 310 : auto &lhs_param = param_vars.at (0);
434 310 : auto &rhs_param = param_vars.at (1);
435 :
436 310 : if (!Backend::function_set_parameters (fndecl, param_vars))
437 0 : return error_mark_node;
438 :
439 310 : enter_intrinsic_block (ctx, fndecl);
440 :
441 : // BUILTIN wrapping_<op> FN BODY BEGIN
442 310 : auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
443 310 : auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
444 :
445 : // Operations are always wrapping in Rust, as we have -fwrapv enabled by
446 : // default. The difference between a wrapping_{add, sub, mul} and a regular
447 : // arithmetic operation is that these intrinsics do not panic - they always
448 : // carry over.
449 310 : auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
450 :
451 310 : auto return_statement
452 310 : = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
453 310 : ctx->add_statement (return_statement);
454 : // BUILTIN wrapping_<op> FN BODY END
455 :
456 310 : finalize_intrinsic_block (ctx, fndecl);
457 :
458 310 : return fndecl;
459 310 : }
460 :
461 : /**
462 : * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
463 : */
464 : tree
465 141 : op_with_overflow (Context *ctx, TyTy::FnType *fntype, tree_code op)
466 : {
467 : // wrapping_<op> intrinsics have two parameter
468 141 : rust_assert (fntype->get_params ().size () == 2);
469 :
470 141 : tree lookup = NULL_TREE;
471 141 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
472 0 : return lookup;
473 :
474 141 : auto fndecl = compile_intrinsic_function (ctx, fntype);
475 :
476 : // setup the params
477 141 : std::vector<Bvariable *> param_vars;
478 141 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
479 :
480 141 : auto &x_param = param_vars.at (0);
481 141 : auto &y_param = param_vars.at (1);
482 :
483 141 : if (!Backend::function_set_parameters (fndecl, param_vars))
484 0 : return error_mark_node;
485 :
486 141 : rust_assert (fntype->get_num_substitutions () == 1);
487 141 : auto ¶m_mapping = fntype->get_substs ().at (0);
488 141 : const auto param_tyty = param_mapping.get_param_ty ();
489 141 : auto resolved_tyty = param_tyty->resolve ();
490 141 : tree template_parameter_type
491 141 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
492 :
493 : // this should match y as well or we can take it from the TyTy structure
494 141 : tree tmp_stmt = error_mark_node;
495 141 : Bvariable *result_variable
496 141 : = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
497 : NULL_TREE, true /*address_is_taken*/,
498 : UNDEF_LOCATION, &tmp_stmt);
499 141 : Bvariable *bool_variable
500 141 : = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
501 : NULL_TREE, true /*address_is_taken*/,
502 : UNDEF_LOCATION, &tmp_stmt);
503 :
504 141 : enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
505 :
506 : // BUILTIN op_with_overflow FN BODY BEGIN
507 141 : auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
508 141 : auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
509 :
510 141 : tree overflow_builtin = error_mark_node;
511 141 : switch (op)
512 : {
513 127 : case PLUS_EXPR:
514 127 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
515 : &overflow_builtin);
516 127 : break;
517 :
518 7 : case MINUS_EXPR:
519 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
520 : &overflow_builtin);
521 7 : break;
522 :
523 7 : case MULT_EXPR:
524 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
525 : &overflow_builtin);
526 7 : break;
527 :
528 0 : default:
529 0 : rust_unreachable ();
530 141 : break;
531 : }
532 141 : rust_assert (overflow_builtin != error_mark_node);
533 :
534 141 : tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
535 141 : tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
536 141 : tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
537 :
538 141 : tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
539 : 3, x, y, result_ref);
540 :
541 141 : tree overflow_assignment
542 141 : = Backend::assignment_statement (bool_decl, builtin_call,
543 : BUILTINS_LOCATION);
544 :
545 141 : ctx->add_statement (overflow_assignment);
546 :
547 282 : std::vector<tree> vals = {result_decl, bool_decl};
548 141 : tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
549 141 : tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
550 : -1, UNDEF_LOCATION);
551 :
552 141 : auto return_statement
553 141 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
554 141 : ctx->add_statement (return_statement);
555 :
556 : // BUILTIN wrapping_<op> FN BODY END
557 :
558 141 : finalize_intrinsic_block (ctx, fndecl);
559 :
560 141 : return fndecl;
561 141 : }
562 :
563 : /**
564 : * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
565 : * fn copy<T>(src: *const T, dst: *mut T, count: usize);
566 : */
567 : tree
568 243 : copy (Context *ctx, TyTy::FnType *fntype, bool overlaps)
569 : {
570 243 : rust_assert (fntype->get_params ().size () == 3);
571 243 : rust_assert (fntype->get_num_substitutions () == 1);
572 :
573 243 : tree lookup = NULL_TREE;
574 243 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
575 0 : return lookup;
576 :
577 243 : auto fndecl = compile_intrinsic_function (ctx, fntype);
578 :
579 : // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
580 243 : TREE_READONLY (fndecl) = 0;
581 243 : TREE_SIDE_EFFECTS (fndecl) = 1;
582 :
583 : // setup the params
584 243 : std::vector<Bvariable *> param_vars;
585 243 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
586 :
587 243 : if (!Backend::function_set_parameters (fndecl, param_vars))
588 0 : return error_mark_node;
589 :
590 243 : enter_intrinsic_block (ctx, fndecl);
591 :
592 : // BUILTIN copy_nonoverlapping BODY BEGIN
593 :
594 243 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
595 243 : auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
596 243 : auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
597 :
598 : // We want to create the following statement
599 : // memcpy(dst, src, size_of::<T>());
600 : // so
601 : // memcpy(dst, src, size_expr);
602 :
603 243 : auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
604 243 : auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
605 :
606 243 : tree size_expr
607 243 : = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
608 :
609 243 : tree memcpy_raw = nullptr;
610 486 : BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
611 : : "__builtin_memcpy",
612 : &memcpy_raw);
613 243 : rust_assert (memcpy_raw);
614 243 : auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
615 :
616 243 : auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
617 : nullptr, UNDEF_LOCATION);
618 :
619 243 : ctx->add_statement (copy_call);
620 :
621 : // BUILTIN copy_nonoverlapping BODY END
622 :
623 243 : finalize_intrinsic_block (ctx, fndecl);
624 :
625 243 : return fndecl;
626 243 : }
627 :
628 : tree
629 70 : atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering)
630 : {
631 70 : rust_assert (fntype->get_params ().size () == 2);
632 70 : rust_assert (fntype->get_num_substitutions () == 1);
633 :
634 70 : tree lookup = NULL_TREE;
635 70 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
636 0 : return lookup;
637 :
638 70 : auto fndecl = compile_intrinsic_function (ctx, fntype);
639 :
640 : // Most intrinsic functions are pure but not the atomic ones
641 70 : TREE_READONLY (fndecl) = 0;
642 70 : TREE_SIDE_EFFECTS (fndecl) = 1;
643 :
644 : // setup the params
645 70 : std::vector<Bvariable *> param_vars;
646 70 : std::vector<tree> types;
647 70 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
648 :
649 70 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
650 70 : rust_assert (ok);
651 :
652 70 : enter_intrinsic_block (ctx, fndecl);
653 :
654 70 : auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
655 70 : TREE_READONLY (dst) = 0;
656 :
657 70 : auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
658 70 : auto memorder = make_unsigned_long_tree (ordering);
659 :
660 70 : auto monomorphized_type
661 70 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
662 :
663 70 : auto builtin_name
664 70 : = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (),
665 70 : monomorphized_type);
666 70 : if (builtin_name.empty ())
667 14 : return error_mark_node;
668 :
669 56 : tree atomic_store_raw = nullptr;
670 56 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
671 : &atomic_store_raw);
672 56 : rust_assert (atomic_store_raw);
673 :
674 56 : auto atomic_store
675 56 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
676 :
677 56 : auto store_call
678 56 : = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
679 : UNDEF_LOCATION);
680 56 : TREE_READONLY (store_call) = 0;
681 56 : TREE_SIDE_EFFECTS (store_call) = 1;
682 :
683 56 : ctx->add_statement (store_call);
684 56 : finalize_intrinsic_block (ctx, fndecl);
685 :
686 56 : return fndecl;
687 70 : }
688 :
689 : tree
690 28 : atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering)
691 : {
692 28 : rust_assert (fntype->get_params ().size () == 1);
693 28 : rust_assert (fntype->get_num_substitutions () == 1);
694 :
695 28 : tree lookup = NULL_TREE;
696 28 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
697 0 : return lookup;
698 :
699 28 : auto fndecl = compile_intrinsic_function (ctx, fntype);
700 :
701 : // Most intrinsic functions are pure but not the atomic ones
702 : // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
703 28 : TREE_READONLY (fndecl) = 0;
704 28 : TREE_SIDE_EFFECTS (fndecl) = 1;
705 :
706 : // setup the params
707 28 : std::vector<Bvariable *> param_vars;
708 28 : std::vector<tree> types;
709 28 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
710 :
711 28 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
712 28 : rust_assert (ok);
713 :
714 28 : enter_intrinsic_block (ctx, fndecl);
715 :
716 28 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
717 28 : auto memorder = make_unsigned_long_tree (ordering);
718 :
719 28 : auto monomorphized_type
720 28 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
721 :
722 28 : auto builtin_name
723 28 : = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
724 28 : monomorphized_type);
725 28 : if (builtin_name.empty ())
726 0 : return error_mark_node;
727 :
728 28 : tree atomic_load_raw = nullptr;
729 28 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
730 : &atomic_load_raw);
731 28 : rust_assert (atomic_load_raw);
732 :
733 28 : auto atomic_load
734 28 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
735 :
736 28 : auto load_call = Backend::call_expression (atomic_load, {src, memorder},
737 : nullptr, UNDEF_LOCATION);
738 28 : auto return_statement
739 28 : = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
740 :
741 28 : TREE_READONLY (load_call) = 0;
742 28 : TREE_SIDE_EFFECTS (load_call) = 1;
743 :
744 28 : ctx->add_statement (return_statement);
745 :
746 28 : finalize_intrinsic_block (ctx, fndecl);
747 :
748 28 : return fndecl;
749 28 : }
750 :
751 : // Shared inner implementation for ctlz and ctlz_nonzero.
752 : //
753 : // nonzero=false → ctlz: ctlz(0) is well-defined in Rust and must return
754 : // bit_size, but __builtin_clz*(0) is undefined behaviour in C, so an
755 : // explicit arg==0 guard is emitted.
756 : //
757 : // nonzero=true → ctlz_nonzero: the caller guarantees arg != 0 (passing 0
758 : // is immediate UB in Rust), so the zero guard is omitted entirely.
759 : static tree
760 422 : ctlz_handler (Context *ctx, TyTy::FnType *fntype, bool nonzero)
761 : {
762 422 : rust_assert (fntype->get_params ().size () == 1);
763 :
764 422 : tree lookup = NULL_TREE;
765 422 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
766 0 : return lookup;
767 :
768 422 : auto fndecl = compile_intrinsic_function (ctx, fntype);
769 :
770 422 : std::vector<Bvariable *> param_vars;
771 422 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
772 :
773 422 : auto arg_param = param_vars.at (0);
774 422 : if (!Backend::function_set_parameters (fndecl, param_vars))
775 0 : return error_mark_node;
776 :
777 422 : rust_assert (fntype->get_num_substitutions () == 1);
778 422 : auto *monomorphized_type
779 422 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
780 422 : if (!check_for_basic_integer_type ("ctlz", fntype->get_locus (),
781 : monomorphized_type))
782 2 : return error_mark_node;
783 :
784 420 : enter_intrinsic_block (ctx, fndecl);
785 :
786 : // BUILTIN ctlz FN BODY BEGIN
787 420 : auto locus = fntype->get_locus ();
788 420 : auto arg_expr = Backend::var_expression (arg_param, locus);
789 420 : tree arg_type = TREE_TYPE (arg_expr);
790 420 : unsigned bit_size = TYPE_PRECISION (arg_type);
791 :
792 : // Convert signed types to their same-width unsigned equivalent before
793 : // widening. Without this, widening a signed type sign-extends it.
794 : // For example, i8(-1) = 0xFF widened to u32 gives 0xFFFFFFFF, so
795 : // __builtin_clz(0xFFFFFFFF) = 0, then 0 - diff(24) = -24.
796 : // Converting to u8 first gives 0xFF → 0x000000FF (zero-extended), so
797 : // __builtin_clz(0x000000FF) = 24, then 24 - 24 = 0.
798 420 : tree unsigned_type
799 420 : = !TYPE_UNSIGNED (arg_type) ? unsigned_type_for (arg_type) : arg_type;
800 420 : tree unsigned_arg = fold_convert (unsigned_type, arg_expr);
801 :
802 : // Pick the narrowest GCC clz builtin whose operand type is wide enough to
803 : // hold bit_size bits. diff records how many extra leading zeros the builtin
804 : // will count due to the width difference and is subtracted from the result.
805 : //
806 : // Example: ctlz(1u8) bit_size=8, int_prec=32, diff=24.
807 : // __builtin_clz(1u) returns 31 (counts from bit 31 down to bit 0).
808 : // 31 - 24 = 7, which is the correct answer for an 8-bit value.
809 : //
810 : // TODO: 128-bit integers are not yet handled.
811 420 : unsigned int_prec = TYPE_PRECISION (unsigned_type_node);
812 420 : unsigned long_prec = TYPE_PRECISION (long_unsigned_type_node);
813 420 : unsigned longlong_prec = TYPE_PRECISION (long_long_unsigned_type_node);
814 :
815 420 : const char *builtin_name = nullptr;
816 420 : tree cast_type = NULL_TREE;
817 420 : int diff = 0;
818 :
819 420 : if (bit_size <= int_prec)
820 : {
821 : // Fits in unsigned int: covers 8/16/32-bit integers on most targets.
822 315 : builtin_name = "__builtin_clz";
823 315 : cast_type = unsigned_type_node;
824 315 : diff = static_cast<int> (int_prec - bit_size);
825 : }
826 105 : else if (bit_size <= long_prec)
827 : {
828 : // Fits in unsigned long but not unsigned int.
829 105 : builtin_name = "__builtin_clzl";
830 105 : cast_type = long_unsigned_type_node;
831 105 : diff = static_cast<int> (long_prec - bit_size);
832 : }
833 0 : else if (bit_size <= longlong_prec)
834 : {
835 : // Fits in unsigned long long but not unsigned long.
836 0 : builtin_name = "__builtin_clzll";
837 0 : cast_type = long_long_unsigned_type_node;
838 0 : diff = static_cast<int> (longlong_prec - bit_size);
839 : }
840 : else
841 : {
842 0 : rust_sorry_at (locus, "ctlz for %u-bit integers is not yet implemented",
843 : bit_size);
844 0 : return error_mark_node;
845 : }
846 :
847 : // Widen the unsigned arg to the chosen builtin's operand type, call it,
848 : // then subtract the padding bits. diff == 0 means the Rust type exactly
849 : // matches the builtin's operand width, so the subtraction is skipped.
850 420 : tree call_arg = fold_convert (cast_type, unsigned_arg);
851 :
852 420 : tree builtin_decl = error_mark_node;
853 420 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &builtin_decl);
854 420 : rust_assert (builtin_decl != error_mark_node);
855 :
856 420 : tree builtin_fn = build_fold_addr_expr_loc (locus, builtin_decl);
857 420 : tree clz_expr
858 420 : = Backend::call_expression (builtin_fn, {call_arg}, nullptr, locus);
859 :
860 420 : if (diff > 0)
861 : {
862 210 : tree diff_cst = build_int_cst (integer_type_node, diff);
863 210 : clz_expr
864 210 : = fold_build2 (MINUS_EXPR, integer_type_node, clz_expr, diff_cst);
865 : }
866 :
867 420 : clz_expr = fold_convert (uint32_type_node, clz_expr);
868 :
869 420 : tree final_expr;
870 420 : if (!nonzero)
871 : {
872 : // ctlz(0) must return bit_size per the Rust reference.
873 : // We cannot pass 0 to __builtin_clz* (UB), so emit:
874 : // arg == 0 ? bit_size : clz_expr
875 252 : tree zero = build_int_cst (arg_type, 0);
876 252 : tree cmp = fold_build2 (EQ_EXPR, boolean_type_node, arg_expr, zero);
877 252 : tree width_cst = build_int_cst (uint32_type_node, bit_size);
878 252 : final_expr
879 252 : = fold_build3 (COND_EXPR, uint32_type_node, cmp, width_cst, clz_expr);
880 : }
881 : else
882 : {
883 : // ctlz_nonzero: arg != 0 is guaranteed by the caller, no guard needed.
884 : final_expr = clz_expr;
885 : }
886 :
887 420 : tree result = fold_convert (TREE_TYPE (DECL_RESULT (fndecl)), final_expr);
888 420 : auto return_stmt = Backend::return_statement (fndecl, result, locus);
889 420 : ctx->add_statement (return_stmt);
890 : // BUILTIN ctlz FN BODY END
891 :
892 420 : finalize_intrinsic_block (ctx, fndecl);
893 420 : return fndecl;
894 422 : }
895 :
896 : // Shared inner implementation for cttz and cttz_nonzero.
897 : //
898 : // nonzero=false → cttz: cttz(0) is well-defined in Rust and must return
899 : // bit_size, but __builtin_ctz*(0) is undefined behaviour in C, so an
900 : // explicit arg==0 guard is emitted.
901 : //
902 : // nonzero=true → cttz_nonzero: the caller guarantees arg != 0 (passing 0
903 : // is immediate UB in Rust), so the zero guard is omitted entirely.
904 : static tree
905 282 : cttz_handler (Context *ctx, TyTy::FnType *fntype, bool nonzero)
906 : {
907 282 : rust_assert (fntype->get_params ().size () == 1);
908 :
909 282 : tree lookup = NULL_TREE;
910 282 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
911 0 : return lookup;
912 :
913 282 : auto fndecl = compile_intrinsic_function (ctx, fntype);
914 :
915 282 : std::vector<Bvariable *> param_vars;
916 282 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
917 :
918 282 : auto arg_param = param_vars.at (0);
919 282 : if (!Backend::function_set_parameters (fndecl, param_vars))
920 0 : return error_mark_node;
921 :
922 282 : rust_assert (fntype->get_num_substitutions () == 1);
923 282 : auto *monomorphized_type
924 282 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
925 282 : if (!check_for_basic_integer_type ("cttz", fntype->get_locus (),
926 : monomorphized_type))
927 2 : return error_mark_node;
928 :
929 280 : enter_intrinsic_block (ctx, fndecl);
930 :
931 : // BUILTIN cttz FN BODY BEGIN
932 280 : auto locus = fntype->get_locus ();
933 280 : auto arg_expr = Backend::var_expression (arg_param, locus);
934 280 : tree arg_type = TREE_TYPE (arg_expr);
935 280 : unsigned bit_size = TYPE_PRECISION (arg_type);
936 :
937 : // Convert signed types to their same-width unsigned equivalent before
938 : // widening. For cttz this is not strictly required for correctness (sign
939 : // extension fills high bits with 1s, which does not alter the trailing-zero
940 : // count at the low end), but it avoids relying on signed-integer
941 : // representations and keeps the approach consistent with ctlz.
942 280 : tree unsigned_type
943 280 : = !TYPE_UNSIGNED (arg_type) ? unsigned_type_for (arg_type) : arg_type;
944 280 : tree unsigned_arg = fold_convert (unsigned_type, arg_expr);
945 :
946 : // Pick the narrowest GCC ctz builtin whose operand type is wide enough to
947 : // hold bit_size bits. Unlike ctlz, no diff adjustment is needed: widening
948 : // a value zero-extends it (fills the added high bits with 0s), which does
949 : // not introduce new trailing zeros at the low end.
950 : //
951 : // Example: cttz(0b00001000_u8) = 3
952 : // Widened to u32: 0x00000008. __builtin_ctz(0x00000008) = 3.
953 : //
954 : // TODO: 128-bit integers are not yet handled.
955 280 : unsigned int_prec = TYPE_PRECISION (unsigned_type_node);
956 280 : unsigned long_prec = TYPE_PRECISION (long_unsigned_type_node);
957 280 : unsigned longlong_prec = TYPE_PRECISION (long_long_unsigned_type_node);
958 :
959 280 : const char *builtin_name = nullptr;
960 280 : tree cast_type = NULL_TREE;
961 :
962 280 : if (bit_size <= int_prec)
963 : {
964 : // Fits in unsigned int: covers 8/16/32-bit integers on most targets.
965 : builtin_name = "__builtin_ctz";
966 : cast_type = unsigned_type_node;
967 : }
968 70 : else if (bit_size <= long_prec)
969 : {
970 : // Fits in unsigned long but not unsigned int.
971 : builtin_name = "__builtin_ctzl";
972 : cast_type = long_unsigned_type_node;
973 : }
974 0 : else if (bit_size <= longlong_prec)
975 : {
976 : // Fits in unsigned long long but not unsigned long.
977 : builtin_name = "__builtin_ctzll";
978 : cast_type = long_long_unsigned_type_node;
979 : }
980 : else
981 : {
982 0 : rust_sorry_at (locus, "cttz for %u-bit integers is not yet implemented",
983 : bit_size);
984 0 : return error_mark_node;
985 : }
986 :
987 280 : tree call_arg = fold_convert (cast_type, unsigned_arg);
988 :
989 280 : tree builtin_decl = error_mark_node;
990 280 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &builtin_decl);
991 280 : rust_assert (builtin_decl != error_mark_node);
992 :
993 280 : tree builtin_fn = build_fold_addr_expr_loc (locus, builtin_decl);
994 280 : tree ctz_expr
995 280 : = Backend::call_expression (builtin_fn, {call_arg}, nullptr, locus);
996 :
997 280 : ctz_expr = fold_convert (uint32_type_node, ctz_expr);
998 :
999 280 : tree final_expr;
1000 280 : if (!nonzero)
1001 : {
1002 : // cttz(0) must return bit_size per the Rust reference.
1003 : // We cannot pass 0 to __builtin_ctz* (UB), so emit:
1004 : // arg == 0 ? bit_size : ctz_expr
1005 168 : tree zero = build_int_cst (arg_type, 0);
1006 168 : tree cmp = fold_build2 (EQ_EXPR, boolean_type_node, arg_expr, zero);
1007 168 : tree width_cst = build_int_cst (uint32_type_node, bit_size);
1008 168 : final_expr
1009 168 : = fold_build3 (COND_EXPR, uint32_type_node, cmp, width_cst, ctz_expr);
1010 : }
1011 : else
1012 : {
1013 : // cttz_nonzero: arg != 0 is guaranteed by the caller, no guard needed.
1014 : final_expr = ctz_expr;
1015 : }
1016 :
1017 280 : tree result = fold_convert (TREE_TYPE (DECL_RESULT (fndecl)), final_expr);
1018 280 : auto return_stmt = Backend::return_statement (fndecl, result, locus);
1019 280 : ctx->add_statement (return_stmt);
1020 : // BUILTIN cttz FN BODY END
1021 :
1022 280 : finalize_intrinsic_block (ctx, fndecl);
1023 280 : return fndecl;
1024 282 : }
1025 :
1026 : } // namespace inner
1027 :
1028 : const HandlerBuilder
1029 14454 : op_with_overflow (tree_code op)
1030 : {
1031 14595 : return [op] (Context *ctx, TyTy::FnType *fntype) {
1032 141 : return inner::op_with_overflow (ctx, fntype, op);
1033 14454 : };
1034 : }
1035 :
1036 : tree
1037 198 : rotate_left (Context *ctx, TyTy::FnType *fntype)
1038 : {
1039 198 : return handlers::rotate (ctx, fntype, LROTATE_EXPR);
1040 : }
1041 :
1042 : tree
1043 155 : rotate_right (Context *ctx, TyTy::FnType *fntype)
1044 : {
1045 155 : return handlers::rotate (ctx, fntype, RROTATE_EXPR);
1046 : }
1047 :
1048 : const HandlerBuilder
1049 14454 : wrapping_op (tree_code op)
1050 : {
1051 14764 : return [op] (Context *ctx, TyTy::FnType *fntype) {
1052 310 : return inner::wrapping_op (ctx, fntype, op);
1053 14454 : };
1054 : }
1055 :
1056 : HandlerBuilder
1057 19272 : atomic_store (int ordering)
1058 : {
1059 19342 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
1060 70 : return inner::atomic_store (ctx, fntype, ordering);
1061 19272 : };
1062 : }
1063 :
1064 : HandlerBuilder
1065 19272 : atomic_load (int ordering)
1066 : {
1067 19300 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
1068 28 : return inner::atomic_load (ctx, fntype, ordering);
1069 19272 : };
1070 : }
1071 :
1072 : const HandlerBuilder
1073 33726 : unchecked_op (tree_code op)
1074 : {
1075 33782 : return [op] (Context *ctx, TyTy::FnType *fntype) {
1076 56 : return inner::unchecked_op (ctx, fntype, op);
1077 33726 : };
1078 : }
1079 :
1080 : const HandlerBuilder
1081 9636 : copy (bool overlaps)
1082 : {
1083 9879 : return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
1084 243 : return inner::copy (ctx, fntype, overlaps);
1085 9636 : };
1086 : }
1087 :
1088 : const HandlerBuilder
1089 9636 : expect (bool likely)
1090 : {
1091 9636 : return [likely] (Context *ctx, TyTy::FnType *fntype) {
1092 0 : return inner::expect (ctx, fntype, likely);
1093 9636 : };
1094 : }
1095 :
1096 : const HandlerBuilder
1097 9636 : try_handler (bool is_new_api)
1098 : {
1099 9638 : return [is_new_api] (Context *ctx, TyTy::FnType *fntype) {
1100 2 : return inner::try_handler (ctx, fntype, is_new_api);
1101 9636 : };
1102 : }
1103 :
1104 : tree
1105 0 : sorry (Context *ctx, TyTy::FnType *fntype)
1106 : {
1107 0 : rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
1108 : fntype->get_identifier ().c_str ());
1109 :
1110 0 : return error_mark_node;
1111 : }
1112 :
1113 : tree
1114 1 : assume (Context *ctx, TyTy::FnType *fntype)
1115 : {
1116 : // TODO: make sure this is actually helping the compiler optimize
1117 :
1118 1 : rust_assert (fntype->get_params ().size () == 1);
1119 1 : rust_assert (fntype->param_at (0).get_type ()->get_kind ()
1120 : == TyTy::TypeKind::BOOL);
1121 :
1122 1 : tree lookup = NULL_TREE;
1123 1 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1124 0 : return lookup;
1125 :
1126 1 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1127 :
1128 : // TODO: make sure these are necessary
1129 1 : TREE_READONLY (fndecl) = 0;
1130 1 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
1131 1 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
1132 1 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
1133 :
1134 1 : std::vector<Bvariable *> param_vars;
1135 1 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1136 :
1137 1 : if (!Backend::function_set_parameters (fndecl, param_vars))
1138 0 : return error_mark_node;
1139 :
1140 1 : enter_intrinsic_block (ctx, fndecl);
1141 :
1142 : // BUILTIN assume FN BODY BEGIN
1143 :
1144 1 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1145 :
1146 1 : tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
1147 : void_type_node, 1, val);
1148 1 : TREE_SIDE_EFFECTS (assume_expr) = 1;
1149 :
1150 1 : ctx->add_statement (assume_expr);
1151 : // BUILTIN assume FN BODY END
1152 :
1153 1 : finalize_intrinsic_block (ctx, fndecl);
1154 :
1155 1 : return fndecl;
1156 1 : }
1157 :
1158 : tree
1159 102 : discriminant_value (Context *ctx, TyTy::FnType *fntype)
1160 : {
1161 102 : rust_assert (fntype->get_params ().size () == 1);
1162 102 : rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
1163 102 : rust_assert (fntype->has_substitutions ());
1164 102 : rust_assert (fntype->get_num_type_params () == 1);
1165 102 : auto &mapping = fntype->get_substs ().at (0);
1166 102 : auto param_ty = mapping.get_param_ty ();
1167 102 : rust_assert (param_ty->can_resolve ());
1168 102 : auto resolved = param_ty->resolve ();
1169 102 : auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
1170 :
1171 102 : TyTy::BaseType *return_type = nullptr;
1172 102 : bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
1173 102 : rust_assert (ok);
1174 :
1175 102 : bool is_adt = resolved->is<TyTy::ADTType> ();
1176 102 : bool is_enum = false;
1177 102 : if (is_adt)
1178 : {
1179 102 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1180 102 : return_type = adt.get_repr_options ().repr;
1181 102 : rust_assert (return_type != nullptr);
1182 102 : is_enum = adt.is_enum ();
1183 : }
1184 :
1185 102 : p->set_associated_type (return_type->get_ref ());
1186 :
1187 102 : tree lookup = NULL_TREE;
1188 102 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1189 0 : return lookup;
1190 :
1191 102 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1192 :
1193 102 : std::vector<Bvariable *> param_vars;
1194 102 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1195 :
1196 102 : if (!Backend::function_set_parameters (fndecl, param_vars))
1197 0 : return error_mark_node;
1198 :
1199 102 : enter_intrinsic_block (ctx, fndecl);
1200 :
1201 : // BUILTIN disriminant_value FN BODY BEGIN
1202 :
1203 102 : tree result = integer_zero_node;
1204 102 : if (is_enum)
1205 : {
1206 102 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1207 102 : tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
1208 102 : result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
1209 : }
1210 :
1211 102 : auto return_statement
1212 102 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1213 102 : ctx->add_statement (return_statement);
1214 :
1215 : // BUILTIN disriminant_value FN BODY END
1216 :
1217 102 : finalize_intrinsic_block (ctx, fndecl);
1218 :
1219 102 : return fndecl;
1220 102 : }
1221 :
1222 : tree
1223 7 : variant_count (Context *ctx, TyTy::FnType *fntype)
1224 : {
1225 7 : rust_assert (fntype->get_num_type_params () == 1);
1226 7 : auto &mapping = fntype->get_substs ().at (0);
1227 7 : auto param_ty = mapping.get_param_ty ();
1228 7 : rust_assert (param_ty->can_resolve ());
1229 7 : auto resolved = param_ty->resolve ();
1230 :
1231 7 : size_t variant_count = 0;
1232 7 : bool is_adt = resolved->is<TyTy::ADTType> ();
1233 7 : if (is_adt)
1234 : {
1235 7 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1236 7 : variant_count = adt.number_of_variants ();
1237 : }
1238 :
1239 7 : tree lookup = NULL_TREE;
1240 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1241 0 : return lookup;
1242 :
1243 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1244 :
1245 7 : std::vector<Bvariable *> param_vars;
1246 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1247 :
1248 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
1249 0 : return error_mark_node;
1250 :
1251 7 : enter_intrinsic_block (ctx, fndecl);
1252 :
1253 : // BUILTIN disriminant_value FN BODY BEGIN
1254 7 : tree result_decl = DECL_RESULT (fndecl);
1255 7 : tree type = TREE_TYPE (result_decl);
1256 :
1257 7 : mpz_t ival;
1258 7 : mpz_init_set_ui (ival, variant_count);
1259 7 : tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
1260 7 : mpz_clear (ival);
1261 :
1262 7 : auto return_statement
1263 7 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1264 7 : ctx->add_statement (return_statement);
1265 :
1266 : // BUILTIN disriminant_value FN BODY END
1267 :
1268 7 : finalize_intrinsic_block (ctx, fndecl);
1269 :
1270 7 : return fndecl;
1271 7 : }
1272 :
1273 : tree
1274 26 : move_val_init (Context *ctx, TyTy::FnType *fntype)
1275 : {
1276 26 : rust_assert (fntype->get_params ().size () == 2);
1277 :
1278 26 : tree lookup = NULL_TREE;
1279 26 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1280 0 : return lookup;
1281 :
1282 26 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1283 :
1284 : // Most intrinsic functions are pure - not `move_val_init`
1285 26 : TREE_READONLY (fndecl) = 0;
1286 26 : TREE_SIDE_EFFECTS (fndecl) = 1;
1287 :
1288 : // get the template parameter type tree fn size_of<T>();
1289 26 : rust_assert (fntype->get_num_substitutions () == 1);
1290 26 : auto ¶m_mapping = fntype->get_substs ().at (0);
1291 26 : auto param_tyty = param_mapping.get_param_ty ();
1292 26 : auto resolved_tyty = param_tyty->resolve ();
1293 26 : tree template_parameter_type
1294 26 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1295 :
1296 26 : std::vector<Bvariable *> param_vars;
1297 26 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1298 :
1299 26 : if (!Backend::function_set_parameters (fndecl, param_vars))
1300 0 : return error_mark_node;
1301 :
1302 26 : enter_intrinsic_block (ctx, fndecl);
1303 :
1304 : // BUILTIN size_of FN BODY BEGIN
1305 :
1306 26 : tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1307 26 : tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1308 26 : tree size = TYPE_SIZE_UNIT (template_parameter_type);
1309 :
1310 26 : tree memcpy_builtin = error_mark_node;
1311 26 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
1312 : &memcpy_builtin);
1313 26 : rust_assert (memcpy_builtin != error_mark_node);
1314 :
1315 26 : src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1316 26 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1317 : dst, src, size);
1318 :
1319 26 : ctx->add_statement (memset_call);
1320 : // BUILTIN size_of FN BODY END
1321 :
1322 26 : finalize_intrinsic_block (ctx, fndecl);
1323 :
1324 26 : return fndecl;
1325 26 : }
1326 :
1327 : tree
1328 92 : uninit (Context *ctx, TyTy::FnType *fntype)
1329 : {
1330 : // uninit has _zero_ parameters its parameter is the generic one
1331 92 : rust_assert (fntype->get_params ().size () == 0);
1332 :
1333 92 : tree lookup = NULL_TREE;
1334 92 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1335 0 : return lookup;
1336 :
1337 92 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1338 :
1339 : // Most intrinsic functions are pure - not `uninit_handler`
1340 92 : TREE_READONLY (fndecl) = 0;
1341 92 : TREE_SIDE_EFFECTS (fndecl) = 1;
1342 :
1343 : // get the template parameter type tree fn uninit<T>();
1344 92 : rust_assert (fntype->get_num_substitutions () == 1);
1345 92 : auto ¶m_mapping = fntype->get_substs ().at (0);
1346 92 : const auto param_tyty = param_mapping.get_param_ty ();
1347 92 : auto resolved_tyty = param_tyty->resolve ();
1348 92 : tree template_parameter_type
1349 92 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1350 :
1351 : // result temporary
1352 92 : tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
1353 92 : rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
1354 : == TYPE_SIZE_UNIT (dst_type));
1355 :
1356 92 : tree tmp_stmt = error_mark_node;
1357 92 : Bvariable *bvar
1358 92 : = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
1359 : true /*address_is_taken*/, UNDEF_LOCATION,
1360 : &tmp_stmt);
1361 :
1362 92 : enter_intrinsic_block (ctx, fndecl, {bvar});
1363 :
1364 : // BUILTIN size_of FN BODY BEGIN
1365 :
1366 92 : tree memset_builtin = error_mark_node;
1367 92 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
1368 : &memset_builtin);
1369 92 : rust_assert (memset_builtin != error_mark_node);
1370 :
1371 : // call memset with 0x01 and size of the thing see
1372 : // https://github.com/Rust-GCC/gccrs/issues/1899
1373 :
1374 92 : tree dst = bvar->get_tree (BUILTINS_LOCATION);
1375 92 : tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
1376 92 : tree constant_byte = build_int_cst (integer_type_node, 0x01);
1377 92 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1378 :
1379 92 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
1380 : dst_addr, constant_byte, size_expr);
1381 92 : ctx->add_statement (memset_call);
1382 :
1383 92 : auto return_statement
1384 92 : = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
1385 92 : ctx->add_statement (return_statement);
1386 : // BUILTIN size_of FN BODY END
1387 :
1388 92 : finalize_intrinsic_block (ctx, fndecl);
1389 :
1390 92 : return fndecl;
1391 : }
1392 :
1393 : tree
1394 7 : prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
1395 : {
1396 7 : return prefetch_data (ctx, fntype, Prefetch::Read);
1397 : }
1398 : tree
1399 7 : prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
1400 : {
1401 7 : return prefetch_data (ctx, fntype, Prefetch::Write);
1402 : }
1403 :
1404 : tree
1405 14 : prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
1406 : {
1407 14 : rust_assert (fntype->get_params ().size () == 2);
1408 :
1409 14 : tree lookup = NULL_TREE;
1410 14 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1411 0 : return lookup;
1412 :
1413 14 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1414 :
1415 : // prefetching isn't pure and shouldn't be discarded after GIMPLE
1416 14 : TREE_READONLY (fndecl) = 0;
1417 14 : TREE_SIDE_EFFECTS (fndecl) = 1;
1418 :
1419 14 : std::vector<Bvariable *> args;
1420 14 : compile_fn_params (ctx, fntype, fndecl, &args);
1421 :
1422 14 : if (!Backend::function_set_parameters (fndecl, args))
1423 0 : return error_mark_node;
1424 :
1425 14 : enter_intrinsic_block (ctx, fndecl);
1426 :
1427 14 : auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
1428 :
1429 : // The core library technically allows you to pass any i32 value as a
1430 : // locality, but LLVM will then complain if the value cannot be constant
1431 : // evaluated. For now, we ignore the locality argument and instead always
1432 : // pass `3` (the most restrictive value). This allows us to still have
1433 : // prefetch behavior, just not as granular as expected. In future Rust
1434 : // versions, we hope that prefetch intrinsics will be split up according to
1435 : // locality, similarly to atomic intrinsics.
1436 : // The solution is to try and perform constant folding for the locality
1437 : // argument, or instead of creating a new function definition, modify the call
1438 : // site directly This has the bad side-effect of creating warnings about
1439 : // `unused name - locality`, which we hack away here:
1440 : // TODO: Take care of handling locality properly
1441 14 : Backend::var_expression (args[1], UNDEF_LOCATION);
1442 :
1443 14 : auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
1444 :
1445 14 : auto prefetch_raw = NULL_TREE;
1446 14 : auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
1447 : &prefetch_raw);
1448 14 : rust_assert (ok);
1449 14 : auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
1450 :
1451 28 : auto prefetch_call = Backend::call_expression (prefetch,
1452 : {addr, rw_flag,
1453 : // locality arg
1454 14 : make_unsigned_long_tree (3)},
1455 : nullptr, UNDEF_LOCATION);
1456 :
1457 14 : TREE_READONLY (prefetch_call) = 0;
1458 14 : TREE_SIDE_EFFECTS (prefetch_call) = 1;
1459 :
1460 14 : ctx->add_statement (prefetch_call);
1461 :
1462 14 : finalize_intrinsic_block (ctx, fndecl);
1463 :
1464 14 : return fndecl;
1465 14 : }
1466 :
1467 : tree
1468 353 : rotate (Context *ctx, TyTy::FnType *fntype, tree_code op)
1469 : {
1470 : // rotate intrinsic has two parameter
1471 353 : rust_assert (fntype->get_params ().size () == 2);
1472 :
1473 353 : tree lookup = NULL_TREE;
1474 353 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1475 0 : return lookup;
1476 :
1477 353 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1478 :
1479 : // setup the params
1480 353 : std::vector<Bvariable *> param_vars;
1481 353 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1482 :
1483 353 : auto &x_param = param_vars.at (0);
1484 353 : auto &y_param = param_vars.at (1);
1485 353 : rust_assert (param_vars.size () == 2);
1486 353 : if (!Backend::function_set_parameters (fndecl, param_vars))
1487 0 : return error_mark_node;
1488 :
1489 353 : enter_intrinsic_block (ctx, fndecl);
1490 :
1491 : // BUILTIN rotate FN BODY BEGIN
1492 353 : tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
1493 353 : tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
1494 353 : tree rotate_expr
1495 353 : = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
1496 353 : auto return_statement
1497 353 : = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
1498 353 : ctx->add_statement (return_statement);
1499 : // BUILTIN rotate FN BODY END
1500 :
1501 353 : finalize_intrinsic_block (ctx, fndecl);
1502 :
1503 353 : return fndecl;
1504 353 : }
1505 :
1506 : tree
1507 191 : transmute (Context *ctx, TyTy::FnType *fntype)
1508 : {
1509 : // transmute intrinsic has one parameter
1510 191 : rust_assert (fntype->get_params ().size () == 1);
1511 :
1512 191 : tree lookup = NULL_TREE;
1513 191 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1514 0 : return lookup;
1515 :
1516 191 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1517 :
1518 191 : std::vector<Bvariable *> param_vars;
1519 191 : std::vector<tree_node *> compiled_types;
1520 191 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &compiled_types);
1521 :
1522 191 : if (!Backend::function_set_parameters (fndecl, param_vars))
1523 0 : return error_mark_node;
1524 :
1525 : // param to convert
1526 191 : Bvariable *convert_me_param = param_vars.at (0);
1527 191 : tree convert_me_expr
1528 191 : = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
1529 :
1530 : // check for transmute pre-conditions
1531 191 : tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
1532 191 : tree source_type_expr = compiled_types.at (0);
1533 191 : tree target_size_expr = TYPE_SIZE (target_type_expr);
1534 191 : tree source_size_expr = TYPE_SIZE (source_type_expr);
1535 : // for some reason, unit types and other zero-sized types return NULL for the
1536 : // size expressions
1537 191 : unsigned HOST_WIDE_INT target_size
1538 191 : = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
1539 191 : unsigned HOST_WIDE_INT source_size
1540 191 : = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
1541 :
1542 : // size check for concrete types
1543 : // TODO(liushuyu): check alignment for pointers; check for dependently-sized
1544 : // types
1545 191 : if (target_size != source_size)
1546 : {
1547 7 : rust_error_at (fntype->get_locus (),
1548 : "cannot transmute between types of different sizes, or "
1549 : "dependently-sized types");
1550 14 : rust_inform (
1551 7 : fntype->get_ident ().locus, "source type: %qs (%lu bits)",
1552 7 : fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
1553 : (unsigned long) source_size);
1554 7 : rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
1555 14 : fntype->get_return_type ()->as_string ().c_str (),
1556 : (unsigned long) target_size);
1557 : }
1558 :
1559 191 : enter_intrinsic_block (ctx, fndecl);
1560 :
1561 : // BUILTIN transmute FN BODY BEGIN
1562 :
1563 : // Return *((orig_type*)&decl) */
1564 :
1565 191 : tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
1566 191 : t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
1567 : build_pointer_type (target_type_expr), t);
1568 191 : tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
1569 :
1570 191 : auto return_statement
1571 191 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
1572 191 : ctx->add_statement (return_statement);
1573 : // BUILTIN transmute FN BODY END
1574 :
1575 191 : finalize_intrinsic_block (ctx, fndecl);
1576 :
1577 191 : return fndecl;
1578 191 : }
1579 :
1580 : tree
1581 474 : sizeof_handler (Context *ctx, TyTy::FnType *fntype)
1582 : {
1583 : // size_of has _zero_ parameters its parameter is the generic one
1584 474 : rust_assert (fntype->get_params ().size () == 0);
1585 :
1586 474 : tree lookup = NULL_TREE;
1587 474 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1588 0 : return lookup;
1589 :
1590 474 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1591 :
1592 : // get the template parameter type tree fn size_of<T>();
1593 474 : rust_assert (fntype->get_num_substitutions () == 1);
1594 474 : auto ¶m_mapping = fntype->get_substs ().at (0);
1595 474 : const auto param_tyty = param_mapping.get_param_ty ();
1596 474 : auto resolved_tyty = param_tyty->resolve ();
1597 474 : tree template_parameter_type
1598 474 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1599 :
1600 474 : enter_intrinsic_block (ctx, fndecl);
1601 :
1602 : // BUILTIN size_of FN BODY BEGIN
1603 474 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1604 474 : auto return_statement
1605 474 : = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
1606 474 : ctx->add_statement (return_statement);
1607 : // BUILTIN size_of FN BODY END
1608 :
1609 474 : finalize_intrinsic_block (ctx, fndecl);
1610 :
1611 474 : return fndecl;
1612 : }
1613 :
1614 : tree
1615 72 : offset (Context *ctx, TyTy::FnType *fntype)
1616 : {
1617 : // offset intrinsic has two params dst pointer and offset isize
1618 72 : rust_assert (fntype->get_params ().size () == 2);
1619 :
1620 72 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1621 :
1622 72 : std::vector<Bvariable *> param_vars;
1623 72 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1624 :
1625 72 : auto &dst_param = param_vars.at (0);
1626 72 : auto &size_param = param_vars.at (1);
1627 72 : rust_assert (param_vars.size () == 2);
1628 72 : if (!Backend::function_set_parameters (fndecl, param_vars))
1629 0 : return error_mark_node;
1630 :
1631 72 : enter_intrinsic_block (ctx, fndecl);
1632 :
1633 : // BUILTIN offset FN BODY BEGIN
1634 72 : tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
1635 72 : tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
1636 72 : tree pointer_offset_expr
1637 72 : = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
1638 72 : auto return_statement
1639 72 : = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
1640 72 : ctx->add_statement (return_statement);
1641 : // BUILTIN offset FN BODY END
1642 :
1643 72 : finalize_intrinsic_block (ctx, fndecl);
1644 :
1645 72 : return fndecl;
1646 72 : }
1647 :
1648 : /**
1649 : * pub const fn bswap<T: Copy>(x: T) -> T;
1650 : */
1651 : tree
1652 9 : bswap_handler (Context *ctx, TyTy::FnType *fntype)
1653 : {
1654 9 : rust_assert (fntype->get_params ().size () == 1);
1655 :
1656 9 : tree lookup = NULL_TREE;
1657 9 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1658 0 : return lookup;
1659 :
1660 9 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1661 :
1662 9 : auto locus = fntype->get_locus ();
1663 :
1664 9 : std::vector<Bvariable *> param_vars;
1665 9 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1666 :
1667 9 : auto &x_param = param_vars.at (0);
1668 9 : rust_assert (param_vars.size () == 1);
1669 9 : if (!Backend::function_set_parameters (fndecl, param_vars))
1670 0 : return error_mark_node;
1671 :
1672 9 : auto *monomorphized_type
1673 9 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
1674 :
1675 9 : check_for_basic_integer_type ("bswap", fntype->get_locus (),
1676 : monomorphized_type);
1677 :
1678 9 : tree template_parameter_type
1679 9 : = TyTyResolveCompile::compile (ctx, monomorphized_type);
1680 :
1681 9 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1682 9 : unsigned HOST_WIDE_INT size = TREE_INT_CST_LOW (size_expr);
1683 :
1684 9 : enter_intrinsic_block (ctx, fndecl);
1685 :
1686 : // BUILTIN bswap FN BODY BEGIN
1687 :
1688 9 : auto expr_x = Backend::var_expression (x_param, locus);
1689 9 : tree result = NULL_TREE;
1690 :
1691 9 : if (size == 1)
1692 : {
1693 : result = expr_x;
1694 : }
1695 : else
1696 : {
1697 8 : tree target_type = NULL_TREE;
1698 8 : const char *builtin_name = nullptr;
1699 8 : switch (size)
1700 : {
1701 2 : case 2:
1702 2 : builtin_name = "__builtin_bswap16";
1703 2 : target_type = uint16_type_node;
1704 2 : break;
1705 2 : case 4:
1706 2 : builtin_name = "__builtin_bswap32";
1707 2 : target_type = uint32_type_node;
1708 2 : break;
1709 2 : case 8:
1710 2 : builtin_name = "__builtin_bswap64";
1711 2 : target_type = uint64_type_node;
1712 2 : break;
1713 2 : case 16:
1714 2 : builtin_name = "__builtin_bswap128";
1715 2 : target_type = uint128_type_node;
1716 2 : break;
1717 0 : default:
1718 0 : return error_mark_node;
1719 : }
1720 :
1721 8 : tree bswap_raw = nullptr;
1722 8 : auto ok = BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
1723 : &bswap_raw);
1724 :
1725 8 : if (ok)
1726 : {
1727 8 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1728 :
1729 8 : auto bswap_x = build1 (CONVERT_EXPR, target_type, expr_x);
1730 :
1731 8 : auto bswap_call
1732 8 : = Backend::call_expression (bswap_fn, {bswap_x}, NULL_TREE, locus);
1733 :
1734 8 : result = build1 (CONVERT_EXPR, template_parameter_type, bswap_call);
1735 : }
1736 : else
1737 : {
1738 0 : auto ok2 = BuiltinsContext::get ().lookup_simple_builtin (
1739 0 : "__builtin_bswap64", &bswap_raw);
1740 0 : rust_assert (ok2);
1741 :
1742 0 : tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
1743 :
1744 0 : tree tmp_in_stmt = error_mark_node;
1745 0 : Bvariable *in_var
1746 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1747 : template_parameter_type, expr_x,
1748 : true, locus, &tmp_in_stmt);
1749 0 : ctx->add_statement (tmp_in_stmt);
1750 :
1751 0 : tree addr_x
1752 0 : = build_fold_addr_expr_loc (locus, in_var->get_tree (locus));
1753 0 : tree u64_ptr_type = build_pointer_type (uint64_type_node);
1754 :
1755 0 : tree low_ptr = fold_convert (u64_ptr_type, addr_x);
1756 0 : tree low = build_fold_indirect_ref_loc (locus, low_ptr);
1757 :
1758 0 : tree high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type, low_ptr,
1759 : size_int (8));
1760 0 : tree high = build_fold_indirect_ref_loc (locus, high_ptr);
1761 :
1762 0 : auto new_high
1763 0 : = Backend::call_expression (bswap_fn, {low}, NULL_TREE, locus);
1764 0 : auto new_low
1765 0 : = Backend::call_expression (bswap_fn, {high}, NULL_TREE, locus);
1766 :
1767 0 : tree tmp_stmt = error_mark_node;
1768 0 : Bvariable *result_var
1769 0 : = Backend::temporary_variable (fndecl, NULL_TREE,
1770 : template_parameter_type, NULL_TREE,
1771 : true, locus, &tmp_stmt);
1772 0 : ctx->add_statement (tmp_stmt);
1773 :
1774 0 : tree addr_res
1775 0 : = build_fold_addr_expr_loc (locus, result_var->get_tree (locus));
1776 0 : tree res_ptr = fold_convert (u64_ptr_type, addr_res);
1777 :
1778 0 : tree store_low
1779 0 : = build2 (MODIFY_EXPR, void_type_node,
1780 : build_fold_indirect_ref_loc (locus, res_ptr), new_low);
1781 0 : ctx->add_statement (store_low);
1782 :
1783 0 : tree res_high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type,
1784 : res_ptr, size_int (8));
1785 0 : tree store_high
1786 0 : = build2 (MODIFY_EXPR, void_type_node,
1787 : build_fold_indirect_ref_loc (locus, res_high_ptr),
1788 : new_high);
1789 0 : ctx->add_statement (store_high);
1790 :
1791 0 : result = result_var->get_tree (locus);
1792 : }
1793 : }
1794 :
1795 9 : auto return_statement = Backend::return_statement (fndecl, result, locus);
1796 :
1797 9 : TREE_READONLY (result) = 1;
1798 :
1799 9 : ctx->add_statement (return_statement);
1800 :
1801 : // BUILTIN bswap FN BODY END
1802 :
1803 9 : finalize_intrinsic_block (ctx, fndecl);
1804 :
1805 9 : return fndecl;
1806 9 : }
1807 :
1808 : tree
1809 253 : ctlz_handler (Context *ctx, TyTy::FnType *fntype)
1810 : {
1811 253 : return inner::ctlz_handler (ctx, fntype, false);
1812 : }
1813 :
1814 : tree
1815 169 : ctlz_nonzero_handler (Context *ctx, TyTy::FnType *fntype)
1816 : {
1817 169 : return inner::ctlz_handler (ctx, fntype, true);
1818 : }
1819 :
1820 : tree
1821 169 : cttz_handler (Context *ctx, TyTy::FnType *fntype)
1822 : {
1823 169 : return inner::cttz_handler (ctx, fntype, false);
1824 : }
1825 :
1826 : tree
1827 113 : cttz_nonzero_handler (Context *ctx, TyTy::FnType *fntype)
1828 : {
1829 113 : return inner::cttz_handler (ctx, fntype, true);
1830 : }
1831 :
1832 : } // namespace handlers
1833 : } // namespace Compile
1834 : } // namespace Rust
|