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