Line data Source code
1 : // This file is part of GCC.
2 :
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.
7 :
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.
12 :
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/>.
16 :
17 : #include "rust-compile-intrinsic.h"
18 : #include "rust-compile-context.h"
19 : #include "rust-compile-type.h"
20 : #include "rust-compile-fnparam.h"
21 : #include "rust-builtins.h"
22 : #include "rust-diagnostics.h"
23 : #include "rust-location.h"
24 : #include "rust-constexpr.h"
25 : #include "rust-session-manager.h"
26 : #include "rust-tree.h"
27 : #include "tree-core.h"
28 : #include "rust-gcc.h"
29 : #include "fold-const.h"
30 : #include "langhooks.h"
31 : #include "rust-constexpr.h"
32 :
33 : // declaration taken from "stringpool.h"
34 : // the get_identifier macro causes compilation issues
35 : extern tree get_identifier (const char *);
36 :
37 : namespace Rust {
38 : namespace Compile {
39 :
40 : static bool
41 154 : is_basic_integer_type (TyTy::BaseType *type)
42 : {
43 154 : switch (type->get_kind ())
44 : {
45 : case TyTy::INT:
46 : case TyTy::UINT:
47 : case TyTy::USIZE:
48 : case TyTy::ISIZE:
49 : return true;
50 21 : default:
51 21 : return false;
52 : break;
53 : }
54 : }
55 :
56 : static bool
57 154 : check_for_basic_integer_type (const std::string &intrinsic_str,
58 : location_t locus, TyTy::BaseType *type)
59 : {
60 154 : auto is_basic_integer = is_basic_integer_type (type);
61 154 : if (!is_basic_integer)
62 : {
63 21 : rust_error_at (
64 : locus,
65 : "%s intrinsics can only be used with basic integer types (got %qs)",
66 42 : intrinsic_str.c_str (), type->get_name ().c_str ());
67 : }
68 :
69 154 : return is_basic_integer;
70 : }
71 :
72 : static tree offset_handler (Context *ctx, TyTy::FnType *fntype);
73 : static tree sizeof_handler (Context *ctx, TyTy::FnType *fntype);
74 : static tree transmute_handler (Context *ctx, TyTy::FnType *fntype);
75 : static tree rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
76 : static tree wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype,
77 : tree_code op);
78 : static tree op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype,
79 : tree_code op);
80 : static tree uninit_handler (Context *ctx, TyTy::FnType *fntype);
81 : static tree move_val_init_handler (Context *ctx, TyTy::FnType *fntype);
82 : static tree assume_handler (Context *ctx, TyTy::FnType *fntype);
83 : static tree discriminant_value_handler (Context *ctx, TyTy::FnType *fntype);
84 : static tree variant_count_handler (Context *ctx, TyTy::FnType *fntype);
85 :
86 : enum class Prefetch
87 : {
88 : Read,
89 : Write
90 : };
91 :
92 : static tree prefetch_data_handler (Context *ctx, TyTy::FnType *fntype,
93 : Prefetch kind);
94 :
95 : static inline tree
96 198 : rotate_left_handler (Context *ctx, TyTy::FnType *fntype)
97 : {
98 198 : return rotate_handler (ctx, fntype, LROTATE_EXPR);
99 : }
100 : static inline tree
101 155 : rotate_right_handler (Context *ctx, TyTy::FnType *fntype)
102 : {
103 155 : return rotate_handler (ctx, fntype, RROTATE_EXPR);
104 : }
105 :
106 : const static std::function<tree (Context *, TyTy::FnType *)>
107 0 : wrapping_op_handler (tree_code op)
108 : {
109 310 : return [op] (Context *ctx, TyTy::FnType *fntype) {
110 310 : return wrapping_op_handler_inner (ctx, fntype, op);
111 0 : };
112 : }
113 :
114 : const static std::function<tree (Context *, TyTy::FnType *)>
115 0 : op_with_overflow (tree_code op)
116 : {
117 141 : return [op] (Context *ctx, TyTy::FnType *fntype) {
118 141 : return op_with_overflow_inner (ctx, fntype, op);
119 0 : };
120 : }
121 :
122 : static inline tree
123 7 : prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
124 : {
125 7 : return prefetch_data_handler (ctx, fntype, Prefetch::Read);
126 : }
127 : static inline tree
128 7 : prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
129 : {
130 7 : return prefetch_data_handler (ctx, fntype, Prefetch::Write);
131 : }
132 :
133 : static tree atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype,
134 : int ordering);
135 : static tree atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype,
136 : int ordering);
137 :
138 : static inline std::function<tree (Context *, TyTy::FnType *)>
139 : atomic_store_handler (int ordering)
140 : {
141 70 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
142 70 : return atomic_store_handler_inner (ctx, fntype, ordering);
143 : };
144 : }
145 :
146 : static inline std::function<tree (Context *, TyTy::FnType *)>
147 : atomic_load_handler (int ordering)
148 : {
149 28 : return [ordering] (Context *ctx, TyTy::FnType *fntype) {
150 28 : return atomic_load_handler_inner (ctx, fntype, ordering);
151 : };
152 : }
153 :
154 : static inline tree unchecked_op_inner (Context *ctx, TyTy::FnType *fntype,
155 : tree_code op);
156 :
157 : const static std::function<tree (Context *, TyTy::FnType *)>
158 0 : unchecked_op_handler (tree_code op)
159 : {
160 56 : return [op] (Context *ctx, TyTy::FnType *fntype) {
161 56 : return unchecked_op_inner (ctx, fntype, op);
162 0 : };
163 : }
164 :
165 : static inline tree copy_handler_inner (Context *ctx, TyTy::FnType *fntype,
166 : bool overlaps);
167 :
168 : const static std::function<tree (Context *, TyTy::FnType *)>
169 0 : copy_handler (bool overlaps)
170 : {
171 243 : return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
172 243 : return copy_handler_inner (ctx, fntype, overlaps);
173 0 : };
174 : }
175 :
176 : static inline tree expect_handler_inner (Context *ctx, TyTy::FnType *fntype,
177 : bool likely);
178 :
179 : const static std::function<tree (Context *, TyTy::FnType *)>
180 0 : expect_handler (bool likely)
181 : {
182 0 : return [likely] (Context *ctx, TyTy::FnType *fntype) {
183 0 : return expect_handler_inner (ctx, fntype, likely);
184 0 : };
185 : }
186 :
187 : static tree try_handler_inner (Context *ctx, TyTy::FnType *fntype,
188 : bool is_new_api);
189 :
190 : const static std::function<tree (Context *, TyTy::FnType *)>
191 0 : try_handler (bool is_new_api)
192 : {
193 2 : return [is_new_api] (Context *ctx, TyTy::FnType *fntype) {
194 2 : return try_handler_inner (ctx, fntype, is_new_api);
195 0 : };
196 : }
197 :
198 : inline tree
199 : sorry_handler (Context *ctx, TyTy::FnType *fntype)
200 : {
201 : rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
202 : fntype->get_identifier ().c_str ());
203 :
204 : return error_mark_node;
205 : }
206 :
207 : static const std::map<std::string,
208 : std::function<tree (Context *, TyTy::FnType *)>>
209 : generic_intrinsics
210 : = {{"offset", offset_handler},
211 : {"size_of", sizeof_handler},
212 : {"transmute", transmute_handler},
213 : {"rotate_left", rotate_left_handler},
214 : {"rotate_right", rotate_right_handler},
215 : {"wrapping_add", wrapping_op_handler (PLUS_EXPR)},
216 : {"wrapping_sub", wrapping_op_handler (MINUS_EXPR)},
217 : {"wrapping_mul", wrapping_op_handler (MULT_EXPR)},
218 : {"add_with_overflow", op_with_overflow (PLUS_EXPR)},
219 : {"sub_with_overflow", op_with_overflow (MINUS_EXPR)},
220 : {"mul_with_overflow", op_with_overflow (MULT_EXPR)},
221 : {"copy", copy_handler (true)},
222 : {"copy_nonoverlapping", copy_handler (false)},
223 : {"prefetch_read_data", prefetch_read_data},
224 : {"prefetch_write_data", prefetch_write_data},
225 : {"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)},
226 : {"atomic_store_release", atomic_store_handler (__ATOMIC_RELEASE)},
227 : {"atomic_store_relaxed", atomic_store_handler (__ATOMIC_RELAXED)},
228 : {"atomic_store_unordered", atomic_store_handler (__ATOMIC_RELAXED)},
229 : {"atomic_load_seqcst", atomic_load_handler (__ATOMIC_SEQ_CST)},
230 : {"atomic_load_acquire", atomic_load_handler (__ATOMIC_ACQUIRE)},
231 : {"atomic_load_relaxed", atomic_load_handler (__ATOMIC_RELAXED)},
232 : {"atomic_load_unordered", atomic_load_handler (__ATOMIC_RELAXED)},
233 : {"unchecked_add", unchecked_op_handler (PLUS_EXPR)},
234 : {"unchecked_sub", unchecked_op_handler (MINUS_EXPR)},
235 : {"unchecked_mul", unchecked_op_handler (MULT_EXPR)},
236 : {"unchecked_div", unchecked_op_handler (TRUNC_DIV_EXPR)},
237 : {"unchecked_rem", unchecked_op_handler (TRUNC_MOD_EXPR)},
238 : {"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)},
239 : {"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)},
240 : {"uninit", uninit_handler},
241 : {"move_val_init", move_val_init_handler},
242 : {"likely", expect_handler (true)},
243 : {"unlikely", expect_handler (false)},
244 : {"assume", assume_handler},
245 : {"try", try_handler (false)},
246 : {"catch_unwind", try_handler (true)},
247 : {"discriminant_value", discriminant_value_handler},
248 : {"variant_count", variant_count_handler}};
249 :
250 2497 : Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
251 :
252 : /**
253 : * Returns a FUNC_DECL corresponding to the intrinsic function FNTYPE. If a
254 : * corresponding builtin exists, returns it. If not, search in the generic
255 : * intrinsics declared and delegate the return to the corresponding handler.
256 : *
257 : * @param fntype The Rust function type that should be implemented by the
258 : * compiler
259 : */
260 : tree
261 2497 : Intrinsics::compile (TyTy::FnType *fntype)
262 : {
263 2497 : rust_assert (fntype->get_abi () == ABI::INTRINSIC);
264 :
265 2497 : tree builtin = error_mark_node;
266 2497 : BuiltinsContext &builtin_ctx = BuiltinsContext::get ();
267 :
268 4994 : if (builtin_ctx.lookup_simple_builtin (fntype->get_identifier (), &builtin))
269 308 : return builtin;
270 :
271 : // is it an generic builtin?
272 4378 : auto it = generic_intrinsics.find (fntype->get_identifier ());
273 2189 : if (it != generic_intrinsics.end ())
274 2182 : return it->second (ctx, fntype);
275 :
276 7 : location_t locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
277 14 : rust_error_at (locus, ErrorCode::E0093,
278 : "unrecognized intrinsic function: %qs",
279 7 : fntype->get_identifier ().c_str ());
280 :
281 7 : return error_mark_node;
282 : }
283 :
284 : /**
285 : * Items can be forward compiled which means we may not need to invoke this
286 : * code. We might also have already compiled this generic function as well.
287 : */
288 : static bool
289 2110 : check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
290 : {
291 2110 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
292 2110 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
293 2110 : if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
294 : fntype->get_id (), fntype, asm_name))
295 : {
296 : return true;
297 : }
298 :
299 : return false;
300 2110 : }
301 :
302 : /**
303 : * Maybe override the Hir Lookups for the substituions in this context
304 : */
305 : static void
306 2182 : maybe_override_ctx (TyTy::FnType *fntype)
307 : {
308 2182 : if (fntype->has_substitutions_defined ())
309 2179 : fntype->override_context ();
310 2182 : }
311 :
312 : /**
313 : * Compile and setup a function's parameters
314 : */
315 : static void
316 1616 : compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
317 : std::vector<Bvariable *> *compiled_param_variables,
318 : std::vector<tree_node *> *compiled_param_types = nullptr)
319 : {
320 4757 : for (auto &parm : fntype->get_params ())
321 : {
322 3141 : auto &referenced_param = parm.get_pattern ();
323 3141 : auto param_tyty = parm.get_type ();
324 3141 : auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
325 :
326 3141 : location_t param_locus = referenced_param.get_locus ();
327 3141 : Bvariable *compiled_param_var
328 3141 : = CompileFnParam::compile (ctx, fndecl, referenced_param,
329 3141 : compiled_param_type, param_locus);
330 :
331 3141 : compiled_param_variables->push_back (compiled_param_var);
332 3141 : if (compiled_param_types)
333 359 : compiled_param_types->push_back (compiled_param_type);
334 : }
335 1616 : }
336 :
337 : static tree
338 2182 : compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
339 : {
340 2182 : maybe_override_ctx (fntype);
341 :
342 2182 : const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
343 :
344 2182 : tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
345 2182 : std::string ir_symbol_name
346 2182 : = canonical_path.get () + fntype->subst_as_string ();
347 2182 : std::string asm_name = ctx->mangle_item (fntype, canonical_path);
348 :
349 2182 : unsigned int flags = 0;
350 2182 : tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
351 2182 : flags, fntype->get_ident ().locus);
352 :
353 2182 : TREE_PUBLIC (fndecl) = 0;
354 2182 : TREE_READONLY (fndecl) = 1;
355 2182 : DECL_ARTIFICIAL (fndecl) = 1;
356 2182 : DECL_EXTERNAL (fndecl) = 0;
357 2182 : DECL_DECLARED_INLINE_P (fndecl) = 1;
358 :
359 2182 : return fndecl;
360 2182 : }
361 :
362 : static void
363 2182 : enter_intrinsic_block (Context *ctx, tree fndecl,
364 : const std::vector<Bvariable *> &vars = {})
365 : {
366 2182 : tree enclosing_scope = NULL_TREE;
367 2182 : location_t start_location = UNDEF_LOCATION;
368 2182 : location_t end_location = UNDEF_LOCATION;
369 :
370 2182 : auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
371 : end_location);
372 :
373 2182 : ctx->push_block (block);
374 2182 : }
375 :
376 : static void
377 2168 : finalize_intrinsic_block (Context *ctx, tree fndecl)
378 : {
379 2168 : tree bind_tree = ctx->pop_block ();
380 :
381 2168 : gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
382 :
383 2168 : DECL_SAVED_TREE (fndecl) = bind_tree;
384 :
385 2168 : ctx->push_function (fndecl);
386 :
387 2168 : DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
388 2168 : maybe_save_constexpr_fundef (fndecl);
389 2168 : }
390 :
391 : static tree
392 72 : offset_handler (Context *ctx, TyTy::FnType *fntype)
393 : {
394 : // offset intrinsic has two params dst pointer and offset isize
395 72 : rust_assert (fntype->get_params ().size () == 2);
396 :
397 72 : auto fndecl = compile_intrinsic_function (ctx, fntype);
398 :
399 72 : std::vector<Bvariable *> param_vars;
400 72 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
401 :
402 72 : auto &dst_param = param_vars.at (0);
403 72 : auto &size_param = param_vars.at (1);
404 72 : rust_assert (param_vars.size () == 2);
405 72 : if (!Backend::function_set_parameters (fndecl, param_vars))
406 0 : return error_mark_node;
407 :
408 72 : enter_intrinsic_block (ctx, fndecl);
409 :
410 : // BUILTIN offset FN BODY BEGIN
411 72 : tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
412 72 : tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
413 72 : tree pointer_offset_expr
414 72 : = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
415 72 : auto return_statement
416 72 : = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
417 72 : ctx->add_statement (return_statement);
418 : // BUILTIN offset FN BODY END
419 :
420 72 : finalize_intrinsic_block (ctx, fndecl);
421 :
422 72 : return fndecl;
423 72 : }
424 :
425 : static tree
426 474 : sizeof_handler (Context *ctx, TyTy::FnType *fntype)
427 : {
428 : // size_of has _zero_ parameters its parameter is the generic one
429 474 : rust_assert (fntype->get_params ().size () == 0);
430 :
431 474 : tree lookup = NULL_TREE;
432 474 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
433 0 : return lookup;
434 :
435 474 : auto fndecl = compile_intrinsic_function (ctx, fntype);
436 :
437 : // get the template parameter type tree fn size_of<T>();
438 474 : rust_assert (fntype->get_num_substitutions () == 1);
439 474 : auto ¶m_mapping = fntype->get_substs ().at (0);
440 474 : const auto param_tyty = param_mapping.get_param_ty ();
441 474 : auto resolved_tyty = param_tyty->resolve ();
442 474 : tree template_parameter_type
443 474 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
444 :
445 474 : enter_intrinsic_block (ctx, fndecl);
446 :
447 : // BUILTIN size_of FN BODY BEGIN
448 474 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
449 474 : auto return_statement
450 474 : = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
451 474 : ctx->add_statement (return_statement);
452 : // BUILTIN size_of FN BODY END
453 :
454 474 : finalize_intrinsic_block (ctx, fndecl);
455 :
456 474 : return fndecl;
457 : }
458 :
459 : static tree
460 191 : transmute_handler (Context *ctx, TyTy::FnType *fntype)
461 : {
462 : // transmute intrinsic has one parameter
463 191 : rust_assert (fntype->get_params ().size () == 1);
464 :
465 191 : tree lookup = NULL_TREE;
466 191 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
467 0 : return lookup;
468 :
469 191 : auto fndecl = compile_intrinsic_function (ctx, fntype);
470 :
471 191 : std::vector<Bvariable *> param_vars;
472 191 : std::vector<tree_node *> compiled_types;
473 191 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &compiled_types);
474 :
475 191 : if (!Backend::function_set_parameters (fndecl, param_vars))
476 0 : return error_mark_node;
477 :
478 : // param to convert
479 191 : Bvariable *convert_me_param = param_vars.at (0);
480 191 : tree convert_me_expr
481 191 : = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
482 :
483 : // check for transmute pre-conditions
484 191 : tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
485 191 : tree source_type_expr = compiled_types.at (0);
486 191 : tree target_size_expr = TYPE_SIZE (target_type_expr);
487 191 : tree source_size_expr = TYPE_SIZE (source_type_expr);
488 : // for some reason, unit types and other zero-sized types return NULL for the
489 : // size expressions
490 191 : unsigned HOST_WIDE_INT target_size
491 191 : = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
492 191 : unsigned HOST_WIDE_INT source_size
493 191 : = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
494 :
495 : // size check for concrete types
496 : // TODO(liushuyu): check alignment for pointers; check for dependently-sized
497 : // types
498 191 : if (target_size != source_size)
499 : {
500 7 : rust_error_at (fntype->get_locus (),
501 : "cannot transmute between types of different sizes, or "
502 : "dependently-sized types");
503 14 : rust_inform (
504 7 : fntype->get_ident ().locus, "source type: %qs (%lu bits)",
505 7 : fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
506 : (unsigned long) source_size);
507 7 : rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
508 14 : fntype->get_return_type ()->as_string ().c_str (),
509 : (unsigned long) target_size);
510 : }
511 :
512 191 : enter_intrinsic_block (ctx, fndecl);
513 :
514 : // BUILTIN transmute FN BODY BEGIN
515 :
516 : // Return *((orig_type*)&decl) */
517 :
518 191 : tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
519 191 : t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
520 : build_pointer_type (target_type_expr), t);
521 191 : tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
522 :
523 191 : auto return_statement
524 191 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
525 191 : ctx->add_statement (return_statement);
526 : // BUILTIN transmute FN BODY END
527 :
528 191 : finalize_intrinsic_block (ctx, fndecl);
529 :
530 191 : return fndecl;
531 191 : }
532 :
533 : static tree
534 353 : rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op)
535 : {
536 : // rotate intrinsic has two parameter
537 353 : rust_assert (fntype->get_params ().size () == 2);
538 :
539 353 : tree lookup = NULL_TREE;
540 353 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
541 0 : return lookup;
542 :
543 353 : auto fndecl = compile_intrinsic_function (ctx, fntype);
544 :
545 : // setup the params
546 353 : std::vector<Bvariable *> param_vars;
547 353 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
548 :
549 353 : auto &x_param = param_vars.at (0);
550 353 : auto &y_param = param_vars.at (1);
551 353 : rust_assert (param_vars.size () == 2);
552 353 : if (!Backend::function_set_parameters (fndecl, param_vars))
553 0 : return error_mark_node;
554 :
555 353 : enter_intrinsic_block (ctx, fndecl);
556 :
557 : // BUILTIN rotate FN BODY BEGIN
558 353 : tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
559 353 : tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
560 353 : tree rotate_expr
561 353 : = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
562 353 : auto return_statement
563 353 : = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
564 353 : ctx->add_statement (return_statement);
565 : // BUILTIN rotate FN BODY END
566 :
567 353 : finalize_intrinsic_block (ctx, fndecl);
568 :
569 353 : return fndecl;
570 353 : }
571 :
572 : /**
573 : * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
574 : */
575 : static tree
576 310 : wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
577 : {
578 : // wrapping_<op> intrinsics have two parameter
579 310 : rust_assert (fntype->get_params ().size () == 2);
580 :
581 310 : tree lookup = NULL_TREE;
582 310 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
583 0 : return lookup;
584 :
585 310 : auto fndecl = compile_intrinsic_function (ctx, fntype);
586 :
587 : // setup the params
588 310 : std::vector<Bvariable *> param_vars;
589 310 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
590 :
591 310 : auto &lhs_param = param_vars.at (0);
592 310 : auto &rhs_param = param_vars.at (1);
593 :
594 310 : if (!Backend::function_set_parameters (fndecl, param_vars))
595 0 : return error_mark_node;
596 :
597 310 : enter_intrinsic_block (ctx, fndecl);
598 :
599 : // BUILTIN wrapping_<op> FN BODY BEGIN
600 310 : auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
601 310 : auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
602 :
603 : // Operations are always wrapping in Rust, as we have -fwrapv enabled by
604 : // default. The difference between a wrapping_{add, sub, mul} and a regular
605 : // arithmetic operation is that these intrinsics do not panic - they always
606 : // carry over.
607 310 : auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
608 :
609 310 : auto return_statement
610 310 : = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
611 310 : ctx->add_statement (return_statement);
612 : // BUILTIN wrapping_<op> FN BODY END
613 :
614 310 : finalize_intrinsic_block (ctx, fndecl);
615 :
616 310 : return fndecl;
617 310 : }
618 :
619 : /**
620 : * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
621 : */
622 : static tree
623 141 : op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
624 : {
625 : // wrapping_<op> intrinsics have two parameter
626 141 : rust_assert (fntype->get_params ().size () == 2);
627 :
628 141 : tree lookup = NULL_TREE;
629 141 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
630 0 : return lookup;
631 :
632 141 : auto fndecl = compile_intrinsic_function (ctx, fntype);
633 :
634 : // setup the params
635 141 : std::vector<Bvariable *> param_vars;
636 141 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
637 :
638 141 : auto &x_param = param_vars.at (0);
639 141 : auto &y_param = param_vars.at (1);
640 :
641 141 : if (!Backend::function_set_parameters (fndecl, param_vars))
642 0 : return error_mark_node;
643 :
644 141 : rust_assert (fntype->get_num_substitutions () == 1);
645 141 : auto ¶m_mapping = fntype->get_substs ().at (0);
646 141 : const auto param_tyty = param_mapping.get_param_ty ();
647 141 : auto resolved_tyty = param_tyty->resolve ();
648 141 : tree template_parameter_type
649 141 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
650 :
651 : // this should match y as well or we can take it from the TyTy structure
652 141 : tree tmp_stmt = error_mark_node;
653 141 : Bvariable *result_variable
654 141 : = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
655 : NULL_TREE, true /*address_is_taken*/,
656 : UNDEF_LOCATION, &tmp_stmt);
657 141 : Bvariable *bool_variable
658 141 : = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
659 : NULL_TREE, true /*address_is_taken*/,
660 : UNDEF_LOCATION, &tmp_stmt);
661 :
662 141 : enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
663 :
664 : // BUILTIN op_with_overflow FN BODY BEGIN
665 141 : auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
666 141 : auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
667 :
668 141 : tree overflow_builtin = error_mark_node;
669 141 : switch (op)
670 : {
671 127 : case PLUS_EXPR:
672 127 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
673 : &overflow_builtin);
674 127 : break;
675 :
676 7 : case MINUS_EXPR:
677 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
678 : &overflow_builtin);
679 7 : break;
680 :
681 7 : case MULT_EXPR:
682 7 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
683 : &overflow_builtin);
684 7 : break;
685 :
686 0 : default:
687 0 : rust_unreachable ();
688 141 : break;
689 : }
690 141 : rust_assert (overflow_builtin != error_mark_node);
691 :
692 141 : tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
693 141 : tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
694 141 : tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
695 :
696 141 : tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
697 : 3, x, y, result_ref);
698 :
699 141 : tree overflow_assignment
700 141 : = Backend::assignment_statement (bool_decl, builtin_call,
701 : BUILTINS_LOCATION);
702 :
703 141 : ctx->add_statement (overflow_assignment);
704 :
705 282 : std::vector<tree> vals = {result_decl, bool_decl};
706 141 : tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
707 141 : tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
708 : -1, UNDEF_LOCATION);
709 :
710 141 : auto return_statement
711 141 : = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
712 141 : ctx->add_statement (return_statement);
713 :
714 : // BUILTIN wrapping_<op> FN BODY END
715 :
716 141 : finalize_intrinsic_block (ctx, fndecl);
717 :
718 141 : return fndecl;
719 141 : }
720 :
721 : /**
722 : * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
723 : * fn copy<T>(src: *const T, dst: *mut T, count: usize);
724 : */
725 : static tree
726 243 : copy_handler_inner (Context *ctx, TyTy::FnType *fntype, bool overlaps)
727 : {
728 243 : rust_assert (fntype->get_params ().size () == 3);
729 243 : rust_assert (fntype->get_num_substitutions () == 1);
730 :
731 243 : tree lookup = NULL_TREE;
732 243 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
733 0 : return lookup;
734 :
735 243 : auto fndecl = compile_intrinsic_function (ctx, fntype);
736 :
737 : // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
738 243 : TREE_READONLY (fndecl) = 0;
739 243 : TREE_SIDE_EFFECTS (fndecl) = 1;
740 :
741 : // setup the params
742 243 : std::vector<Bvariable *> param_vars;
743 243 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
744 :
745 243 : if (!Backend::function_set_parameters (fndecl, param_vars))
746 0 : return error_mark_node;
747 :
748 243 : enter_intrinsic_block (ctx, fndecl);
749 :
750 : // BUILTIN copy_nonoverlapping BODY BEGIN
751 :
752 243 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
753 243 : auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
754 243 : auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
755 :
756 : // We want to create the following statement
757 : // memcpy(dst, src, size_of::<T>());
758 : // so
759 : // memcpy(dst, src, size_expr);
760 :
761 243 : auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
762 243 : auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
763 :
764 243 : tree size_expr
765 243 : = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
766 :
767 243 : tree memcpy_raw = nullptr;
768 486 : BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
769 : : "__builtin_memcpy",
770 : &memcpy_raw);
771 243 : rust_assert (memcpy_raw);
772 243 : auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
773 :
774 243 : auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
775 : nullptr, UNDEF_LOCATION);
776 :
777 243 : ctx->add_statement (copy_call);
778 :
779 : // BUILTIN copy_nonoverlapping BODY END
780 :
781 243 : finalize_intrinsic_block (ctx, fndecl);
782 :
783 243 : return fndecl;
784 243 : }
785 :
786 : static tree
787 126 : make_unsigned_long_tree (unsigned long value)
788 : {
789 14 : return build_int_cst (integer_type_node, value);
790 : }
791 :
792 : static tree
793 14 : prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
794 : {
795 14 : rust_assert (fntype->get_params ().size () == 2);
796 :
797 14 : tree lookup = NULL_TREE;
798 14 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
799 0 : return lookup;
800 :
801 14 : auto fndecl = compile_intrinsic_function (ctx, fntype);
802 :
803 : // prefetching isn't pure and shouldn't be discarded after GIMPLE
804 14 : TREE_READONLY (fndecl) = 0;
805 14 : TREE_SIDE_EFFECTS (fndecl) = 1;
806 :
807 14 : std::vector<Bvariable *> args;
808 14 : compile_fn_params (ctx, fntype, fndecl, &args);
809 :
810 14 : if (!Backend::function_set_parameters (fndecl, args))
811 0 : return error_mark_node;
812 :
813 14 : enter_intrinsic_block (ctx, fndecl);
814 :
815 14 : auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
816 :
817 : // The core library technically allows you to pass any i32 value as a
818 : // locality, but LLVM will then complain if the value cannot be constant
819 : // evaluated. For now, we ignore the locality argument and instead always
820 : // pass `3` (the most restrictive value). This allows us to still have
821 : // prefetch behavior, just not as granular as expected. In future Rust
822 : // versions, we hope that prefetch intrinsics will be split up according to
823 : // locality, similarly to atomic intrinsics.
824 : // The solution is to try and perform constant folding for the locality
825 : // argument, or instead of creating a new function definition, modify the call
826 : // site directly This has the bad side-effect of creating warnings about
827 : // `unused name - locality`, which we hack away here:
828 : // TODO: Take care of handling locality properly
829 14 : Backend::var_expression (args[1], UNDEF_LOCATION);
830 :
831 14 : auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
832 :
833 14 : auto prefetch_raw = NULL_TREE;
834 14 : auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
835 : &prefetch_raw);
836 14 : rust_assert (ok);
837 14 : auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
838 :
839 28 : auto prefetch_call = Backend::call_expression (prefetch,
840 : {addr, rw_flag,
841 : // locality arg
842 14 : make_unsigned_long_tree (3)},
843 : nullptr, UNDEF_LOCATION);
844 :
845 14 : TREE_READONLY (prefetch_call) = 0;
846 14 : TREE_SIDE_EFFECTS (prefetch_call) = 1;
847 :
848 14 : ctx->add_statement (prefetch_call);
849 :
850 14 : finalize_intrinsic_block (ctx, fndecl);
851 :
852 14 : return fndecl;
853 14 : }
854 :
855 : static std::string
856 98 : build_atomic_builtin_name (const std::string &prefix, location_t locus,
857 : TyTy::BaseType *operand_type)
858 : {
859 98 : static const std::map<std::string, std::string> allowed_types = {
860 : {"i8", "1"}, {"i16", "2"}, {"i32", "4"}, {"i64", "8"},
861 : {"i128", "16"}, {"isize", "8"}, {"u8", "1"}, {"u16", "2"},
862 : {"u32", "4"}, {"u64", "8"}, {"u128", "16"}, {"usize", "8"},
863 462 : };
864 :
865 : // TODO: Can we maybe get the generic version (atomic_store_n) to work... This
866 : // would be so much better
867 :
868 98 : std::string result = "__" + prefix; // + "n";
869 :
870 98 : auto type_name = operand_type->get_name ();
871 98 : if (type_name == "usize" || type_name == "isize")
872 : {
873 0 : rust_sorry_at (
874 : locus, "atomics are not yet available for size types (usize, isize)");
875 0 : return "";
876 : }
877 :
878 98 : if (type_name.at (0) == 'i')
879 : {
880 0 : rust_sorry_at (locus, "atomics are not yet supported for signed "
881 : "integer types (i8, i16, i32, i64, i128)");
882 0 : return "";
883 : }
884 :
885 98 : auto type_size_str = allowed_types.find (type_name);
886 :
887 98 : if (!check_for_basic_integer_type ("atomic", locus, operand_type))
888 14 : return "";
889 :
890 84 : result += type_size_str->second;
891 :
892 84 : return result;
893 98 : }
894 :
895 : static tree
896 70 : atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering)
897 : {
898 70 : rust_assert (fntype->get_params ().size () == 2);
899 70 : rust_assert (fntype->get_num_substitutions () == 1);
900 :
901 70 : tree lookup = NULL_TREE;
902 70 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
903 0 : return lookup;
904 :
905 70 : auto fndecl = compile_intrinsic_function (ctx, fntype);
906 :
907 : // Most intrinsic functions are pure but not the atomic ones
908 70 : TREE_READONLY (fndecl) = 0;
909 70 : TREE_SIDE_EFFECTS (fndecl) = 1;
910 :
911 : // setup the params
912 70 : std::vector<Bvariable *> param_vars;
913 70 : std::vector<tree> types;
914 70 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
915 :
916 70 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
917 70 : rust_assert (ok);
918 :
919 70 : enter_intrinsic_block (ctx, fndecl);
920 :
921 70 : auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
922 70 : TREE_READONLY (dst) = 0;
923 :
924 70 : auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
925 70 : auto memorder = make_unsigned_long_tree (ordering);
926 :
927 70 : auto monomorphized_type
928 70 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
929 :
930 70 : auto builtin_name
931 70 : = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (),
932 70 : monomorphized_type);
933 70 : if (builtin_name.empty ())
934 14 : return error_mark_node;
935 :
936 56 : tree atomic_store_raw = nullptr;
937 56 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
938 : &atomic_store_raw);
939 56 : rust_assert (atomic_store_raw);
940 :
941 56 : auto atomic_store
942 56 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
943 :
944 56 : auto store_call
945 56 : = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
946 : UNDEF_LOCATION);
947 56 : TREE_READONLY (store_call) = 0;
948 56 : TREE_SIDE_EFFECTS (store_call) = 1;
949 :
950 56 : ctx->add_statement (store_call);
951 56 : finalize_intrinsic_block (ctx, fndecl);
952 :
953 56 : return fndecl;
954 70 : }
955 :
956 : static tree
957 28 : atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering)
958 : {
959 28 : rust_assert (fntype->get_params ().size () == 1);
960 28 : rust_assert (fntype->get_num_substitutions () == 1);
961 :
962 28 : tree lookup = NULL_TREE;
963 28 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
964 0 : return lookup;
965 :
966 28 : auto fndecl = compile_intrinsic_function (ctx, fntype);
967 :
968 : // Most intrinsic functions are pure but not the atomic ones
969 : // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
970 28 : TREE_READONLY (fndecl) = 0;
971 28 : TREE_SIDE_EFFECTS (fndecl) = 1;
972 :
973 : // setup the params
974 28 : std::vector<Bvariable *> param_vars;
975 28 : std::vector<tree> types;
976 28 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types);
977 :
978 28 : auto ok = Backend::function_set_parameters (fndecl, param_vars);
979 28 : rust_assert (ok);
980 :
981 28 : enter_intrinsic_block (ctx, fndecl);
982 :
983 28 : auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
984 28 : auto memorder = make_unsigned_long_tree (ordering);
985 :
986 28 : auto monomorphized_type
987 28 : = fntype->get_substs ()[0].get_param_ty ()->resolve ();
988 :
989 28 : auto builtin_name
990 28 : = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
991 28 : monomorphized_type);
992 28 : if (builtin_name.empty ())
993 0 : return error_mark_node;
994 :
995 28 : tree atomic_load_raw = nullptr;
996 28 : BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
997 : &atomic_load_raw);
998 28 : rust_assert (atomic_load_raw);
999 :
1000 28 : auto atomic_load
1001 28 : = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
1002 :
1003 28 : auto load_call = Backend::call_expression (atomic_load, {src, memorder},
1004 : nullptr, UNDEF_LOCATION);
1005 28 : auto return_statement
1006 28 : = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
1007 :
1008 28 : TREE_READONLY (load_call) = 0;
1009 28 : TREE_SIDE_EFFECTS (load_call) = 1;
1010 :
1011 28 : ctx->add_statement (return_statement);
1012 :
1013 28 : finalize_intrinsic_block (ctx, fndecl);
1014 :
1015 28 : return fndecl;
1016 28 : }
1017 :
1018 : static inline tree
1019 56 : unchecked_op_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
1020 : {
1021 56 : rust_assert (fntype->get_params ().size () == 2);
1022 56 : rust_assert (fntype->get_num_substitutions () == 1);
1023 :
1024 56 : tree lookup = NULL_TREE;
1025 56 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1026 0 : return lookup;
1027 :
1028 56 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1029 :
1030 : // setup the params
1031 56 : std::vector<Bvariable *> param_vars;
1032 56 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1033 :
1034 56 : if (!Backend::function_set_parameters (fndecl, param_vars))
1035 0 : return error_mark_node;
1036 :
1037 56 : enter_intrinsic_block (ctx, fndecl);
1038 :
1039 : // BUILTIN unchecked_<op> BODY BEGIN
1040 :
1041 56 : auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1042 56 : auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1043 :
1044 56 : auto *monomorphized_type
1045 56 : = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
1046 :
1047 56 : check_for_basic_integer_type ("unchecked operation", fntype->get_locus (),
1048 : monomorphized_type);
1049 :
1050 56 : auto expr = build2 (op, TREE_TYPE (x), x, y);
1051 56 : auto return_statement
1052 56 : = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
1053 :
1054 56 : ctx->add_statement (return_statement);
1055 :
1056 : // BUILTIN unchecked_<op> BODY END
1057 :
1058 56 : finalize_intrinsic_block (ctx, fndecl);
1059 :
1060 56 : return fndecl;
1061 56 : }
1062 :
1063 : static tree
1064 92 : uninit_handler (Context *ctx, TyTy::FnType *fntype)
1065 : {
1066 : // uninit has _zero_ parameters its parameter is the generic one
1067 92 : rust_assert (fntype->get_params ().size () == 0);
1068 :
1069 92 : tree lookup = NULL_TREE;
1070 92 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1071 0 : return lookup;
1072 :
1073 92 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1074 :
1075 : // Most intrinsic functions are pure - not `uninit_handler`
1076 92 : TREE_READONLY (fndecl) = 0;
1077 92 : TREE_SIDE_EFFECTS (fndecl) = 1;
1078 :
1079 : // get the template parameter type tree fn uninit<T>();
1080 92 : rust_assert (fntype->get_num_substitutions () == 1);
1081 92 : auto ¶m_mapping = fntype->get_substs ().at (0);
1082 92 : const auto param_tyty = param_mapping.get_param_ty ();
1083 92 : auto resolved_tyty = param_tyty->resolve ();
1084 92 : tree template_parameter_type
1085 92 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1086 :
1087 : // result temporary
1088 92 : tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
1089 92 : rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
1090 : == TYPE_SIZE_UNIT (dst_type));
1091 :
1092 92 : tree tmp_stmt = error_mark_node;
1093 92 : Bvariable *bvar
1094 92 : = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
1095 : true /*address_is_taken*/, UNDEF_LOCATION,
1096 : &tmp_stmt);
1097 :
1098 92 : enter_intrinsic_block (ctx, fndecl, {bvar});
1099 :
1100 : // BUILTIN size_of FN BODY BEGIN
1101 :
1102 92 : tree memset_builtin = error_mark_node;
1103 92 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
1104 : &memset_builtin);
1105 92 : rust_assert (memset_builtin != error_mark_node);
1106 :
1107 : // call memset with 0x01 and size of the thing see
1108 : // https://github.com/Rust-GCC/gccrs/issues/1899
1109 :
1110 92 : tree dst = bvar->get_tree (BUILTINS_LOCATION);
1111 92 : tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
1112 92 : tree constant_byte = build_int_cst (integer_type_node, 0x01);
1113 92 : tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
1114 :
1115 92 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
1116 : dst_addr, constant_byte, size_expr);
1117 92 : ctx->add_statement (memset_call);
1118 :
1119 92 : auto return_statement
1120 92 : = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
1121 92 : ctx->add_statement (return_statement);
1122 : // BUILTIN size_of FN BODY END
1123 :
1124 92 : finalize_intrinsic_block (ctx, fndecl);
1125 :
1126 92 : return fndecl;
1127 : }
1128 :
1129 : static tree
1130 26 : move_val_init_handler (Context *ctx, TyTy::FnType *fntype)
1131 : {
1132 26 : rust_assert (fntype->get_params ().size () == 2);
1133 :
1134 26 : tree lookup = NULL_TREE;
1135 26 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1136 0 : return lookup;
1137 :
1138 26 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1139 :
1140 : // Most intrinsic functions are pure - not `move_val_init`
1141 26 : TREE_READONLY (fndecl) = 0;
1142 26 : TREE_SIDE_EFFECTS (fndecl) = 1;
1143 :
1144 : // get the template parameter type tree fn size_of<T>();
1145 26 : rust_assert (fntype->get_num_substitutions () == 1);
1146 26 : auto ¶m_mapping = fntype->get_substs ().at (0);
1147 26 : auto param_tyty = param_mapping.get_param_ty ();
1148 26 : auto resolved_tyty = param_tyty->resolve ();
1149 26 : tree template_parameter_type
1150 26 : = TyTyResolveCompile::compile (ctx, resolved_tyty);
1151 :
1152 26 : std::vector<Bvariable *> param_vars;
1153 26 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1154 :
1155 26 : if (!Backend::function_set_parameters (fndecl, param_vars))
1156 0 : return error_mark_node;
1157 :
1158 26 : enter_intrinsic_block (ctx, fndecl);
1159 :
1160 : // BUILTIN size_of FN BODY BEGIN
1161 :
1162 26 : tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1163 26 : tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1164 26 : tree size = TYPE_SIZE_UNIT (template_parameter_type);
1165 :
1166 26 : tree memcpy_builtin = error_mark_node;
1167 26 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
1168 : &memcpy_builtin);
1169 26 : rust_assert (memcpy_builtin != error_mark_node);
1170 :
1171 26 : src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
1172 26 : tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
1173 : dst, src, size);
1174 :
1175 26 : ctx->add_statement (memset_call);
1176 : // BUILTIN size_of FN BODY END
1177 :
1178 26 : finalize_intrinsic_block (ctx, fndecl);
1179 :
1180 26 : return fndecl;
1181 26 : }
1182 :
1183 : static inline tree
1184 0 : expect_handler_inner (Context *ctx, TyTy::FnType *fntype, bool likely)
1185 : {
1186 0 : rust_assert (fntype->get_params ().size () == 1);
1187 :
1188 0 : tree lookup = NULL_TREE;
1189 0 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1190 0 : return lookup;
1191 :
1192 0 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1193 :
1194 0 : enter_intrinsic_block (ctx, fndecl);
1195 :
1196 : // BUILTIN expect_handler_inner FN BODY BEGIN
1197 : // setup the params
1198 0 : std::vector<Bvariable *> param_vars;
1199 0 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1200 0 : tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1201 0 : tree expect_fn_raw = nullptr;
1202 0 : BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
1203 : &expect_fn_raw);
1204 0 : rust_assert (expect_fn_raw);
1205 0 : auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
1206 :
1207 : // we need to convert the expression return type to long to match the expected
1208 : // parameter type of __builtin_expect
1209 0 : auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
1210 0 : auto expect_value
1211 0 : = make_unsigned_long_tree (static_cast<unsigned long> (likely));
1212 :
1213 0 : auto expect_call
1214 0 : = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
1215 : BUILTINS_LOCATION);
1216 : // the return value also needs to be casted (to bool)
1217 0 : auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, expect_call);
1218 0 : auto return_statement
1219 0 : = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
1220 0 : ctx->add_statement (return_statement);
1221 : // BUILTIN expect_handler_inner FN BODY END
1222 :
1223 0 : finalize_intrinsic_block (ctx, fndecl);
1224 :
1225 0 : return fndecl;
1226 0 : }
1227 :
1228 : static tree
1229 1 : assume_handler (Context *ctx, TyTy::FnType *fntype)
1230 : {
1231 : // TODO: make sure this is actually helping the compiler optimize
1232 :
1233 1 : rust_assert (fntype->get_params ().size () == 1);
1234 1 : rust_assert (fntype->param_at (0).get_type ()->get_kind ()
1235 : == TyTy::TypeKind::BOOL);
1236 :
1237 1 : tree lookup = NULL_TREE;
1238 1 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1239 0 : return lookup;
1240 :
1241 1 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1242 :
1243 : // TODO: make sure these are necessary
1244 1 : TREE_READONLY (fndecl) = 0;
1245 1 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
1246 1 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
1247 1 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
1248 :
1249 1 : std::vector<Bvariable *> param_vars;
1250 1 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1251 :
1252 1 : if (!Backend::function_set_parameters (fndecl, param_vars))
1253 0 : return error_mark_node;
1254 :
1255 1 : enter_intrinsic_block (ctx, fndecl);
1256 :
1257 : // BUILTIN assume FN BODY BEGIN
1258 :
1259 1 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1260 :
1261 1 : tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
1262 : void_type_node, 1, val);
1263 1 : TREE_SIDE_EFFECTS (assume_expr) = 1;
1264 :
1265 1 : ctx->add_statement (assume_expr);
1266 : // BUILTIN assume FN BODY END
1267 :
1268 1 : finalize_intrinsic_block (ctx, fndecl);
1269 :
1270 1 : return fndecl;
1271 1 : }
1272 :
1273 : static tree
1274 2 : try_handler_inner (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
1275 : {
1276 2 : rust_assert (fntype->get_params ().size () == 3);
1277 :
1278 2 : tree lookup = NULL_TREE;
1279 2 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1280 0 : return lookup;
1281 2 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1282 :
1283 2 : enter_intrinsic_block (ctx, fndecl);
1284 :
1285 : // The following tricks are needed to make sure the try-catch blocks are not
1286 : // optimized away
1287 2 : TREE_READONLY (fndecl) = 0;
1288 2 : DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
1289 2 : DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
1290 2 : NULL_TREE, DECL_ATTRIBUTES (fndecl));
1291 :
1292 : // BUILTIN try_handler FN BODY BEGIN
1293 : // setup the params
1294 2 : std::vector<Bvariable *> param_vars;
1295 2 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1296 2 : if (!Backend::function_set_parameters (fndecl, param_vars))
1297 0 : return error_mark_node;
1298 2 : tree enclosing_scope = NULL_TREE;
1299 :
1300 2 : bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
1301 2 : == CompileOptions::PanicStrategy::Abort;
1302 2 : tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1303 2 : tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
1304 2 : tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
1305 2 : tree normal_return_stmt = NULL_TREE;
1306 2 : tree error_return_stmt = NULL_TREE;
1307 2 : tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
1308 : BUILTINS_LOCATION);
1309 2 : tree catch_call = NULL_TREE;
1310 2 : tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
1311 : UNDEF_LOCATION);
1312 :
1313 2 : if (is_new_api)
1314 : {
1315 1 : auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
1316 1 : auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
1317 : UNDEF_LOCATION);
1318 1 : normal_return_stmt
1319 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
1320 1 : error_return_stmt
1321 1 : = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
1322 : }
1323 : else
1324 : {
1325 1 : normal_return_stmt = Backend::return_statement (fndecl, integer_zero_node,
1326 : BUILTINS_LOCATION);
1327 1 : error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
1328 : BUILTINS_LOCATION);
1329 : }
1330 2 : Backend::block_add_statements (try_block,
1331 2 : std::vector<tree>{try_call,
1332 2 : normal_return_stmt});
1333 2 : if (panic_is_abort)
1334 : {
1335 : // skip building the try-catch construct
1336 0 : ctx->add_statement (try_block);
1337 0 : finalize_intrinsic_block (ctx, fndecl);
1338 0 : return fndecl;
1339 : }
1340 :
1341 2 : tree eh_pointer
1342 2 : = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
1343 : integer_zero_node);
1344 2 : catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
1345 : NULL_TREE, BUILTINS_LOCATION);
1346 :
1347 2 : tree catch_block = Backend::block (fndecl, enclosing_scope, {},
1348 : UNDEF_LOCATION, UNDEF_LOCATION);
1349 2 : Backend::block_add_statements (catch_block,
1350 2 : std::vector<tree>{catch_call,
1351 2 : error_return_stmt});
1352 : // emulate what cc1plus is doing for C++ try-catch
1353 2 : tree inner_eh_construct
1354 2 : = Backend::exception_handler_statement (catch_call, NULL_TREE,
1355 : error_return_stmt,
1356 : BUILTINS_LOCATION);
1357 : // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
1358 2 : auto eh_construct
1359 2 : = Backend::exception_handler_statement (try_block, inner_eh_construct,
1360 : NULL_TREE, BUILTINS_LOCATION);
1361 2 : ctx->add_statement (eh_construct);
1362 : // BUILTIN try_handler FN BODY END
1363 2 : finalize_intrinsic_block (ctx, fndecl);
1364 :
1365 2 : return fndecl;
1366 2 : }
1367 :
1368 : static tree
1369 102 : discriminant_value_handler (Context *ctx, TyTy::FnType *fntype)
1370 : {
1371 102 : rust_assert (fntype->get_params ().size () == 1);
1372 102 : rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
1373 102 : rust_assert (fntype->has_substitutions ());
1374 102 : rust_assert (fntype->get_num_type_params () == 1);
1375 102 : auto &mapping = fntype->get_substs ().at (0);
1376 102 : auto param_ty = mapping.get_param_ty ();
1377 102 : rust_assert (param_ty->can_resolve ());
1378 102 : auto resolved = param_ty->resolve ();
1379 102 : auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
1380 :
1381 102 : TyTy::BaseType *return_type = nullptr;
1382 102 : bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
1383 102 : rust_assert (ok);
1384 :
1385 102 : bool is_adt = resolved->is<TyTy::ADTType> ();
1386 102 : bool is_enum = false;
1387 102 : if (is_adt)
1388 : {
1389 102 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1390 102 : return_type = adt.get_repr_options ().repr;
1391 102 : rust_assert (return_type != nullptr);
1392 102 : is_enum = adt.is_enum ();
1393 : }
1394 :
1395 102 : p->set_associated_type (return_type->get_ref ());
1396 :
1397 102 : tree lookup = NULL_TREE;
1398 102 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1399 0 : return lookup;
1400 :
1401 102 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1402 :
1403 102 : std::vector<Bvariable *> param_vars;
1404 102 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1405 :
1406 102 : if (!Backend::function_set_parameters (fndecl, param_vars))
1407 0 : return error_mark_node;
1408 :
1409 102 : enter_intrinsic_block (ctx, fndecl);
1410 :
1411 : // BUILTIN disriminant_value FN BODY BEGIN
1412 :
1413 102 : tree result = integer_zero_node;
1414 102 : if (is_enum)
1415 : {
1416 102 : tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
1417 102 : tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
1418 102 : result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
1419 : }
1420 :
1421 102 : auto return_statement
1422 102 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1423 102 : ctx->add_statement (return_statement);
1424 :
1425 : // BUILTIN disriminant_value FN BODY END
1426 :
1427 102 : finalize_intrinsic_block (ctx, fndecl);
1428 :
1429 102 : return fndecl;
1430 102 : }
1431 :
1432 : static tree
1433 7 : variant_count_handler (Context *ctx, TyTy::FnType *fntype)
1434 : {
1435 7 : rust_assert (fntype->get_num_type_params () == 1);
1436 7 : auto &mapping = fntype->get_substs ().at (0);
1437 7 : auto param_ty = mapping.get_param_ty ();
1438 7 : rust_assert (param_ty->can_resolve ());
1439 7 : auto resolved = param_ty->resolve ();
1440 :
1441 7 : size_t variant_count = 0;
1442 7 : bool is_adt = resolved->is<TyTy::ADTType> ();
1443 7 : if (is_adt)
1444 : {
1445 7 : const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
1446 7 : variant_count = adt.number_of_variants ();
1447 : }
1448 :
1449 7 : tree lookup = NULL_TREE;
1450 7 : if (check_for_cached_intrinsic (ctx, fntype, &lookup))
1451 0 : return lookup;
1452 :
1453 7 : auto fndecl = compile_intrinsic_function (ctx, fntype);
1454 :
1455 7 : std::vector<Bvariable *> param_vars;
1456 7 : compile_fn_params (ctx, fntype, fndecl, ¶m_vars);
1457 :
1458 7 : if (!Backend::function_set_parameters (fndecl, param_vars))
1459 0 : return error_mark_node;
1460 :
1461 7 : enter_intrinsic_block (ctx, fndecl);
1462 :
1463 : // BUILTIN disriminant_value FN BODY BEGIN
1464 7 : tree result_decl = DECL_RESULT (fndecl);
1465 7 : tree type = TREE_TYPE (result_decl);
1466 :
1467 7 : mpz_t ival;
1468 7 : mpz_init_set_ui (ival, variant_count);
1469 7 : tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
1470 7 : mpz_clear (ival);
1471 :
1472 7 : auto return_statement
1473 7 : = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
1474 7 : ctx->add_statement (return_statement);
1475 :
1476 : // BUILTIN disriminant_value FN BODY END
1477 :
1478 7 : finalize_intrinsic_block (ctx, fndecl);
1479 :
1480 7 : return fndecl;
1481 7 : }
1482 :
1483 : } // namespace Compile
1484 : } // namespace Rust
|