libcpp, c, middle-end: Optimize initializers using #embed in C
[official-gcc.git] / gcc / rust / backend / rust-compile-intrinsic.cc
blob49ee4c0ead9f58d614dbc7cd3a9f022f3ebf544a
1 // This file is part of GCC.
3 // GCC is free software; you can redistribute it and/or modify it under
4 // the terms of the GNU General Public License as published by the Free
5 // Software Foundation; either version 3, or (at your option) any later
6 // version.
8 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
9 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 // for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with GCC; see the file COPYING3. If not see
15 // <http://www.gnu.org/licenses/>.
17 #include "rust-compile-intrinsic.h"
18 #include "rust-compile-context.h"
19 #include "rust-compile-type.h"
20 #include "rust-compile-expr.h"
21 #include "rust-compile-fnparam.h"
22 #include "rust-builtins.h"
23 #include "rust-diagnostics.h"
24 #include "rust-location.h"
25 #include "rust-constexpr.h"
26 #include "rust-tree.h"
27 #include "tree-core.h"
28 #include "rust-gcc.h"
29 #include "print-tree.h"
30 #include "fold-const.h"
31 #include "langhooks.h"
32 #include "rust-gcc.h"
33 #include "rust-constexpr.h"
35 #include "print-tree.h"
37 // declaration taken from "stringpool.h"
38 // the get_identifier macro causes compilation issues
39 extern tree
40 get_identifier (const char *);
42 namespace Rust {
43 namespace Compile {
45 static bool
46 is_basic_integer_type (TyTy::BaseType *type)
48 switch (type->get_kind ())
50 case TyTy::INT:
51 case TyTy::UINT:
52 case TyTy::USIZE:
53 case TyTy::ISIZE:
54 return true;
55 default:
56 return false;
57 break;
61 static bool
62 check_for_basic_integer_type (const std::string &intrinsic_str,
63 location_t locus, TyTy::BaseType *type)
65 auto is_basic_integer = is_basic_integer_type (type);
66 if (!is_basic_integer)
68 rust_error_at (
69 locus,
70 "%s intrinsics can only be used with basic integer types (got %qs)",
71 intrinsic_str.c_str (), type->get_name ().c_str ());
74 return is_basic_integer;
77 static tree
78 offset_handler (Context *ctx, TyTy::FnType *fntype);
79 static tree
80 sizeof_handler (Context *ctx, TyTy::FnType *fntype);
81 static tree
82 transmute_handler (Context *ctx, TyTy::FnType *fntype);
83 static tree
84 rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
85 static tree
86 wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op);
87 static tree
88 op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op);
89 static tree
90 uninit_handler (Context *ctx, TyTy::FnType *fntype);
91 static tree
92 move_val_init_handler (Context *ctx, TyTy::FnType *fntype);
93 static tree
94 assume_handler (Context *ctx, TyTy::FnType *fntype);
96 enum class Prefetch
98 Read,
99 Write
102 static tree
103 prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind);
105 static inline tree
106 rotate_left_handler (Context *ctx, TyTy::FnType *fntype)
108 return rotate_handler (ctx, fntype, LROTATE_EXPR);
110 static inline tree
111 rotate_right_handler (Context *ctx, TyTy::FnType *fntype)
113 return rotate_handler (ctx, fntype, RROTATE_EXPR);
116 const static std::function<tree (Context *, TyTy::FnType *)>
117 wrapping_op_handler (tree_code op)
119 return [op] (Context *ctx, TyTy::FnType *fntype) {
120 return wrapping_op_handler_inner (ctx, fntype, op);
124 const static std::function<tree (Context *, TyTy::FnType *)>
125 op_with_overflow (tree_code op)
127 return [op] (Context *ctx, TyTy::FnType *fntype) {
128 return op_with_overflow_inner (ctx, fntype, op);
132 static inline tree
133 prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
135 return prefetch_data_handler (ctx, fntype, Prefetch::Read);
137 static inline tree
138 prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
140 return prefetch_data_handler (ctx, fntype, Prefetch::Write);
143 static tree
144 atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering);
145 static tree
146 atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering);
148 static inline std::function<tree (Context *, TyTy::FnType *)>
149 atomic_store_handler (int ordering)
151 return [ordering] (Context *ctx, TyTy::FnType *fntype) {
152 return atomic_store_handler_inner (ctx, fntype, ordering);
156 static inline std::function<tree (Context *, TyTy::FnType *)>
157 atomic_load_handler (int ordering)
159 return [ordering] (Context *ctx, TyTy::FnType *fntype) {
160 return atomic_load_handler_inner (ctx, fntype, ordering);
164 static inline tree
165 unchecked_op_inner (Context *ctx, TyTy::FnType *fntype, tree_code op);
167 const static std::function<tree (Context *, TyTy::FnType *)>
168 unchecked_op_handler (tree_code op)
170 return [op] (Context *ctx, TyTy::FnType *fntype) {
171 return unchecked_op_inner (ctx, fntype, op);
175 static inline tree
176 copy_handler_inner (Context *ctx, TyTy::FnType *fntype, bool overlaps);
178 const static std::function<tree (Context *, TyTy::FnType *)>
179 copy_handler (bool overlaps)
181 return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
182 return copy_handler_inner (ctx, fntype, overlaps);
186 static inline tree
187 expect_handler_inner (Context *ctx, TyTy::FnType *fntype, bool likely);
189 const static std::function<tree (Context *, TyTy::FnType *)>
190 expect_handler (bool likely)
192 return [likely] (Context *ctx, TyTy::FnType *fntype) {
193 return expect_handler_inner (ctx, fntype, likely);
197 inline tree
198 sorry_handler (Context *ctx, TyTy::FnType *fntype)
200 rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
201 fntype->get_identifier ().c_str ());
203 return error_mark_node;
206 static const std::map<std::string,
207 std::function<tree (Context *, TyTy::FnType *)>>
208 generic_intrinsics = {
209 {"offset", offset_handler},
210 {"size_of", sizeof_handler},
211 {"transmute", transmute_handler},
212 {"rotate_left", rotate_left_handler},
213 {"rotate_right", rotate_right_handler},
214 {"wrapping_add", wrapping_op_handler (PLUS_EXPR)},
215 {"wrapping_sub", wrapping_op_handler (MINUS_EXPR)},
216 {"wrapping_mul", wrapping_op_handler (MULT_EXPR)},
217 {"add_with_overflow", op_with_overflow (PLUS_EXPR)},
218 {"sub_with_overflow", op_with_overflow (MINUS_EXPR)},
219 {"mul_with_overflow", op_with_overflow (MULT_EXPR)},
220 {"copy", copy_handler (true)},
221 {"copy_nonoverlapping", copy_handler (false)},
222 {"prefetch_read_data", prefetch_read_data},
223 {"prefetch_write_data", prefetch_write_data},
224 {"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)},
225 {"atomic_store_release", atomic_store_handler (__ATOMIC_RELEASE)},
226 {"atomic_store_relaxed", atomic_store_handler (__ATOMIC_RELAXED)},
227 {"atomic_store_unordered", atomic_store_handler (__ATOMIC_RELAXED)},
228 {"atomic_load_seqcst", atomic_load_handler (__ATOMIC_SEQ_CST)},
229 {"atomic_load_acquire", atomic_load_handler (__ATOMIC_ACQUIRE)},
230 {"atomic_load_relaxed", atomic_load_handler (__ATOMIC_RELAXED)},
231 {"atomic_load_unordered", atomic_load_handler (__ATOMIC_RELAXED)},
232 {"unchecked_add", unchecked_op_handler (PLUS_EXPR)},
233 {"unchecked_sub", unchecked_op_handler (MINUS_EXPR)},
234 {"unchecked_mul", unchecked_op_handler (MULT_EXPR)},
235 {"unchecked_div", unchecked_op_handler (TRUNC_DIV_EXPR)},
236 {"unchecked_rem", unchecked_op_handler (TRUNC_MOD_EXPR)},
237 {"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)},
238 {"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)},
239 {"uninit", uninit_handler},
240 {"move_val_init", move_val_init_handler},
241 {"likely", expect_handler (true)},
242 {"unlikely", expect_handler (false)},
243 {"assume", assume_handler},
246 Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
249 * Returns a FUNC_DECL corresponding to the intrinsic function FNTYPE. If a
250 * corresponding builtin exists, returns it. If not, search in the generic
251 * intrinsics declared and delegate the return to the corresponding handler.
253 * @param fntype The Rust function type that should be implemented by the
254 * compiler
256 tree
257 Intrinsics::compile (TyTy::FnType *fntype)
259 rust_assert (fntype->get_abi () == ABI::INTRINSIC);
261 tree builtin = error_mark_node;
262 BuiltinsContext &builtin_ctx = BuiltinsContext::get ();
264 if (builtin_ctx.lookup_simple_builtin (fntype->get_identifier (), &builtin))
265 return builtin;
267 // is it an generic builtin?
268 auto it = generic_intrinsics.find (fntype->get_identifier ());
269 if (it != generic_intrinsics.end ())
270 return it->second (ctx, fntype);
272 location_t locus = ctx->get_mappings ()->lookup_location (fntype->get_ref ());
273 rust_error_at (locus, ErrorCode::E0093,
274 "unrecognized intrinsic function: %<%s%>",
275 fntype->get_identifier ().c_str ());
277 return error_mark_node;
281 * Items can be forward compiled which means we may not need to invoke this
282 * code. We might also have already compiled this generic function as well.
284 static bool
285 check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
287 const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
288 std::string asm_name = ctx->mangle_item (fntype, canonical_path);
289 if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
290 fntype->get_id (), fntype, asm_name))
292 return true;
295 return false;
299 * Maybe override the Hir Lookups for the substituions in this context
301 static void
302 maybe_override_ctx (TyTy::FnType *fntype)
304 if (fntype->has_substitutions_defined ())
305 fntype->override_context ();
309 * Compile and setup a function's parameters
311 static void
312 compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
313 std::vector<Bvariable *> *compiled_param_variables,
314 std::vector<tree_node *> *compiled_param_types = nullptr)
316 for (auto &parm : fntype->get_params ())
318 auto &referenced_param = parm.first;
319 auto &param_tyty = parm.second;
320 auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
322 location_t param_locus = referenced_param->get_locus ();
323 Bvariable *compiled_param_var
324 = CompileFnParam::compile (ctx, fndecl, referenced_param,
325 compiled_param_type, param_locus);
327 compiled_param_variables->push_back (compiled_param_var);
328 if (compiled_param_types)
329 compiled_param_types->push_back (compiled_param_type);
333 static tree
334 compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
336 maybe_override_ctx (fntype);
338 const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
340 tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
341 std::string ir_symbol_name
342 = canonical_path.get () + fntype->subst_as_string ();
343 std::string asm_name = ctx->mangle_item (fntype, canonical_path);
345 unsigned int flags = 0;
346 tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
347 flags, fntype->get_ident ().locus);
349 TREE_PUBLIC (fndecl) = 0;
350 TREE_READONLY (fndecl) = 1;
351 DECL_ARTIFICIAL (fndecl) = 1;
352 DECL_EXTERNAL (fndecl) = 0;
353 DECL_DECLARED_INLINE_P (fndecl) = 1;
355 return fndecl;
358 static void
359 enter_intrinsic_block (Context *ctx, tree fndecl,
360 const std::vector<Bvariable *> &vars = {})
362 tree enclosing_scope = NULL_TREE;
363 location_t start_location = UNDEF_LOCATION;
364 location_t end_location = UNDEF_LOCATION;
366 auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
367 end_location);
369 ctx->push_block (block);
372 static void
373 finalize_intrinsic_block (Context *ctx, tree fndecl)
375 tree bind_tree = ctx->pop_block ();
377 gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
379 DECL_SAVED_TREE (fndecl) = bind_tree;
381 ctx->push_function (fndecl);
383 DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
384 maybe_save_constexpr_fundef (fndecl);
387 static tree
388 offset_handler (Context *ctx, TyTy::FnType *fntype)
390 // offset intrinsic has two params dst pointer and offset isize
391 rust_assert (fntype->get_params ().size () == 2);
393 auto fndecl = compile_intrinsic_function (ctx, fntype);
395 std::vector<Bvariable *> param_vars;
396 compile_fn_params (ctx, fntype, fndecl, &param_vars);
398 auto &dst_param = param_vars.at (0);
399 auto &size_param = param_vars.at (1);
400 rust_assert (param_vars.size () == 2);
401 if (!Backend::function_set_parameters (fndecl, param_vars))
402 return error_mark_node;
404 enter_intrinsic_block (ctx, fndecl);
406 // BUILTIN offset FN BODY BEGIN
407 tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
408 tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
409 tree pointer_offset_expr
410 = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
411 auto return_statement
412 = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
413 ctx->add_statement (return_statement);
414 // BUILTIN offset FN BODY END
416 finalize_intrinsic_block (ctx, fndecl);
418 return fndecl;
421 static tree
422 sizeof_handler (Context *ctx, TyTy::FnType *fntype)
424 // size_of has _zero_ parameters its parameter is the generic one
425 rust_assert (fntype->get_params ().size () == 0);
427 tree lookup = NULL_TREE;
428 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
429 return lookup;
431 auto fndecl = compile_intrinsic_function (ctx, fntype);
433 // get the template parameter type tree fn size_of<T>();
434 rust_assert (fntype->get_num_substitutions () == 1);
435 auto &param_mapping = fntype->get_substs ().at (0);
436 const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
437 TyTy::BaseType *resolved_tyty = param_tyty->resolve ();
438 tree template_parameter_type
439 = TyTyResolveCompile::compile (ctx, resolved_tyty);
441 enter_intrinsic_block (ctx, fndecl);
443 // BUILTIN size_of FN BODY BEGIN
444 tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
445 auto return_statement
446 = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
447 ctx->add_statement (return_statement);
448 // BUILTIN size_of FN BODY END
450 finalize_intrinsic_block (ctx, fndecl);
452 return fndecl;
455 static tree
456 transmute_handler (Context *ctx, TyTy::FnType *fntype)
458 // transmute intrinsic has one parameter
459 rust_assert (fntype->get_params ().size () == 1);
461 tree lookup = NULL_TREE;
462 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
463 return lookup;
465 auto fndecl = compile_intrinsic_function (ctx, fntype);
467 std::vector<Bvariable *> param_vars;
468 std::vector<tree_node *> compiled_types;
469 compile_fn_params (ctx, fntype, fndecl, &param_vars, &compiled_types);
471 if (!Backend::function_set_parameters (fndecl, param_vars))
472 return error_mark_node;
474 // param to convert
475 Bvariable *convert_me_param = param_vars.at (0);
476 tree convert_me_expr
477 = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
479 // check for transmute pre-conditions
480 tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
481 tree source_type_expr = compiled_types.at (0);
482 tree target_size_expr = TYPE_SIZE (target_type_expr);
483 tree source_size_expr = TYPE_SIZE (source_type_expr);
484 // for some reason, unit types and other zero-sized types return NULL for the
485 // size expressions
486 unsigned HOST_WIDE_INT target_size
487 = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
488 unsigned HOST_WIDE_INT source_size
489 = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
491 // size check for concrete types
492 // TODO(liushuyu): check alignment for pointers; check for dependently-sized
493 // types
494 if (target_size != source_size)
496 rust_error_at (fntype->get_locus (),
497 "cannot transmute between types of different sizes, or "
498 "dependently-sized types");
499 rust_inform (fntype->get_ident ().locus, "source type: %qs (%lu bits)",
500 fntype->get_params ().at (0).second->as_string ().c_str (),
501 (unsigned long) source_size);
502 rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
503 fntype->get_return_type ()->as_string ().c_str (),
504 (unsigned long) target_size);
507 enter_intrinsic_block (ctx, fndecl);
509 // BUILTIN transmute FN BODY BEGIN
511 // Return *((orig_type*)&decl) */
513 tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
514 t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
515 build_pointer_type (target_type_expr), t);
516 tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
518 auto return_statement
519 = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
520 ctx->add_statement (return_statement);
521 // BUILTIN transmute FN BODY END
523 finalize_intrinsic_block (ctx, fndecl);
525 return fndecl;
528 static tree
529 rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op)
531 // rotate intrinsic has two parameter
532 rust_assert (fntype->get_params ().size () == 2);
534 tree lookup = NULL_TREE;
535 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
536 return lookup;
538 auto fndecl = compile_intrinsic_function (ctx, fntype);
540 // setup the params
541 std::vector<Bvariable *> param_vars;
542 compile_fn_params (ctx, fntype, fndecl, &param_vars);
544 auto &x_param = param_vars.at (0);
545 auto &y_param = param_vars.at (1);
546 rust_assert (param_vars.size () == 2);
547 if (!Backend::function_set_parameters (fndecl, param_vars))
548 return error_mark_node;
550 enter_intrinsic_block (ctx, fndecl);
552 // BUILTIN rotate FN BODY BEGIN
553 tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
554 tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
555 tree rotate_expr
556 = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
557 auto return_statement
558 = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
559 ctx->add_statement (return_statement);
560 // BUILTIN rotate FN BODY END
562 finalize_intrinsic_block (ctx, fndecl);
564 return fndecl;
568 * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
570 static tree
571 wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
573 // wrapping_<op> intrinsics have two parameter
574 rust_assert (fntype->get_params ().size () == 2);
576 tree lookup = NULL_TREE;
577 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
578 return lookup;
580 auto fndecl = compile_intrinsic_function (ctx, fntype);
582 // setup the params
583 std::vector<Bvariable *> param_vars;
584 compile_fn_params (ctx, fntype, fndecl, &param_vars);
586 auto &lhs_param = param_vars.at (0);
587 auto &rhs_param = param_vars.at (1);
589 if (!Backend::function_set_parameters (fndecl, param_vars))
590 return error_mark_node;
592 enter_intrinsic_block (ctx, fndecl);
594 // BUILTIN wrapping_<op> FN BODY BEGIN
595 auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
596 auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
598 // Operations are always wrapping in Rust, as we have -fwrapv enabled by
599 // default. The difference between a wrapping_{add, sub, mul} and a regular
600 // arithmetic operation is that these intrinsics do not panic - they always
601 // carry over.
602 auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
604 auto return_statement
605 = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
606 ctx->add_statement (return_statement);
607 // BUILTIN wrapping_<op> FN BODY END
609 finalize_intrinsic_block (ctx, fndecl);
611 return fndecl;
615 * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
617 static tree
618 op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
620 // wrapping_<op> intrinsics have two parameter
621 rust_assert (fntype->get_params ().size () == 2);
623 tree lookup = NULL_TREE;
624 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
625 return lookup;
627 auto fndecl = compile_intrinsic_function (ctx, fntype);
629 // setup the params
630 std::vector<Bvariable *> param_vars;
631 compile_fn_params (ctx, fntype, fndecl, &param_vars);
633 auto &x_param = param_vars.at (0);
634 auto &y_param = param_vars.at (1);
636 if (!Backend::function_set_parameters (fndecl, param_vars))
637 return error_mark_node;
639 rust_assert (fntype->get_num_substitutions () == 1);
640 auto &param_mapping = fntype->get_substs ().at (0);
641 const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
642 TyTy::BaseType *resolved_tyty = param_tyty->resolve ();
643 tree template_parameter_type
644 = TyTyResolveCompile::compile (ctx, resolved_tyty);
646 // this should match y as well or we can take it from the TyTy structure
647 tree tmp_stmt = error_mark_node;
648 Bvariable *result_variable
649 = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
650 NULL_TREE, true /*address_is_taken*/,
651 UNDEF_LOCATION, &tmp_stmt);
652 Bvariable *bool_variable
653 = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
654 NULL_TREE, true /*address_is_taken*/,
655 UNDEF_LOCATION, &tmp_stmt);
657 enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
659 // BUILTIN op_with_overflow FN BODY BEGIN
660 auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
661 auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
663 tree overflow_builtin = error_mark_node;
664 switch (op)
666 case PLUS_EXPR:
667 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
668 &overflow_builtin);
669 break;
671 case MINUS_EXPR:
672 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
673 &overflow_builtin);
674 break;
676 case MULT_EXPR:
677 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
678 &overflow_builtin);
679 break;
681 default:
682 rust_unreachable ();
683 break;
685 rust_assert (overflow_builtin != error_mark_node);
687 tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
688 tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
689 tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
691 tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
692 3, x, y, result_ref);
694 tree overflow_assignment
695 = Backend::assignment_statement (bool_decl, builtin_call,
696 BUILTINS_LOCATION);
698 ctx->add_statement (overflow_assignment);
700 std::vector<tree> vals = {result_decl, bool_decl};
701 tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
702 tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
703 -1, UNDEF_LOCATION);
705 auto return_statement
706 = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
707 ctx->add_statement (return_statement);
709 // BUILTIN wrapping_<op> FN BODY END
711 finalize_intrinsic_block (ctx, fndecl);
713 return fndecl;
717 * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
718 * fn copy<T>(src: *const T, dst: *mut T, count: usize);
720 static tree
721 copy_handler_inner (Context *ctx, TyTy::FnType *fntype, bool overlaps)
723 rust_assert (fntype->get_params ().size () == 3);
724 rust_assert (fntype->get_num_substitutions () == 1);
726 tree lookup = NULL_TREE;
727 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
728 return lookup;
730 auto fndecl = compile_intrinsic_function (ctx, fntype);
732 // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
733 TREE_READONLY (fndecl) = 0;
734 TREE_SIDE_EFFECTS (fndecl) = 1;
736 // setup the params
737 std::vector<Bvariable *> param_vars;
738 compile_fn_params (ctx, fntype, fndecl, &param_vars);
740 if (!Backend::function_set_parameters (fndecl, param_vars))
741 return error_mark_node;
743 enter_intrinsic_block (ctx, fndecl);
745 // BUILTIN copy_nonoverlapping BODY BEGIN
747 auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
748 auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
749 auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
751 // We want to create the following statement
752 // memcpy(dst, src, size_of::<T>());
753 // so
754 // memcpy(dst, src, size_expr);
756 auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
757 auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
759 tree size_expr
760 = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
762 tree memcpy_raw = nullptr;
763 BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
764 : "__builtin_memcpy",
765 &memcpy_raw);
766 rust_assert (memcpy_raw);
767 auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
769 auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
770 nullptr, UNDEF_LOCATION);
772 ctx->add_statement (copy_call);
774 // BUILTIN copy_nonoverlapping BODY END
776 finalize_intrinsic_block (ctx, fndecl);
778 return fndecl;
781 static tree
782 make_unsigned_long_tree (unsigned long value)
784 return build_int_cst (integer_type_node, value);
787 static tree
788 prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
790 rust_assert (fntype->get_params ().size () == 2);
792 tree lookup = NULL_TREE;
793 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
794 return lookup;
796 auto fndecl = compile_intrinsic_function (ctx, fntype);
798 // prefetching isn't pure and shouldn't be discarded after GIMPLE
799 TREE_READONLY (fndecl) = 0;
800 TREE_SIDE_EFFECTS (fndecl) = 1;
802 std::vector<Bvariable *> args;
803 compile_fn_params (ctx, fntype, fndecl, &args);
805 if (!Backend::function_set_parameters (fndecl, args))
806 return error_mark_node;
808 enter_intrinsic_block (ctx, fndecl);
810 auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
812 // The core library technically allows you to pass any i32 value as a
813 // locality, but LLVM will then complain if the value cannot be constant
814 // evaluated. For now, we ignore the locality argument and instead always
815 // pass `3` (the most restrictive value). This allows us to still have
816 // prefetch behavior, just not as granular as expected. In future Rust
817 // versions, we hope that prefetch intrinsics will be split up according to
818 // locality, similarly to atomic intrinsics.
819 // The solution is to try and perform constant folding for the locality
820 // argument, or instead of creating a new function definition, modify the call
821 // site directly This has the bad side-effect of creating warnings about
822 // `unused name - locality`, which we hack away here:
823 // TODO: Take care of handling locality properly
824 Backend::var_expression (args[1], UNDEF_LOCATION);
826 auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
828 auto prefetch_raw = NULL_TREE;
829 auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
830 &prefetch_raw);
831 rust_assert (ok);
832 auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
834 auto prefetch_call = Backend::call_expression (prefetch,
835 {addr, rw_flag,
836 // locality arg
837 make_unsigned_long_tree (3)},
838 nullptr, UNDEF_LOCATION);
840 TREE_READONLY (prefetch_call) = 0;
841 TREE_SIDE_EFFECTS (prefetch_call) = 1;
843 ctx->add_statement (prefetch_call);
845 finalize_intrinsic_block (ctx, fndecl);
847 return fndecl;
850 static std::string
851 build_atomic_builtin_name (const std::string &prefix, location_t locus,
852 TyTy::BaseType *operand_type)
854 static const std::map<std::string, std::string> allowed_types = {
855 {"i8", "1"}, {"i16", "2"}, {"i32", "4"}, {"i64", "8"},
856 {"i128", "16"}, {"isize", "8"}, {"u8", "1"}, {"u16", "2"},
857 {"u32", "4"}, {"u64", "8"}, {"u128", "16"}, {"usize", "8"},
860 // TODO: Can we maybe get the generic version (atomic_store_n) to work... This
861 // would be so much better
863 std::string result = "__" + prefix; // + "n";
865 auto type_name = operand_type->get_name ();
866 if (type_name == "usize" || type_name == "isize")
868 rust_sorry_at (
869 locus, "atomics are not yet available for size types (usize, isize)");
870 return "";
873 if (type_name.at (0) == 'i')
875 rust_sorry_at (locus, "atomics are not yet supported for signed "
876 "integer types (i8, i16, i32, i64, i128)");
877 return "";
880 auto type_size_str = allowed_types.find (type_name);
882 if (!check_for_basic_integer_type ("atomic", locus, operand_type))
883 return "";
885 result += type_size_str->second;
887 return result;
890 static tree
891 atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering)
893 rust_assert (fntype->get_params ().size () == 2);
894 rust_assert (fntype->get_num_substitutions () == 1);
896 tree lookup = NULL_TREE;
897 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
898 return lookup;
900 auto fndecl = compile_intrinsic_function (ctx, fntype);
902 // Most intrinsic functions are pure but not the atomic ones
903 TREE_READONLY (fndecl) = 0;
904 TREE_SIDE_EFFECTS (fndecl) = 1;
906 // setup the params
907 std::vector<Bvariable *> param_vars;
908 std::vector<tree> types;
909 compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
911 auto ok = Backend::function_set_parameters (fndecl, param_vars);
912 rust_assert (ok);
914 enter_intrinsic_block (ctx, fndecl);
916 auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
917 TREE_READONLY (dst) = 0;
919 auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
920 auto memorder = make_unsigned_long_tree (ordering);
922 auto monomorphized_type
923 = fntype->get_substs ()[0].get_param_ty ()->resolve ();
925 auto builtin_name
926 = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (),
927 monomorphized_type);
928 if (builtin_name.empty ())
929 return error_mark_node;
931 tree atomic_store_raw = nullptr;
932 BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
933 &atomic_store_raw);
934 rust_assert (atomic_store_raw);
936 auto atomic_store
937 = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
939 auto store_call
940 = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
941 UNDEF_LOCATION);
942 TREE_READONLY (store_call) = 0;
943 TREE_SIDE_EFFECTS (store_call) = 1;
945 ctx->add_statement (store_call);
946 finalize_intrinsic_block (ctx, fndecl);
948 return fndecl;
951 static tree
952 atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering)
954 rust_assert (fntype->get_params ().size () == 1);
955 rust_assert (fntype->get_num_substitutions () == 1);
957 tree lookup = NULL_TREE;
958 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
959 return lookup;
961 auto fndecl = compile_intrinsic_function (ctx, fntype);
963 // Most intrinsic functions are pure but not the atomic ones
964 // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
965 TREE_READONLY (fndecl) = 0;
966 TREE_SIDE_EFFECTS (fndecl) = 1;
968 // setup the params
969 std::vector<Bvariable *> param_vars;
970 std::vector<tree> types;
971 compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
973 auto ok = Backend::function_set_parameters (fndecl, param_vars);
974 rust_assert (ok);
976 enter_intrinsic_block (ctx, fndecl);
978 auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
979 auto memorder = make_unsigned_long_tree (ordering);
981 auto monomorphized_type
982 = fntype->get_substs ()[0].get_param_ty ()->resolve ();
984 auto builtin_name
985 = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
986 monomorphized_type);
987 if (builtin_name.empty ())
988 return error_mark_node;
990 tree atomic_load_raw = nullptr;
991 BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
992 &atomic_load_raw);
993 rust_assert (atomic_load_raw);
995 auto atomic_load
996 = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
998 auto load_call = Backend::call_expression (atomic_load, {src, memorder},
999 nullptr, UNDEF_LOCATION);
1000 auto return_statement
1001 = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
1003 TREE_READONLY (load_call) = 0;
1004 TREE_SIDE_EFFECTS (load_call) = 1;
1006 ctx->add_statement (return_statement);
1008 finalize_intrinsic_block (ctx, fndecl);
1010 return fndecl;
1013 static inline tree
1014 unchecked_op_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
1016 rust_assert (fntype->get_params ().size () == 2);
1017 rust_assert (fntype->get_num_substitutions () == 1);
1019 tree lookup = NULL_TREE;
1020 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1021 return lookup;
1023 auto fndecl = compile_intrinsic_function (ctx, fntype);
1025 // setup the params
1026 std::vector<Bvariable *> param_vars;
1027 compile_fn_params (ctx, fntype, fndecl, &param_vars);
1029 if (!Backend::function_set_parameters (fndecl, param_vars))
1030 return error_mark_node;
1032 enter_intrinsic_block (ctx, fndecl);
1034 // BUILTIN unchecked_<op> BODY BEGIN
1036 auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1037 auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1039 auto *monomorphized_type
1040 = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
1042 check_for_basic_integer_type ("unchecked operation", fntype->get_locus (),
1043 monomorphized_type);
1045 auto expr = build2 (op, TREE_TYPE (x), x, y);
1046 auto return_statement
1047 = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
1049 ctx->add_statement (return_statement);
1051 // BUILTIN unchecked_<op> BODY END
1053 finalize_intrinsic_block (ctx, fndecl);
1055 return fndecl;
1058 static tree
1059 uninit_handler (Context *ctx, TyTy::FnType *fntype)
1061 // uninit has _zero_ parameters its parameter is the generic one
1062 rust_assert (fntype->get_params ().size () == 0);
1064 tree lookup = NULL_TREE;
1065 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1066 return lookup;
1068 auto fndecl = compile_intrinsic_function (ctx, fntype);
1070 // Most intrinsic functions are pure - not `uninit_handler`
1071 TREE_READONLY (fndecl) = 0;
1072 TREE_SIDE_EFFECTS (fndecl) = 1;
1074 // get the template parameter type tree fn uninit<T>();
1075 rust_assert (fntype->get_num_substitutions () == 1);
1076 auto &param_mapping = fntype->get_substs ().at (0);
1077 const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
1078 TyTy::BaseType *resolved_tyty = param_tyty->resolve ();
1079 tree template_parameter_type
1080 = TyTyResolveCompile::compile (ctx, resolved_tyty);
1082 // result temporary
1083 tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
1084 rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
1085 == TYPE_SIZE_UNIT (dst_type));
1087 tree tmp_stmt = error_mark_node;
1088 Bvariable *bvar
1089 = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
1090 true /*address_is_taken*/, UNDEF_LOCATION,
1091 &tmp_stmt);
1093 enter_intrinsic_block (ctx, fndecl, {bvar});
1095 // BUILTIN size_of FN BODY BEGIN
1097 tree memset_builtin = error_mark_node;
1098 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
1099 &memset_builtin);
1100 rust_assert (memset_builtin != error_mark_node);
1102 // call memset with 0x01 and size of the thing see
1103 // https://github.com/Rust-GCC/gccrs/issues/1899
1105 tree dst = bvar->get_tree (BUILTINS_LOCATION);
1106 tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
1107 tree constant_byte = build_int_cst (integer_type_node, 0x01);
1108 tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1110 tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
1111 dst_addr, constant_byte, size_expr);
1112 ctx->add_statement (memset_call);
1114 auto return_statement
1115 = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
1116 ctx->add_statement (return_statement);
1117 // BUILTIN size_of FN BODY END
1119 finalize_intrinsic_block (ctx, fndecl);
1121 return fndecl;
1124 static tree
1125 move_val_init_handler (Context *ctx, TyTy::FnType *fntype)
1127 rust_assert (fntype->get_params ().size () == 2);
1129 tree lookup = NULL_TREE;
1130 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1131 return lookup;
1133 auto fndecl = compile_intrinsic_function (ctx, fntype);
1135 // Most intrinsic functions are pure - not `move_val_init`
1136 TREE_READONLY (fndecl) = 0;
1137 TREE_SIDE_EFFECTS (fndecl) = 1;
1139 // get the template parameter type tree fn size_of<T>();
1140 rust_assert (fntype->get_num_substitutions () == 1);
1141 auto &param_mapping = fntype->get_substs ().at (0);
1142 const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
1143 TyTy::BaseType *resolved_tyty = param_tyty->resolve ();
1144 tree template_parameter_type
1145 = TyTyResolveCompile::compile (ctx, resolved_tyty);
1147 std::vector<Bvariable *> param_vars;
1148 compile_fn_params (ctx, fntype, fndecl, &param_vars);
1150 if (!Backend::function_set_parameters (fndecl, param_vars))
1151 return error_mark_node;
1153 enter_intrinsic_block (ctx, fndecl);
1155 // BUILTIN size_of FN BODY BEGIN
1157 tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1158 tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1159 tree size = TYPE_SIZE_UNIT (template_parameter_type);
1161 tree memcpy_builtin = error_mark_node;
1162 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
1163 &memcpy_builtin);
1164 rust_assert (memcpy_builtin != error_mark_node);
1166 src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1167 tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1168 dst, src, size);
1170 ctx->add_statement (memset_call);
1171 // BUILTIN size_of FN BODY END
1173 finalize_intrinsic_block (ctx, fndecl);
1175 return fndecl;
1178 static inline tree
1179 expect_handler_inner (Context *ctx, TyTy::FnType *fntype, bool likely)
1181 rust_assert (fntype->get_params ().size () == 1);
1183 tree lookup = NULL_TREE;
1184 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1185 return lookup;
1187 auto fndecl = compile_intrinsic_function (ctx, fntype);
1189 enter_intrinsic_block (ctx, fndecl);
1191 // BUILTIN expect_handler_inner FN BODY BEGIN
1192 // setup the params
1193 std::vector<Bvariable *> param_vars;
1194 compile_fn_params (ctx, fntype, fndecl, &param_vars);
1195 tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1196 tree expect_fn_raw = nullptr;
1197 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
1198 &expect_fn_raw);
1199 rust_assert (expect_fn_raw);
1200 auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
1202 // we need to convert the expression return type to long to match the expected
1203 // parameter type of __builtin_expect
1204 auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
1205 auto expect_value
1206 = make_unsigned_long_tree (static_cast<unsigned long> (likely));
1208 auto expect_call
1209 = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
1210 BUILTINS_LOCATION);
1211 // the return value also needs to be casted (to bool)
1212 auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, expect_call);
1213 auto return_statement
1214 = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
1215 ctx->add_statement (return_statement);
1216 // BUILTIN expect_handler_inner FN BODY END
1218 finalize_intrinsic_block (ctx, fndecl);
1220 return fndecl;
1223 static tree
1224 assume_handler (Context *ctx, TyTy::FnType *fntype)
1226 // TODO: make sure this is actually helping the compiler optimize
1228 rust_assert (fntype->get_params ().size () == 1);
1229 rust_assert (fntype->param_at (0).second->get_kind ()
1230 == TyTy::TypeKind::BOOL);
1232 tree lookup = NULL_TREE;
1233 if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1234 return lookup;
1236 auto fndecl = compile_intrinsic_function (ctx, fntype);
1238 // TODO: make sure these are necessary
1239 TREE_READONLY (fndecl) = 0;
1240 DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
1241 DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
1242 NULL_TREE, DECL_ATTRIBUTES (fndecl));
1244 std::vector<Bvariable *> param_vars;
1245 compile_fn_params (ctx, fntype, fndecl, &param_vars);
1247 if (!Backend::function_set_parameters (fndecl, param_vars))
1248 return error_mark_node;
1250 enter_intrinsic_block (ctx, fndecl);
1252 // BUILTIN assume FN BODY BEGIN
1254 tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1256 tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
1257 void_type_node, 1, val);
1258 TREE_SIDE_EFFECTS (assume_expr) = 1;
1260 ctx->add_statement (assume_expr);
1261 // BUILTIN size_of FN BODY END
1263 finalize_intrinsic_block (ctx, fndecl);
1265 return fndecl;
1268 } // namespace Compile
1269 } // namespace Rust