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
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
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"
29 #include "print-tree.h"
30 #include "fold-const.h"
31 #include "langhooks.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
40 get_identifier (const char *);
46 is_basic_integer_type (TyTy::BaseType
*type
)
48 switch (type
->get_kind ())
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
)
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
;
78 offset_handler (Context
*ctx
, TyTy::FnType
*fntype
);
80 sizeof_handler (Context
*ctx
, TyTy::FnType
*fntype
);
82 transmute_handler (Context
*ctx
, TyTy::FnType
*fntype
);
84 rotate_handler (Context
*ctx
, TyTy::FnType
*fntype
, tree_code op
);
86 wrapping_op_handler_inner (Context
*ctx
, TyTy::FnType
*fntype
, tree_code op
);
88 op_with_overflow_inner (Context
*ctx
, TyTy::FnType
*fntype
, tree_code op
);
90 uninit_handler (Context
*ctx
, TyTy::FnType
*fntype
);
92 move_val_init_handler (Context
*ctx
, TyTy::FnType
*fntype
);
94 assume_handler (Context
*ctx
, TyTy::FnType
*fntype
);
103 prefetch_data_handler (Context
*ctx
, TyTy::FnType
*fntype
, Prefetch kind
);
106 rotate_left_handler (Context
*ctx
, TyTy::FnType
*fntype
)
108 return rotate_handler (ctx
, fntype
, LROTATE_EXPR
);
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
);
133 prefetch_read_data (Context
*ctx
, TyTy::FnType
*fntype
)
135 return prefetch_data_handler (ctx
, fntype
, Prefetch::Read
);
138 prefetch_write_data (Context
*ctx
, TyTy::FnType
*fntype
)
140 return prefetch_data_handler (ctx
, fntype
, Prefetch::Write
);
144 atomic_store_handler_inner (Context
*ctx
, TyTy::FnType
*fntype
, int ordering
);
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
);
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
);
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
);
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
);
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
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
))
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.
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
))
299 * Maybe override the Hir Lookups for the substituions in this context
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
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 ¶m_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
);
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;
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
,
369 ctx
->push_block (block
);
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
);
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
, ¶m_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
);
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
))
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 ¶m_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
);
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
))
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
, ¶m_vars
, &compiled_types
);
471 if (!Backend::function_set_parameters (fndecl
, param_vars
))
472 return error_mark_node
;
475 Bvariable
*convert_me_param
= param_vars
.at (0);
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
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
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
);
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
))
538 auto fndecl
= compile_intrinsic_function (ctx
, fntype
);
541 std::vector
<Bvariable
*> param_vars
;
542 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_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
);
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
);
568 * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
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
))
580 auto fndecl
= compile_intrinsic_function (ctx
, fntype
);
583 std::vector
<Bvariable
*> param_vars
;
584 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_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
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
);
615 * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
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
))
627 auto fndecl
= compile_intrinsic_function (ctx
, fntype
);
630 std::vector
<Bvariable
*> param_vars
;
631 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_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 ¶m_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
;
667 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
672 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
677 BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
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
,
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
,
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
);
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);
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
))
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;
737 std::vector
<Bvariable
*> param_vars
;
738 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_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>());
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
);
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",
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
);
782 make_unsigned_long_tree (unsigned long value
)
784 return build_int_cst (integer_type_node
, value
);
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
))
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",
832 auto prefetch
= build_fold_addr_expr_loc (UNKNOWN_LOCATION
, prefetch_raw
);
834 auto prefetch_call
= Backend::call_expression (prefetch
,
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
);
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")
869 locus
, "atomics are not yet available for size types (usize, isize)");
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)");
880 auto type_size_str
= allowed_types
.find (type_name
);
882 if (!check_for_basic_integer_type ("atomic", locus
, operand_type
))
885 result
+= type_size_str
->second
;
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
))
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;
907 std::vector
<Bvariable
*> param_vars
;
908 std::vector
<tree
> types
;
909 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_vars
, &types
);
911 auto ok
= Backend::function_set_parameters (fndecl
, param_vars
);
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 ();
926 = build_atomic_builtin_name ("atomic_store_", fntype
->get_locus (),
928 if (builtin_name
.empty ())
929 return error_mark_node
;
931 tree atomic_store_raw
= nullptr;
932 BuiltinsContext::get ().lookup_simple_builtin (builtin_name
,
934 rust_assert (atomic_store_raw
);
937 = build_fold_addr_expr_loc (UNKNOWN_LOCATION
, atomic_store_raw
);
940 = Backend::call_expression (atomic_store
, {dst
, value
, memorder
}, nullptr,
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
);
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
))
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;
969 std::vector
<Bvariable
*> param_vars
;
970 std::vector
<tree
> types
;
971 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_vars
, &types
);
973 auto ok
= Backend::function_set_parameters (fndecl
, param_vars
);
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 ();
985 = build_atomic_builtin_name ("atomic_load_", fntype
->get_locus (),
987 if (builtin_name
.empty ())
988 return error_mark_node
;
990 tree atomic_load_raw
= nullptr;
991 BuiltinsContext::get ().lookup_simple_builtin (builtin_name
,
993 rust_assert (atomic_load_raw
);
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
);
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
))
1023 auto fndecl
= compile_intrinsic_function (ctx
, fntype
);
1026 std::vector
<Bvariable
*> param_vars
;
1027 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_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
);
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
))
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 ¶m_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
);
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
;
1089 = Backend::temporary_variable (fndecl
, NULL_TREE
, dst_type
, NULL_TREE
,
1090 true /*address_is_taken*/, UNDEF_LOCATION
,
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",
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
);
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
))
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 ¶m_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
, ¶m_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",
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,
1170 ctx
->add_statement (memset_call
);
1171 // BUILTIN size_of FN BODY END
1173 finalize_intrinsic_block (ctx
, fndecl
);
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
))
1187 auto fndecl
= compile_intrinsic_function (ctx
, fntype
);
1189 enter_intrinsic_block (ctx
, fndecl
);
1191 // BUILTIN expect_handler_inner FN BODY BEGIN
1193 std::vector
<Bvariable
*> param_vars
;
1194 compile_fn_params (ctx
, fntype
, fndecl
, ¶m_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",
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
);
1206 = make_unsigned_long_tree (static_cast<unsigned long> (likely
));
1209 = Backend::call_expression (expect_fn
, {expect_src
, expect_value
}, nullptr,
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
);
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
))
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
, ¶m_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
);
1268 } // namespace Compile