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