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