| 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 | |
| 7 | const std = @import("std"); |
| 8 | const builtin = @import("builtin"); |
| 9 | const assert = std.debug.assert; |
| 10 | |
| 11 | const Air = @This(); |
| 12 | const InternPool = @import("InternPool.zig"); |
| 13 | const Type = @import("Type.zig"); |
| 14 | const Value = @import("Value.zig"); |
| 15 | const Zcu = @import("Zcu.zig"); |
| 16 | const print = @import("Air/print.zig"); |
| 17 | |
| 18 | pub const Legalize = @import("Air/Legalize.zig"); |
| 19 | pub const Liveness = @import("Air/Liveness.zig"); |
| 20 | pub const Verify = @import("Air/Verify.zig"); |
| 21 | |
| 22 | instructions: 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. |
| 25 | extra: std.ArrayList(u32), |
| 26 | |
| 27 | pub const ExtraIndex = enum(u32) { |
| 28 | /// Payload index of the main `Block` in the `extra` array. |
| 29 | main_block, |
| 30 | |
| 31 | _, |
| 32 | }; |
| 33 | |
| 34 | pub 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`. |
| 1378 | pub const Block = struct { |
| 1379 | body_len: u32, |
| 1380 | }; |
| 1381 | |
| 1382 | /// Trailing is a list of instruction indexes for every `body_len`. |
| 1383 | pub 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`. |
| 1389 | pub 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`. |
| 1396 | pub 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`. |
| 1415 | pub 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 |
| 1432 | pub 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 |
| 1438 | pub const TryPtr = struct { |
| 1439 | ptr: Inst.Ref, |
| 1440 | body_len: u32, |
| 1441 | }; |
| 1442 | |
| 1443 | pub 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 | |
| 1449 | pub const Bin = struct { |
| 1450 | lhs: Inst.Ref, |
| 1451 | rhs: Inst.Ref, |
| 1452 | }; |
| 1453 | |
| 1454 | pub const FieldParentPtr = struct { |
| 1455 | field_ptr: Inst.Ref, |
| 1456 | field_index: u32, |
| 1457 | }; |
| 1458 | |
| 1459 | pub 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. |
| 1475 | pub 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. |
| 1501 | pub 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. |
| 1546 | pub 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 | |
| 1560 | pub 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 | |
| 1577 | pub 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 | |
| 1592 | pub const UnionInit = struct { |
| 1593 | field_index: u32, |
| 1594 | init: Inst.Ref, |
| 1595 | }; |
| 1596 | |
| 1597 | pub 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 | |
| 1603 | pub 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 | |
| 1611 | pub 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. |
| 1885 | pub 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 | |
| 1904 | pub fn deinit(air: *Air, gpa: std.mem.Allocator) void { |
| 1905 | air.instructions.deinit(gpa); |
| 1906 | air.extra.deinit(gpa); |
| 1907 | air.* = undefined; |
| 1908 | } |
| 1909 | |
| 1910 | pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref { |
| 1911 | return .fromIntern(ip_index); |
| 1912 | } |
| 1913 | |
| 1914 | pub 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. |
| 1929 | pub 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 | |
| 2161 | pub 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 | |
| 2238 | pub 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 | |
| 2257 | pub const UnwrappedDbgInlineBlock = struct { |
| 2258 | func: InternPool.Index, |
| 2259 | body: []const Inst.Index, |
| 2260 | ty: Type, |
| 2261 | }; |
| 2262 | |
| 2263 | pub 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 | |
| 2276 | pub const UnwrappedBlock = struct { |
| 2277 | body: []const Inst.Index, |
| 2278 | ty: Type, |
| 2279 | }; |
| 2280 | |
| 2281 | pub 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 | |
| 2295 | pub const UnwrappedCall = struct { |
| 2296 | callee: Inst.Ref, |
| 2297 | args: []const Air.Inst.Ref, |
| 2298 | }; |
| 2299 | |
| 2300 | pub 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 | |
| 2314 | pub const UnwrappedCompilerRtCall = struct { |
| 2315 | func: CompilerRtFunc, |
| 2316 | args: []const Air.Inst.Ref, |
| 2317 | }; |
| 2318 | |
| 2319 | pub 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 | |
| 2331 | pub 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 | |
| 2338 | pub 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 | |
| 2352 | pub const UnwrappedTry = struct { |
| 2353 | error_union: Inst.Ref, |
| 2354 | else_body: []const Inst.Index, |
| 2355 | }; |
| 2356 | |
| 2357 | pub 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 | |
| 2369 | pub const UnwrappedTryPtr = struct { |
| 2370 | error_union_payload_ptr_ty: Type, |
| 2371 | error_union_ptr: Inst.Ref, |
| 2372 | else_body: []const Inst.Index, |
| 2373 | }; |
| 2374 | |
| 2375 | pub 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 | |
| 2388 | pub 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 | |
| 2444 | pub 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 | |
| 2473 | pub const UnwrappedShuffleOne = struct { |
| 2474 | result_ty: Type, |
| 2475 | operand: Inst.Ref, |
| 2476 | mask: []const ShuffleOneMask, |
| 2477 | }; |
| 2478 | |
| 2479 | pub 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 | |
| 2495 | pub const UnwrappedShuffleTwo = struct { |
| 2496 | result_ty: Type, |
| 2497 | operand_a: Inst.Ref, |
| 2498 | operand_b: Inst.Ref, |
| 2499 | mask: []const ShuffleTwoMask, |
| 2500 | }; |
| 2501 | |
| 2502 | pub 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 | |
| 2519 | pub const legalize = Legalize.legalize; |
| 2520 | pub const write = print.write; |
| 2521 | pub const writeInst = print.writeInst; |
| 2522 | pub const dump = print.dump; |
| 2523 | pub const dumpInst = print.dumpInst; |
| 2524 | |
| 2525 | pub 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 | |
| 2534 | pub 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 | }; |