1//! Analyzed Intermediate Representation.
2//!
3//! This data is produced by Sema and consumed by codegen.
4//! Unlike ZIR where there is one instance for an entire source file, each function
5//! gets its own `Air` instance.
6
7const std = @import("std");
8const builtin = @import("builtin");
9const assert = std.debug.assert;
10
11const Air = @This();
12const InternPool = @import("InternPool.zig");
13const Type = @import("Type.zig");
14const Value = @import("Value.zig");
15const Zcu = @import("Zcu.zig");
16const print = @import("Air/print.zig");
17
18pub const Legalize = @import("Air/Legalize.zig");
19pub const Liveness = @import("Air/Liveness.zig");
20pub const Verify = @import("Air/Verify.zig");
21
22instructions: std.MultiArrayList(Inst).Slice,
23/// The meaning of this data is determined by `Inst.Tag` value.
24/// The first few indexes are reserved. See `ExtraIndex` for the values.
25extra: std.ArrayList(u32),
26
27pub const ExtraIndex = enum(u32) {
28 /// Payload index of the main `Block` in the `extra` array.
29 main_block,
30
31 _,
32};
33
34pub const Inst = struct {
35 tag: Tag,
36 data: Data,
37
38 pub const Tag = enum(u8) {
39 /// The first N instructions in the main block must be one arg instruction per
40 /// function parameter. This makes function parameters participate in
41 /// liveness analysis without any special handling.
42 /// Uses the `arg` field.
43 arg,
44 /// Float or integer addition. For integers, wrapping is illegal behavior.
45 /// Both operands are guaranteed to be the same type, and the result type
46 /// is the same as both operands.
47 /// Uses the `bin_op` field.
48 add,
49 /// Integer addition. Wrapping is a safety panic.
50 /// Both operands are guaranteed to be the same type, and the result type
51 /// is the same as both operands.
52 /// The panic handler function must be populated before lowering AIR
53 /// that contains this instruction.
54 /// Uses the `bin_op` field.
55 add_safe,
56 /// Float addition. The instruction is allowed to have equal or more
57 /// mathematical accuracy than strict IEEE-757 float addition.
58 /// If either operand is NaN, the result value is undefined.
59 /// Uses the `bin_op` field.
60 add_optimized,
61 /// Twos complement wrapping integer addition.
62 /// Both operands are guaranteed to be the same type, and the result type
63 /// is the same as both operands.
64 /// Uses the `bin_op` field.
65 add_wrap,
66 /// Saturating integer addition.
67 /// Both operands are guaranteed to be the same type, and the result type
68 /// is the same as both operands.
69 /// Uses the `bin_op` field.
70 add_sat,
71 /// Float or integer subtraction. For integers, wrapping is illegal behavior.
72 /// Both operands are guaranteed to be the same type, and the result type
73 /// is the same as both operands.
74 /// Uses the `bin_op` field.
75 sub,
76 /// Integer subtraction. Wrapping is a safety panic.
77 /// Both operands are guaranteed to be the same type, and the result type
78 /// is the same as both operands.
79 /// The panic handler function must be populated before lowering AIR
80 /// that contains this instruction.
81 /// Uses the `bin_op` field.
82 sub_safe,
83 /// Float subtraction. The instruction is allowed to have equal or more
84 /// mathematical accuracy than strict IEEE-757 float subtraction.
85 /// If either operand is NaN, the result value is undefined.
86 /// Uses the `bin_op` field.
87 sub_optimized,
88 /// Twos complement wrapping integer subtraction.
89 /// Both operands are guaranteed to be the same type, and the result type
90 /// is the same as both operands.
91 /// Uses the `bin_op` field.
92 sub_wrap,
93 /// Saturating integer subtraction.
94 /// Both operands are guaranteed to be the same type, and the result type
95 /// is the same as both operands.
96 /// Uses the `bin_op` field.
97 sub_sat,
98 /// Float or integer multiplication. For integers, wrapping is illegal behavior.
99 /// Both operands are guaranteed to be the same type, and the result type
100 /// is the same as both operands.
101 /// Uses the `bin_op` field.
102 mul,
103 /// Integer multiplication. Wrapping is a safety panic.
104 /// Both operands are guaranteed to be the same type, and the result type
105 /// is the same as both operands.
106 /// The panic handler function must be populated before lowering AIR
107 /// that contains this instruction.
108 /// Uses the `bin_op` field.
109 mul_safe,
110 /// Float multiplication. The instruction is allowed to have equal or more
111 /// mathematical accuracy than strict IEEE-757 float multiplication.
112 /// If either operand is NaN, the result value is undefined.
113 /// Uses the `bin_op` field.
114 mul_optimized,
115 /// Twos complement wrapping integer multiplication.
116 /// Both operands are guaranteed to be the same type, and the result type
117 /// is the same as both operands.
118 /// Uses the `bin_op` field.
119 mul_wrap,
120 /// Saturating integer multiplication.
121 /// Both operands are guaranteed to be the same type, and the result type
122 /// is the same as both operands.
123 /// Uses the `bin_op` field.
124 mul_sat,
125 /// Float division.
126 /// Both operands are guaranteed to be the same type, and the result type
127 /// is the same as both operands.
128 /// Uses the `bin_op` field.
129 div_float,
130 /// Same as `div_float` with optimized float mode.
131 div_float_optimized,
132 /// Truncating integer or float division. For integers, wrapping is illegal behavior.
133 /// Both operands are guaranteed to be the same type, and the result type
134 /// is the same as both operands.
135 /// Uses the `bin_op` field.
136 div_trunc,
137 /// Same as `div_trunc` with optimized float mode.
138 div_trunc_optimized,
139 /// Flooring integer or float division. For integers, wrapping is illegal behavior.
140 /// Both operands are guaranteed to be the same type, and the result type
141 /// is the same as both operands.
142 /// Uses the `bin_op` field.
143 div_floor,
144 /// Same as `div_floor` with optimized float mode.
145 div_floor_optimized,
146 /// Ceiling integer or float division. For integers, wrapping is illegal behavior.
147 /// Both operands are guaranteed to be the same type, and the result type
148 /// is the same as both operands.
149 /// Uses the `bin_op` field.
150 div_ceil,
151 /// Same as `div_ceil` with optimized float mode.
152 div_ceil_optimized,
153 /// Integer or float division.
154 /// If a remainder would be produced, illegal behavior occurs.
155 /// For integers, overflow is illegal behavior.
156 /// Both operands are guaranteed to be the same type, and the result type
157 /// is the same as both operands.
158 /// Uses the `bin_op` field.
159 div_exact,
160 /// Same as `div_exact` with optimized float mode.
161 div_exact_optimized,
162 /// Integer or float remainder division.
163 /// Both operands are guaranteed to be the same type, and the result type
164 /// is the same as both operands.
165 /// Uses the `bin_op` field.
166 rem,
167 /// Same as `rem` with optimized float mode.
168 rem_optimized,
169 /// Integer or float modulus division.
170 /// Both operands are guaranteed to be the same type, and the result type
171 /// is the same as both operands.
172 /// Uses the `bin_op` field.
173 mod,
174 /// Same as `mod` with optimized float mode.
175 mod_optimized,
176 /// Add an offset, in element type units, to a pointer, returning a new
177 /// pointer. Element type may not be zero bits.
178 ///
179 /// Wrapping is illegal behavior. If the newly computed address is
180 /// outside the provenance of the operand, the result is undefined.
181 ///
182 /// Uses the `ty_pl` field. Payload is `Bin`. The lhs is the pointer,
183 /// rhs is the offset. Result type is the same as lhs. The operand type's
184 /// pointer size may be `.slice`, `.many`, or `.c`.
185 ptr_add,
186 /// Subtract an offset, in element type units, from a pointer,
187 /// returning a new pointer. Element type may not be zero bits.
188 ///
189 /// Wrapping is illegal behavior. If the newly computed address is
190 /// outside the provenance of the operand, the result is undefined.
191 ///
192 /// Uses the `ty_pl` field. Payload is `Bin`. The lhs is the pointer,
193 /// rhs is the offset. Result type is the same as lhs. The operand type's
194 /// pointer size may be `.slice`, `.many`, or `.c`.
195 ptr_sub,
196 /// Given two operands which can be floats, integers, or vectors, returns the
197 /// greater of the operands. For vectors it operates element-wise.
198 /// Both operands are guaranteed to be the same type, and the result type
199 /// is the same as both operands.
200 /// Uses the `bin_op` field.
201 max,
202 /// Given two operands which can be floats, integers, or vectors, returns the
203 /// lesser of the operands. For vectors it operates element-wise.
204 /// Both operands are guaranteed to be the same type, and the result type
205 /// is the same as both operands.
206 /// Uses the `bin_op` field.
207 min,
208 /// Integer addition with overflow. Both operands are guaranteed to be the same type,
209 /// and the result is a tuple with .{res, ov}. The wrapped value is written to res
210 /// and if an overflow happens, ov is 1. Otherwise ov is 0.
211 /// Uses the `ty_pl` field. Payload is `Bin`.
212 add_with_overflow,
213 /// Integer subtraction with overflow. Both operands are guaranteed to be the same type,
214 /// and the result is a tuple with .{res, ov}. The wrapped value is written to res
215 /// and if an overflow happens, ov is 1. Otherwise ov is 0.
216 /// Uses the `ty_pl` field. Payload is `Bin`.
217 sub_with_overflow,
218 /// Integer multiplication with overflow. Both operands are guaranteed to be the same type,
219 /// and the result is a tuple with .{res, ov}. The wrapped value is written to res
220 /// and if an overflow happens, ov is 1. Otherwise ov is 0.
221 /// Uses the `ty_pl` field. Payload is `Bin`.
222 mul_with_overflow,
223 /// Integer left-shift with overflow. Both operands are guaranteed to be the same type,
224 /// and the result is a tuple with .{res, ov}. The wrapped value is written to res
225 /// and if an overflow happens, ov is 1. Otherwise ov is 0.
226 /// Uses the `ty_pl` field. Payload is `Bin`.
227 shl_with_overflow,
228 /// Allocates stack local memory.
229 /// Uses the `ty` field.
230 alloc,
231 /// This special instruction only exists temporarily during semantic
232 /// analysis and is guaranteed to be unreachable in machine code
233 /// backends. It tracks a set of types that have been stored to an
234 /// inferred allocation.
235 /// Uses the `inferred_alloc` field.
236 inferred_alloc,
237 /// This special instruction only exists temporarily during semantic
238 /// analysis and is guaranteed to be unreachable in machine code
239 /// backends. Used to coordinate alloc_inferred, store_to_inferred_ptr,
240 /// and resolve_inferred_alloc instructions for comptime code.
241 /// Uses the `inferred_alloc_comptime` field.
242 inferred_alloc_comptime,
243 /// If the function will pass the result by-ref, this instruction returns the
244 /// result pointer. Otherwise it is equivalent to `alloc`.
245 /// Uses the `ty` field.
246 ret_ptr,
247 /// Inline assembly. Uses the `ty_pl` field. Payload is `Asm`.
248 assembly,
249 /// Bitwise AND. `&`.
250 /// Result type is the same as both operands.
251 /// Uses the `bin_op` field.
252 bit_and,
253 /// Bitwise OR. `|`.
254 /// Result type is the same as both operands.
255 /// Uses the `bin_op` field.
256 bit_or,
257 /// Shift right. `>>`
258 /// The rhs type may be a scalar version of the lhs type.
259 /// Uses the `bin_op` field.
260 shr,
261 /// Shift right. The shift produces a poison value if it shifts out any non-zero bits.
262 /// The rhs type may be a scalar version of the lhs type.
263 /// Uses the `bin_op` field.
264 shr_exact,
265 /// Shift left. `<<`
266 /// The rhs type may be a scalar version of the lhs type.
267 /// Uses the `bin_op` field.
268 shl,
269 /// Shift left; For unsigned integers, the shift produces a poison value if it shifts
270 /// out any non-zero bits. For signed integers, the shift produces a poison value if
271 /// it shifts out any bits that disagree with the resultant sign bit.
272 /// The rhs type may be a scalar version of the lhs type.
273 /// Uses the `bin_op` field.
274 shl_exact,
275 /// Saturating integer shift left. `<<|`. The result is the same type as the `lhs`.
276 /// The `rhs` must have the same vector shape as the `lhs`, but with any unsigned
277 /// integer as the scalar type.
278 /// The rhs type may be a scalar version of the lhs type.
279 /// Uses the `bin_op` field.
280 shl_sat,
281 /// Bitwise XOR. `^`
282 /// Uses the `bin_op` field.
283 xor,
284 /// Boolean or binary NOT.
285 /// Uses the `ty_op` field.
286 not,
287 /// Implements `@bitCast`.
288 ///
289 /// Uses the `ty_op` field.
290 bit_cast,
291 /// Like `bit_cast`, but triggers a safety panic if the destination type is an exhaustive
292 /// enum and the operand is not a valid value of this type;
293 /// i.e. equivalent to a safety check based on `.is_named_enum_value`
294 bit_cast_safe,
295 /// Cast a pointer to a different pointer type. The result type is a slice iff the operand
296 /// type is a slice (the length of the slice does not change). All other pointer attributes
297 /// except for the address space may change.
298 ///
299 /// Supports vectors of pointers.
300 ///
301 /// Uses the `ty_op` field.
302 ptr_cast,
303 /// Cast an integer to a pointer (not a slice). Operand type is always `usize`.
304 ///
305 /// Supports vectors of integers.
306 ///
307 /// Uses the `ty_op` field.
308 ptr_from_int,
309 /// Cast a pointer (not a slice) to an integer. Result type is always `usize`.
310 ///
311 /// Supports vectors of pointers.
312 ///
313 /// Uses the `ty_op` field.
314 int_from_ptr,
315 /// Cast an error set `E1` to a different error set `E2`, or cast an error union `E1!T` to
316 /// an error union `E2!T` with the same payload type but a different error set type.
317 ///
318 /// Uses the `ty_op` field.
319 error_cast,
320 /// Cast an integer to an error set type. The integer operand type is unsigned and has bit
321 /// width equal to `zcu.errorSetBits()`.
322 ///
323 /// Uses the `ty_op` field.
324 error_from_int,
325 /// Cast an error set to an integer type. The integer destination type is unsigned and has
326 /// bit width equal to `zcu.errorSetBits()`.
327 ///
328 /// Uses the `ty_op` field.
329 int_from_error,
330 /// Cast an enum value to a tagged union, whose tag type is that enum, and which has no
331 /// payload bits (i.e. all payloads are equivalent to `void`).
332 ///
333 /// Uses the `ty_op` field.
334 union_from_enum,
335 /// A block runs its body which always ends with a `noreturn` instruction,
336 /// so the only way to proceed to the code after the `block` is to encounter a `br`
337 /// that targets this `block`. If the `block` type is `noreturn`,
338 /// then there do not exist any `br` instructions targeting this `block`.
339 /// Uses the `ty_pl` field with payload `Block`.
340 ///
341 /// See `unwrapBlock` for a way to load this tag's data.
342 block,
343 /// A labeled block of code that loops forever. The body must be `noreturn`: loops
344 /// occur through an explicit `repeat` instruction pointing back to this one.
345 /// Result type is always `noreturn`; no instructions in a block follow this one.
346 /// There is always at least one `repeat` instruction referencing the loop.
347 /// Uses the `ty_pl` field. Payload is `Block`.
348 ///
349 /// See `unwrapBlock` for a way to load this tag's data.
350 loop,
351 /// Sends control flow back to the beginning of a parent `loop` body.
352 /// Uses the `repeat` field.
353 repeat,
354 /// Return from a block with a result.
355 /// Result type is always noreturn; no instructions in a block follow this one.
356 /// Uses the `br` field.
357 br,
358 /// Lowers to a trap/jam instruction causing program abortion.
359 /// This may lower to an instruction known to be invalid.
360 /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code.
361 /// Result type is always noreturn; no instructions in a block follow this one.
362 trap,
363 /// Lowers to a trap instruction causing debuggers to break here, or the next best thing.
364 /// The debugger or something else may allow the program to resume after this point.
365 /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code.
366 /// Result type is always void.
367 breakpoint,
368 /// Yields the return address of the current function.
369 /// Uses the `no_op` field.
370 ret_addr,
371 /// Implements @frameAddress builtin.
372 /// Uses the `no_op` field.
373 frame_addr,
374 /// Function call.
375 /// Result type is the return type of the function being called.
376 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
377 /// Triggers `resolveTypeLayout` on the return type of the callee.
378 ///
379 /// See `unwrapCall` for a way to load this tag's data.
380 call,
381 /// Same as `call` except with the `always_tail` attribute.
382 call_always_tail,
383 /// Same as `call` except with the `never_tail` attribute.
384 call_never_tail,
385 /// Same as `call` except with the `never_inline` attribute.
386 call_never_inline,
387 /// Count leading zeroes of an integer according to its representation in twos complement.
388 /// Result type will always be an unsigned integer big enough to fit the answer.
389 /// Uses the `ty_op` field.
390 clz,
391 /// Count trailing zeroes of an integer according to its representation in twos complement.
392 /// Result type will always be an unsigned integer big enough to fit the answer.
393 /// Uses the `ty_op` field.
394 ctz,
395 /// Count number of 1 bits in an integer according to its representation in twos complement.
396 /// Result type will always be an unsigned integer big enough to fit the answer.
397 /// Uses the `ty_op` field.
398 popcount,
399 /// Reverse the bytes in an integer according to its representation in twos complement.
400 /// Uses the `ty_op` field.
401 byte_swap,
402 /// Reverse the bits in an integer according to its representation in twos complement.
403 /// Uses the `ty_op` field.
404 bit_reverse,
405
406 /// Square root of a floating point number.
407 /// Uses the `un_op` field.
408 sqrt,
409 /// Sine function on a floating point number.
410 /// Uses the `un_op` field.
411 sin,
412 /// Cosine function on a floating point number.
413 /// Uses the `un_op` field.
414 cos,
415 /// Tangent function on a floating point number.
416 /// Uses the `un_op` field.
417 tan,
418 /// Base e exponential of a floating point number.
419 /// Uses the `un_op` field.
420 exp,
421 /// Base 2 exponential of a floating point number.
422 /// Uses the `un_op` field.
423 exp2,
424 /// Natural (base e) logarithm of a floating point number.
425 /// Uses the `un_op` field.
426 log,
427 /// Base 2 logarithm of a floating point number.
428 /// Uses the `un_op` field.
429 log2,
430 /// Base 10 logarithm of a floating point number.
431 /// Uses the `un_op` field.
432 log10,
433 /// Absolute value of an integer, floating point number or vector.
434 /// Result type is always unsigned if the operand is an integer.
435 /// Uses the `ty_op` field.
436 abs,
437 /// Floor: rounds a floating pointer number down to the nearest integer.
438 /// Uses the `un_op` field.
439 floor,
440 /// Ceiling: rounds a floating pointer number up to the nearest integer.
441 /// Uses the `un_op` field.
442 ceil,
443 /// Rounds a floating pointer number to the nearest integer.
444 /// Uses the `un_op` field.
445 round,
446 /// Rounds a floating pointer number to the nearest integer towards zero.
447 /// Uses the `un_op` field.
448 trunc_float,
449 /// Float negation. This affects the sign of zero, inf, and NaN, which is impossible
450 /// to do with sub. Integers are not allowed and must be represented with sub with
451 /// LHS of zero.
452 /// Uses the `un_op` field.
453 neg,
454 /// Same as `neg` with optimized float mode.
455 neg_optimized,
456
457 /// `<`. Result type is always bool.
458 /// Uses the `bin_op` field.
459 cmp_lt,
460 /// Same as `cmp_lt` with optimized float mode.
461 cmp_lt_optimized,
462 /// `<=`. Result type is always bool.
463 /// Uses the `bin_op` field.
464 cmp_lte,
465 /// Same as `cmp_lte` with optimized float mode.
466 cmp_lte_optimized,
467 /// `==`. Result type is always bool.
468 /// Uses the `bin_op` field.
469 cmp_eq,
470 /// Same as `cmp_eq` with optimized float mode.
471 cmp_eq_optimized,
472 /// `>=`. Result type is always bool.
473 /// Uses the `bin_op` field.
474 cmp_gte,
475 /// Same as `cmp_gte` with optimized float mode.
476 cmp_gte_optimized,
477 /// `>`. Result type is always bool.
478 /// Uses the `bin_op` field.
479 cmp_gt,
480 /// Same as `cmp_gt` with optimized float mode.
481 cmp_gt_optimized,
482 /// `!=`. Result type is always bool.
483 /// Uses the `bin_op` field.
484 cmp_neq,
485 /// Same as `cmp_neq` with optimized float mode.
486 cmp_neq_optimized,
487 /// Conditional between two vectors.
488 /// Result type is always a vector of bools.
489 /// Uses the `ty_pl` field, payload is `VectorCmp`.
490 cmp_vector,
491 /// Same as `cmp_vector` with optimized float mode.
492 cmp_vector_optimized,
493
494 /// Conditional branch.
495 /// Result type is always noreturn; no instructions in a block follow this one.
496 /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`.
497 ///
498 /// See `unwrapCondBr` for a way to load this tags's data.
499 cond_br,
500 /// Switch branch.
501 /// Result type is always noreturn; no instructions in a block follow this one.
502 /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.
503 ///
504 /// See `unwrapSwitch` for a way to load this tags's data.
505 switch_br,
506 /// Switch branch which can dispatch back to itself with a different operand.
507 /// Result type is always noreturn; no instructions in a block follow this one.
508 /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.
509 ///
510 /// See `unwrapSwitch` for a way to load this tags's data.
511 loop_switch_br,
512 /// Dispatches back to a branch of a parent `loop_switch_br`.
513 /// Result type is always noreturn; no instructions in a block follow this one.
514 /// Uses the `br` field. `block_inst` is a `loop_switch_br` instruction.
515 switch_dispatch,
516 /// Given an operand which is an error union, splits control flow. In
517 /// case of error, control flow goes into the block that is part of this
518 /// instruction, which is guaranteed to end with a return instruction
519 /// and never breaks out of the block.
520 /// In the case of non-error, control flow proceeds to the next instruction
521 /// after the `try`, with the result of this instruction being the unwrapped
522 /// payload value, as if `unwrap_errunion_payload` was executed on the operand.
523 /// The error branch is considered to have a branch hint of `.unlikely`.
524 /// Uses the `pl_op` field. Payload is `Try`.
525 ///
526 /// See `unwrapTry` for a way to load this tag's data.
527 @"try",
528 /// Same as `try` except the error branch hint is `.cold`.
529 try_cold,
530 /// Same as `try` except the operand is a pointer to an error union, and the
531 /// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr`
532 /// was executed on the operand.
533 /// Uses the `ty_pl` field. Payload is `TryPtr`.
534 ///
535 /// See `unwrapTryPtr` for a way to load this tag's data.
536 try_ptr,
537 /// Same as `try_ptr` except the error branch hint is `.cold`.
538 try_ptr_cold,
539 /// Notes the beginning of a source code statement and marks the line and column.
540 /// Result type is always void.
541 /// Uses the `dbg_stmt` field.
542 dbg_stmt,
543 /// Marks a statement that can be stepped to but produces no code.
544 dbg_empty_stmt,
545 /// A block that represents an inlined function call.
546 /// Uses the `ty_pl` field. Payload is `DbgInlineBlock`.
547 ///
548 /// See `unwrapBlock` for a way to load this tag's data.
549 dbg_inline_block,
550 /// Marks the beginning of a local variable. The operand is a pointer pointing
551 /// to the storage for the variable. The local may be a const or a var.
552 /// Result type is always void.
553 /// Uses `pl_op`. The payload index is the variable name. It points to the extra
554 /// array, reinterpreting the bytes there as a null-terminated string.
555 dbg_var_ptr,
556 /// Same as `dbg_var_ptr` except the local is a const, not a var, and the
557 /// operand is the local's value.
558 dbg_var_val,
559 /// Same as `dbg_var_val` except the local is an inline function argument.
560 dbg_arg_inline,
561 /// ?T => bool
562 /// Result type is always bool.
563 /// Uses the `un_op` field.
564 is_null,
565 /// ?T => bool (inverted logic)
566 /// Result type is always bool.
567 /// Uses the `un_op` field.
568 is_non_null,
569 /// *?T => bool
570 /// Result type is always bool.
571 /// Uses the `un_op` field.
572 is_null_ptr,
573 /// *?T => bool (inverted logic)
574 /// Result type is always bool.
575 /// Uses the `un_op` field.
576 is_non_null_ptr,
577 /// E!T => bool
578 /// Result type is always bool.
579 /// Uses the `un_op` field.
580 is_err,
581 /// E!T => bool (inverted logic)
582 /// Result type is always bool.
583 /// Uses the `un_op` field.
584 is_non_err,
585 /// *E!T => bool
586 /// Result type is always bool.
587 /// Uses the `un_op` field.
588 is_err_ptr,
589 /// *E!T => bool (inverted logic)
590 /// Result type is always bool.
591 /// Uses the `un_op` field.
592 is_non_err_ptr,
593 /// Read a value from a pointer.
594 /// Uses the `ty_op` field.
595 load,
596 /// Return a value from a function.
597 /// Result type is always noreturn; no instructions in a block follow this one.
598 /// Uses the `un_op` field.
599 /// Triggers `resolveTypeLayout` on the return type.
600 ret,
601 /// Same as `ret`, except if the operand is undefined, the
602 /// returned value is 0xaa bytes, and any other safety metadata
603 /// such as Valgrind integrations should be notified of
604 /// this value being undefined.
605 ret_safe,
606 /// This instruction communicates that the function's result value is pointed to by
607 /// the operand. If the function will pass the result by-ref, the operand is a
608 /// `ret_ptr` instruction. Otherwise, this instruction is equivalent to a `load`
609 /// on the operand, followed by a `ret` on the loaded value.
610 /// Result type is always noreturn; no instructions in a block follow this one.
611 /// Uses the `un_op` field.
612 /// Triggers `resolveTypeLayout` on the return type.
613 ret_load,
614 /// Write a value to a pointer. LHS is pointer, RHS is value.
615 /// Result type is always void.
616 /// Uses the `bin_op` field.
617 /// The value to store may be undefined, in which case the destination
618 /// memory region has undefined bytes after this instruction is
619 /// evaluated. In such case ignoring this instruction is legal
620 /// lowering.
621 store,
622 /// Same as `store`, except if the value to store is undefined, the
623 /// memory region should be filled with 0xaa bytes, and any other
624 /// safety metadata such as Valgrind integrations should be notified of
625 /// this memory region being undefined.
626 store_safe,
627 /// Indicates the program counter will never get to this instruction.
628 /// Result type is always noreturn; no instructions in a block follow this one.
629 unreach,
630 /// Convert from a float type to a smaller one.
631 /// Uses the `ty_op` field.
632 fptrunc,
633 /// Convert from a float type to a wider one.
634 /// Uses the `ty_op` field.
635 fpext,
636 /// Returns an integer with a different type than the operand. The new type may have
637 /// fewer, the same, or more bits than the operand type. The new type may also
638 /// differ in signedness from the operand type. However, the instruction
639 /// guarantees that the same integer value fits in both types.
640 /// The new type may also be an enum type, in which case the integer cast operates on
641 /// the integer tag type of the enum.
642 /// See `trunc` for integer truncation.
643 /// Uses the `ty_op` field.
644 int_cast,
645 /// Like `int_cast`, but includes two safety checks:
646 /// * triggers a safety panic if the cast truncates bits
647 /// * triggers a safety panic if the destination type is an exhaustive enum
648 /// and the operand is not a valid value of this type; i.e. equivalent to
649 /// a safety check based on `.is_named_enum_value`
650 int_cast_safe,
651 /// Truncate higher bits from an integer, resulting in an integer type with the same
652 /// sign but an equal or smaller number of bits.
653 /// Uses the `ty_op` field.
654 trunc,
655 /// ?T => T. If the value is null, illegal behavior.
656 /// Uses the `ty_op` field.
657 optional_payload,
658 /// *?T => *T. If the value is null, illegal behavior.
659 /// Uses the `ty_op` field.
660 optional_payload_ptr,
661 /// *?T => *T. Sets the value to non-null with an undefined payload value.
662 /// Uses the `ty_op` field.
663 optional_payload_ptr_set,
664 /// Given a payload value, wraps it in an optional type.
665 /// Uses the `ty_op` field.
666 wrap_optional,
667 /// E!T -> T. If the value is an error, illegal behavior.
668 /// Uses the `ty_op` field.
669 unwrap_errunion_payload,
670 /// E!T -> E. If the value is not an error, illegal behavior.
671 /// Uses the `ty_op` field.
672 unwrap_errunion_err,
673 /// *(E!T) -> *T. If the value is an error, illegal behavior.
674 /// Uses the `ty_op` field.
675 unwrap_errunion_payload_ptr,
676 /// *(E!T) -> E. If the value is not an error, illegal behavior.
677 /// Uses the `ty_op` field.
678 unwrap_errunion_err_ptr,
679 /// *(E!T) => *T. Sets the value to non-error with an undefined payload value.
680 /// Uses the `ty_op` field.
681 errunion_payload_ptr_set,
682 /// wrap from T to E!T
683 /// Uses the `ty_op` field.
684 wrap_errunion_payload,
685 /// wrap from E to E!T
686 /// Uses the `ty_op` field.
687 wrap_errunion_err,
688 /// Given a pointer to a struct or union and a field index, returns a pointer to the field.
689 /// Uses the `ty_pl` field, payload is `StructField`.
690 /// TODO rename to `agg_field_ptr`.
691 struct_field_ptr,
692 /// Given a pointer to a struct or union, returns a pointer to the field.
693 /// The field index is the number at the end of the name.
694 /// Uses `ty_op` field.
695 /// TODO rename to `agg_field_ptr_index_X`
696 struct_field_ptr_index_0,
697 struct_field_ptr_index_1,
698 struct_field_ptr_index_2,
699 struct_field_ptr_index_3,
700 /// Given a byval struct or union and a field index, returns the field byval.
701 /// Uses the `ty_pl` field, payload is `StructField`.
702 agg_field_val,
703 /// Given a pointer to a tagged union, set its tag to the provided value.
704 /// Result type is always void.
705 /// Uses the `bin_op` field. LHS is union pointer, RHS is new tag value.
706 set_union_tag,
707 /// Given a tagged union value, get its tag value.
708 /// Uses the `ty_op` field.
709 get_union_tag,
710 /// Constructs a slice from a pointer and a length.
711 /// Uses the `ty_pl` field, payload is `Bin`. lhs is ptr, rhs is len.
712 slice,
713 /// Given a slice value, return the length.
714 /// Result type is always usize.
715 /// Uses the `ty_op` field.
716 slice_len,
717 /// Given a slice value, return the pointer.
718 /// Uses the `ty_op` field.
719 slice_ptr,
720 /// Given a pointer to a slice, return a pointer to the length of the slice.
721 /// Uses the `ty_op` field.
722 ptr_slice_len_ptr,
723 /// Given a pointer to a slice, return a pointer to the pointer of the slice.
724 /// Uses the `ty_op` field.
725 ptr_slice_ptr_ptr,
726 /// Given an (array value or vector value) and element index, return the element value at
727 /// that index. If the lhs is a vector value, the index is guaranteed to be comptime-known.
728 /// Result type is the element type of the array operand.
729 /// Uses the `bin_op` field.
730 array_elem_val,
731 /// Given a slice value, and element index, return the element value at that index.
732 /// Result type is the element type of the slice operand.
733 /// Uses the `bin_op` field.
734 slice_elem_val,
735 /// Given a slice value and element index, return a pointer to the element value at that index.
736 /// Result type is a pointer to the element type of the slice operand.
737 /// Uses the `ty_pl` field with payload `Bin`.
738 slice_elem_ptr,
739 /// Given a pointer value, and element index, return the element value at that index.
740 /// The pointer size is either `.c` or `.many`.
741 /// Result type is the element type of the pointer operand.
742 /// Uses the `bin_op` field.
743 ptr_elem_val,
744 /// Given a pointer value, and element index, return the element pointer at that index.
745 /// Result type is pointer to the element type of the pointer operand.
746 /// Uses the `ty_pl` field with payload `Bin`.
747 ptr_elem_ptr,
748 /// Given a pointer to an array, return a slice.
749 /// Uses the `ty_op` field.
750 array_to_slice,
751 /// Given an array, return a vector with the same element type and length. A sentinel on
752 /// the operand type is not included in the result.
753 ///
754 /// Vectors have no well-defined in-memory layout, so only the backend can know whether
755 /// the array representation may be reinterpreted rather than copied element-by-element.
756 /// Backends which do not lower this directly can enable
757 /// `Air.Legalize.Feature.expand_array_to_vector`.
758 ///
759 /// Uses the `ty_op` field.
760 array_to_vector,
761 /// Given a float operand, return the integer with the closest mathematical meaning.
762 /// Uses the `ty_op` field.
763 int_from_float,
764 /// Same as `int_from_float` with optimized float mode.
765 int_from_float_optimized,
766 /// Same as `int_from_float`, but with a safety check that the operand is in bounds.
767 int_from_float_safe,
768 /// Same as `int_from_float_optimized`, but with a safety check that the operand is in bounds.
769 int_from_float_optimized_safe,
770 /// Given an integer operand, return the float with the closest mathematical meaning.
771 /// Uses the `ty_op` field.
772 float_from_int,
773
774 /// Transforms a vector into a scalar value by performing a sequential
775 /// horizontal reduction of its elements using the specified operator.
776 /// The vector element type (and hence result type) will be:
777 /// * and, or, xor => integer or boolean
778 /// * min, max, add, mul => integer or float
779 /// Uses the `reduce` field.
780 reduce,
781 /// Same as `reduce` with optimized float mode.
782 reduce_optimized,
783 /// Given an operand, return a vector or array with all elements equal to the operand.
784 /// For a sentinel-terminated array, the sentinel is derived from the result type.
785 /// Uses the `ty_op` field.
786 splat,
787 /// Constructs a vector by selecting elements from a single vector based on a mask. Each
788 /// mask element is either an index into the vector, or a comptime-known value, or "undef".
789 /// Uses the `ty_pl` field, where the payload index points to:
790 /// 1. mask_elem: ShuffleOneMask // for each `mask_len`, which comes from `ty_pl.ty`
791 /// 2. operand: Ref // guaranteed not to be an interned value
792 /// See `unwrapShuffleOne` for a way to load this tag's data.
793 shuffle_one,
794 /// Constructs a vector by selecting elements from two vectors based on a mask. Each mask
795 /// element is either an index into one of the vectors, or "undef".
796 /// Uses the `ty_pl` field, where the payload index points to:
797 /// 1. mask_elem: ShuffleOneMask // for each `mask_len`, which comes from `ty_pl.ty`
798 /// 2. operand_a: Ref // guaranteed not to be an interned value
799 /// 3. operand_b: Ref // guaranteed not to be an interned value
800 /// See `unwrapShuffleTwo` for a way to load this tag's data..
801 shuffle_two,
802 /// Constructs a vector element-wise from `a` or `b` based on `pred`.
803 /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`.
804 select,
805
806 /// Given dest pointer and value, set all elements at dest to value.
807 /// Dest pointer is either a slice or a pointer to array.
808 /// The element type may be any type, and the slice may have any alignment.
809 /// Result type is always void.
810 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the element value.
811 /// The element value may be undefined, in which case the destination
812 /// memory region has undefined bytes after this instruction is
813 /// evaluated. In such case ignoring this instruction is legal
814 /// lowering.
815 /// If the length is compile-time known (due to the destination being a
816 /// pointer-to-array), then it is guaranteed to be greater than zero.
817 memset,
818 /// Same as `memset`, except if the element value is undefined, the memory region
819 /// should be filled with 0xaa bytes, and any other safety metadata such as Valgrind
820 /// integrations should be notified of this memory region being undefined.
821 memset_safe,
822 /// Given dest pointer and source pointer, copy elements from source to dest.
823 /// Dest pointer is either a slice or a pointer to array.
824 /// The dest element type may be any type.
825 /// Source pointer must have same element type as dest element type.
826 /// Dest slice may have any alignment; source pointer may have any alignment.
827 /// The two memory regions must not overlap.
828 /// Result type is always void.
829 ///
830 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
831 ///
832 /// If the length is compile-time known (due to the destination or
833 /// source being a pointer-to-array), then it is guaranteed to be
834 /// greater than zero.
835 memcpy,
836 /// Given dest pointer and source pointer, copy elements from source to dest.
837 /// Dest pointer is either a slice or a pointer to array.
838 /// The dest element type may be any type.
839 /// Source pointer must have same element type as dest element type.
840 /// Dest slice may have any alignment; source pointer may have any alignment.
841 /// The two memory regions may overlap.
842 /// Result type is always void.
843 ///
844 /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer.
845 ///
846 /// If the length is compile-time known (due to the destination or
847 /// source being a pointer-to-array), then it is guaranteed to be
848 /// greater than zero.
849 memmove,
850
851 /// Uses the `ty_pl` field with payload `Cmpxchg`.
852 cmpxchg_weak,
853 /// Uses the `ty_pl` field with payload `Cmpxchg`.
854 cmpxchg_strong,
855 /// Atomically load from a pointer.
856 /// Result type is the element type of the pointer.
857 /// Uses the `atomic_load` field.
858 atomic_load,
859 /// Atomically store through a pointer.
860 /// Result type is always `void`.
861 /// Uses the `bin_op` field. LHS is pointer, RHS is element.
862 atomic_store_unordered,
863 /// Same as `atomic_store_unordered` but with `AtomicOrder.monotonic`.
864 atomic_store_monotonic,
865 /// Same as `atomic_store_unordered` but with `AtomicOrder.release`.
866 atomic_store_release,
867 /// Same as `atomic_store_unordered` but with `AtomicOrder.seq_cst`.
868 atomic_store_seq_cst,
869 /// Atomically read-modify-write via a pointer.
870 /// Result type is the element type of the pointer.
871 /// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`.
872 atomic_rmw,
873
874 /// Returns true if enum tag value has a name.
875 /// Uses the `un_op` field.
876 is_named_enum_value,
877
878 /// Given an enum tag value, returns the tag name. The enum type may be non-exhaustive.
879 /// Result type is always `[:0]const u8`.
880 /// Uses the `un_op` field.
881 tag_name,
882
883 /// Given an error value, return the error name. Result type is always `[:0]const u8`.
884 /// Uses the `un_op` field.
885 error_name,
886
887 /// Returns true if error set has error with value.
888 /// Uses the `ty_op` field.
889 error_set_has_value,
890
891 /// Constructs a vector, tuple, struct, or array value out of runtime-known elements.
892 /// Some of the elements may be comptime-known.
893 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which
894 /// is a `Ref`. Length of the array is given by the vector type.
895 /// If the type is an array with a sentinel, the AIR elements do not include it
896 /// explicitly.
897 aggregate_init,
898
899 /// Constructs a union from a field index and a runtime-known init value.
900 /// Uses the `ty_pl` field with payload `UnionInit`.
901 union_init,
902
903 /// Communicates an intent to load memory.
904 /// Result is always unused.
905 /// Uses the `prefetch` field.
906 prefetch,
907
908 /// Computes `(a * b) + c`, but only rounds once.
909 /// Uses the `pl_op` field with payload `Bin`.
910 /// The operand is the addend. The mulends are lhs and rhs.
911 mul_add,
912
913 /// Implements @fieldParentPtr builtin.
914 /// Uses the `ty_pl` field.
915 field_parent_ptr,
916
917 /// Implements @wasmMemorySize builtin.
918 /// Result type is always `usize`,
919 /// Uses the `pl_op` field, payload represents the index of the target memory.
920 /// The operand is unused and always set to `Ref.none`.
921 wasm_memory_size,
922
923 /// Implements @wasmMemoryGrow builtin.
924 /// Result type is always `isize`,
925 /// Uses the `pl_op` field, payload represents the index of the target memory.
926 wasm_memory_grow,
927
928 /// Returns `true` if and only if the operand, an integer with the same
929 /// size as the error integer type, is less than *or equal to* the total
930 /// number of errors in the Zcu. The "or equal to" is a consequence of
931 /// value 0 being reserved for the "non-error" status in error unions.
932 ///
933 /// This instruction exists (as opposed to just using `cmp_lte` against
934 /// a constant) because the number of errors in the Zcu is not known
935 /// until `Compilation.flush`. Before then, semantic analysis could
936 /// discover new errors at any time.
937 ///
938 /// Result type is always `bool`.
939 ///
940 /// Uses the `un_op` field.
941 cmp_lte_errors_len,
942
943 /// Returns pointer to current error return trace.
944 err_return_trace,
945
946 /// Sets the operand as the current error return trace,
947 set_err_return_trace,
948
949 /// Convert the address space of a pointer.
950 /// Uses the `ty_op` field.
951 addrspace_cast,
952
953 /// Saves the error return trace index, if any. Otherwise, returns 0.
954 /// Uses the `ty_pl` field.
955 save_err_return_trace_index,
956
957 /// Compute a pointer to a `Nav` at runtime, always one of:
958 ///
959 /// * `threadlocal var`
960 /// * `extern threadlocal var` (or corresponding `@extern`)
961 /// * `@extern` with `.is_dll_import = true`
962 /// * `@extern` with `.relocation = .pcrel`
963 ///
964 /// Such pointers are runtime values, so cannot be represented with an InternPool index.
965 ///
966 /// Uses the `ty_nav` field.
967 runtime_nav_ptr,
968
969 /// Implements @cVaArg builtin.
970 /// Uses the `ty_op` field.
971 c_va_arg,
972 /// Implements @cVaCopy builtin.
973 /// Uses the `ty_op` field.
974 c_va_copy,
975 /// Implements @cVaEnd builtin.
976 /// Uses the `un_op` field.
977 c_va_end,
978 /// Implements @cVaStart builtin.
979 /// Uses the `ty` field.
980 c_va_start,
981
982 /// Implements `.len` field for `@SpirvType(.{ .runtime_array = T })`.
983 /// Result type is always `u32`.
984 /// Uses the `ty_pl` field, payload is `StructField`.
985 spirv_runtime_array_len,
986
987 /// Implements @workItemId builtin.
988 /// Result type is always `u32`
989 /// Uses the `pl_op` field, payload is the dimension to get the work item id for.
990 /// Operand is unused and set to Ref.none
991 work_item_id,
992 /// Implements @workGroupSize builtin.
993 /// Result type is always `u32`
994 /// Uses the `pl_op` field, payload is the dimension to get the work group size for.
995 /// Operand is unused and set to Ref.none
996 work_group_size,
997 /// Implements @workGroupId builtin.
998 /// Result type is always `u32`
999 /// Uses the `pl_op` field, payload is the dimension to get the work group id for.
1000 /// Operand is unused and set to Ref.none
1001 work_group_id,
1002
1003 // The remaining instructions are not emitted by Sema. They are only emitted by `Legalize`,
1004 // depending on the enabled features. As such, backends can consider them `unreachable` if
1005 // they do not enable the relevant legalizations.
1006
1007 /// Given a pointer to a vector, a runtime-known index, and a scalar value, store the value
1008 /// into the vector at the given index. Zig does not support this operation, but `Legalize`
1009 /// may emit it when scalarizing vector operations.
1010 ///
1011 /// Uses the `pl_op` field with payload `Bin`. `operand` is the vector pointer. `lhs` is the
1012 /// element index of type `usize`. `rhs` is the element value. Result is always void.
1013 legalize_vec_store_elem,
1014 /// Given a vector value and a runtime-known index, return the element value at that index.
1015 /// This instruction is similar to `array_elem_val`; the only difference is that the index
1016 /// here is runtime-known, which is usually not allowed for vectors. `Legalize` may emit
1017 /// this instruction when scalarizing vector operations.
1018 ///
1019 /// Uses the `bin_op` field. `lhs` is the vector value. `rhs` is the element index. Result
1020 /// type is the vector element type.
1021 legalize_vec_elem_val,
1022
1023 /// A call to a compiler_rt routine. `Legalize` may emit this instruction if any soft-float
1024 /// legalizations are enabled.
1025 ///
1026 /// Uses the `legalize_compiler_rt_call` union field.
1027 ///
1028 /// The name of the function symbol is given by `func.name(target)`.
1029 /// The calling convention is given by `func.@"callconv"(target)`.
1030 /// The return type (and hence the result type of this instruction) is `func.returnType()`.
1031 /// The parameter types are the types of the arguments given in `Air.Call`.
1032 ///
1033 /// See `unwrapCompilerRtCall` for a way to load this tag's data.
1034 legalize_compiler_rt_call,
1035
1036 pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag {
1037 switch (op) {
1038 .lt => return if (optimized) .cmp_lt_optimized else .cmp_lt,
1039 .lte => return if (optimized) .cmp_lte_optimized else .cmp_lte,
1040 .eq => return if (optimized) .cmp_eq_optimized else .cmp_eq,
1041 .gte => return if (optimized) .cmp_gte_optimized else .cmp_gte,
1042 .gt => return if (optimized) .cmp_gt_optimized else .cmp_gt,
1043 .neq => return if (optimized) .cmp_neq_optimized else .cmp_neq,
1044 }
1045 }
1046
1047 pub fn toCmpOp(tag: Tag) ?std.math.CompareOperator {
1048 return switch (tag) {
1049 .cmp_lt, .cmp_lt_optimized => .lt,
1050 .cmp_lte, .cmp_lte_optimized => .lte,
1051 .cmp_eq, .cmp_eq_optimized => .eq,
1052 .cmp_gte, .cmp_gte_optimized => .gte,
1053 .cmp_gt, .cmp_gt_optimized => .gt,
1054 .cmp_neq, .cmp_neq_optimized => .neq,
1055 else => null,
1056 };
1057 }
1058 };
1059
1060 /// The position of an AIR instruction within the `Air` instructions array.
1061 pub const Index = enum(u32) {
1062 _,
1063
1064 pub fn unwrap(index: Index) union(enum) { ref: Inst.Ref, target: u31 } {
1065 const low_index: u31 = @truncate(@backingInt(index));
1066 return switch (@as(u1, @intCast(@backingInt(index) >> 31))) {
1067 0 => .{ .ref = @fromBackingInt(@intCast(@as(u32, 1 << 31) | low_index)) },
1068 1 => .{ .target = low_index },
1069 };
1070 }
1071
1072 pub fn toRef(index: Index) Inst.Ref {
1073 return index.unwrap().ref;
1074 }
1075
1076 pub fn fromTargetIndex(index: u31) Index {
1077 return @fromBackingInt(@intCast((1 << 31) | @as(u32, index)));
1078 }
1079
1080 pub fn toTargetIndex(index: Index) u31 {
1081 return index.unwrap().target;
1082 }
1083
1084 pub fn format(index: Index, w: *std.Io.Writer) std.Io.Writer.Error!void {
1085 try w.writeByte('%');
1086 switch (index.unwrap()) {
1087 .ref => {},
1088 .target => try w.writeByte('t'),
1089 }
1090 try w.print("{d}", .{@as(u31, @truncate(@backingInt(index)))});
1091 }
1092 };
1093
1094 /// Either a reference to a value stored in the InternPool, or a reference to an AIR instruction.
1095 /// The most-significant bit of the value is a tag bit. This bit is 1 if the value represents an
1096 /// instruction index and 0 if it represents an InternPool index.
1097 ///
1098 /// The ref `none` is an exception: it has the tag bit set but refers to the InternPool.
1099 pub const Ref = enum(u32) {
1100 u0_type = @backingInt(InternPool.Index.u0_type),
1101 u1_type = @backingInt(InternPool.Index.u1_type),
1102 u8_type = @backingInt(InternPool.Index.u8_type),
1103 i8_type = @backingInt(InternPool.Index.i8_type),
1104 u16_type = @backingInt(InternPool.Index.u16_type),
1105 i16_type = @backingInt(InternPool.Index.i16_type),
1106 u29_type = @backingInt(InternPool.Index.u29_type),
1107 u32_type = @backingInt(InternPool.Index.u32_type),
1108 i32_type = @backingInt(InternPool.Index.i32_type),
1109 u64_type = @backingInt(InternPool.Index.u64_type),
1110 i64_type = @backingInt(InternPool.Index.i64_type),
1111 u80_type = @backingInt(InternPool.Index.u80_type),
1112 u128_type = @backingInt(InternPool.Index.u128_type),
1113 i128_type = @backingInt(InternPool.Index.i128_type),
1114 u256_type = @backingInt(InternPool.Index.u256_type),
1115 usize_type = @backingInt(InternPool.Index.usize_type),
1116 isize_type = @backingInt(InternPool.Index.isize_type),
1117 c_char_type = @backingInt(InternPool.Index.c_char_type),
1118 c_short_type = @backingInt(InternPool.Index.c_short_type),
1119 c_ushort_type = @backingInt(InternPool.Index.c_ushort_type),
1120 c_int_type = @backingInt(InternPool.Index.c_int_type),
1121 c_uint_type = @backingInt(InternPool.Index.c_uint_type),
1122 c_long_type = @backingInt(InternPool.Index.c_long_type),
1123 c_ulong_type = @backingInt(InternPool.Index.c_ulong_type),
1124 c_longlong_type = @backingInt(InternPool.Index.c_longlong_type),
1125 c_ulonglong_type = @backingInt(InternPool.Index.c_ulonglong_type),
1126 c_longdouble_type = @backingInt(InternPool.Index.c_longdouble_type),
1127 f16_type = @backingInt(InternPool.Index.f16_type),
1128 f32_type = @backingInt(InternPool.Index.f32_type),
1129 f64_type = @backingInt(InternPool.Index.f64_type),
1130 f80_type = @backingInt(InternPool.Index.f80_type),
1131 f128_type = @backingInt(InternPool.Index.f128_type),
1132 anyopaque_type = @backingInt(InternPool.Index.anyopaque_type),
1133 bool_type = @backingInt(InternPool.Index.bool_type),
1134 void_type = @backingInt(InternPool.Index.void_type),
1135 type_type = @backingInt(InternPool.Index.type_type),
1136 anyerror_type = @backingInt(InternPool.Index.anyerror_type),
1137 comptime_int_type = @backingInt(InternPool.Index.comptime_int_type),
1138 comptime_float_type = @backingInt(InternPool.Index.comptime_float_type),
1139 noreturn_type = @backingInt(InternPool.Index.noreturn_type),
1140 anyframe_type = @backingInt(InternPool.Index.anyframe_type),
1141 null_type = @backingInt(InternPool.Index.null_type),
1142 undefined_type = @backingInt(InternPool.Index.undefined_type),
1143 enum_literal_type = @backingInt(InternPool.Index.enum_literal_type),
1144 ptr_usize_type = @backingInt(InternPool.Index.ptr_usize_type),
1145 ptr_const_comptime_int_type = @backingInt(InternPool.Index.ptr_const_comptime_int_type),
1146 manyptr_u8_type = @backingInt(InternPool.Index.manyptr_u8_type),
1147 manyptr_const_u8_type = @backingInt(InternPool.Index.manyptr_const_u8_type),
1148 manyptr_const_u8_sentinel_0_type = @backingInt(InternPool.Index.manyptr_const_u8_sentinel_0_type),
1149 slice_const_u8_type = @backingInt(InternPool.Index.slice_const_u8_type),
1150 slice_const_u8_sentinel_0_type = @backingInt(InternPool.Index.slice_const_u8_sentinel_0_type),
1151 manyptr_const_slice_const_u8_type = @backingInt(InternPool.Index.manyptr_const_slice_const_u8_type),
1152 slice_const_slice_const_u8_type = @backingInt(InternPool.Index.slice_const_slice_const_u8_type),
1153 optional_type_type = @backingInt(InternPool.Index.optional_type_type),
1154 manyptr_const_type_type = @backingInt(InternPool.Index.manyptr_const_type_type),
1155 slice_const_type_type = @backingInt(InternPool.Index.slice_const_type_type),
1156 vector_8_i8_type = @backingInt(InternPool.Index.vector_8_i8_type),
1157 vector_16_i8_type = @backingInt(InternPool.Index.vector_16_i8_type),
1158 vector_32_i8_type = @backingInt(InternPool.Index.vector_32_i8_type),
1159 vector_64_i8_type = @backingInt(InternPool.Index.vector_64_i8_type),
1160 vector_1_u8_type = @backingInt(InternPool.Index.vector_1_u8_type),
1161 vector_2_u8_type = @backingInt(InternPool.Index.vector_2_u8_type),
1162 vector_4_u8_type = @backingInt(InternPool.Index.vector_4_u8_type),
1163 vector_8_u8_type = @backingInt(InternPool.Index.vector_8_u8_type),
1164 vector_16_u8_type = @backingInt(InternPool.Index.vector_16_u8_type),
1165 vector_32_u8_type = @backingInt(InternPool.Index.vector_32_u8_type),
1166 vector_64_u8_type = @backingInt(InternPool.Index.vector_64_u8_type),
1167 vector_2_i16_type = @backingInt(InternPool.Index.vector_2_i16_type),
1168 vector_4_i16_type = @backingInt(InternPool.Index.vector_4_i16_type),
1169 vector_8_i16_type = @backingInt(InternPool.Index.vector_8_i16_type),
1170 vector_16_i16_type = @backingInt(InternPool.Index.vector_16_i16_type),
1171 vector_32_i16_type = @backingInt(InternPool.Index.vector_32_i16_type),
1172 vector_4_u16_type = @backingInt(InternPool.Index.vector_4_u16_type),
1173 vector_8_u16_type = @backingInt(InternPool.Index.vector_8_u16_type),
1174 vector_16_u16_type = @backingInt(InternPool.Index.vector_16_u16_type),
1175 vector_32_u16_type = @backingInt(InternPool.Index.vector_32_u16_type),
1176 vector_2_i32_type = @backingInt(InternPool.Index.vector_2_i32_type),
1177 vector_4_i32_type = @backingInt(InternPool.Index.vector_4_i32_type),
1178 vector_8_i32_type = @backingInt(InternPool.Index.vector_8_i32_type),
1179 vector_16_i32_type = @backingInt(InternPool.Index.vector_16_i32_type),
1180 vector_4_u32_type = @backingInt(InternPool.Index.vector_4_u32_type),
1181 vector_8_u32_type = @backingInt(InternPool.Index.vector_8_u32_type),
1182 vector_16_u32_type = @backingInt(InternPool.Index.vector_16_u32_type),
1183 vector_2_i64_type = @backingInt(InternPool.Index.vector_2_i64_type),
1184 vector_4_i64_type = @backingInt(InternPool.Index.vector_4_i64_type),
1185 vector_8_i64_type = @backingInt(InternPool.Index.vector_8_i64_type),
1186 vector_2_u64_type = @backingInt(InternPool.Index.vector_2_u64_type),
1187 vector_4_u64_type = @backingInt(InternPool.Index.vector_4_u64_type),
1188 vector_8_u64_type = @backingInt(InternPool.Index.vector_8_u64_type),
1189 vector_1_u128_type = @backingInt(InternPool.Index.vector_1_u128_type),
1190 vector_2_u128_type = @backingInt(InternPool.Index.vector_2_u128_type),
1191 vector_1_u256_type = @backingInt(InternPool.Index.vector_1_u256_type),
1192 vector_4_f16_type = @backingInt(InternPool.Index.vector_4_f16_type),
1193 vector_8_f16_type = @backingInt(InternPool.Index.vector_8_f16_type),
1194 vector_16_f16_type = @backingInt(InternPool.Index.vector_16_f16_type),
1195 vector_32_f16_type = @backingInt(InternPool.Index.vector_32_f16_type),
1196 vector_2_f32_type = @backingInt(InternPool.Index.vector_2_f32_type),
1197 vector_4_f32_type = @backingInt(InternPool.Index.vector_4_f32_type),
1198 vector_8_f32_type = @backingInt(InternPool.Index.vector_8_f32_type),
1199 vector_16_f32_type = @backingInt(InternPool.Index.vector_16_f32_type),
1200 vector_2_f64_type = @backingInt(InternPool.Index.vector_2_f64_type),
1201 vector_4_f64_type = @backingInt(InternPool.Index.vector_4_f64_type),
1202 vector_8_f64_type = @backingInt(InternPool.Index.vector_8_f64_type),
1203 optional_noreturn_type = @backingInt(InternPool.Index.optional_noreturn_type),
1204 anyerror_void_error_union_type = @backingInt(InternPool.Index.anyerror_void_error_union_type),
1205 adhoc_inferred_error_set_type = @backingInt(InternPool.Index.adhoc_inferred_error_set_type),
1206 generic_poison_type = @backingInt(InternPool.Index.generic_poison_type),
1207 empty_tuple_type = @backingInt(InternPool.Index.empty_tuple_type),
1208 undef = @backingInt(InternPool.Index.undef),
1209 undef_bool = @backingInt(InternPool.Index.undef_bool),
1210 undef_usize = @backingInt(InternPool.Index.undef_usize),
1211 undef_u1 = @backingInt(InternPool.Index.undef_u1),
1212 zero = @backingInt(InternPool.Index.zero),
1213 zero_usize = @backingInt(InternPool.Index.zero_usize),
1214 zero_u1 = @backingInt(InternPool.Index.zero_u1),
1215 zero_u8 = @backingInt(InternPool.Index.zero_u8),
1216 one = @backingInt(InternPool.Index.one),
1217 one_usize = @backingInt(InternPool.Index.one_usize),
1218 one_u1 = @backingInt(InternPool.Index.one_u1),
1219 one_u8 = @backingInt(InternPool.Index.one_u8),
1220 four_u8 = @backingInt(InternPool.Index.four_u8),
1221 negative_one = @backingInt(InternPool.Index.negative_one),
1222 void_value = @backingInt(InternPool.Index.void_value),
1223 unreachable_value = @backingInt(InternPool.Index.unreachable_value),
1224 null_value = @backingInt(InternPool.Index.null_value),
1225 bool_true = @backingInt(InternPool.Index.bool_true),
1226 bool_false = @backingInt(InternPool.Index.bool_false),
1227 empty_tuple = @backingInt(InternPool.Index.empty_tuple),
1228
1229 /// This Ref does not correspond to any AIR instruction or constant
1230 /// value and may instead be used as a sentinel to indicate null.
1231 none = @backingInt(InternPool.Index.none),
1232 _,
1233
1234 pub fn toInterned(ref: Ref) ?InternPool.Index {
1235 assert(ref != .none);
1236 return ref.toInternedAllowNone();
1237 }
1238
1239 pub fn toInternedAllowNone(ref: Ref) ?InternPool.Index {
1240 return switch (ref) {
1241 .none => .none,
1242 else => if (@backingInt(ref) >> 31 == 0)
1243 @fromBackingInt(@intCast(@as(u31, @truncate(@backingInt(ref)))))
1244 else
1245 null,
1246 };
1247 }
1248
1249 pub fn toIndex(ref: Ref) ?Index {
1250 assert(ref != .none);
1251 return ref.toIndexAllowNone();
1252 }
1253
1254 pub fn toIndexAllowNone(ref: Ref) ?Index {
1255 return switch (ref) {
1256 .none => null,
1257 else => if (@backingInt(ref) >> 31 != 0)
1258 @fromBackingInt(@intCast(@as(u31, @truncate(@backingInt(ref)))))
1259 else
1260 null,
1261 };
1262 }
1263
1264 pub fn toType(ref: Ref) Type {
1265 return .fromInterned(ref.toInterned().?);
1266 }
1267
1268 pub fn fromIntern(ip_index: InternPool.Index) Ref {
1269 return switch (ip_index) {
1270 .none => .none,
1271 else => {
1272 assert(@backingInt(ip_index) >> 31 == 0);
1273 return @fromBackingInt(@intCast(@as(u31, @intCast(@backingInt(ip_index)))));
1274 },
1275 };
1276 }
1277
1278 pub fn fromValue(v: Value) Ref {
1279 return .fromIntern(v.toIntern());
1280 }
1281
1282 pub fn fromType(t: Type) Ref {
1283 return .fromIntern(t.toIntern());
1284 }
1285 };
1286
1287 /// All instructions have an 8-byte payload, which is contained within
1288 /// this union. `Tag` determines which union field is active, as well as
1289 /// how to interpret the data within.
1290 pub const Data = union {
1291 no_op: void,
1292 un_op: Ref,
1293
1294 bin_op: struct {
1295 lhs: Ref,
1296 rhs: Ref,
1297 },
1298 ty: Type,
1299 arg: struct {
1300 ty: Type,
1301 zir_param_index: u32,
1302 },
1303 ty_op: struct {
1304 ty: Type,
1305 operand: Ref,
1306 },
1307 ty_pl: struct {
1308 ty: Type,
1309 // Index into a different array.
1310 payload: u32,
1311 },
1312 br: struct {
1313 block_inst: Index,
1314 operand: Ref,
1315 },
1316 repeat: struct {
1317 loop_inst: Index,
1318 },
1319 pl_op: struct {
1320 operand: Ref,
1321 payload: u32,
1322 },
1323 dbg_stmt: struct {
1324 line: u32,
1325 column: u32,
1326 },
1327 atomic_load: struct {
1328 ptr: Ref,
1329 order: std.lang.AtomicOrder,
1330 },
1331 prefetch: struct {
1332 ptr: Ref,
1333 rw: std.lang.PrefetchOptions.Rw,
1334 locality: u2,
1335 cache: std.lang.PrefetchOptions.Cache,
1336 },
1337 reduce: struct {
1338 operand: Ref,
1339 operation: std.lang.ReduceOp,
1340 },
1341 ty_nav: struct {
1342 ty: Type,
1343 nav: InternPool.Nav.Index,
1344 },
1345 legalize_compiler_rt_call: struct {
1346 func: CompilerRtFunc,
1347 /// Index into `extra` to a payload of type `Call`.
1348 payload: u32,
1349 },
1350 inferred_alloc_comptime: InferredAllocComptime,
1351 inferred_alloc: InferredAlloc,
1352
1353 pub const InferredAllocComptime = struct {
1354 alignment: InternPool.Alignment,
1355 is_const: bool,
1356 /// This is `undefined` until we encounter a `store_to_inferred_alloc`,
1357 /// at which point the pointer is created and stored here.
1358 ptr: InternPool.Index,
1359 };
1360
1361 pub const InferredAlloc = struct {
1362 alignment: InternPool.Alignment,
1363 is_const: bool,
1364 };
1365
1366 // Make sure we don't accidentally add a field to make this union
1367 // bigger than expected. Note that in safety builds, Zig is allowed
1368 // to insert a secret field for safety checks.
1369 comptime {
1370 if (!std.debug.runtime_safety) {
1371 assert(@sizeOf(Data) == 8);
1372 }
1373 }
1374 };
1375};
1376
1377/// Trailing is a list of instruction indexes for every `body_len`.
1378pub const Block = struct {
1379 body_len: u32,
1380};
1381
1382/// Trailing is a list of instruction indexes for every `body_len`.
1383pub const DbgInlineBlock = struct {
1384 func: InternPool.Index,
1385 body_len: u32,
1386};
1387
1388/// Trailing is a list of `Inst.Ref` for every `args_len`.
1389pub const Call = struct {
1390 args_len: u32,
1391};
1392
1393/// This data is stored inside extra, with two sets of trailing `Inst.Ref`:
1394/// * 0. the then body, according to `then_body_len`.
1395/// * 1. the else body, according to `else_body_len`.
1396pub const CondBr = struct {
1397 then_body_len: u32,
1398 else_body_len: u32,
1399 branch_hints: BranchHints,
1400 pub const BranchHints = packed struct(u32) {
1401 true: std.lang.BranchHint = .none,
1402 false: std.lang.BranchHint = .none,
1403 then_cov: CoveragePoint = .none,
1404 else_cov: CoveragePoint = .none,
1405 _: u24 = 0,
1406 };
1407};
1408
1409/// Trailing:
1410/// * 0. `BranchHint` for each `cases_len + 1`. bit-packed into `u32`
1411/// elems such that each `u32` contains up to 10x `BranchHint`.
1412/// LSBs are first case. Final hint is `else`.
1413/// * 1. `Case` for each `cases_len`
1414/// * 2. the else body, according to `else_body_len`.
1415pub const SwitchBr = struct {
1416 cases_len: u32,
1417 else_body_len: u32,
1418
1419 /// Trailing:
1420 /// * item: Inst.Ref // for each `items_len`
1421 /// * { range_start: Inst.Ref, range_end: Inst.Ref } // for each `ranges_len`
1422 /// * body_inst: Inst.Index // for each `body_len`
1423 pub const Case = struct {
1424 items_len: u32,
1425 ranges_len: u32,
1426 body_len: u32,
1427 };
1428};
1429
1430/// This data is stored inside extra. Trailing:
1431/// 0. body: Inst.Index // for each body_len
1432pub const Try = struct {
1433 body_len: u32,
1434};
1435
1436/// This data is stored inside extra. Trailing:
1437/// 0. body: Inst.Index // for each body_len
1438pub const TryPtr = struct {
1439 ptr: Inst.Ref,
1440 body_len: u32,
1441};
1442
1443pub const StructField = struct {
1444 /// Whether this is a pointer or byval is determined by the AIR tag.
1445 struct_operand: Inst.Ref,
1446 field_index: u32,
1447};
1448
1449pub const Bin = struct {
1450 lhs: Inst.Ref,
1451 rhs: Inst.Ref,
1452};
1453
1454pub const FieldParentPtr = struct {
1455 field_ptr: Inst.Ref,
1456 field_index: u32,
1457};
1458
1459pub const VectorCmp = struct {
1460 lhs: Inst.Ref,
1461 rhs: Inst.Ref,
1462 op: u32,
1463
1464 pub fn compareOperator(self: VectorCmp) std.math.CompareOperator {
1465 return @fromBackingInt(@intCast(@as(u3, @intCast(self.op))));
1466 }
1467
1468 pub fn encodeOp(compare_operator: std.math.CompareOperator) u32 {
1469 return @backingInt(compare_operator);
1470 }
1471};
1472
1473/// Used by `Inst.Tag.shuffle_one`. Represents a mask element which either indexes into a
1474/// runtime-known vector, or is a comptime-known value.
1475pub const ShuffleOneMask = packed struct(u32) {
1476 index: u31,
1477 kind: enum(u1) { elem, value },
1478 pub fn elem(idx: u32) ShuffleOneMask {
1479 return .{ .index = @intCast(idx), .kind = .elem };
1480 }
1481 pub fn value(val: Value) ShuffleOneMask {
1482 return .{ .index = @intCast(@backingInt(val.toIntern())), .kind = .value };
1483 }
1484 pub const Unwrapped = union(enum) {
1485 /// The resulting element is this index into the runtime vector.
1486 elem: u32,
1487 /// The resulting element is this comptime-known value.
1488 /// It is correctly typed. It might be `undefined`.
1489 value: InternPool.Index,
1490 };
1491 pub fn unwrap(raw: ShuffleOneMask) Unwrapped {
1492 return switch (raw.kind) {
1493 .elem => .{ .elem = raw.index },
1494 .value => .{ .value = @fromBackingInt(@intCast(raw.index)) },
1495 };
1496 }
1497};
1498
1499/// Used by `Inst.Tag.shuffle_two`. Represents a mask element which either indexes into one
1500/// of two runtime-known vectors, or is undefined.
1501pub const ShuffleTwoMask = enum(u32) {
1502 undef = std.math.maxInt(u32),
1503 _,
1504 pub fn aElem(idx: u32) ShuffleTwoMask {
1505 return @fromBackingInt(@intCast(idx << 1));
1506 }
1507 pub fn bElem(idx: u32) ShuffleTwoMask {
1508 return @fromBackingInt(@intCast(idx << 1 | 1));
1509 }
1510 pub const Unwrapped = union(enum) {
1511 /// The resulting element is this index into the first runtime vector.
1512 a_elem: u32,
1513 /// The resulting element is this index into the second runtime vector.
1514 b_elem: u32,
1515 /// The resulting element is `undefined`.
1516 undef,
1517 };
1518 pub fn unwrap(raw: ShuffleTwoMask) Unwrapped {
1519 switch (raw) {
1520 .undef => return .undef,
1521 _ => {},
1522 }
1523 const x = @backingInt(raw);
1524 return switch (@as(u1, @truncate(x))) {
1525 0 => .{ .a_elem = x >> 1 },
1526 1 => .{ .b_elem = x >> 1 },
1527 };
1528 }
1529};
1530
1531/// Trailing:
1532/// 0. `Inst.Ref` for every outputs_len
1533/// 1. `Inst.Ref` for every inputs_len
1534/// 2. A number of u32 elements follow according to the equation `@divCeil(source_len, 4)`.
1535/// Memory starting at this position is reinterpreted as the source bytes.
1536/// 3. for every outputs_len
1537/// - constraint: memory at this position is reinterpreted as a null
1538/// terminated string.
1539/// - name: memory at this position is reinterpreted as a null
1540/// terminated string. pad to the next u32 after the null byte.
1541/// 4. for every inputs_len
1542/// - constraint: memory at this position is reinterpreted as a null
1543/// terminated string.
1544/// - name: memory at this position is reinterpreted as a null
1545/// terminated string. pad to the next u32 after the null byte.
1546pub const Asm = struct {
1547 /// Length of the assembly source in bytes.
1548 source_len: u32,
1549 inputs_len: u32,
1550 /// A comptime `std.lang.assembly.Clobbers` value for the target architecture.
1551 clobbers: InternPool.Index,
1552 flags: Flags,
1553
1554 pub const Flags = packed struct(u32) {
1555 outputs_len: u31,
1556 is_volatile: bool,
1557 };
1558};
1559
1560pub const Cmpxchg = struct {
1561 ptr: Inst.Ref,
1562 expected_value: Inst.Ref,
1563 new_value: Inst.Ref,
1564 /// 0b00000000000000000000000000000XXX - success_order
1565 /// 0b00000000000000000000000000XXX000 - failure_order
1566 flags: u32,
1567
1568 pub fn successOrder(self: Cmpxchg) std.lang.AtomicOrder {
1569 return @fromBackingInt(@intCast(@as(u3, @truncate(self.flags))));
1570 }
1571
1572 pub fn failureOrder(self: Cmpxchg) std.lang.AtomicOrder {
1573 return @fromBackingInt(@intCast(@as(u3, @intCast(self.flags >> 3))));
1574 }
1575};
1576
1577pub const AtomicRmw = struct {
1578 operand: Inst.Ref,
1579 /// 0b00000000000000000000000000000XXX - ordering
1580 /// 0b0000000000000000000000000XXXX000 - op
1581 flags: u32,
1582
1583 pub fn ordering(self: AtomicRmw) std.lang.AtomicOrder {
1584 return @fromBackingInt(@intCast(@as(u3, @truncate(self.flags))));
1585 }
1586
1587 pub fn op(self: AtomicRmw) std.lang.AtomicRmwOp {
1588 return @fromBackingInt(@intCast(@as(u4, @intCast(self.flags >> 3))));
1589 }
1590};
1591
1592pub const UnionInit = struct {
1593 field_index: u32,
1594 init: Inst.Ref,
1595};
1596
1597pub fn getMainBody(air: Air) []const Air.Inst.Index {
1598 const body_index = air.extra.items[@backingInt(ExtraIndex.main_block)];
1599 const extra = air.extraData(Block, body_index);
1600 return @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]);
1601}
1602
1603pub fn typeOf(air: *const Air, inst: Air.Inst.Ref, ip: *const InternPool) Type {
1604 if (inst.toInterned()) |ip_index| {
1605 return .fromInterned(ip.typeOf(ip_index));
1606 } else {
1607 return air.typeOfIndex(inst.toIndex().?, ip);
1608 }
1609}
1610
1611pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) Type {
1612 const datas = air.instructions.items(.data);
1613 switch (air.instructions.items(.tag)[@backingInt(inst)]) {
1614 .add,
1615 .add_safe,
1616 .add_wrap,
1617 .add_sat,
1618 .sub,
1619 .sub_safe,
1620 .sub_wrap,
1621 .sub_sat,
1622 .mul,
1623 .mul_safe,
1624 .mul_wrap,
1625 .mul_sat,
1626 .div_float,
1627 .div_trunc,
1628 .div_floor,
1629 .div_ceil,
1630 .div_exact,
1631 .rem,
1632 .mod,
1633 .bit_and,
1634 .bit_or,
1635 .xor,
1636 .shr,
1637 .shr_exact,
1638 .shl,
1639 .shl_exact,
1640 .shl_sat,
1641 .min,
1642 .max,
1643 .add_optimized,
1644 .sub_optimized,
1645 .mul_optimized,
1646 .div_float_optimized,
1647 .div_trunc_optimized,
1648 .div_floor_optimized,
1649 .div_ceil_optimized,
1650 .div_exact_optimized,
1651 .rem_optimized,
1652 .mod_optimized,
1653 => return air.typeOf(datas[@backingInt(inst)].bin_op.lhs, ip),
1654
1655 .sqrt,
1656 .sin,
1657 .cos,
1658 .tan,
1659 .exp,
1660 .exp2,
1661 .log,
1662 .log2,
1663 .log10,
1664 .floor,
1665 .ceil,
1666 .round,
1667 .trunc_float,
1668 .neg,
1669 .neg_optimized,
1670 => return air.typeOf(datas[@backingInt(inst)].un_op, ip),
1671
1672 .cmp_lt,
1673 .cmp_lte,
1674 .cmp_eq,
1675 .cmp_gte,
1676 .cmp_gt,
1677 .cmp_neq,
1678 .cmp_lt_optimized,
1679 .cmp_lte_optimized,
1680 .cmp_eq_optimized,
1681 .cmp_gte_optimized,
1682 .cmp_gt_optimized,
1683 .cmp_neq_optimized,
1684 .cmp_lte_errors_len,
1685 .is_null,
1686 .is_non_null,
1687 .is_null_ptr,
1688 .is_non_null_ptr,
1689 .is_err,
1690 .is_non_err,
1691 .is_err_ptr,
1692 .is_non_err_ptr,
1693 .is_named_enum_value,
1694 .error_set_has_value,
1695 => return .bool,
1696
1697 .alloc,
1698 .ret_ptr,
1699 .err_return_trace,
1700 .c_va_start,
1701 => return datas[@backingInt(inst)].ty,
1702
1703 .arg => return datas[@backingInt(inst)].arg.ty,
1704
1705 .assembly,
1706 .block,
1707 .dbg_inline_block,
1708 .struct_field_ptr,
1709 .agg_field_val,
1710 .slice_elem_ptr,
1711 .ptr_elem_ptr,
1712 .cmpxchg_weak,
1713 .cmpxchg_strong,
1714 .slice,
1715 .aggregate_init,
1716 .union_init,
1717 .field_parent_ptr,
1718 .cmp_vector,
1719 .cmp_vector_optimized,
1720 .add_with_overflow,
1721 .sub_with_overflow,
1722 .mul_with_overflow,
1723 .shl_with_overflow,
1724 .ptr_add,
1725 .ptr_sub,
1726 .try_ptr,
1727 .try_ptr_cold,
1728 .shuffle_one,
1729 .shuffle_two,
1730 => return datas[@backingInt(inst)].ty_pl.ty,
1731
1732 .not,
1733 .bit_cast,
1734 .bit_cast_safe,
1735 .ptr_cast,
1736 .ptr_from_int,
1737 .int_from_ptr,
1738 .error_cast,
1739 .error_from_int,
1740 .int_from_error,
1741 .union_from_enum,
1742 .load,
1743 .fpext,
1744 .fptrunc,
1745 .int_cast,
1746 .int_cast_safe,
1747 .trunc,
1748 .optional_payload,
1749 .optional_payload_ptr,
1750 .optional_payload_ptr_set,
1751 .errunion_payload_ptr_set,
1752 .wrap_optional,
1753 .unwrap_errunion_payload,
1754 .unwrap_errunion_err,
1755 .unwrap_errunion_payload_ptr,
1756 .unwrap_errunion_err_ptr,
1757 .wrap_errunion_payload,
1758 .wrap_errunion_err,
1759 .slice_ptr,
1760 .ptr_slice_len_ptr,
1761 .ptr_slice_ptr_ptr,
1762 .struct_field_ptr_index_0,
1763 .struct_field_ptr_index_1,
1764 .struct_field_ptr_index_2,
1765 .struct_field_ptr_index_3,
1766 .array_to_slice,
1767 .array_to_vector,
1768 .int_from_float,
1769 .int_from_float_optimized,
1770 .int_from_float_safe,
1771 .int_from_float_optimized_safe,
1772 .float_from_int,
1773 .splat,
1774 .get_union_tag,
1775 .clz,
1776 .ctz,
1777 .popcount,
1778 .byte_swap,
1779 .bit_reverse,
1780 .addrspace_cast,
1781 .c_va_arg,
1782 .c_va_copy,
1783 .abs,
1784 => return datas[@backingInt(inst)].ty_op.ty,
1785
1786 .loop,
1787 .repeat,
1788 .br,
1789 .cond_br,
1790 .switch_br,
1791 .loop_switch_br,
1792 .switch_dispatch,
1793 .ret,
1794 .ret_safe,
1795 .ret_load,
1796 .unreach,
1797 .trap,
1798 => return .noreturn,
1799
1800 .breakpoint,
1801 .dbg_stmt,
1802 .dbg_empty_stmt,
1803 .dbg_var_ptr,
1804 .dbg_var_val,
1805 .dbg_arg_inline,
1806 .store,
1807 .store_safe,
1808 .atomic_store_unordered,
1809 .atomic_store_monotonic,
1810 .atomic_store_release,
1811 .atomic_store_seq_cst,
1812 .memset,
1813 .memset_safe,
1814 .memcpy,
1815 .memmove,
1816 .set_union_tag,
1817 .prefetch,
1818 .set_err_return_trace,
1819 .c_va_end,
1820 .legalize_vec_store_elem,
1821 => return .void,
1822
1823 .slice_len,
1824 .ret_addr,
1825 .frame_addr,
1826 .save_err_return_trace_index,
1827 => return .usize,
1828
1829 .wasm_memory_grow => return .isize,
1830 .wasm_memory_size => return .usize,
1831
1832 .tag_name, .error_name => return .slice_const_u8_sentinel_0,
1833
1834 .call, .call_always_tail, .call_never_tail, .call_never_inline => {
1835 const callee_ty = air.typeOf(datas[@backingInt(inst)].pl_op.operand, ip);
1836 return .fromInterned(ip.funcTypeReturnType(callee_ty.toIntern()));
1837 },
1838
1839 .slice_elem_val, .ptr_elem_val, .array_elem_val, .legalize_vec_elem_val => {
1840 const ptr_ty = air.typeOf(datas[@backingInt(inst)].bin_op.lhs, ip);
1841 return ptr_ty.childTypeIp(ip);
1842 },
1843 .atomic_load => {
1844 const ptr_ty = air.typeOf(datas[@backingInt(inst)].atomic_load.ptr, ip);
1845 return ptr_ty.childTypeIp(ip);
1846 },
1847 .atomic_rmw => {
1848 const ptr_ty = air.typeOf(datas[@backingInt(inst)].pl_op.operand, ip);
1849 return ptr_ty.childTypeIp(ip);
1850 },
1851
1852 .reduce, .reduce_optimized => {
1853 const operand_ty = air.typeOf(datas[@backingInt(inst)].reduce.operand, ip);
1854 return .fromInterned(ip.indexToKey(operand_ty.ip_index).vector_type.child);
1855 },
1856
1857 .mul_add => return air.typeOf(datas[@backingInt(inst)].pl_op.operand, ip),
1858 .select => {
1859 const extra = air.extraData(Air.Bin, datas[@backingInt(inst)].pl_op.payload).data;
1860 return air.typeOf(extra.lhs, ip);
1861 },
1862
1863 .@"try", .try_cold => {
1864 const err_union_ty = air.typeOf(datas[@backingInt(inst)].pl_op.operand, ip);
1865 return .fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type);
1866 },
1867
1868 .runtime_nav_ptr => return datas[@backingInt(inst)].ty_nav.ty,
1869
1870 .work_item_id,
1871 .work_group_size,
1872 .work_group_id,
1873 .spirv_runtime_array_len,
1874 => return .u32,
1875
1876 .legalize_compiler_rt_call => return datas[@backingInt(inst)].legalize_compiler_rt_call.func.returnType(),
1877
1878 .inferred_alloc => unreachable,
1879 .inferred_alloc_comptime => unreachable,
1880 }
1881}
1882
1883/// Returns the requested data, as well as the new index which is at the start of the
1884/// trailers for the object.
1885pub fn extraData(air: Air, comptime T: type, index: usize) struct { data: T, end: usize } {
1886 const info = @typeInfo(T).@"struct";
1887 var i: usize = index;
1888 var result: T = undefined;
1889 inline for (info.field_names, info.field_types) |field_name, field_type| {
1890 @field(result, field_name) = switch (field_type) {
1891 u32 => air.extra.items[i],
1892 InternPool.Index, Inst.Ref => @fromBackingInt(@intCast(air.extra.items[i])),
1893 i32, CondBr.BranchHints, Asm.Flags => @bitCast(air.extra.items[i]),
1894 else => @compileError("bad field type: " ++ @typeName(field_type)),
1895 };
1896 i += 1;
1897 }
1898 return .{
1899 .data = result,
1900 .end = i,
1901 };
1902}
1903
1904pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
1905 air.instructions.deinit(gpa);
1906 air.extra.deinit(gpa);
1907 air.* = undefined;
1908}
1909
1910pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {
1911 return .fromIntern(ip_index);
1912}
1913
1914pub const NullTerminatedString = enum(u32) {
1915 none = std.math.maxInt(u32),
1916 _,
1917
1918 pub fn toSlice(nts: NullTerminatedString, air: Air) [:0]const u8 {
1919 if (nts == .none) return "";
1920 const bytes = std.mem.sliceAsBytes(air.extra.items[@backingInt(nts)..]);
1921 return bytes[0..std.mem.findScalar(u8, bytes, 0).? :0];
1922 }
1923};
1924
1925/// Returns whether the given instruction must always be lowered, for instance
1926/// because it can cause side effects. If an instruction does not need to be
1927/// lowered, and Liveness determines its result is unused, backends should
1928/// avoid lowering it.
1929pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1930 const data = air.instructions.items(.data)[@backingInt(inst)];
1931 return switch (air.instructions.items(.tag)[@backingInt(inst)]) {
1932 .arg,
1933 .assembly,
1934 .block,
1935 .loop,
1936 .repeat,
1937 .br,
1938 .trap,
1939 .breakpoint,
1940 .call,
1941 .call_always_tail,
1942 .call_never_tail,
1943 .call_never_inline,
1944 .cond_br,
1945 .switch_br,
1946 .loop_switch_br,
1947 .switch_dispatch,
1948 .@"try",
1949 .try_cold,
1950 .try_ptr,
1951 .try_ptr_cold,
1952 .dbg_stmt,
1953 .dbg_empty_stmt,
1954 .dbg_inline_block,
1955 .dbg_var_ptr,
1956 .dbg_var_val,
1957 .dbg_arg_inline,
1958 .ret,
1959 .ret_safe,
1960 .ret_load,
1961 .store,
1962 .store_safe,
1963 .unreach,
1964 .optional_payload_ptr_set,
1965 .errunion_payload_ptr_set,
1966 .set_union_tag,
1967 .memset,
1968 .memset_safe,
1969 .memcpy,
1970 .memmove,
1971 .cmpxchg_weak,
1972 .cmpxchg_strong,
1973 .atomic_store_unordered,
1974 .atomic_store_monotonic,
1975 .atomic_store_release,
1976 .atomic_store_seq_cst,
1977 .atomic_rmw,
1978 .prefetch,
1979 .wasm_memory_grow,
1980 .set_err_return_trace,
1981 .c_va_arg,
1982 .c_va_copy,
1983 .c_va_end,
1984 .c_va_start,
1985 .add_safe,
1986 .sub_safe,
1987 .mul_safe,
1988 .bit_cast_safe,
1989 .int_cast_safe,
1990 .int_from_float_safe,
1991 .int_from_float_optimized_safe,
1992 .legalize_vec_store_elem,
1993 .legalize_compiler_rt_call,
1994 => true,
1995
1996 .add,
1997 .add_optimized,
1998 .add_wrap,
1999 .add_sat,
2000 .sub,
2001 .sub_optimized,
2002 .sub_wrap,
2003 .sub_sat,
2004 .mul,
2005 .mul_optimized,
2006 .mul_wrap,
2007 .mul_sat,
2008 .div_float,
2009 .div_float_optimized,
2010 .div_trunc,
2011 .div_trunc_optimized,
2012 .div_floor,
2013 .div_floor_optimized,
2014 .div_ceil,
2015 .div_ceil_optimized,
2016 .div_exact,
2017 .div_exact_optimized,
2018 .rem,
2019 .rem_optimized,
2020 .mod,
2021 .mod_optimized,
2022 .ptr_add,
2023 .ptr_sub,
2024 .max,
2025 .min,
2026 .add_with_overflow,
2027 .sub_with_overflow,
2028 .mul_with_overflow,
2029 .shl_with_overflow,
2030 .alloc,
2031 .inferred_alloc,
2032 .inferred_alloc_comptime,
2033 .ret_ptr,
2034 .bit_and,
2035 .bit_or,
2036 .shr,
2037 .shr_exact,
2038 .shl,
2039 .shl_exact,
2040 .shl_sat,
2041 .xor,
2042 .not,
2043 .bit_cast,
2044 .ptr_cast,
2045 .ptr_from_int,
2046 .int_from_ptr,
2047 .error_cast,
2048 .error_from_int,
2049 .int_from_error,
2050 .union_from_enum,
2051 .ret_addr,
2052 .frame_addr,
2053 .clz,
2054 .ctz,
2055 .popcount,
2056 .byte_swap,
2057 .bit_reverse,
2058 .sqrt,
2059 .sin,
2060 .cos,
2061 .tan,
2062 .exp,
2063 .exp2,
2064 .log,
2065 .log2,
2066 .log10,
2067 .abs,
2068 .floor,
2069 .ceil,
2070 .round,
2071 .trunc_float,
2072 .neg,
2073 .neg_optimized,
2074 .cmp_lt,
2075 .cmp_lt_optimized,
2076 .cmp_lte,
2077 .cmp_lte_optimized,
2078 .cmp_eq,
2079 .cmp_eq_optimized,
2080 .cmp_gte,
2081 .cmp_gte_optimized,
2082 .cmp_gt,
2083 .cmp_gt_optimized,
2084 .cmp_neq,
2085 .cmp_neq_optimized,
2086 .cmp_vector,
2087 .cmp_vector_optimized,
2088 .is_null,
2089 .is_non_null,
2090 .is_err,
2091 .is_non_err,
2092 .fptrunc,
2093 .fpext,
2094 .int_cast,
2095 .trunc,
2096 .optional_payload,
2097 .optional_payload_ptr,
2098 .wrap_optional,
2099 .unwrap_errunion_payload,
2100 .unwrap_errunion_err,
2101 .unwrap_errunion_payload_ptr,
2102 .wrap_errunion_payload,
2103 .wrap_errunion_err,
2104 .struct_field_ptr,
2105 .struct_field_ptr_index_0,
2106 .struct_field_ptr_index_1,
2107 .struct_field_ptr_index_2,
2108 .struct_field_ptr_index_3,
2109 .agg_field_val,
2110 .get_union_tag,
2111 .slice,
2112 .slice_len,
2113 .slice_ptr,
2114 .ptr_slice_len_ptr,
2115 .ptr_slice_ptr_ptr,
2116 .array_elem_val,
2117 .slice_elem_ptr,
2118 .ptr_elem_ptr,
2119 .array_to_slice,
2120 .array_to_vector,
2121 .int_from_float,
2122 .int_from_float_optimized,
2123 .float_from_int,
2124 .reduce,
2125 .reduce_optimized,
2126 .splat,
2127 .shuffle_one,
2128 .shuffle_two,
2129 .select,
2130 .is_named_enum_value,
2131 .tag_name,
2132 .error_name,
2133 .error_set_has_value,
2134 .aggregate_init,
2135 .union_init,
2136 .mul_add,
2137 .field_parent_ptr,
2138 .wasm_memory_size,
2139 .cmp_lte_errors_len,
2140 .err_return_trace,
2141 .addrspace_cast,
2142 .save_err_return_trace_index,
2143 .runtime_nav_ptr,
2144 .work_item_id,
2145 .work_group_size,
2146 .work_group_id,
2147 .legalize_vec_elem_val,
2148 .spirv_runtime_array_len,
2149 => false,
2150
2151 .is_non_null_ptr, .is_null_ptr, .is_non_err_ptr, .is_err_ptr => air.typeOf(data.un_op, ip).isVolatilePtrIp(ip),
2152 .load, .unwrap_errunion_err_ptr => air.typeOf(data.ty_op.operand, ip).isVolatilePtrIp(ip),
2153 .slice_elem_val, .ptr_elem_val => air.typeOf(data.bin_op.lhs, ip).isVolatilePtrIp(ip),
2154 .atomic_load => switch (data.atomic_load.order) {
2155 .unordered, .monotonic => air.typeOf(data.atomic_load.ptr, ip).isVolatilePtrIp(ip),
2156 else => true, // Stronger memory orderings have inter-thread side effects.
2157 },
2158 };
2159}
2160
2161pub const UnwrappedSwitch = struct {
2162 air: *const Air,
2163 operand: Inst.Ref,
2164 cases_len: u32,
2165 else_body_len: u32,
2166 branch_hints_start: u32,
2167 cases_start: u32,
2168
2169 /// Asserts that `case_idx < us.cases_len`.
2170 pub fn getHint(us: UnwrappedSwitch, case_idx: u32) std.lang.BranchHint {
2171 assert(case_idx < us.cases_len);
2172 return us.getHintInner(case_idx);
2173 }
2174 pub fn getElseHint(us: UnwrappedSwitch) std.lang.BranchHint {
2175 return us.getHintInner(us.cases_len);
2176 }
2177 fn getHintInner(us: UnwrappedSwitch, idx: u32) std.lang.BranchHint {
2178 const bag = us.air.extra.items[us.branch_hints_start..][idx / 10];
2179 const bits: u3 = @truncate(bag >> @intCast(3 * (idx % 10)));
2180 return @fromBackingInt(@intCast(bits));
2181 }
2182
2183 pub fn iterateCases(us: UnwrappedSwitch) CaseIterator {
2184 return .{
2185 .air = us.air,
2186 .cases_len = us.cases_len,
2187 .else_body_len = us.else_body_len,
2188 .next_case = 0,
2189 .extra_index = us.cases_start,
2190 };
2191 }
2192 pub const CaseIterator = struct {
2193 air: *const Air,
2194 cases_len: u32,
2195 else_body_len: u32,
2196 next_case: u32,
2197 extra_index: u32,
2198
2199 pub fn next(it: *CaseIterator) ?Case {
2200 if (it.next_case == it.cases_len) return null;
2201 const idx = it.next_case;
2202 it.next_case += 1;
2203
2204 const extra = it.air.extraData(SwitchBr.Case, it.extra_index);
2205 var extra_index = extra.end;
2206 const items: []const Inst.Ref = @ptrCast(it.air.extra.items[extra_index..][0..extra.data.items_len]);
2207 extra_index += items.len;
2208 // TODO: ptrcast from []const Inst.Ref to []const [2]Inst.Ref when supported
2209 const ranges_ptr: [*]const [2]Inst.Ref = @ptrCast(it.air.extra.items[extra_index..]);
2210 const ranges: []const [2]Inst.Ref = ranges_ptr[0..extra.data.ranges_len];
2211 extra_index += ranges.len * 2;
2212 const body: []const Inst.Index = @ptrCast(it.air.extra.items[extra_index..][0..extra.data.body_len]);
2213 extra_index += body.len;
2214 it.extra_index = @intCast(extra_index);
2215
2216 return .{
2217 .idx = idx,
2218 .items = items,
2219 .ranges = ranges,
2220 .body = body,
2221 };
2222 }
2223 /// Only valid to call once all cases have been iterated, i.e. `next` returns `null`.
2224 /// Returns the body of the "default" (`else`) case.
2225 pub fn elseBody(it: *CaseIterator) []const Inst.Index {
2226 assert(it.next_case == it.cases_len);
2227 return @ptrCast(it.air.extra.items[it.extra_index..][0..it.else_body_len]);
2228 }
2229 pub const Case = struct {
2230 idx: u32,
2231 items: []const Inst.Ref,
2232 ranges: []const [2]Inst.Ref,
2233 body: []const Inst.Index,
2234 };
2235 };
2236};
2237
2238pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
2239 const inst = air.instructions.get(@backingInt(switch_inst));
2240 switch (inst.tag) {
2241 .switch_br, .loop_switch_br => {},
2242 else => unreachable, // assertion failure
2243 }
2244 const pl_op = inst.data.pl_op;
2245 const extra = air.extraData(SwitchBr, pl_op.payload);
2246 const hint_bag_count = @divCeil(extra.data.cases_len + 1, 10);
2247 return .{
2248 .air = air,
2249 .operand = pl_op.operand,
2250 .cases_len = extra.data.cases_len,
2251 .else_body_len = extra.data.else_body_len,
2252 .branch_hints_start = @intCast(extra.end),
2253 .cases_start = @intCast(extra.end + hint_bag_count),
2254 };
2255}
2256
2257pub const UnwrappedDbgInlineBlock = struct {
2258 func: InternPool.Index,
2259 body: []const Inst.Index,
2260 ty: Type,
2261};
2262
2263pub fn unwrapDbgBlock(air: *const Air, inst_index: Inst.Index) UnwrappedDbgInlineBlock {
2264 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2265 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2266 assert(tag == .dbg_inline_block);
2267 const payload = data.ty_pl.payload;
2268 const extra = air.extraData(Air.DbgInlineBlock, payload);
2269 return .{
2270 .func = extra.data.func,
2271 .ty = data.ty_pl.ty,
2272 .body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
2273 };
2274}
2275
2276pub const UnwrappedBlock = struct {
2277 body: []const Inst.Index,
2278 ty: Type,
2279};
2280
2281pub fn unwrapBlock(air: *const Air, inst_index: Inst.Index) UnwrappedBlock {
2282 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2283 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2284 const payload = switch (tag) {
2285 .block, .loop => data.ty_pl.payload,
2286 else => unreachable,
2287 };
2288 const extra = air.extraData(Air.Block, payload);
2289 return .{
2290 .ty = data.ty_pl.ty,
2291 .body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
2292 };
2293}
2294
2295pub const UnwrappedCall = struct {
2296 callee: Inst.Ref,
2297 args: []const Air.Inst.Ref,
2298};
2299
2300pub fn unwrapCall(air: *const Air, inst_index: Inst.Index) UnwrappedCall {
2301 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2302 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2303 const payload = switch (tag) {
2304 .call, .call_always_tail, .call_never_tail, .call_never_inline => data.pl_op.payload,
2305 else => unreachable,
2306 };
2307 const extra = air.extraData(Air.Call, payload);
2308 return .{
2309 .callee = data.pl_op.operand,
2310 .args = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]),
2311 };
2312}
2313
2314pub const UnwrappedCompilerRtCall = struct {
2315 func: CompilerRtFunc,
2316 args: []const Air.Inst.Ref,
2317};
2318
2319pub fn unwrapCompilerRtCall(air: *const Air, inst_index: Inst.Index) UnwrappedCompilerRtCall {
2320 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2321 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2322 assert(tag == .legalize_compiler_rt_call);
2323 const payload = data.legalize_compiler_rt_call.payload;
2324 const extra = air.extraData(Air.Call, payload);
2325 return .{
2326 .func = data.legalize_compiler_rt_call.func,
2327 .args = @ptrCast(air.extra.items[extra.end..][0..extra.data.args_len]),
2328 };
2329}
2330
2331pub const UnwrappedCondBr = struct {
2332 condition: Inst.Ref,
2333 then_body: []const Inst.Index,
2334 else_body: []const Inst.Index,
2335 branch_hints: CondBr.BranchHints,
2336};
2337
2338pub fn unwrapCondBr(air: *const Air, inst_index: Inst.Index) UnwrappedCondBr {
2339 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2340 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2341 assert(tag == .cond_br);
2342 const payload = data.pl_op.payload;
2343 const extra = air.extraData(Air.CondBr, payload);
2344 return .{
2345 .condition = data.pl_op.operand,
2346 .then_body = @ptrCast(air.extra.items[extra.end..][0..extra.data.then_body_len]),
2347 .else_body = @ptrCast(air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]),
2348 .branch_hints = extra.data.branch_hints,
2349 };
2350}
2351
2352pub const UnwrappedTry = struct {
2353 error_union: Inst.Ref,
2354 else_body: []const Inst.Index,
2355};
2356
2357pub fn unwrapTry(air: *const Air, inst_index: Inst.Index) UnwrappedTry {
2358 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2359 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2360 assert(tag == .@"try" or tag == .try_cold);
2361 const payload = data.pl_op.payload;
2362 const extra = air.extraData(Air.Try, payload);
2363 return .{
2364 .error_union = data.pl_op.operand,
2365 .else_body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
2366 };
2367}
2368
2369pub const UnwrappedTryPtr = struct {
2370 error_union_payload_ptr_ty: Type,
2371 error_union_ptr: Inst.Ref,
2372 else_body: []const Inst.Index,
2373};
2374
2375pub fn unwrapTryPtr(air: *const Air, inst_index: Inst.Index) UnwrappedTryPtr {
2376 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2377 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2378 assert(tag == .try_ptr or tag == .try_ptr_cold);
2379 const payload = data.ty_pl.payload;
2380 const extra = air.extraData(Air.TryPtr, payload);
2381 return .{
2382 .error_union_ptr = extra.data.ptr,
2383 .error_union_payload_ptr_ty = data.ty_pl.ty,
2384 .else_body = @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]),
2385 };
2386}
2387
2388pub const UnwrappedAsm = struct {
2389 outputs: []const Air.Inst.Ref,
2390 inputs: []const Air.Inst.Ref,
2391 source: [:0]u8,
2392 input_constraint_names: []const u32,
2393 output_constraint_names: []const u32,
2394 clobbers: InternPool.Index,
2395 is_volatile: bool,
2396
2397 const AsmIterator = struct {
2398 current: u32,
2399 operands: []const Air.Inst.Ref,
2400 constraint_names: []const u32,
2401
2402 pub fn next(self: *AsmIterator) ?struct { constraint: []const u8, operand: Inst.Ref, name: []const u8, index: u32 } {
2403 if (self.current >= self.operands.len) {
2404 return null;
2405 }
2406 defer {
2407 self.current += 1;
2408 }
2409
2410 const constraint_name = std.mem.sliceAsBytes(self.constraint_names);
2411 const constraint = std.mem.sliceTo(constraint_name, 0);
2412 const name = std.mem.sliceTo(constraint_name[constraint.len + 1 ..], 0);
2413 // This equation accounts for the fact that even if we have exactly 4 bytes
2414 // for the string, we still use the next u32 for the null terminator.
2415 const next_offset = @divCeil(constraint.len + 1 + name.len + 1, @sizeOf(u32));
2416 self.constraint_names = self.constraint_names[next_offset..];
2417
2418 return .{
2419 .constraint = constraint,
2420 .operand = self.operands[self.current],
2421 .name = name,
2422 .index = self.current,
2423 };
2424 }
2425 };
2426
2427 pub fn iterateInputs(self: *const UnwrappedAsm) AsmIterator {
2428 return .{
2429 .current = 0,
2430 .operands = self.inputs,
2431 .constraint_names = self.input_constraint_names,
2432 };
2433 }
2434
2435 pub fn iterateOutputs(self: *const UnwrappedAsm) AsmIterator {
2436 return .{
2437 .current = 0,
2438 .operands = self.outputs,
2439 .constraint_names = self.output_constraint_names,
2440 };
2441 }
2442};
2443
2444pub fn unwrapAsm(air: *const Air, inst_index: Inst.Index) UnwrappedAsm {
2445 const data = air.instructions.items(.data)[@backingInt(inst_index)];
2446 const tag = air.instructions.items(.tag)[@backingInt(inst_index)];
2447 assert(tag == .assembly);
2448 const payload = data.ty_pl.payload;
2449 const extra = air.extraData(Air.Asm, payload);
2450 const source_start = extra.end + extra.data.flags.outputs_len + extra.data.inputs_len;
2451 const output_constraint_name_start = source_start + (extra.data.source_len / 4) + 1;
2452 const output_constraint_name = air.extra.items[output_constraint_name_start..];
2453 const outputs: []Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.flags.outputs_len]);
2454 // Get the input names and constraints offset place after the output.
2455 var it = UnwrappedAsm.AsmIterator{
2456 .current = 0,
2457 .constraint_names = output_constraint_name,
2458 .operands = outputs,
2459 };
2460 while (it.next()) |_| {}
2461
2462 return .{
2463 .clobbers = extra.data.clobbers,
2464 .is_volatile = extra.data.flags.is_volatile,
2465 .inputs = @ptrCast(air.extra.items[extra.end + extra.data.flags.outputs_len ..][0..extra.data.inputs_len]),
2466 .outputs = outputs,
2467 .source = std.mem.sliceAsBytes(air.extra.items[source_start..])[0..extra.data.source_len :0],
2468 .output_constraint_names = output_constraint_name,
2469 .input_constraint_names = it.constraint_names,
2470 };
2471}
2472
2473pub const UnwrappedShuffleOne = struct {
2474 result_ty: Type,
2475 operand: Inst.Ref,
2476 mask: []const ShuffleOneMask,
2477};
2478
2479pub fn unwrapShuffleOne(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index) UnwrappedShuffleOne {
2480 const inst = air.instructions.get(@backingInt(inst_index));
2481 switch (inst.tag) {
2482 .shuffle_one => {},
2483 else => unreachable, // assertion failure
2484 }
2485 const result_ty: Type = inst.data.ty_pl.ty;
2486 const mask_len: u32 = result_ty.vectorLen(zcu);
2487 const extra_idx = inst.data.ty_pl.payload;
2488 return .{
2489 .result_ty = result_ty,
2490 .operand = @fromBackingInt(@intCast(air.extra.items[extra_idx + mask_len])),
2491 .mask = @ptrCast(air.extra.items[extra_idx..][0..mask_len]),
2492 };
2493}
2494
2495pub const UnwrappedShuffleTwo = struct {
2496 result_ty: Type,
2497 operand_a: Inst.Ref,
2498 operand_b: Inst.Ref,
2499 mask: []const ShuffleTwoMask,
2500};
2501
2502pub fn unwrapShuffleTwo(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index) UnwrappedShuffleTwo {
2503 const inst = air.instructions.get(@backingInt(inst_index));
2504 switch (inst.tag) {
2505 .shuffle_two => {},
2506 else => unreachable, // assertion failure
2507 }
2508 const result_ty: Type = inst.data.ty_pl.ty;
2509 const mask_len: u32 = result_ty.vectorLen(zcu);
2510 const extra_idx = inst.data.ty_pl.payload;
2511 return .{
2512 .result_ty = result_ty,
2513 .operand_a = @fromBackingInt(@intCast(air.extra.items[extra_idx + mask_len + 0])),
2514 .operand_b = @fromBackingInt(@intCast(air.extra.items[extra_idx + mask_len + 1])),
2515 .mask = @ptrCast(air.extra.items[extra_idx..][0..mask_len]),
2516 };
2517}
2518
2519pub const legalize = Legalize.legalize;
2520pub const write = print.write;
2521pub const writeInst = print.writeInst;
2522pub const dump = print.dump;
2523pub const dumpInst = print.dumpInst;
2524
2525pub const CoveragePoint = enum(u1) {
2526 /// Indicates the block is not a place of interest corresponding to
2527 /// a source location for coverage purposes.
2528 none,
2529 /// Point of interest. The next instruction emitted corresponds to
2530 /// a source location used for coverage instrumentation.
2531 poi,
2532};
2533
2534pub const CompilerRtFunc = enum(u32) {
2535 // zig fmt: off
2536
2537 // float simple arithmetic
2538 __addhf3, __addsf3, __adddf3, __addxf3, __addtf3,
2539 __subhf3, __subsf3, __subdf3, __subxf3, __subtf3,
2540 __mulhf3, __mulsf3, __muldf3, __mulxf3, __multf3,
2541 __divhf3, __divsf3, __divdf3, __divxf3, __divtf3,
2542
2543 // float minmax
2544 __fminh, fminf, fmin, __fminx, fminq,
2545 __fmaxh, fmaxf, fmax, __fmaxx, fmaxq,
2546
2547 // float round
2548 __ceilh, ceilf, ceil, __ceilx, ceilq,
2549 __floorh, floorf, floor, __floorx, floorq,
2550 __trunch, truncf, trunc, __truncx, truncq,
2551 __roundh, roundf, round, __roundx, roundq,
2552
2553 // float log
2554 __logh, logf, log, __logx, logq,
2555 __log2h, log2f, log2, __log2x, log2q,
2556 __log10h, log10f, log10, __log10x, log10q,
2557
2558 // float exp
2559 __exph, expf, exp, __expx, expq,
2560 __exp2h, exp2f, exp2, __exp2x, exp2q,
2561
2562 // float trigonometry
2563 __sinh, sinf, sin, __sinx, sinq,
2564 __cosh, cosf, cos, __cosx, cosq,
2565 __tanh, tanf, tan, __tanx, tanq,
2566
2567 // float misc ops
2568 __fabsh, fabsf, fabs, __fabsx, fabsq,
2569 __sqrth, sqrtf, sqrt, __sqrtx, sqrtq,
2570 __fmodh, fmodf, fmod, __fmodx, fmodq,
2571 __fmah, fmaf, fma, __fmax, fmaq,
2572
2573 // float comparison
2574 __eqhf2, __eqsf2, __eqdf2, __eqxf2, __eqtf2, // == iff return == 0
2575 __nehf2, __nesf2, __nedf2, __nexf2, __netf2, // != iff return != 0
2576 __lthf2, __ltsf2, __ltdf2, __ltxf2, __lttf2, // < iff return < 0
2577 __lehf2, __lesf2, __ledf2, __lexf2, __letf2, // <= iff return <= 0
2578 __gthf2, __gtsf2, __gtdf2, __gtxf2, __gttf2, // > iff return > 0
2579 __gehf2, __gesf2, __gedf2, __gexf2, __getf2, // >= iff return >= 0
2580
2581 // AEABI float comparison. On ARM, the `sf`/`df` functions above are not available,
2582 // and these must be used instead. They are not just aliases for the above functions
2583 // because they have a different (better) ABI.
2584 __aeabi_fcmpeq, __aeabi_dcmpeq, // ==, returns bool
2585 __aeabi_fcmplt, __aeabi_dcmplt, // <, returns bool
2586 __aeabi_fcmple, __aeabi_dcmple, // <=, returns bool
2587 __aeabi_fcmpgt, __aeabi_dcmpgt, // >, returns bool
2588 __aeabi_fcmpge, __aeabi_dcmpge, // >=, returns bool
2589
2590 // float shortening
2591 // to f16 // to f32 // to f64 // to f80
2592 __trunctfhf2, __trunctfsf2, __trunctfdf2, __trunctfxf2, // from f128
2593 __truncxfhf2, __truncxfsf2, __truncxfdf2, // from f80
2594 __truncdfhf2, __truncdfsf2, // from f64
2595 __truncsfhf2, // from f32
2596
2597 // float widening
2598 // to f128 // to f80 // to f64 // to f32
2599 __extendhftf2, __extendhfxf2, __extendhfdf2, __extendhfsf2, // from f16
2600 __extendsftf2, __extendsfxf2, __extendsfdf2, // from f32
2601 __extenddftf2, __extenddfxf2, // from f64
2602 __extendxftf2, // from f80
2603
2604 // int to float
2605 __floatsihf, __floatsisf, __floatsidf, __floatsixf, __floatsitf, // i32 to float
2606 __floatdihf, __floatdisf, __floatdidf, __floatdixf, __floatditf, // i64 to float
2607 __floattihf, __floattisf, __floattidf, __floattixf, __floattitf, // i128 to float
2608 __floateihf, __floateisf, __floateidf, __floateixf, __floateitf, // arbitrary iN to float
2609 __floatunsihf, __floatunsisf, __floatunsidf, __floatunsixf, __floatunsitf, // u32 to float
2610 __floatundihf, __floatundisf, __floatundidf, __floatundixf, __floatunditf, // u64 to float
2611 __floatuntihf, __floatuntisf, __floatuntidf, __floatuntixf, __floatuntitf, // u128 to float
2612 __floatuneihf, __floatuneisf, __floatuneidf, __floatuneixf, __floatuneitf, // arbitrary uN to float
2613
2614 // float to int
2615 __fixhfsi, __fixsfsi, __fixdfsi, __fixxfsi, __fixtfsi, // float to i32
2616 __fixhfdi, __fixsfdi, __fixdfdi, __fixxfdi, __fixtfdi, // float to i64
2617 __fixhfti, __fixsfti, __fixdfti, __fixxfti, __fixtfti, // float to i128
2618 __fixhfei, __fixsfei, __fixdfei, __fixxfei, __fixtfei, // float to arbitray iN
2619 __fixunshfsi, __fixunssfsi, __fixunsdfsi, __fixunsxfsi, __fixunstfsi, // float to u32
2620 __fixunshfdi, __fixunssfdi, __fixunsdfdi, __fixunsxfdi, __fixunstfdi, // float to u64
2621 __fixunshfti, __fixunssfti, __fixunsdfti, __fixunsxfti, __fixunstfti, // float to u128
2622 __fixunshfei, __fixunssfei, __fixunsdfei, __fixunsxfei, __fixunstfei, // float to arbitray uN
2623
2624 // zig fmt: on
2625
2626 /// Usually, the tag names of `CompilerRtFunc` match the corresponding symbol name, but not
2627 /// always; some target triples have slightly different compiler-rt ABIs for one reason or
2628 /// another.
2629 pub fn name(f: CompilerRtFunc, target: *const std.Target) []const u8 {
2630 const use_gnu_f16_abi = switch (target.cpu.arch) {
2631 .wasm32,
2632 .wasm64,
2633 .riscv64,
2634 .riscv64be,
2635 .riscv32,
2636 .riscv32be,
2637 => false,
2638 .x86, .x86_64 => true,
2639 .arm, .armeb, .thumb, .thumbeb => switch (target.abi) {
2640 .eabi, .eabihf => false,
2641 else => true,
2642 },
2643 else => !target.os.tag.isDarwin(),
2644 };
2645 const use_aeabi = target.cpu.arch.isArm() and switch (target.abi) {
2646 .eabi,
2647 .eabihf,
2648 .musleabi,
2649 .musleabihf,
2650 .gnueabi,
2651 .gnueabihf,
2652 .android,
2653 .androideabi,
2654 => true,
2655 else => false,
2656 };
2657
2658 // GNU didn't like the standard names specifically for conversions between f16
2659 // and f32, so decided to make their own naming convention with blackjack and
2660 // hookers (but only use it on a few random targets of course). This overrides
2661 // the ARM EABI in some cases. I don't like GNU.
2662 if (use_gnu_f16_abi) switch (f) {
2663 .__truncsfhf2 => return "__gnu_f2h_ieee",
2664 .__extendhfsf2 => return "__gnu_h2f_ieee",
2665 else => {},
2666 };
2667
2668 if (use_aeabi) return switch (f) {
2669 .__addsf3 => "__aeabi_fadd",
2670 .__adddf3 => "__aeabi_dadd",
2671 .__subsf3 => "__aeabi_fsub",
2672 .__subdf3 => "__aeabi_dsub",
2673 .__mulsf3 => "__aeabi_fmul",
2674 .__muldf3 => "__aeabi_dmul",
2675 .__divsf3 => "__aeabi_fdiv",
2676 .__divdf3 => "__aeabi_ddiv",
2677 .__truncdfhf2 => "__aeabi_d2h",
2678 .__truncdfsf2 => "__aeabi_d2f",
2679 .__truncsfhf2 => "__aeabi_f2h",
2680 .__extendsfdf2 => "__aeabi_f2d",
2681 .__extendhfsf2 => "__aeabi_h2f",
2682 .__floatsisf => "__aeabi_i2f",
2683 .__floatsidf => "__aeabi_i2d",
2684 .__floatdisf => "__aeabi_l2f",
2685 .__floatdidf => "__aeabi_l2d",
2686 .__floatunsisf => "__aeabi_ui2f",
2687 .__floatunsidf => "__aeabi_ui2d",
2688 .__floatundisf => "__aeabi_ul2f",
2689 .__floatundidf => "__aeabi_ul2d",
2690 .__fixsfsi => "__aeabi_f2iz",
2691 .__fixdfsi => "__aeabi_d2iz",
2692 .__fixsfdi => "__aeabi_f2lz",
2693 .__fixdfdi => "__aeabi_d2lz",
2694 .__fixunssfsi => "__aeabi_f2uiz",
2695 .__fixunsdfsi => "__aeabi_d2uiz",
2696 .__fixunssfdi => "__aeabi_f2ulz",
2697 .__fixunsdfdi => "__aeabi_d2ulz",
2698
2699 // These functions are not available on AEABI. The AEABI equivalents are
2700 // separate fields rather than aliases because they have a different ABI.
2701 .__eqsf2, .__eqdf2 => unreachable,
2702 .__nesf2, .__nedf2 => unreachable,
2703 .__ltsf2, .__ltdf2 => unreachable,
2704 .__lesf2, .__ledf2 => unreachable,
2705 .__gtsf2, .__gtdf2 => unreachable,
2706 .__gesf2, .__gedf2 => unreachable,
2707
2708 else => @tagName(f),
2709 };
2710
2711 return switch (f) {
2712 // These functions are only available on AEABI.
2713 .__aeabi_fcmpeq, .__aeabi_dcmpeq => unreachable,
2714 .__aeabi_fcmplt, .__aeabi_dcmplt => unreachable,
2715 .__aeabi_fcmple, .__aeabi_dcmple => unreachable,
2716 .__aeabi_fcmpgt, .__aeabi_dcmpgt => unreachable,
2717 .__aeabi_fcmpge, .__aeabi_dcmpge => unreachable,
2718
2719 else => @tagName(f),
2720 };
2721 }
2722
2723 pub fn @"callconv"(f: CompilerRtFunc, target: *const std.Target) std.lang.CallingConvention {
2724 const use_gnu_f16_abi = switch (target.cpu.arch) {
2725 .wasm32,
2726 .wasm64,
2727 .riscv64,
2728 .riscv64be,
2729 .riscv32,
2730 .riscv32be,
2731 => false,
2732 .x86, .x86_64 => true,
2733 .arm, .armeb, .thumb, .thumbeb => switch (target.abi) {
2734 .eabi, .eabihf => false,
2735 else => true,
2736 },
2737 else => !target.os.tag.isDarwin(),
2738 };
2739 const use_aeabi = target.cpu.arch.isArm() and switch (target.abi) {
2740 .eabi,
2741 .eabihf,
2742 .musleabi,
2743 .musleabihf,
2744 .gnueabi,
2745 .gnueabihf,
2746 .android,
2747 .androideabi,
2748 => true,
2749 else => false,
2750 };
2751
2752 if (use_gnu_f16_abi) switch (f) {
2753 .__truncsfhf2,
2754 .__extendhfsf2,
2755 => return target.cCallingConvention().?,
2756 else => {},
2757 };
2758
2759 if (use_aeabi) switch (f) {
2760 // zig fmt: off
2761 .__addsf3, .__adddf3, .__subsf3, .__subdf3,
2762 .__mulsf3, .__muldf3, .__divsf3, .__divdf3,
2763 .__truncdfhf2, .__truncdfsf2, .__truncsfhf2,
2764 .__extendsfdf2, .__extendhfsf2,
2765 .__floatsisf, .__floatsidf, .__floatdisf, .__floatdidf,
2766 .__floatunsisf, .__floatunsidf, .__floatundisf, .__floatundidf,
2767 .__fixsfsi, .__fixdfsi, .__fixsfdi, .__fixdfdi,
2768 .__fixunssfsi, .__fixunsdfsi, .__fixunssfdi, .__fixunsdfdi,
2769 => return .{ .arm_aapcs = .{} },
2770 // zig fmt: on
2771 else => {},
2772 };
2773
2774 return target.cCallingConvention().?;
2775 }
2776
2777 pub fn returnType(f: CompilerRtFunc) Type {
2778 return switch (f) {
2779 .__addhf3, .__subhf3, .__mulhf3, .__divhf3 => .f16,
2780 .__addsf3, .__subsf3, .__mulsf3, .__divsf3 => .f32,
2781 .__adddf3, .__subdf3, .__muldf3, .__divdf3 => .f64,
2782 .__addxf3, .__subxf3, .__mulxf3, .__divxf3 => .f80,
2783 .__addtf3, .__subtf3, .__multf3, .__divtf3 => .f128,
2784
2785 // zig fmt: off
2786 .__fminh, .__fmaxh,
2787 .__ceilh, .__floorh, .__trunch, .__roundh,
2788 .__logh, .__log2h, .__log10h,
2789 .__exph, .__exp2h,
2790 .__sinh, .__cosh, .__tanh,
2791 .__fabsh, .__sqrth, .__fmodh, .__fmah,
2792 => .f16,
2793 .fminf, .fmaxf,
2794 .ceilf, .floorf, .truncf, .roundf,
2795 .logf, .log2f, .log10f,
2796 .expf, .exp2f,
2797 .sinf, .cosf, .tanf,
2798 .fabsf, .sqrtf, .fmodf, .fmaf,
2799 => .f32,
2800 .fmin, .fmax,
2801 .ceil, .floor, .trunc, .round,
2802 .log, .log2, .log10,
2803 .exp, .exp2,
2804 .sin, .cos, .tan,
2805 .fabs, .sqrt, .fmod, .fma,
2806 => .f64,
2807 .__fminx, .__fmaxx,
2808 .__ceilx, .__floorx, .__truncx, .__roundx,
2809 .__logx, .__log2x, .__log10x,
2810 .__expx, .__exp2x,
2811 .__sinx, .__cosx, .__tanx,
2812 .__fabsx, .__sqrtx, .__fmodx, .__fmax,
2813 => .f80,
2814 .fminq, .fmaxq,
2815 .ceilq, .floorq, .truncq, .roundq,
2816 .logq, .log2q, .log10q,
2817 .expq, .exp2q,
2818 .sinq, .cosq, .tanq,
2819 .fabsq, .sqrtq, .fmodq, .fmaq,
2820 => .f128,
2821 // zig fmt: on
2822
2823 .__eqhf2, .__eqsf2, .__eqdf2, .__eqxf2, .__eqtf2 => .i32,
2824 .__nehf2, .__nesf2, .__nedf2, .__nexf2, .__netf2 => .i32,
2825 .__lthf2, .__ltsf2, .__ltdf2, .__ltxf2, .__lttf2 => .i32,
2826 .__lehf2, .__lesf2, .__ledf2, .__lexf2, .__letf2 => .i32,
2827 .__gthf2, .__gtsf2, .__gtdf2, .__gtxf2, .__gttf2 => .i32,
2828 .__gehf2, .__gesf2, .__gedf2, .__gexf2, .__getf2 => .i32,
2829
2830 .__aeabi_fcmpeq, .__aeabi_dcmpeq => .i32,
2831 .__aeabi_fcmplt, .__aeabi_dcmplt => .i32,
2832 .__aeabi_fcmple, .__aeabi_dcmple => .i32,
2833 .__aeabi_fcmpgt, .__aeabi_dcmpgt => .i32,
2834 .__aeabi_fcmpge, .__aeabi_dcmpge => .i32,
2835
2836 .__trunctfhf2, .__truncxfhf2, .__truncdfhf2, .__truncsfhf2 => .f16,
2837 .__trunctfsf2, .__truncxfsf2, .__truncdfsf2 => .f32,
2838 .__trunctfdf2, .__truncxfdf2 => .f64,
2839 .__trunctfxf2 => .f80,
2840
2841 .__extendhftf2, .__extendsftf2, .__extenddftf2, .__extendxftf2 => .f128,
2842 .__extendhfxf2, .__extendsfxf2, .__extenddfxf2 => .f80,
2843 .__extendhfdf2, .__extendsfdf2 => .f64,
2844 .__extendhfsf2 => .f32,
2845
2846 .__floatsihf, .__floatdihf, .__floattihf, .__floateihf => .f16,
2847 .__floatsisf, .__floatdisf, .__floattisf, .__floateisf => .f32,
2848 .__floatsidf, .__floatdidf, .__floattidf, .__floateidf => .f64,
2849 .__floatsixf, .__floatdixf, .__floattixf, .__floateixf => .f80,
2850 .__floatsitf, .__floatditf, .__floattitf, .__floateitf => .f128,
2851 .__floatunsihf, .__floatundihf, .__floatuntihf, .__floatuneihf => .f16,
2852 .__floatunsisf, .__floatundisf, .__floatuntisf, .__floatuneisf => .f32,
2853 .__floatunsidf, .__floatundidf, .__floatuntidf, .__floatuneidf => .f64,
2854 .__floatunsixf, .__floatundixf, .__floatuntixf, .__floatuneixf => .f80,
2855 .__floatunsitf, .__floatunditf, .__floatuntitf, .__floatuneitf => .f128,
2856
2857 .__fixhfsi, .__fixsfsi, .__fixdfsi, .__fixxfsi, .__fixtfsi => .i32,
2858 .__fixhfdi, .__fixsfdi, .__fixdfdi, .__fixxfdi, .__fixtfdi => .i64,
2859 .__fixhfti, .__fixsfti, .__fixdfti, .__fixxfti, .__fixtfti => .i128,
2860 .__fixhfei, .__fixsfei, .__fixdfei, .__fixxfei, .__fixtfei => .void,
2861 .__fixunshfsi, .__fixunssfsi, .__fixunsdfsi, .__fixunsxfsi, .__fixunstfsi => .u32,
2862 .__fixunshfdi, .__fixunssfdi, .__fixunsdfdi, .__fixunsxfdi, .__fixunstfdi => .u64,
2863 .__fixunshfti, .__fixunssfti, .__fixunsdfti, .__fixunsxfti, .__fixunstfti => .u128,
2864 .__fixunshfei, .__fixunssfei, .__fixunsdfei, .__fixunsxfei, .__fixunstfei => .void,
2865 };
2866 }
2867};