| author | |
| committer | |
| log | 79dee75b1ccd8f3f595aad0d4150851cff58f691 |
| tree | 964cff23e1de4c43f1d1d3ba01313b1c1a0fe4bf |
| parent | 9d311e9960952838edb19a422e5023c670f9995d |
We've settled on the nomenclature for the artifacts the compiler
pipeline produces:
1. Tokens
2. AST (Abstract Syntax Tree)
3. ZIR (Zig Intermediate Representation)
4. AIR (Analyzed Intermediate Representation)
5. Machine Code
Renaming `ir` identifiers to `air` will come with the inevitable
air-memory-layout branch that I plan to start after the 0.8.0 release.15 files changed, 1163 insertions(+), 1163 deletions(-)
CMakeLists.txt+1-1| ... | ... | @@ -554,7 +554,7 @@ set(ZIG_STAGE2_SOURCES |
| 554 | 554 | "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" |
| 555 | 555 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| 556 | 556 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" |
| 557 | "${CMAKE_SOURCE_DIR}/src/ir.zig" | |
| 557 | "${CMAKE_SOURCE_DIR}/src/air.zig" | |
| 558 | 558 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" |
| 559 | 559 | "${CMAKE_SOURCE_DIR}/src/libcxx.zig" |
| 560 | 560 | "${CMAKE_SOURCE_DIR}/src/libtsan.zig" |
src/Module.zig+1-1| ... | ... | @@ -21,7 +21,7 @@ const Type = @import("type.zig").Type; |
| 21 | 21 | const TypedValue = @import("TypedValue.zig"); |
| 22 | 22 | const Package = @import("Package.zig"); |
| 23 | 23 | const link = @import("link.zig"); |
| 24 | const ir = @import("ir.zig"); | |
| 24 | const ir = @import("air.zig"); | |
| 25 | 25 | const Zir = @import("Zir.zig"); |
| 26 | 26 | const trace = @import("tracy.zig").trace; |
| 27 | 27 | const AstGen = @import("AstGen.zig"); |
src/Sema.zig+1-1| ... | ... | @@ -52,7 +52,7 @@ const Sema = @This(); |
| 52 | 52 | const Value = @import("value.zig").Value; |
| 53 | 53 | const Type = @import("type.zig").Type; |
| 54 | 54 | const TypedValue = @import("TypedValue.zig"); |
| 55 | const ir = @import("ir.zig"); | |
| 55 | const ir = @import("air.zig"); | |
| 56 | 56 | const Zir = @import("Zir.zig"); |
| 57 | 57 | const Module = @import("Module.zig"); |
| 58 | 58 | const Inst = ir.Inst; |
src/Zir.zig+1-1| ... | ... | @@ -22,7 +22,7 @@ const Zir = @This(); |
| 22 | 22 | const Type = @import("type.zig").Type; |
| 23 | 23 | const Value = @import("value.zig").Value; |
| 24 | 24 | const TypedValue = @import("TypedValue.zig"); |
| 25 | const ir = @import("ir.zig"); | |
| 25 | const ir = @import("air.zig"); | |
| 26 | 26 | const Module = @import("Module.zig"); |
| 27 | 27 | const LazySrcLoc = Module.LazySrcLoc; |
| 28 | 28 |
src/air.zig created+1150| ... | ... | @@ -0,0 +1,1150 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Value = @import("value.zig").Value; | |
| 3 | const Type = @import("type.zig").Type; | |
| 4 | const Module = @import("Module.zig"); | |
| 5 | const assert = std.debug.assert; | |
| 6 | const codegen = @import("codegen.zig"); | |
| 7 | const ast = std.zig.ast; | |
| 8 | ||
| 9 | /// These are in-memory, analyzed instructions. See `zir.Inst` for the representation | |
| 10 | /// of instructions that correspond to the ZIR text format. | |
| 11 | /// This struct owns the `Value` and `Type` memory. When the struct is deallocated, | |
| 12 | /// so are the `Value` and `Type`. The value of a constant must be copied into | |
| 13 | /// a memory location for the value to survive after a const instruction. | |
| 14 | pub const Inst = struct { | |
| 15 | tag: Tag, | |
| 16 | /// Each bit represents the index of an `Inst` parameter in the `args` field. | |
| 17 | /// If a bit is set, it marks the end of the lifetime of the corresponding | |
| 18 | /// instruction parameter. For example, 0b101 means that the first and | |
| 19 | /// third `Inst` parameters' lifetimes end after this instruction, and will | |
| 20 | /// not have any more following references. | |
| 21 | /// The most significant bit being set means that the instruction itself is | |
| 22 | /// never referenced, in other words its lifetime ends as soon as it finishes. | |
| 23 | /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced. | |
| 24 | /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the | |
| 25 | /// lifetimes of operands are encoded elsewhere. | |
| 26 | deaths: DeathsInt = undefined, | |
| 27 | ty: Type, | |
| 28 | src: Module.LazySrcLoc, | |
| 29 | ||
| 30 | pub const DeathsInt = u16; | |
| 31 | pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); | |
| 32 | pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1; | |
| 33 | pub const deaths_bits = unreferenced_bit_index - 1; | |
| 34 | ||
| 35 | pub fn isUnused(self: Inst) bool { | |
| 36 | return (self.deaths & (1 << unreferenced_bit_index)) != 0; | |
| 37 | } | |
| 38 | ||
| 39 | pub fn operandDies(self: Inst, index: DeathsBitIndex) bool { | |
| 40 | assert(index < deaths_bits); | |
| 41 | return @truncate(u1, self.deaths >> index) != 0; | |
| 42 | } | |
| 43 | ||
| 44 | pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void { | |
| 45 | assert(index < deaths_bits); | |
| 46 | self.deaths &= ~(@as(DeathsInt, 1) << index); | |
| 47 | } | |
| 48 | ||
| 49 | pub fn specialOperandDeaths(self: Inst) bool { | |
| 50 | return (self.deaths & (1 << deaths_bits)) != 0; | |
| 51 | } | |
| 52 | ||
| 53 | pub const Tag = enum { | |
| 54 | add, | |
| 55 | addwrap, | |
| 56 | alloc, | |
| 57 | arg, | |
| 58 | assembly, | |
| 59 | bit_and, | |
| 60 | bitcast, | |
| 61 | bit_or, | |
| 62 | block, | |
| 63 | br, | |
| 64 | /// Same as `br` except the operand is a list of instructions to be treated as | |
| 65 | /// a flat block; that is there is only 1 break instruction from the block, and | |
| 66 | /// it is implied to be after the last instruction, and the last instruction is | |
| 67 | /// the break operand. | |
| 68 | /// This instruction exists for late-stage semantic analysis patch ups, to | |
| 69 | /// replace one br operand with multiple instructions, without moving anything else around. | |
| 70 | br_block_flat, | |
| 71 | breakpoint, | |
| 72 | br_void, | |
| 73 | call, | |
| 74 | cmp_lt, | |
| 75 | cmp_lte, | |
| 76 | cmp_eq, | |
| 77 | cmp_gte, | |
| 78 | cmp_gt, | |
| 79 | cmp_neq, | |
| 80 | condbr, | |
| 81 | constant, | |
| 82 | dbg_stmt, | |
| 83 | /// ?T => bool | |
| 84 | is_null, | |
| 85 | /// ?T => bool (inverted logic) | |
| 86 | is_non_null, | |
| 87 | /// *?T => bool | |
| 88 | is_null_ptr, | |
| 89 | /// *?T => bool (inverted logic) | |
| 90 | is_non_null_ptr, | |
| 91 | /// E!T => bool | |
| 92 | is_err, | |
| 93 | /// *E!T => bool | |
| 94 | is_err_ptr, | |
| 95 | /// E => u16 | |
| 96 | error_to_int, | |
| 97 | /// u16 => E | |
| 98 | int_to_error, | |
| 99 | bool_and, | |
| 100 | bool_or, | |
| 101 | /// Read a value from a pointer. | |
| 102 | load, | |
| 103 | /// A labeled block of code that loops forever. At the end of the body it is implied | |
| 104 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. | |
| 105 | loop, | |
| 106 | ptrtoint, | |
| 107 | ref, | |
| 108 | ret, | |
| 109 | retvoid, | |
| 110 | varptr, | |
| 111 | /// Write a value to a pointer. LHS is pointer, RHS is value. | |
| 112 | store, | |
| 113 | sub, | |
| 114 | subwrap, | |
| 115 | unreach, | |
| 116 | mul, | |
| 117 | mulwrap, | |
| 118 | div, | |
| 119 | not, | |
| 120 | floatcast, | |
| 121 | intcast, | |
| 122 | /// ?T => T | |
| 123 | optional_payload, | |
| 124 | /// *?T => *T | |
| 125 | optional_payload_ptr, | |
| 126 | wrap_optional, | |
| 127 | /// E!T -> T | |
| 128 | unwrap_errunion_payload, | |
| 129 | /// E!T -> E | |
| 130 | unwrap_errunion_err, | |
| 131 | /// *(E!T) -> *T | |
| 132 | unwrap_errunion_payload_ptr, | |
| 133 | /// *(E!T) -> E | |
| 134 | unwrap_errunion_err_ptr, | |
| 135 | /// wrap from T to E!T | |
| 136 | wrap_errunion_payload, | |
| 137 | /// wrap from E to E!T | |
| 138 | wrap_errunion_err, | |
| 139 | xor, | |
| 140 | switchbr, | |
| 141 | /// Given a pointer to a struct and a field index, returns a pointer to the field. | |
| 142 | struct_field_ptr, | |
| 143 | ||
| 144 | pub fn Type(tag: Tag) type { | |
| 145 | return switch (tag) { | |
| 146 | .alloc, | |
| 147 | .retvoid, | |
| 148 | .unreach, | |
| 149 | .breakpoint, | |
| 150 | => NoOp, | |
| 151 | ||
| 152 | .ref, | |
| 153 | .ret, | |
| 154 | .bitcast, | |
| 155 | .not, | |
| 156 | .is_non_null, | |
| 157 | .is_non_null_ptr, | |
| 158 | .is_null, | |
| 159 | .is_null_ptr, | |
| 160 | .is_err, | |
| 161 | .is_err_ptr, | |
| 162 | .int_to_error, | |
| 163 | .error_to_int, | |
| 164 | .ptrtoint, | |
| 165 | .floatcast, | |
| 166 | .intcast, | |
| 167 | .load, | |
| 168 | .optional_payload, | |
| 169 | .optional_payload_ptr, | |
| 170 | .wrap_optional, | |
| 171 | .unwrap_errunion_payload, | |
| 172 | .unwrap_errunion_err, | |
| 173 | .unwrap_errunion_payload_ptr, | |
| 174 | .unwrap_errunion_err_ptr, | |
| 175 | .wrap_errunion_payload, | |
| 176 | .wrap_errunion_err, | |
| 177 | => UnOp, | |
| 178 | ||
| 179 | .add, | |
| 180 | .addwrap, | |
| 181 | .sub, | |
| 182 | .subwrap, | |
| 183 | .mul, | |
| 184 | .mulwrap, | |
| 185 | .div, | |
| 186 | .cmp_lt, | |
| 187 | .cmp_lte, | |
| 188 | .cmp_eq, | |
| 189 | .cmp_gte, | |
| 190 | .cmp_gt, | |
| 191 | .cmp_neq, | |
| 192 | .store, | |
| 193 | .bool_and, | |
| 194 | .bool_or, | |
| 195 | .bit_and, | |
| 196 | .bit_or, | |
| 197 | .xor, | |
| 198 | => BinOp, | |
| 199 | ||
| 200 | .arg => Arg, | |
| 201 | .assembly => Assembly, | |
| 202 | .block => Block, | |
| 203 | .br => Br, | |
| 204 | .br_block_flat => BrBlockFlat, | |
| 205 | .br_void => BrVoid, | |
| 206 | .call => Call, | |
| 207 | .condbr => CondBr, | |
| 208 | .constant => Constant, | |
| 209 | .loop => Loop, | |
| 210 | .varptr => VarPtr, | |
| 211 | .struct_field_ptr => StructFieldPtr, | |
| 212 | .switchbr => SwitchBr, | |
| 213 | .dbg_stmt => DbgStmt, | |
| 214 | }; | |
| 215 | } | |
| 216 | ||
| 217 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { | |
| 218 | return switch (op) { | |
| 219 | .lt => .cmp_lt, | |
| 220 | .lte => .cmp_lte, | |
| 221 | .eq => .cmp_eq, | |
| 222 | .gte => .cmp_gte, | |
| 223 | .gt => .cmp_gt, | |
| 224 | .neq => .cmp_neq, | |
| 225 | }; | |
| 226 | } | |
| 227 | }; | |
| 228 | ||
| 229 | /// Prefer `castTag` to this. | |
| 230 | pub fn cast(base: *Inst, comptime T: type) ?*T { | |
| 231 | if (@hasField(T, "base_tag")) { | |
| 232 | return base.castTag(T.base_tag); | |
| 233 | } | |
| 234 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 235 | const tag = @intToEnum(Tag, field.value); | |
| 236 | if (base.tag == tag) { | |
| 237 | if (T == tag.Type()) { | |
| 238 | return @fieldParentPtr(T, "base", base); | |
| 239 | } | |
| 240 | return null; | |
| 241 | } | |
| 242 | } | |
| 243 | unreachable; | |
| 244 | } | |
| 245 | ||
| 246 | pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() { | |
| 247 | if (base.tag == tag) { | |
| 248 | return @fieldParentPtr(tag.Type(), "base", base); | |
| 249 | } | |
| 250 | return null; | |
| 251 | } | |
| 252 | ||
| 253 | pub fn Args(comptime T: type) type { | |
| 254 | return std.meta.fieldInfo(T, .args).field_type; | |
| 255 | } | |
| 256 | ||
| 257 | /// Returns `null` if runtime-known. | |
| 258 | /// Should be called by codegen, not by Sema. Sema functions should call | |
| 259 | /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead. | |
| 260 | /// TODO audit Sema code for violations to the above guidance. | |
| 261 | pub fn value(base: *Inst) ?Value { | |
| 262 | if (base.ty.onePossibleValue()) |opv| return opv; | |
| 263 | ||
| 264 | const inst = base.castTag(.constant) orelse return null; | |
| 265 | return inst.val; | |
| 266 | } | |
| 267 | ||
| 268 | pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator { | |
| 269 | return switch (base.tag) { | |
| 270 | .cmp_lt => .lt, | |
| 271 | .cmp_lte => .lte, | |
| 272 | .cmp_eq => .eq, | |
| 273 | .cmp_gte => .gte, | |
| 274 | .cmp_gt => .gt, | |
| 275 | .cmp_neq => .neq, | |
| 276 | else => null, | |
| 277 | }; | |
| 278 | } | |
| 279 | ||
| 280 | pub fn operandCount(base: *Inst) usize { | |
| 281 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 282 | const tag = @intToEnum(Tag, field.value); | |
| 283 | if (tag == base.tag) { | |
| 284 | return @fieldParentPtr(tag.Type(), "base", base).operandCount(); | |
| 285 | } | |
| 286 | } | |
| 287 | unreachable; | |
| 288 | } | |
| 289 | ||
| 290 | pub fn getOperand(base: *Inst, index: usize) ?*Inst { | |
| 291 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 292 | const tag = @intToEnum(Tag, field.value); | |
| 293 | if (tag == base.tag) { | |
| 294 | return @fieldParentPtr(tag.Type(), "base", base).getOperand(index); | |
| 295 | } | |
| 296 | } | |
| 297 | unreachable; | |
| 298 | } | |
| 299 | ||
| 300 | pub fn breakBlock(base: *Inst) ?*Block { | |
| 301 | return switch (base.tag) { | |
| 302 | .br => base.castTag(.br).?.block, | |
| 303 | .br_void => base.castTag(.br_void).?.block, | |
| 304 | .br_block_flat => base.castTag(.br_block_flat).?.block, | |
| 305 | else => null, | |
| 306 | }; | |
| 307 | } | |
| 308 | ||
| 309 | pub const NoOp = struct { | |
| 310 | base: Inst, | |
| 311 | ||
| 312 | pub fn operandCount(self: *const NoOp) usize { | |
| 313 | return 0; | |
| 314 | } | |
| 315 | pub fn getOperand(self: *const NoOp, index: usize) ?*Inst { | |
| 316 | return null; | |
| 317 | } | |
| 318 | }; | |
| 319 | ||
| 320 | pub const UnOp = struct { | |
| 321 | base: Inst, | |
| 322 | operand: *Inst, | |
| 323 | ||
| 324 | pub fn operandCount(self: *const UnOp) usize { | |
| 325 | return 1; | |
| 326 | } | |
| 327 | pub fn getOperand(self: *const UnOp, index: usize) ?*Inst { | |
| 328 | if (index == 0) | |
| 329 | return self.operand; | |
| 330 | return null; | |
| 331 | } | |
| 332 | }; | |
| 333 | ||
| 334 | pub const BinOp = struct { | |
| 335 | base: Inst, | |
| 336 | lhs: *Inst, | |
| 337 | rhs: *Inst, | |
| 338 | ||
| 339 | pub fn operandCount(self: *const BinOp) usize { | |
| 340 | return 2; | |
| 341 | } | |
| 342 | pub fn getOperand(self: *const BinOp, index: usize) ?*Inst { | |
| 343 | var i = index; | |
| 344 | ||
| 345 | if (i < 1) | |
| 346 | return self.lhs; | |
| 347 | i -= 1; | |
| 348 | ||
| 349 | if (i < 1) | |
| 350 | return self.rhs; | |
| 351 | i -= 1; | |
| 352 | ||
| 353 | return null; | |
| 354 | } | |
| 355 | }; | |
| 356 | ||
| 357 | pub const Arg = struct { | |
| 358 | pub const base_tag = Tag.arg; | |
| 359 | ||
| 360 | base: Inst, | |
| 361 | /// This exists to be emitted into debug info. | |
| 362 | name: [*:0]const u8, | |
| 363 | ||
| 364 | pub fn operandCount(self: *const Arg) usize { | |
| 365 | return 0; | |
| 366 | } | |
| 367 | pub fn getOperand(self: *const Arg, index: usize) ?*Inst { | |
| 368 | return null; | |
| 369 | } | |
| 370 | }; | |
| 371 | ||
| 372 | pub const Assembly = struct { | |
| 373 | pub const base_tag = Tag.assembly; | |
| 374 | ||
| 375 | base: Inst, | |
| 376 | asm_source: []const u8, | |
| 377 | is_volatile: bool, | |
| 378 | output_constraint: ?[]const u8, | |
| 379 | inputs: []const []const u8, | |
| 380 | clobbers: []const []const u8, | |
| 381 | args: []const *Inst, | |
| 382 | ||
| 383 | pub fn operandCount(self: *const Assembly) usize { | |
| 384 | return self.args.len; | |
| 385 | } | |
| 386 | pub fn getOperand(self: *const Assembly, index: usize) ?*Inst { | |
| 387 | if (index < self.args.len) | |
| 388 | return self.args[index]; | |
| 389 | return null; | |
| 390 | } | |
| 391 | }; | |
| 392 | ||
| 393 | pub const Block = struct { | |
| 394 | pub const base_tag = Tag.block; | |
| 395 | ||
| 396 | base: Inst, | |
| 397 | body: Body, | |
| 398 | /// This memory is reserved for codegen code to do whatever it needs to here. | |
| 399 | codegen: codegen.BlockData = .{}, | |
| 400 | ||
| 401 | pub fn operandCount(self: *const Block) usize { | |
| 402 | return 0; | |
| 403 | } | |
| 404 | pub fn getOperand(self: *const Block, index: usize) ?*Inst { | |
| 405 | return null; | |
| 406 | } | |
| 407 | }; | |
| 408 | ||
| 409 | pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br)); | |
| 410 | pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br)); | |
| 411 | comptime { | |
| 412 | assert(@byteOffsetOf(BrBlockFlat, "base") == @byteOffsetOf(Br, "base")); | |
| 413 | } | |
| 414 | ||
| 415 | pub const BrBlockFlat = struct { | |
| 416 | pub const base_tag = Tag.br_block_flat; | |
| 417 | ||
| 418 | base: Inst, | |
| 419 | block: *Block, | |
| 420 | body: Body, | |
| 421 | ||
| 422 | pub fn operandCount(self: *const BrBlockFlat) usize { | |
| 423 | return 0; | |
| 424 | } | |
| 425 | pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst { | |
| 426 | return null; | |
| 427 | } | |
| 428 | }; | |
| 429 | ||
| 430 | pub const Br = struct { | |
| 431 | pub const base_tag = Tag.br; | |
| 432 | ||
| 433 | base: Inst, | |
| 434 | block: *Block, | |
| 435 | operand: *Inst, | |
| 436 | ||
| 437 | pub fn operandCount(self: *const Br) usize { | |
| 438 | return 1; | |
| 439 | } | |
| 440 | pub fn getOperand(self: *const Br, index: usize) ?*Inst { | |
| 441 | if (index == 0) | |
| 442 | return self.operand; | |
| 443 | return null; | |
| 444 | } | |
| 445 | }; | |
| 446 | ||
| 447 | pub const BrVoid = struct { | |
| 448 | pub const base_tag = Tag.br_void; | |
| 449 | ||
| 450 | base: Inst, | |
| 451 | block: *Block, | |
| 452 | ||
| 453 | pub fn operandCount(self: *const BrVoid) usize { | |
| 454 | return 0; | |
| 455 | } | |
| 456 | pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst { | |
| 457 | return null; | |
| 458 | } | |
| 459 | }; | |
| 460 | ||
| 461 | pub const Call = struct { | |
| 462 | pub const base_tag = Tag.call; | |
| 463 | ||
| 464 | base: Inst, | |
| 465 | func: *Inst, | |
| 466 | args: []const *Inst, | |
| 467 | ||
| 468 | pub fn operandCount(self: *const Call) usize { | |
| 469 | return self.args.len + 1; | |
| 470 | } | |
| 471 | pub fn getOperand(self: *const Call, index: usize) ?*Inst { | |
| 472 | var i = index; | |
| 473 | ||
| 474 | if (i < 1) | |
| 475 | return self.func; | |
| 476 | i -= 1; | |
| 477 | ||
| 478 | if (i < self.args.len) | |
| 479 | return self.args[i]; | |
| 480 | i -= self.args.len; | |
| 481 | ||
| 482 | return null; | |
| 483 | } | |
| 484 | }; | |
| 485 | ||
| 486 | pub const CondBr = struct { | |
| 487 | pub const base_tag = Tag.condbr; | |
| 488 | ||
| 489 | base: Inst, | |
| 490 | condition: *Inst, | |
| 491 | then_body: Body, | |
| 492 | else_body: Body, | |
| 493 | /// Set of instructions whose lifetimes end at the start of one of the branches. | |
| 494 | /// The `then` branch is first: `deaths[0..then_death_count]`. | |
| 495 | /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`. | |
| 496 | deaths: [*]*Inst = undefined, | |
| 497 | then_death_count: u32 = 0, | |
| 498 | else_death_count: u32 = 0, | |
| 499 | ||
| 500 | pub fn operandCount(self: *const CondBr) usize { | |
| 501 | return 1; | |
| 502 | } | |
| 503 | pub fn getOperand(self: *const CondBr, index: usize) ?*Inst { | |
| 504 | var i = index; | |
| 505 | ||
| 506 | if (i < 1) | |
| 507 | return self.condition; | |
| 508 | i -= 1; | |
| 509 | ||
| 510 | return null; | |
| 511 | } | |
| 512 | pub fn thenDeaths(self: *const CondBr) []*Inst { | |
| 513 | return self.deaths[0..self.then_death_count]; | |
| 514 | } | |
| 515 | pub fn elseDeaths(self: *const CondBr) []*Inst { | |
| 516 | return (self.deaths + self.then_death_count)[0..self.else_death_count]; | |
| 517 | } | |
| 518 | }; | |
| 519 | ||
| 520 | pub const Constant = struct { | |
| 521 | pub const base_tag = Tag.constant; | |
| 522 | ||
| 523 | base: Inst, | |
| 524 | val: Value, | |
| 525 | ||
| 526 | pub fn operandCount(self: *const Constant) usize { | |
| 527 | return 0; | |
| 528 | } | |
| 529 | pub fn getOperand(self: *const Constant, index: usize) ?*Inst { | |
| 530 | return null; | |
| 531 | } | |
| 532 | }; | |
| 533 | ||
| 534 | pub const Loop = struct { | |
| 535 | pub const base_tag = Tag.loop; | |
| 536 | ||
| 537 | base: Inst, | |
| 538 | body: Body, | |
| 539 | ||
| 540 | pub fn operandCount(self: *const Loop) usize { | |
| 541 | return 0; | |
| 542 | } | |
| 543 | pub fn getOperand(self: *const Loop, index: usize) ?*Inst { | |
| 544 | return null; | |
| 545 | } | |
| 546 | }; | |
| 547 | ||
| 548 | pub const VarPtr = struct { | |
| 549 | pub const base_tag = Tag.varptr; | |
| 550 | ||
| 551 | base: Inst, | |
| 552 | variable: *Module.Var, | |
| 553 | ||
| 554 | pub fn operandCount(self: *const VarPtr) usize { | |
| 555 | return 0; | |
| 556 | } | |
| 557 | pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst { | |
| 558 | return null; | |
| 559 | } | |
| 560 | }; | |
| 561 | ||
| 562 | pub const StructFieldPtr = struct { | |
| 563 | pub const base_tag = Tag.struct_field_ptr; | |
| 564 | ||
| 565 | base: Inst, | |
| 566 | struct_ptr: *Inst, | |
| 567 | field_index: usize, | |
| 568 | ||
| 569 | pub fn operandCount(self: *const StructFieldPtr) usize { | |
| 570 | return 1; | |
| 571 | } | |
| 572 | pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst { | |
| 573 | var i = index; | |
| 574 | ||
| 575 | if (i < 1) | |
| 576 | return self.struct_ptr; | |
| 577 | i -= 1; | |
| 578 | ||
| 579 | return null; | |
| 580 | } | |
| 581 | }; | |
| 582 | ||
| 583 | pub const SwitchBr = struct { | |
| 584 | pub const base_tag = Tag.switchbr; | |
| 585 | ||
| 586 | base: Inst, | |
| 587 | target: *Inst, | |
| 588 | cases: []Case, | |
| 589 | /// Set of instructions whose lifetimes end at the start of one of the cases. | |
| 590 | /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. | |
| 591 | deaths: [*]*Inst = undefined, | |
| 592 | else_index: u32 = 0, | |
| 593 | else_deaths: u32 = 0, | |
| 594 | else_body: Body, | |
| 595 | ||
| 596 | pub const Case = struct { | |
| 597 | item: Value, | |
| 598 | body: Body, | |
| 599 | index: u32 = 0, | |
| 600 | deaths: u32 = 0, | |
| 601 | }; | |
| 602 | ||
| 603 | pub fn operandCount(self: *const SwitchBr) usize { | |
| 604 | return 1; | |
| 605 | } | |
| 606 | pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst { | |
| 607 | var i = index; | |
| 608 | ||
| 609 | if (i < 1) | |
| 610 | return self.target; | |
| 611 | i -= 1; | |
| 612 | ||
| 613 | return null; | |
| 614 | } | |
| 615 | pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst { | |
| 616 | const case = self.cases[case_index]; | |
| 617 | return (self.deaths + case.index)[0..case.deaths]; | |
| 618 | } | |
| 619 | pub fn elseDeaths(self: *const SwitchBr) []*Inst { | |
| 620 | return (self.deaths + self.else_index)[0..self.else_deaths]; | |
| 621 | } | |
| 622 | }; | |
| 623 | ||
| 624 | pub const DbgStmt = struct { | |
| 625 | pub const base_tag = Tag.dbg_stmt; | |
| 626 | ||
| 627 | base: Inst, | |
| 628 | line: u32, | |
| 629 | column: u32, | |
| 630 | ||
| 631 | pub fn operandCount(self: *const DbgStmt) usize { | |
| 632 | return 0; | |
| 633 | } | |
| 634 | pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst { | |
| 635 | return null; | |
| 636 | } | |
| 637 | }; | |
| 638 | }; | |
| 639 | ||
| 640 | pub const Body = struct { | |
| 641 | instructions: []*Inst, | |
| 642 | }; | |
| 643 | ||
| 644 | /// For debugging purposes, prints a function representation to stderr. | |
| 645 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | |
| 646 | const allocator = old_module.gpa; | |
| 647 | var ctx: DumpTzir = .{ | |
| 648 | .allocator = allocator, | |
| 649 | .arena = std.heap.ArenaAllocator.init(allocator), | |
| 650 | .old_module = &old_module, | |
| 651 | .module_fn = module_fn, | |
| 652 | .indent = 2, | |
| 653 | .inst_table = DumpTzir.InstTable.init(allocator), | |
| 654 | .partial_inst_table = DumpTzir.InstTable.init(allocator), | |
| 655 | .const_table = DumpTzir.InstTable.init(allocator), | |
| 656 | }; | |
| 657 | defer ctx.inst_table.deinit(); | |
| 658 | defer ctx.partial_inst_table.deinit(); | |
| 659 | defer ctx.const_table.deinit(); | |
| 660 | defer ctx.arena.deinit(); | |
| 661 | ||
| 662 | switch (module_fn.state) { | |
| 663 | .queued => std.debug.print("(queued)", .{}), | |
| 664 | .inline_only => std.debug.print("(inline_only)", .{}), | |
| 665 | .in_progress => std.debug.print("(in_progress)", .{}), | |
| 666 | .sema_failure => std.debug.print("(sema_failure)", .{}), | |
| 667 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), | |
| 668 | .success => { | |
| 669 | const writer = std.io.getStdErr().writer(); | |
| 670 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); | |
| 671 | }, | |
| 672 | } | |
| 673 | } | |
| 674 | ||
| 675 | const DumpTzir = struct { | |
| 676 | allocator: *std.mem.Allocator, | |
| 677 | arena: std.heap.ArenaAllocator, | |
| 678 | old_module: *const Module, | |
| 679 | module_fn: *Module.Fn, | |
| 680 | indent: usize, | |
| 681 | inst_table: InstTable, | |
| 682 | partial_inst_table: InstTable, | |
| 683 | const_table: InstTable, | |
| 684 | next_index: usize = 0, | |
| 685 | next_partial_index: usize = 0, | |
| 686 | next_const_index: usize = 0, | |
| 687 | ||
| 688 | const InstTable = std.AutoArrayHashMap(*Inst, usize); | |
| 689 | ||
| 690 | /// TODO: Improve this code to include a stack of Body and store the instructions | |
| 691 | /// in there. Now we are putting all the instructions in a function local table, | |
| 692 | /// however instructions that are in a Body can be thown away when the Body ends. | |
| 693 | fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void { | |
| 694 | // First pass to pre-populate the table so that we can show even invalid references. | |
| 695 | // Must iterate the same order we iterate the second time. | |
| 696 | // We also look for constants and put them in the const_table. | |
| 697 | try dtz.fetchInstsAndResolveConsts(body); | |
| 698 | ||
| 699 | std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); | |
| 700 | ||
| 701 | for (dtz.const_table.items()) |entry| { | |
| 702 | const constant = entry.key.castTag(.constant).?; | |
| 703 | try writer.print(" @{d}: {} = {};\n", .{ | |
| 704 | entry.value, constant.base.ty, constant.val, | |
| 705 | }); | |
| 706 | } | |
| 707 | ||
| 708 | return dtz.dumpBody(body, writer); | |
| 709 | } | |
| 710 | ||
| 711 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void { | |
| 712 | for (body.instructions) |inst| { | |
| 713 | try dtz.inst_table.put(inst, dtz.next_index); | |
| 714 | dtz.next_index += 1; | |
| 715 | switch (inst.tag) { | |
| 716 | .alloc, | |
| 717 | .retvoid, | |
| 718 | .unreach, | |
| 719 | .breakpoint, | |
| 720 | .dbg_stmt, | |
| 721 | .arg, | |
| 722 | => {}, | |
| 723 | ||
| 724 | .ref, | |
| 725 | .ret, | |
| 726 | .bitcast, | |
| 727 | .not, | |
| 728 | .is_non_null, | |
| 729 | .is_non_null_ptr, | |
| 730 | .is_null, | |
| 731 | .is_null_ptr, | |
| 732 | .is_err, | |
| 733 | .is_err_ptr, | |
| 734 | .error_to_int, | |
| 735 | .int_to_error, | |
| 736 | .ptrtoint, | |
| 737 | .floatcast, | |
| 738 | .intcast, | |
| 739 | .load, | |
| 740 | .optional_payload, | |
| 741 | .optional_payload_ptr, | |
| 742 | .wrap_optional, | |
| 743 | .wrap_errunion_payload, | |
| 744 | .wrap_errunion_err, | |
| 745 | .unwrap_errunion_payload, | |
| 746 | .unwrap_errunion_err, | |
| 747 | .unwrap_errunion_payload_ptr, | |
| 748 | .unwrap_errunion_err_ptr, | |
| 749 | => { | |
| 750 | const un_op = inst.cast(Inst.UnOp).?; | |
| 751 | try dtz.findConst(un_op.operand); | |
| 752 | }, | |
| 753 | ||
| 754 | .add, | |
| 755 | .addwrap, | |
| 756 | .sub, | |
| 757 | .subwrap, | |
| 758 | .mul, | |
| 759 | .mulwrap, | |
| 760 | .div, | |
| 761 | .cmp_lt, | |
| 762 | .cmp_lte, | |
| 763 | .cmp_eq, | |
| 764 | .cmp_gte, | |
| 765 | .cmp_gt, | |
| 766 | .cmp_neq, | |
| 767 | .store, | |
| 768 | .bool_and, | |
| 769 | .bool_or, | |
| 770 | .bit_and, | |
| 771 | .bit_or, | |
| 772 | .xor, | |
| 773 | => { | |
| 774 | const bin_op = inst.cast(Inst.BinOp).?; | |
| 775 | try dtz.findConst(bin_op.lhs); | |
| 776 | try dtz.findConst(bin_op.rhs); | |
| 777 | }, | |
| 778 | ||
| 779 | .br => { | |
| 780 | const br = inst.castTag(.br).?; | |
| 781 | try dtz.findConst(&br.block.base); | |
| 782 | try dtz.findConst(br.operand); | |
| 783 | }, | |
| 784 | ||
| 785 | .br_block_flat => { | |
| 786 | const br_block_flat = inst.castTag(.br_block_flat).?; | |
| 787 | try dtz.findConst(&br_block_flat.block.base); | |
| 788 | try dtz.fetchInstsAndResolveConsts(br_block_flat.body); | |
| 789 | }, | |
| 790 | ||
| 791 | .br_void => { | |
| 792 | const br_void = inst.castTag(.br_void).?; | |
| 793 | try dtz.findConst(&br_void.block.base); | |
| 794 | }, | |
| 795 | ||
| 796 | .block => { | |
| 797 | const block = inst.castTag(.block).?; | |
| 798 | try dtz.fetchInstsAndResolveConsts(block.body); | |
| 799 | }, | |
| 800 | ||
| 801 | .condbr => { | |
| 802 | const condbr = inst.castTag(.condbr).?; | |
| 803 | try dtz.findConst(condbr.condition); | |
| 804 | try dtz.fetchInstsAndResolveConsts(condbr.then_body); | |
| 805 | try dtz.fetchInstsAndResolveConsts(condbr.else_body); | |
| 806 | }, | |
| 807 | .switchbr => { | |
| 808 | const switchbr = inst.castTag(.switchbr).?; | |
| 809 | try dtz.findConst(switchbr.target); | |
| 810 | try dtz.fetchInstsAndResolveConsts(switchbr.else_body); | |
| 811 | for (switchbr.cases) |case| { | |
| 812 | try dtz.fetchInstsAndResolveConsts(case.body); | |
| 813 | } | |
| 814 | }, | |
| 815 | ||
| 816 | .loop => { | |
| 817 | const loop = inst.castTag(.loop).?; | |
| 818 | try dtz.fetchInstsAndResolveConsts(loop.body); | |
| 819 | }, | |
| 820 | .call => { | |
| 821 | const call = inst.castTag(.call).?; | |
| 822 | try dtz.findConst(call.func); | |
| 823 | for (call.args) |arg| { | |
| 824 | try dtz.findConst(arg); | |
| 825 | } | |
| 826 | }, | |
| 827 | .struct_field_ptr => { | |
| 828 | const struct_field_ptr = inst.castTag(.struct_field_ptr).?; | |
| 829 | try dtz.findConst(struct_field_ptr.struct_ptr); | |
| 830 | }, | |
| 831 | ||
| 832 | // TODO fill out this debug printing | |
| 833 | .assembly, | |
| 834 | .constant, | |
| 835 | .varptr, | |
| 836 | => {}, | |
| 837 | } | |
| 838 | } | |
| 839 | } | |
| 840 | ||
| 841 | fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { | |
| 842 | for (body.instructions) |inst| { | |
| 843 | const my_index = dtz.next_partial_index; | |
| 844 | try dtz.partial_inst_table.put(inst, my_index); | |
| 845 | dtz.next_partial_index += 1; | |
| 846 | ||
| 847 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 848 | try writer.print("%{d}: {} = {s}(", .{ | |
| 849 | my_index, inst.ty, @tagName(inst.tag), | |
| 850 | }); | |
| 851 | switch (inst.tag) { | |
| 852 | .alloc, | |
| 853 | .retvoid, | |
| 854 | .unreach, | |
| 855 | .breakpoint, | |
| 856 | .dbg_stmt, | |
| 857 | => try writer.writeAll(")\n"), | |
| 858 | ||
| 859 | .ref, | |
| 860 | .ret, | |
| 861 | .bitcast, | |
| 862 | .not, | |
| 863 | .is_non_null, | |
| 864 | .is_null, | |
| 865 | .is_non_null_ptr, | |
| 866 | .is_null_ptr, | |
| 867 | .is_err, | |
| 868 | .is_err_ptr, | |
| 869 | .error_to_int, | |
| 870 | .int_to_error, | |
| 871 | .ptrtoint, | |
| 872 | .floatcast, | |
| 873 | .intcast, | |
| 874 | .load, | |
| 875 | .optional_payload, | |
| 876 | .optional_payload_ptr, | |
| 877 | .wrap_optional, | |
| 878 | .wrap_errunion_err, | |
| 879 | .wrap_errunion_payload, | |
| 880 | .unwrap_errunion_err, | |
| 881 | .unwrap_errunion_payload, | |
| 882 | .unwrap_errunion_payload_ptr, | |
| 883 | .unwrap_errunion_err_ptr, | |
| 884 | => { | |
| 885 | const un_op = inst.cast(Inst.UnOp).?; | |
| 886 | const kinky = try dtz.writeInst(writer, un_op.operand); | |
| 887 | if (kinky != null) { | |
| 888 | try writer.writeAll(") // Instruction does not dominate all uses!\n"); | |
| 889 | } else { | |
| 890 | try writer.writeAll(")\n"); | |
| 891 | } | |
| 892 | }, | |
| 893 | ||
| 894 | .add, | |
| 895 | .addwrap, | |
| 896 | .sub, | |
| 897 | .subwrap, | |
| 898 | .mul, | |
| 899 | .mulwrap, | |
| 900 | .div, | |
| 901 | .cmp_lt, | |
| 902 | .cmp_lte, | |
| 903 | .cmp_eq, | |
| 904 | .cmp_gte, | |
| 905 | .cmp_gt, | |
| 906 | .cmp_neq, | |
| 907 | .store, | |
| 908 | .bool_and, | |
| 909 | .bool_or, | |
| 910 | .bit_and, | |
| 911 | .bit_or, | |
| 912 | .xor, | |
| 913 | => { | |
| 914 | const bin_op = inst.cast(Inst.BinOp).?; | |
| 915 | ||
| 916 | const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs); | |
| 917 | try writer.writeAll(", "); | |
| 918 | const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs); | |
| 919 | ||
| 920 | if (lhs_kinky != null or rhs_kinky != null) { | |
| 921 | try writer.writeAll(") // Instruction does not dominate all uses!"); | |
| 922 | if (lhs_kinky) |lhs| { | |
| 923 | try writer.print(" %{d}", .{lhs}); | |
| 924 | } | |
| 925 | if (rhs_kinky) |rhs| { | |
| 926 | try writer.print(" %{d}", .{rhs}); | |
| 927 | } | |
| 928 | try writer.writeAll("\n"); | |
| 929 | } else { | |
| 930 | try writer.writeAll(")\n"); | |
| 931 | } | |
| 932 | }, | |
| 933 | ||
| 934 | .arg => { | |
| 935 | const arg = inst.castTag(.arg).?; | |
| 936 | try writer.print("{s})\n", .{arg.name}); | |
| 937 | }, | |
| 938 | ||
| 939 | .br => { | |
| 940 | const br = inst.castTag(.br).?; | |
| 941 | ||
| 942 | const lhs_kinky = try dtz.writeInst(writer, &br.block.base); | |
| 943 | try writer.writeAll(", "); | |
| 944 | const rhs_kinky = try dtz.writeInst(writer, br.operand); | |
| 945 | ||
| 946 | if (lhs_kinky != null or rhs_kinky != null) { | |
| 947 | try writer.writeAll(") // Instruction does not dominate all uses!"); | |
| 948 | if (lhs_kinky) |lhs| { | |
| 949 | try writer.print(" %{d}", .{lhs}); | |
| 950 | } | |
| 951 | if (rhs_kinky) |rhs| { | |
| 952 | try writer.print(" %{d}", .{rhs}); | |
| 953 | } | |
| 954 | try writer.writeAll("\n"); | |
| 955 | } else { | |
| 956 | try writer.writeAll(")\n"); | |
| 957 | } | |
| 958 | }, | |
| 959 | ||
| 960 | .br_block_flat => { | |
| 961 | const br_block_flat = inst.castTag(.br_block_flat).?; | |
| 962 | const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base); | |
| 963 | if (block_kinky != null) { | |
| 964 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | |
| 965 | } else { | |
| 966 | try writer.writeAll(", {\n"); | |
| 967 | } | |
| 968 | ||
| 969 | const old_indent = dtz.indent; | |
| 970 | dtz.indent += 2; | |
| 971 | try dtz.dumpBody(br_block_flat.body, writer); | |
| 972 | dtz.indent = old_indent; | |
| 973 | ||
| 974 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 975 | try writer.writeAll("})\n"); | |
| 976 | }, | |
| 977 | ||
| 978 | .br_void => { | |
| 979 | const br_void = inst.castTag(.br_void).?; | |
| 980 | const kinky = try dtz.writeInst(writer, &br_void.block.base); | |
| 981 | if (kinky) |_| { | |
| 982 | try writer.writeAll(") // Instruction does not dominate all uses!\n"); | |
| 983 | } else { | |
| 984 | try writer.writeAll(")\n"); | |
| 985 | } | |
| 986 | }, | |
| 987 | ||
| 988 | .block => { | |
| 989 | const block = inst.castTag(.block).?; | |
| 990 | ||
| 991 | try writer.writeAll("{\n"); | |
| 992 | ||
| 993 | const old_indent = dtz.indent; | |
| 994 | dtz.indent += 2; | |
| 995 | try dtz.dumpBody(block.body, writer); | |
| 996 | dtz.indent = old_indent; | |
| 997 | ||
| 998 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 999 | try writer.writeAll("})\n"); | |
| 1000 | }, | |
| 1001 | ||
| 1002 | .condbr => { | |
| 1003 | const condbr = inst.castTag(.condbr).?; | |
| 1004 | ||
| 1005 | const condition_kinky = try dtz.writeInst(writer, condbr.condition); | |
| 1006 | if (condition_kinky != null) { | |
| 1007 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | |
| 1008 | } else { | |
| 1009 | try writer.writeAll(", {\n"); | |
| 1010 | } | |
| 1011 | ||
| 1012 | const old_indent = dtz.indent; | |
| 1013 | dtz.indent += 2; | |
| 1014 | try dtz.dumpBody(condbr.then_body, writer); | |
| 1015 | ||
| 1016 | try writer.writeByteNTimes(' ', old_indent); | |
| 1017 | try writer.writeAll("}, {\n"); | |
| 1018 | ||
| 1019 | try dtz.dumpBody(condbr.else_body, writer); | |
| 1020 | dtz.indent = old_indent; | |
| 1021 | ||
| 1022 | try writer.writeByteNTimes(' ', old_indent); | |
| 1023 | try writer.writeAll("})\n"); | |
| 1024 | }, | |
| 1025 | ||
| 1026 | .switchbr => { | |
| 1027 | const switchbr = inst.castTag(.switchbr).?; | |
| 1028 | ||
| 1029 | const condition_kinky = try dtz.writeInst(writer, switchbr.target); | |
| 1030 | if (condition_kinky != null) { | |
| 1031 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | |
| 1032 | } else { | |
| 1033 | try writer.writeAll(", {\n"); | |
| 1034 | } | |
| 1035 | const old_indent = dtz.indent; | |
| 1036 | ||
| 1037 | if (switchbr.else_body.instructions.len != 0) { | |
| 1038 | dtz.indent += 2; | |
| 1039 | try dtz.dumpBody(switchbr.else_body, writer); | |
| 1040 | ||
| 1041 | try writer.writeByteNTimes(' ', old_indent); | |
| 1042 | try writer.writeAll("}, {\n"); | |
| 1043 | dtz.indent = old_indent; | |
| 1044 | } | |
| 1045 | for (switchbr.cases) |case| { | |
| 1046 | dtz.indent += 2; | |
| 1047 | try dtz.dumpBody(case.body, writer); | |
| 1048 | ||
| 1049 | try writer.writeByteNTimes(' ', old_indent); | |
| 1050 | try writer.writeAll("}, {\n"); | |
| 1051 | dtz.indent = old_indent; | |
| 1052 | } | |
| 1053 | ||
| 1054 | try writer.writeByteNTimes(' ', old_indent); | |
| 1055 | try writer.writeAll("})\n"); | |
| 1056 | }, | |
| 1057 | ||
| 1058 | .loop => { | |
| 1059 | const loop = inst.castTag(.loop).?; | |
| 1060 | ||
| 1061 | try writer.writeAll("{\n"); | |
| 1062 | ||
| 1063 | const old_indent = dtz.indent; | |
| 1064 | dtz.indent += 2; | |
| 1065 | try dtz.dumpBody(loop.body, writer); | |
| 1066 | dtz.indent = old_indent; | |
| 1067 | ||
| 1068 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 1069 | try writer.writeAll("})\n"); | |
| 1070 | }, | |
| 1071 | ||
| 1072 | .call => { | |
| 1073 | const call = inst.castTag(.call).?; | |
| 1074 | ||
| 1075 | const args_kinky = try dtz.allocator.alloc(?usize, call.args.len); | |
| 1076 | defer dtz.allocator.free(args_kinky); | |
| 1077 | std.mem.set(?usize, args_kinky, null); | |
| 1078 | var any_kinky_args = false; | |
| 1079 | ||
| 1080 | const func_kinky = try dtz.writeInst(writer, call.func); | |
| 1081 | ||
| 1082 | for (call.args) |arg, i| { | |
| 1083 | try writer.writeAll(", "); | |
| 1084 | ||
| 1085 | args_kinky[i] = try dtz.writeInst(writer, arg); | |
| 1086 | any_kinky_args = any_kinky_args or args_kinky[i] != null; | |
| 1087 | } | |
| 1088 | ||
| 1089 | if (func_kinky != null or any_kinky_args) { | |
| 1090 | try writer.writeAll(") // Instruction does not dominate all uses!"); | |
| 1091 | if (func_kinky) |func_index| { | |
| 1092 | try writer.print(" %{d}", .{func_index}); | |
| 1093 | } | |
| 1094 | for (args_kinky) |arg_kinky| { | |
| 1095 | if (arg_kinky) |arg_index| { | |
| 1096 | try writer.print(" %{d}", .{arg_index}); | |
| 1097 | } | |
| 1098 | } | |
| 1099 | try writer.writeAll("\n"); | |
| 1100 | } else { | |
| 1101 | try writer.writeAll(")\n"); | |
| 1102 | } | |
| 1103 | }, | |
| 1104 | ||
| 1105 | .struct_field_ptr => { | |
| 1106 | const struct_field_ptr = inst.castTag(.struct_field_ptr).?; | |
| 1107 | const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr); | |
| 1108 | if (kinky != null) { | |
| 1109 | try writer.print("{d}) // Instruction does not dominate all uses!\n", .{ | |
| 1110 | struct_field_ptr.field_index, | |
| 1111 | }); | |
| 1112 | } else { | |
| 1113 | try writer.print("{d})\n", .{struct_field_ptr.field_index}); | |
| 1114 | } | |
| 1115 | }, | |
| 1116 | ||
| 1117 | // TODO fill out this debug printing | |
| 1118 | .assembly, | |
| 1119 | .constant, | |
| 1120 | .varptr, | |
| 1121 | => { | |
| 1122 | try writer.writeAll("!TODO!)\n"); | |
| 1123 | }, | |
| 1124 | } | |
| 1125 | } | |
| 1126 | } | |
| 1127 | ||
| 1128 | fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize { | |
| 1129 | if (dtz.partial_inst_table.get(inst)) |operand_index| { | |
| 1130 | try writer.print("%{d}", .{operand_index}); | |
| 1131 | return null; | |
| 1132 | } else if (dtz.const_table.get(inst)) |operand_index| { | |
| 1133 | try writer.print("@{d}", .{operand_index}); | |
| 1134 | return null; | |
| 1135 | } else if (dtz.inst_table.get(inst)) |operand_index| { | |
| 1136 | try writer.print("%{d}", .{operand_index}); | |
| 1137 | return operand_index; | |
| 1138 | } else { | |
| 1139 | try writer.writeAll("!BADREF!"); | |
| 1140 | return null; | |
| 1141 | } | |
| 1142 | } | |
| 1143 | ||
| 1144 | fn findConst(dtz: *DumpTzir, operand: *Inst) !void { | |
| 1145 | if (operand.tag == .constant) { | |
| 1146 | try dtz.const_table.put(operand, dtz.next_const_index); | |
| 1147 | dtz.next_const_index += 1; | |
| 1148 | } | |
| 1149 | } | |
| 1150 | }; |
src/codegen.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | const ir = @import("ir.zig"); | |
| 5 | const ir = @import("air.zig"); | |
| 6 | 6 | const Type = @import("type.zig").Type; |
| 7 | 7 | const Value = @import("value.zig").Value; |
| 8 | 8 | const TypedValue = @import("TypedValue.zig"); |
src/codegen/c.zig+1-1| ... | ... | @@ -6,7 +6,7 @@ const log = std.log.scoped(.c); |
| 6 | 6 | const link = @import("../link.zig"); |
| 7 | 7 | const Module = @import("../Module.zig"); |
| 8 | 8 | const Compilation = @import("../Compilation.zig"); |
| 9 | const ir = @import("../ir.zig"); | |
| 9 | const ir = @import("../air.zig"); | |
| 10 | 10 | const Inst = ir.Inst; |
| 11 | 11 | const Value = @import("../value.zig").Value; |
| 12 | 12 | const Type = @import("../type.zig").Type; |
src/codegen/llvm.zig+1-1| ... | ... | @@ -9,7 +9,7 @@ const math = std.math; |
| 9 | 9 | |
| 10 | 10 | const Module = @import("../Module.zig"); |
| 11 | 11 | const TypedValue = @import("../TypedValue.zig"); |
| 12 | const ir = @import("../ir.zig"); | |
| 12 | const ir = @import("../air.zig"); | |
| 13 | 13 | const Inst = ir.Inst; |
| 14 | 14 | |
| 15 | 15 | const Value = @import("../value.zig").Value; |
src/codegen/spirv.zig+1-1| ... | ... | @@ -11,7 +11,7 @@ const Decl = Module.Decl; |
| 11 | 11 | const Type = @import("../type.zig").Type; |
| 12 | 12 | const Value = @import("../value.zig").Value; |
| 13 | 13 | const LazySrcLoc = Module.LazySrcLoc; |
| 14 | const ir = @import("../ir.zig"); | |
| 14 | const ir = @import("../air.zig"); | |
| 15 | 15 | const Inst = ir.Inst; |
| 16 | 16 | |
| 17 | 17 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); |
src/codegen/wasm.zig+1-1| ... | ... | @@ -9,7 +9,7 @@ const wasm = std.wasm; |
| 9 | 9 | |
| 10 | 10 | const Module = @import("../Module.zig"); |
| 11 | 11 | const Decl = Module.Decl; |
| 12 | const ir = @import("../ir.zig"); | |
| 12 | const ir = @import("../air.zig"); | |
| 13 | 13 | const Inst = ir.Inst; |
| 14 | 14 | const Type = @import("../type.zig").Type; |
| 15 | 15 | const Value = @import("../value.zig").Value; |
src/ir.zig deleted-1150| ... | ... | @@ -1,1150 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Value = @import("value.zig").Value; | |
| 3 | const Type = @import("type.zig").Type; | |
| 4 | const Module = @import("Module.zig"); | |
| 5 | const assert = std.debug.assert; | |
| 6 | const codegen = @import("codegen.zig"); | |
| 7 | const ast = std.zig.ast; | |
| 8 | ||
| 9 | /// These are in-memory, analyzed instructions. See `zir.Inst` for the representation | |
| 10 | /// of instructions that correspond to the ZIR text format. | |
| 11 | /// This struct owns the `Value` and `Type` memory. When the struct is deallocated, | |
| 12 | /// so are the `Value` and `Type`. The value of a constant must be copied into | |
| 13 | /// a memory location for the value to survive after a const instruction. | |
| 14 | pub const Inst = struct { | |
| 15 | tag: Tag, | |
| 16 | /// Each bit represents the index of an `Inst` parameter in the `args` field. | |
| 17 | /// If a bit is set, it marks the end of the lifetime of the corresponding | |
| 18 | /// instruction parameter. For example, 0b101 means that the first and | |
| 19 | /// third `Inst` parameters' lifetimes end after this instruction, and will | |
| 20 | /// not have any more following references. | |
| 21 | /// The most significant bit being set means that the instruction itself is | |
| 22 | /// never referenced, in other words its lifetime ends as soon as it finishes. | |
| 23 | /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced. | |
| 24 | /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the | |
| 25 | /// lifetimes of operands are encoded elsewhere. | |
| 26 | deaths: DeathsInt = undefined, | |
| 27 | ty: Type, | |
| 28 | src: Module.LazySrcLoc, | |
| 29 | ||
| 30 | pub const DeathsInt = u16; | |
| 31 | pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); | |
| 32 | pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1; | |
| 33 | pub const deaths_bits = unreferenced_bit_index - 1; | |
| 34 | ||
| 35 | pub fn isUnused(self: Inst) bool { | |
| 36 | return (self.deaths & (1 << unreferenced_bit_index)) != 0; | |
| 37 | } | |
| 38 | ||
| 39 | pub fn operandDies(self: Inst, index: DeathsBitIndex) bool { | |
| 40 | assert(index < deaths_bits); | |
| 41 | return @truncate(u1, self.deaths >> index) != 0; | |
| 42 | } | |
| 43 | ||
| 44 | pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void { | |
| 45 | assert(index < deaths_bits); | |
| 46 | self.deaths &= ~(@as(DeathsInt, 1) << index); | |
| 47 | } | |
| 48 | ||
| 49 | pub fn specialOperandDeaths(self: Inst) bool { | |
| 50 | return (self.deaths & (1 << deaths_bits)) != 0; | |
| 51 | } | |
| 52 | ||
| 53 | pub const Tag = enum { | |
| 54 | add, | |
| 55 | addwrap, | |
| 56 | alloc, | |
| 57 | arg, | |
| 58 | assembly, | |
| 59 | bit_and, | |
| 60 | bitcast, | |
| 61 | bit_or, | |
| 62 | block, | |
| 63 | br, | |
| 64 | /// Same as `br` except the operand is a list of instructions to be treated as | |
| 65 | /// a flat block; that is there is only 1 break instruction from the block, and | |
| 66 | /// it is implied to be after the last instruction, and the last instruction is | |
| 67 | /// the break operand. | |
| 68 | /// This instruction exists for late-stage semantic analysis patch ups, to | |
| 69 | /// replace one br operand with multiple instructions, without moving anything else around. | |
| 70 | br_block_flat, | |
| 71 | breakpoint, | |
| 72 | br_void, | |
| 73 | call, | |
| 74 | cmp_lt, | |
| 75 | cmp_lte, | |
| 76 | cmp_eq, | |
| 77 | cmp_gte, | |
| 78 | cmp_gt, | |
| 79 | cmp_neq, | |
| 80 | condbr, | |
| 81 | constant, | |
| 82 | dbg_stmt, | |
| 83 | /// ?T => bool | |
| 84 | is_null, | |
| 85 | /// ?T => bool (inverted logic) | |
| 86 | is_non_null, | |
| 87 | /// *?T => bool | |
| 88 | is_null_ptr, | |
| 89 | /// *?T => bool (inverted logic) | |
| 90 | is_non_null_ptr, | |
| 91 | /// E!T => bool | |
| 92 | is_err, | |
| 93 | /// *E!T => bool | |
| 94 | is_err_ptr, | |
| 95 | /// E => u16 | |
| 96 | error_to_int, | |
| 97 | /// u16 => E | |
| 98 | int_to_error, | |
| 99 | bool_and, | |
| 100 | bool_or, | |
| 101 | /// Read a value from a pointer. | |
| 102 | load, | |
| 103 | /// A labeled block of code that loops forever. At the end of the body it is implied | |
| 104 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. | |
| 105 | loop, | |
| 106 | ptrtoint, | |
| 107 | ref, | |
| 108 | ret, | |
| 109 | retvoid, | |
| 110 | varptr, | |
| 111 | /// Write a value to a pointer. LHS is pointer, RHS is value. | |
| 112 | store, | |
| 113 | sub, | |
| 114 | subwrap, | |
| 115 | unreach, | |
| 116 | mul, | |
| 117 | mulwrap, | |
| 118 | div, | |
| 119 | not, | |
| 120 | floatcast, | |
| 121 | intcast, | |
| 122 | /// ?T => T | |
| 123 | optional_payload, | |
| 124 | /// *?T => *T | |
| 125 | optional_payload_ptr, | |
| 126 | wrap_optional, | |
| 127 | /// E!T -> T | |
| 128 | unwrap_errunion_payload, | |
| 129 | /// E!T -> E | |
| 130 | unwrap_errunion_err, | |
| 131 | /// *(E!T) -> *T | |
| 132 | unwrap_errunion_payload_ptr, | |
| 133 | /// *(E!T) -> E | |
| 134 | unwrap_errunion_err_ptr, | |
| 135 | /// wrap from T to E!T | |
| 136 | wrap_errunion_payload, | |
| 137 | /// wrap from E to E!T | |
| 138 | wrap_errunion_err, | |
| 139 | xor, | |
| 140 | switchbr, | |
| 141 | /// Given a pointer to a struct and a field index, returns a pointer to the field. | |
| 142 | struct_field_ptr, | |
| 143 | ||
| 144 | pub fn Type(tag: Tag) type { | |
| 145 | return switch (tag) { | |
| 146 | .alloc, | |
| 147 | .retvoid, | |
| 148 | .unreach, | |
| 149 | .breakpoint, | |
| 150 | => NoOp, | |
| 151 | ||
| 152 | .ref, | |
| 153 | .ret, | |
| 154 | .bitcast, | |
| 155 | .not, | |
| 156 | .is_non_null, | |
| 157 | .is_non_null_ptr, | |
| 158 | .is_null, | |
| 159 | .is_null_ptr, | |
| 160 | .is_err, | |
| 161 | .is_err_ptr, | |
| 162 | .int_to_error, | |
| 163 | .error_to_int, | |
| 164 | .ptrtoint, | |
| 165 | .floatcast, | |
| 166 | .intcast, | |
| 167 | .load, | |
| 168 | .optional_payload, | |
| 169 | .optional_payload_ptr, | |
| 170 | .wrap_optional, | |
| 171 | .unwrap_errunion_payload, | |
| 172 | .unwrap_errunion_err, | |
| 173 | .unwrap_errunion_payload_ptr, | |
| 174 | .unwrap_errunion_err_ptr, | |
| 175 | .wrap_errunion_payload, | |
| 176 | .wrap_errunion_err, | |
| 177 | => UnOp, | |
| 178 | ||
| 179 | .add, | |
| 180 | .addwrap, | |
| 181 | .sub, | |
| 182 | .subwrap, | |
| 183 | .mul, | |
| 184 | .mulwrap, | |
| 185 | .div, | |
| 186 | .cmp_lt, | |
| 187 | .cmp_lte, | |
| 188 | .cmp_eq, | |
| 189 | .cmp_gte, | |
| 190 | .cmp_gt, | |
| 191 | .cmp_neq, | |
| 192 | .store, | |
| 193 | .bool_and, | |
| 194 | .bool_or, | |
| 195 | .bit_and, | |
| 196 | .bit_or, | |
| 197 | .xor, | |
| 198 | => BinOp, | |
| 199 | ||
| 200 | .arg => Arg, | |
| 201 | .assembly => Assembly, | |
| 202 | .block => Block, | |
| 203 | .br => Br, | |
| 204 | .br_block_flat => BrBlockFlat, | |
| 205 | .br_void => BrVoid, | |
| 206 | .call => Call, | |
| 207 | .condbr => CondBr, | |
| 208 | .constant => Constant, | |
| 209 | .loop => Loop, | |
| 210 | .varptr => VarPtr, | |
| 211 | .struct_field_ptr => StructFieldPtr, | |
| 212 | .switchbr => SwitchBr, | |
| 213 | .dbg_stmt => DbgStmt, | |
| 214 | }; | |
| 215 | } | |
| 216 | ||
| 217 | pub fn fromCmpOp(op: std.math.CompareOperator) Tag { | |
| 218 | return switch (op) { | |
| 219 | .lt => .cmp_lt, | |
| 220 | .lte => .cmp_lte, | |
| 221 | .eq => .cmp_eq, | |
| 222 | .gte => .cmp_gte, | |
| 223 | .gt => .cmp_gt, | |
| 224 | .neq => .cmp_neq, | |
| 225 | }; | |
| 226 | } | |
| 227 | }; | |
| 228 | ||
| 229 | /// Prefer `castTag` to this. | |
| 230 | pub fn cast(base: *Inst, comptime T: type) ?*T { | |
| 231 | if (@hasField(T, "base_tag")) { | |
| 232 | return base.castTag(T.base_tag); | |
| 233 | } | |
| 234 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 235 | const tag = @intToEnum(Tag, field.value); | |
| 236 | if (base.tag == tag) { | |
| 237 | if (T == tag.Type()) { | |
| 238 | return @fieldParentPtr(T, "base", base); | |
| 239 | } | |
| 240 | return null; | |
| 241 | } | |
| 242 | } | |
| 243 | unreachable; | |
| 244 | } | |
| 245 | ||
| 246 | pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() { | |
| 247 | if (base.tag == tag) { | |
| 248 | return @fieldParentPtr(tag.Type(), "base", base); | |
| 249 | } | |
| 250 | return null; | |
| 251 | } | |
| 252 | ||
| 253 | pub fn Args(comptime T: type) type { | |
| 254 | return std.meta.fieldInfo(T, .args).field_type; | |
| 255 | } | |
| 256 | ||
| 257 | /// Returns `null` if runtime-known. | |
| 258 | /// Should be called by codegen, not by Sema. Sema functions should call | |
| 259 | /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead. | |
| 260 | /// TODO audit Sema code for violations to the above guidance. | |
| 261 | pub fn value(base: *Inst) ?Value { | |
| 262 | if (base.ty.onePossibleValue()) |opv| return opv; | |
| 263 | ||
| 264 | const inst = base.castTag(.constant) orelse return null; | |
| 265 | return inst.val; | |
| 266 | } | |
| 267 | ||
| 268 | pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator { | |
| 269 | return switch (base.tag) { | |
| 270 | .cmp_lt => .lt, | |
| 271 | .cmp_lte => .lte, | |
| 272 | .cmp_eq => .eq, | |
| 273 | .cmp_gte => .gte, | |
| 274 | .cmp_gt => .gt, | |
| 275 | .cmp_neq => .neq, | |
| 276 | else => null, | |
| 277 | }; | |
| 278 | } | |
| 279 | ||
| 280 | pub fn operandCount(base: *Inst) usize { | |
| 281 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 282 | const tag = @intToEnum(Tag, field.value); | |
| 283 | if (tag == base.tag) { | |
| 284 | return @fieldParentPtr(tag.Type(), "base", base).operandCount(); | |
| 285 | } | |
| 286 | } | |
| 287 | unreachable; | |
| 288 | } | |
| 289 | ||
| 290 | pub fn getOperand(base: *Inst, index: usize) ?*Inst { | |
| 291 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 292 | const tag = @intToEnum(Tag, field.value); | |
| 293 | if (tag == base.tag) { | |
| 294 | return @fieldParentPtr(tag.Type(), "base", base).getOperand(index); | |
| 295 | } | |
| 296 | } | |
| 297 | unreachable; | |
| 298 | } | |
| 299 | ||
| 300 | pub fn breakBlock(base: *Inst) ?*Block { | |
| 301 | return switch (base.tag) { | |
| 302 | .br => base.castTag(.br).?.block, | |
| 303 | .br_void => base.castTag(.br_void).?.block, | |
| 304 | .br_block_flat => base.castTag(.br_block_flat).?.block, | |
| 305 | else => null, | |
| 306 | }; | |
| 307 | } | |
| 308 | ||
| 309 | pub const NoOp = struct { | |
| 310 | base: Inst, | |
| 311 | ||
| 312 | pub fn operandCount(self: *const NoOp) usize { | |
| 313 | return 0; | |
| 314 | } | |
| 315 | pub fn getOperand(self: *const NoOp, index: usize) ?*Inst { | |
| 316 | return null; | |
| 317 | } | |
| 318 | }; | |
| 319 | ||
| 320 | pub const UnOp = struct { | |
| 321 | base: Inst, | |
| 322 | operand: *Inst, | |
| 323 | ||
| 324 | pub fn operandCount(self: *const UnOp) usize { | |
| 325 | return 1; | |
| 326 | } | |
| 327 | pub fn getOperand(self: *const UnOp, index: usize) ?*Inst { | |
| 328 | if (index == 0) | |
| 329 | return self.operand; | |
| 330 | return null; | |
| 331 | } | |
| 332 | }; | |
| 333 | ||
| 334 | pub const BinOp = struct { | |
| 335 | base: Inst, | |
| 336 | lhs: *Inst, | |
| 337 | rhs: *Inst, | |
| 338 | ||
| 339 | pub fn operandCount(self: *const BinOp) usize { | |
| 340 | return 2; | |
| 341 | } | |
| 342 | pub fn getOperand(self: *const BinOp, index: usize) ?*Inst { | |
| 343 | var i = index; | |
| 344 | ||
| 345 | if (i < 1) | |
| 346 | return self.lhs; | |
| 347 | i -= 1; | |
| 348 | ||
| 349 | if (i < 1) | |
| 350 | return self.rhs; | |
| 351 | i -= 1; | |
| 352 | ||
| 353 | return null; | |
| 354 | } | |
| 355 | }; | |
| 356 | ||
| 357 | pub const Arg = struct { | |
| 358 | pub const base_tag = Tag.arg; | |
| 359 | ||
| 360 | base: Inst, | |
| 361 | /// This exists to be emitted into debug info. | |
| 362 | name: [*:0]const u8, | |
| 363 | ||
| 364 | pub fn operandCount(self: *const Arg) usize { | |
| 365 | return 0; | |
| 366 | } | |
| 367 | pub fn getOperand(self: *const Arg, index: usize) ?*Inst { | |
| 368 | return null; | |
| 369 | } | |
| 370 | }; | |
| 371 | ||
| 372 | pub const Assembly = struct { | |
| 373 | pub const base_tag = Tag.assembly; | |
| 374 | ||
| 375 | base: Inst, | |
| 376 | asm_source: []const u8, | |
| 377 | is_volatile: bool, | |
| 378 | output_constraint: ?[]const u8, | |
| 379 | inputs: []const []const u8, | |
| 380 | clobbers: []const []const u8, | |
| 381 | args: []const *Inst, | |
| 382 | ||
| 383 | pub fn operandCount(self: *const Assembly) usize { | |
| 384 | return self.args.len; | |
| 385 | } | |
| 386 | pub fn getOperand(self: *const Assembly, index: usize) ?*Inst { | |
| 387 | if (index < self.args.len) | |
| 388 | return self.args[index]; | |
| 389 | return null; | |
| 390 | } | |
| 391 | }; | |
| 392 | ||
| 393 | pub const Block = struct { | |
| 394 | pub const base_tag = Tag.block; | |
| 395 | ||
| 396 | base: Inst, | |
| 397 | body: Body, | |
| 398 | /// This memory is reserved for codegen code to do whatever it needs to here. | |
| 399 | codegen: codegen.BlockData = .{}, | |
| 400 | ||
| 401 | pub fn operandCount(self: *const Block) usize { | |
| 402 | return 0; | |
| 403 | } | |
| 404 | pub fn getOperand(self: *const Block, index: usize) ?*Inst { | |
| 405 | return null; | |
| 406 | } | |
| 407 | }; | |
| 408 | ||
| 409 | pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br)); | |
| 410 | pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br)); | |
| 411 | comptime { | |
| 412 | assert(@byteOffsetOf(BrBlockFlat, "base") == @byteOffsetOf(Br, "base")); | |
| 413 | } | |
| 414 | ||
| 415 | pub const BrBlockFlat = struct { | |
| 416 | pub const base_tag = Tag.br_block_flat; | |
| 417 | ||
| 418 | base: Inst, | |
| 419 | block: *Block, | |
| 420 | body: Body, | |
| 421 | ||
| 422 | pub fn operandCount(self: *const BrBlockFlat) usize { | |
| 423 | return 0; | |
| 424 | } | |
| 425 | pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst { | |
| 426 | return null; | |
| 427 | } | |
| 428 | }; | |
| 429 | ||
| 430 | pub const Br = struct { | |
| 431 | pub const base_tag = Tag.br; | |
| 432 | ||
| 433 | base: Inst, | |
| 434 | block: *Block, | |
| 435 | operand: *Inst, | |
| 436 | ||
| 437 | pub fn operandCount(self: *const Br) usize { | |
| 438 | return 1; | |
| 439 | } | |
| 440 | pub fn getOperand(self: *const Br, index: usize) ?*Inst { | |
| 441 | if (index == 0) | |
| 442 | return self.operand; | |
| 443 | return null; | |
| 444 | } | |
| 445 | }; | |
| 446 | ||
| 447 | pub const BrVoid = struct { | |
| 448 | pub const base_tag = Tag.br_void; | |
| 449 | ||
| 450 | base: Inst, | |
| 451 | block: *Block, | |
| 452 | ||
| 453 | pub fn operandCount(self: *const BrVoid) usize { | |
| 454 | return 0; | |
| 455 | } | |
| 456 | pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst { | |
| 457 | return null; | |
| 458 | } | |
| 459 | }; | |
| 460 | ||
| 461 | pub const Call = struct { | |
| 462 | pub const base_tag = Tag.call; | |
| 463 | ||
| 464 | base: Inst, | |
| 465 | func: *Inst, | |
| 466 | args: []const *Inst, | |
| 467 | ||
| 468 | pub fn operandCount(self: *const Call) usize { | |
| 469 | return self.args.len + 1; | |
| 470 | } | |
| 471 | pub fn getOperand(self: *const Call, index: usize) ?*Inst { | |
| 472 | var i = index; | |
| 473 | ||
| 474 | if (i < 1) | |
| 475 | return self.func; | |
| 476 | i -= 1; | |
| 477 | ||
| 478 | if (i < self.args.len) | |
| 479 | return self.args[i]; | |
| 480 | i -= self.args.len; | |
| 481 | ||
| 482 | return null; | |
| 483 | } | |
| 484 | }; | |
| 485 | ||
| 486 | pub const CondBr = struct { | |
| 487 | pub const base_tag = Tag.condbr; | |
| 488 | ||
| 489 | base: Inst, | |
| 490 | condition: *Inst, | |
| 491 | then_body: Body, | |
| 492 | else_body: Body, | |
| 493 | /// Set of instructions whose lifetimes end at the start of one of the branches. | |
| 494 | /// The `then` branch is first: `deaths[0..then_death_count]`. | |
| 495 | /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`. | |
| 496 | deaths: [*]*Inst = undefined, | |
| 497 | then_death_count: u32 = 0, | |
| 498 | else_death_count: u32 = 0, | |
| 499 | ||
| 500 | pub fn operandCount(self: *const CondBr) usize { | |
| 501 | return 1; | |
| 502 | } | |
| 503 | pub fn getOperand(self: *const CondBr, index: usize) ?*Inst { | |
| 504 | var i = index; | |
| 505 | ||
| 506 | if (i < 1) | |
| 507 | return self.condition; | |
| 508 | i -= 1; | |
| 509 | ||
| 510 | return null; | |
| 511 | } | |
| 512 | pub fn thenDeaths(self: *const CondBr) []*Inst { | |
| 513 | return self.deaths[0..self.then_death_count]; | |
| 514 | } | |
| 515 | pub fn elseDeaths(self: *const CondBr) []*Inst { | |
| 516 | return (self.deaths + self.then_death_count)[0..self.else_death_count]; | |
| 517 | } | |
| 518 | }; | |
| 519 | ||
| 520 | pub const Constant = struct { | |
| 521 | pub const base_tag = Tag.constant; | |
| 522 | ||
| 523 | base: Inst, | |
| 524 | val: Value, | |
| 525 | ||
| 526 | pub fn operandCount(self: *const Constant) usize { | |
| 527 | return 0; | |
| 528 | } | |
| 529 | pub fn getOperand(self: *const Constant, index: usize) ?*Inst { | |
| 530 | return null; | |
| 531 | } | |
| 532 | }; | |
| 533 | ||
| 534 | pub const Loop = struct { | |
| 535 | pub const base_tag = Tag.loop; | |
| 536 | ||
| 537 | base: Inst, | |
| 538 | body: Body, | |
| 539 | ||
| 540 | pub fn operandCount(self: *const Loop) usize { | |
| 541 | return 0; | |
| 542 | } | |
| 543 | pub fn getOperand(self: *const Loop, index: usize) ?*Inst { | |
| 544 | return null; | |
| 545 | } | |
| 546 | }; | |
| 547 | ||
| 548 | pub const VarPtr = struct { | |
| 549 | pub const base_tag = Tag.varptr; | |
| 550 | ||
| 551 | base: Inst, | |
| 552 | variable: *Module.Var, | |
| 553 | ||
| 554 | pub fn operandCount(self: *const VarPtr) usize { | |
| 555 | return 0; | |
| 556 | } | |
| 557 | pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst { | |
| 558 | return null; | |
| 559 | } | |
| 560 | }; | |
| 561 | ||
| 562 | pub const StructFieldPtr = struct { | |
| 563 | pub const base_tag = Tag.struct_field_ptr; | |
| 564 | ||
| 565 | base: Inst, | |
| 566 | struct_ptr: *Inst, | |
| 567 | field_index: usize, | |
| 568 | ||
| 569 | pub fn operandCount(self: *const StructFieldPtr) usize { | |
| 570 | return 1; | |
| 571 | } | |
| 572 | pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst { | |
| 573 | var i = index; | |
| 574 | ||
| 575 | if (i < 1) | |
| 576 | return self.struct_ptr; | |
| 577 | i -= 1; | |
| 578 | ||
| 579 | return null; | |
| 580 | } | |
| 581 | }; | |
| 582 | ||
| 583 | pub const SwitchBr = struct { | |
| 584 | pub const base_tag = Tag.switchbr; | |
| 585 | ||
| 586 | base: Inst, | |
| 587 | target: *Inst, | |
| 588 | cases: []Case, | |
| 589 | /// Set of instructions whose lifetimes end at the start of one of the cases. | |
| 590 | /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. | |
| 591 | deaths: [*]*Inst = undefined, | |
| 592 | else_index: u32 = 0, | |
| 593 | else_deaths: u32 = 0, | |
| 594 | else_body: Body, | |
| 595 | ||
| 596 | pub const Case = struct { | |
| 597 | item: Value, | |
| 598 | body: Body, | |
| 599 | index: u32 = 0, | |
| 600 | deaths: u32 = 0, | |
| 601 | }; | |
| 602 | ||
| 603 | pub fn operandCount(self: *const SwitchBr) usize { | |
| 604 | return 1; | |
| 605 | } | |
| 606 | pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst { | |
| 607 | var i = index; | |
| 608 | ||
| 609 | if (i < 1) | |
| 610 | return self.target; | |
| 611 | i -= 1; | |
| 612 | ||
| 613 | return null; | |
| 614 | } | |
| 615 | pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst { | |
| 616 | const case = self.cases[case_index]; | |
| 617 | return (self.deaths + case.index)[0..case.deaths]; | |
| 618 | } | |
| 619 | pub fn elseDeaths(self: *const SwitchBr) []*Inst { | |
| 620 | return (self.deaths + self.else_index)[0..self.else_deaths]; | |
| 621 | } | |
| 622 | }; | |
| 623 | ||
| 624 | pub const DbgStmt = struct { | |
| 625 | pub const base_tag = Tag.dbg_stmt; | |
| 626 | ||
| 627 | base: Inst, | |
| 628 | line: u32, | |
| 629 | column: u32, | |
| 630 | ||
| 631 | pub fn operandCount(self: *const DbgStmt) usize { | |
| 632 | return 0; | |
| 633 | } | |
| 634 | pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst { | |
| 635 | return null; | |
| 636 | } | |
| 637 | }; | |
| 638 | }; | |
| 639 | ||
| 640 | pub const Body = struct { | |
| 641 | instructions: []*Inst, | |
| 642 | }; | |
| 643 | ||
| 644 | /// For debugging purposes, prints a function representation to stderr. | |
| 645 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | |
| 646 | const allocator = old_module.gpa; | |
| 647 | var ctx: DumpTzir = .{ | |
| 648 | .allocator = allocator, | |
| 649 | .arena = std.heap.ArenaAllocator.init(allocator), | |
| 650 | .old_module = &old_module, | |
| 651 | .module_fn = module_fn, | |
| 652 | .indent = 2, | |
| 653 | .inst_table = DumpTzir.InstTable.init(allocator), | |
| 654 | .partial_inst_table = DumpTzir.InstTable.init(allocator), | |
| 655 | .const_table = DumpTzir.InstTable.init(allocator), | |
| 656 | }; | |
| 657 | defer ctx.inst_table.deinit(); | |
| 658 | defer ctx.partial_inst_table.deinit(); | |
| 659 | defer ctx.const_table.deinit(); | |
| 660 | defer ctx.arena.deinit(); | |
| 661 | ||
| 662 | switch (module_fn.state) { | |
| 663 | .queued => std.debug.print("(queued)", .{}), | |
| 664 | .inline_only => std.debug.print("(inline_only)", .{}), | |
| 665 | .in_progress => std.debug.print("(in_progress)", .{}), | |
| 666 | .sema_failure => std.debug.print("(sema_failure)", .{}), | |
| 667 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), | |
| 668 | .success => { | |
| 669 | const writer = std.io.getStdErr().writer(); | |
| 670 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); | |
| 671 | }, | |
| 672 | } | |
| 673 | } | |
| 674 | ||
| 675 | const DumpTzir = struct { | |
| 676 | allocator: *std.mem.Allocator, | |
| 677 | arena: std.heap.ArenaAllocator, | |
| 678 | old_module: *const Module, | |
| 679 | module_fn: *Module.Fn, | |
| 680 | indent: usize, | |
| 681 | inst_table: InstTable, | |
| 682 | partial_inst_table: InstTable, | |
| 683 | const_table: InstTable, | |
| 684 | next_index: usize = 0, | |
| 685 | next_partial_index: usize = 0, | |
| 686 | next_const_index: usize = 0, | |
| 687 | ||
| 688 | const InstTable = std.AutoArrayHashMap(*Inst, usize); | |
| 689 | ||
| 690 | /// TODO: Improve this code to include a stack of Body and store the instructions | |
| 691 | /// in there. Now we are putting all the instructions in a function local table, | |
| 692 | /// however instructions that are in a Body can be thown away when the Body ends. | |
| 693 | fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void { | |
| 694 | // First pass to pre-populate the table so that we can show even invalid references. | |
| 695 | // Must iterate the same order we iterate the second time. | |
| 696 | // We also look for constants and put them in the const_table. | |
| 697 | try dtz.fetchInstsAndResolveConsts(body); | |
| 698 | ||
| 699 | std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); | |
| 700 | ||
| 701 | for (dtz.const_table.items()) |entry| { | |
| 702 | const constant = entry.key.castTag(.constant).?; | |
| 703 | try writer.print(" @{d}: {} = {};\n", .{ | |
| 704 | entry.value, constant.base.ty, constant.val, | |
| 705 | }); | |
| 706 | } | |
| 707 | ||
| 708 | return dtz.dumpBody(body, writer); | |
| 709 | } | |
| 710 | ||
| 711 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void { | |
| 712 | for (body.instructions) |inst| { | |
| 713 | try dtz.inst_table.put(inst, dtz.next_index); | |
| 714 | dtz.next_index += 1; | |
| 715 | switch (inst.tag) { | |
| 716 | .alloc, | |
| 717 | .retvoid, | |
| 718 | .unreach, | |
| 719 | .breakpoint, | |
| 720 | .dbg_stmt, | |
| 721 | .arg, | |
| 722 | => {}, | |
| 723 | ||
| 724 | .ref, | |
| 725 | .ret, | |
| 726 | .bitcast, | |
| 727 | .not, | |
| 728 | .is_non_null, | |
| 729 | .is_non_null_ptr, | |
| 730 | .is_null, | |
| 731 | .is_null_ptr, | |
| 732 | .is_err, | |
| 733 | .is_err_ptr, | |
| 734 | .error_to_int, | |
| 735 | .int_to_error, | |
| 736 | .ptrtoint, | |
| 737 | .floatcast, | |
| 738 | .intcast, | |
| 739 | .load, | |
| 740 | .optional_payload, | |
| 741 | .optional_payload_ptr, | |
| 742 | .wrap_optional, | |
| 743 | .wrap_errunion_payload, | |
| 744 | .wrap_errunion_err, | |
| 745 | .unwrap_errunion_payload, | |
| 746 | .unwrap_errunion_err, | |
| 747 | .unwrap_errunion_payload_ptr, | |
| 748 | .unwrap_errunion_err_ptr, | |
| 749 | => { | |
| 750 | const un_op = inst.cast(Inst.UnOp).?; | |
| 751 | try dtz.findConst(un_op.operand); | |
| 752 | }, | |
| 753 | ||
| 754 | .add, | |
| 755 | .addwrap, | |
| 756 | .sub, | |
| 757 | .subwrap, | |
| 758 | .mul, | |
| 759 | .mulwrap, | |
| 760 | .div, | |
| 761 | .cmp_lt, | |
| 762 | .cmp_lte, | |
| 763 | .cmp_eq, | |
| 764 | .cmp_gte, | |
| 765 | .cmp_gt, | |
| 766 | .cmp_neq, | |
| 767 | .store, | |
| 768 | .bool_and, | |
| 769 | .bool_or, | |
| 770 | .bit_and, | |
| 771 | .bit_or, | |
| 772 | .xor, | |
| 773 | => { | |
| 774 | const bin_op = inst.cast(Inst.BinOp).?; | |
| 775 | try dtz.findConst(bin_op.lhs); | |
| 776 | try dtz.findConst(bin_op.rhs); | |
| 777 | }, | |
| 778 | ||
| 779 | .br => { | |
| 780 | const br = inst.castTag(.br).?; | |
| 781 | try dtz.findConst(&br.block.base); | |
| 782 | try dtz.findConst(br.operand); | |
| 783 | }, | |
| 784 | ||
| 785 | .br_block_flat => { | |
| 786 | const br_block_flat = inst.castTag(.br_block_flat).?; | |
| 787 | try dtz.findConst(&br_block_flat.block.base); | |
| 788 | try dtz.fetchInstsAndResolveConsts(br_block_flat.body); | |
| 789 | }, | |
| 790 | ||
| 791 | .br_void => { | |
| 792 | const br_void = inst.castTag(.br_void).?; | |
| 793 | try dtz.findConst(&br_void.block.base); | |
| 794 | }, | |
| 795 | ||
| 796 | .block => { | |
| 797 | const block = inst.castTag(.block).?; | |
| 798 | try dtz.fetchInstsAndResolveConsts(block.body); | |
| 799 | }, | |
| 800 | ||
| 801 | .condbr => { | |
| 802 | const condbr = inst.castTag(.condbr).?; | |
| 803 | try dtz.findConst(condbr.condition); | |
| 804 | try dtz.fetchInstsAndResolveConsts(condbr.then_body); | |
| 805 | try dtz.fetchInstsAndResolveConsts(condbr.else_body); | |
| 806 | }, | |
| 807 | .switchbr => { | |
| 808 | const switchbr = inst.castTag(.switchbr).?; | |
| 809 | try dtz.findConst(switchbr.target); | |
| 810 | try dtz.fetchInstsAndResolveConsts(switchbr.else_body); | |
| 811 | for (switchbr.cases) |case| { | |
| 812 | try dtz.fetchInstsAndResolveConsts(case.body); | |
| 813 | } | |
| 814 | }, | |
| 815 | ||
| 816 | .loop => { | |
| 817 | const loop = inst.castTag(.loop).?; | |
| 818 | try dtz.fetchInstsAndResolveConsts(loop.body); | |
| 819 | }, | |
| 820 | .call => { | |
| 821 | const call = inst.castTag(.call).?; | |
| 822 | try dtz.findConst(call.func); | |
| 823 | for (call.args) |arg| { | |
| 824 | try dtz.findConst(arg); | |
| 825 | } | |
| 826 | }, | |
| 827 | .struct_field_ptr => { | |
| 828 | const struct_field_ptr = inst.castTag(.struct_field_ptr).?; | |
| 829 | try dtz.findConst(struct_field_ptr.struct_ptr); | |
| 830 | }, | |
| 831 | ||
| 832 | // TODO fill out this debug printing | |
| 833 | .assembly, | |
| 834 | .constant, | |
| 835 | .varptr, | |
| 836 | => {}, | |
| 837 | } | |
| 838 | } | |
| 839 | } | |
| 840 | ||
| 841 | fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { | |
| 842 | for (body.instructions) |inst| { | |
| 843 | const my_index = dtz.next_partial_index; | |
| 844 | try dtz.partial_inst_table.put(inst, my_index); | |
| 845 | dtz.next_partial_index += 1; | |
| 846 | ||
| 847 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 848 | try writer.print("%{d}: {} = {s}(", .{ | |
| 849 | my_index, inst.ty, @tagName(inst.tag), | |
| 850 | }); | |
| 851 | switch (inst.tag) { | |
| 852 | .alloc, | |
| 853 | .retvoid, | |
| 854 | .unreach, | |
| 855 | .breakpoint, | |
| 856 | .dbg_stmt, | |
| 857 | => try writer.writeAll(")\n"), | |
| 858 | ||
| 859 | .ref, | |
| 860 | .ret, | |
| 861 | .bitcast, | |
| 862 | .not, | |
| 863 | .is_non_null, | |
| 864 | .is_null, | |
| 865 | .is_non_null_ptr, | |
| 866 | .is_null_ptr, | |
| 867 | .is_err, | |
| 868 | .is_err_ptr, | |
| 869 | .error_to_int, | |
| 870 | .int_to_error, | |
| 871 | .ptrtoint, | |
| 872 | .floatcast, | |
| 873 | .intcast, | |
| 874 | .load, | |
| 875 | .optional_payload, | |
| 876 | .optional_payload_ptr, | |
| 877 | .wrap_optional, | |
| 878 | .wrap_errunion_err, | |
| 879 | .wrap_errunion_payload, | |
| 880 | .unwrap_errunion_err, | |
| 881 | .unwrap_errunion_payload, | |
| 882 | .unwrap_errunion_payload_ptr, | |
| 883 | .unwrap_errunion_err_ptr, | |
| 884 | => { | |
| 885 | const un_op = inst.cast(Inst.UnOp).?; | |
| 886 | const kinky = try dtz.writeInst(writer, un_op.operand); | |
| 887 | if (kinky != null) { | |
| 888 | try writer.writeAll(") // Instruction does not dominate all uses!\n"); | |
| 889 | } else { | |
| 890 | try writer.writeAll(")\n"); | |
| 891 | } | |
| 892 | }, | |
| 893 | ||
| 894 | .add, | |
| 895 | .addwrap, | |
| 896 | .sub, | |
| 897 | .subwrap, | |
| 898 | .mul, | |
| 899 | .mulwrap, | |
| 900 | .div, | |
| 901 | .cmp_lt, | |
| 902 | .cmp_lte, | |
| 903 | .cmp_eq, | |
| 904 | .cmp_gte, | |
| 905 | .cmp_gt, | |
| 906 | .cmp_neq, | |
| 907 | .store, | |
| 908 | .bool_and, | |
| 909 | .bool_or, | |
| 910 | .bit_and, | |
| 911 | .bit_or, | |
| 912 | .xor, | |
| 913 | => { | |
| 914 | const bin_op = inst.cast(Inst.BinOp).?; | |
| 915 | ||
| 916 | const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs); | |
| 917 | try writer.writeAll(", "); | |
| 918 | const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs); | |
| 919 | ||
| 920 | if (lhs_kinky != null or rhs_kinky != null) { | |
| 921 | try writer.writeAll(") // Instruction does not dominate all uses!"); | |
| 922 | if (lhs_kinky) |lhs| { | |
| 923 | try writer.print(" %{d}", .{lhs}); | |
| 924 | } | |
| 925 | if (rhs_kinky) |rhs| { | |
| 926 | try writer.print(" %{d}", .{rhs}); | |
| 927 | } | |
| 928 | try writer.writeAll("\n"); | |
| 929 | } else { | |
| 930 | try writer.writeAll(")\n"); | |
| 931 | } | |
| 932 | }, | |
| 933 | ||
| 934 | .arg => { | |
| 935 | const arg = inst.castTag(.arg).?; | |
| 936 | try writer.print("{s})\n", .{arg.name}); | |
| 937 | }, | |
| 938 | ||
| 939 | .br => { | |
| 940 | const br = inst.castTag(.br).?; | |
| 941 | ||
| 942 | const lhs_kinky = try dtz.writeInst(writer, &br.block.base); | |
| 943 | try writer.writeAll(", "); | |
| 944 | const rhs_kinky = try dtz.writeInst(writer, br.operand); | |
| 945 | ||
| 946 | if (lhs_kinky != null or rhs_kinky != null) { | |
| 947 | try writer.writeAll(") // Instruction does not dominate all uses!"); | |
| 948 | if (lhs_kinky) |lhs| { | |
| 949 | try writer.print(" %{d}", .{lhs}); | |
| 950 | } | |
| 951 | if (rhs_kinky) |rhs| { | |
| 952 | try writer.print(" %{d}", .{rhs}); | |
| 953 | } | |
| 954 | try writer.writeAll("\n"); | |
| 955 | } else { | |
| 956 | try writer.writeAll(")\n"); | |
| 957 | } | |
| 958 | }, | |
| 959 | ||
| 960 | .br_block_flat => { | |
| 961 | const br_block_flat = inst.castTag(.br_block_flat).?; | |
| 962 | const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base); | |
| 963 | if (block_kinky != null) { | |
| 964 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | |
| 965 | } else { | |
| 966 | try writer.writeAll(", {\n"); | |
| 967 | } | |
| 968 | ||
| 969 | const old_indent = dtz.indent; | |
| 970 | dtz.indent += 2; | |
| 971 | try dtz.dumpBody(br_block_flat.body, writer); | |
| 972 | dtz.indent = old_indent; | |
| 973 | ||
| 974 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 975 | try writer.writeAll("})\n"); | |
| 976 | }, | |
| 977 | ||
| 978 | .br_void => { | |
| 979 | const br_void = inst.castTag(.br_void).?; | |
| 980 | const kinky = try dtz.writeInst(writer, &br_void.block.base); | |
| 981 | if (kinky) |_| { | |
| 982 | try writer.writeAll(") // Instruction does not dominate all uses!\n"); | |
| 983 | } else { | |
| 984 | try writer.writeAll(")\n"); | |
| 985 | } | |
| 986 | }, | |
| 987 | ||
| 988 | .block => { | |
| 989 | const block = inst.castTag(.block).?; | |
| 990 | ||
| 991 | try writer.writeAll("{\n"); | |
| 992 | ||
| 993 | const old_indent = dtz.indent; | |
| 994 | dtz.indent += 2; | |
| 995 | try dtz.dumpBody(block.body, writer); | |
| 996 | dtz.indent = old_indent; | |
| 997 | ||
| 998 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 999 | try writer.writeAll("})\n"); | |
| 1000 | }, | |
| 1001 | ||
| 1002 | .condbr => { | |
| 1003 | const condbr = inst.castTag(.condbr).?; | |
| 1004 | ||
| 1005 | const condition_kinky = try dtz.writeInst(writer, condbr.condition); | |
| 1006 | if (condition_kinky != null) { | |
| 1007 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | |
| 1008 | } else { | |
| 1009 | try writer.writeAll(", {\n"); | |
| 1010 | } | |
| 1011 | ||
| 1012 | const old_indent = dtz.indent; | |
| 1013 | dtz.indent += 2; | |
| 1014 | try dtz.dumpBody(condbr.then_body, writer); | |
| 1015 | ||
| 1016 | try writer.writeByteNTimes(' ', old_indent); | |
| 1017 | try writer.writeAll("}, {\n"); | |
| 1018 | ||
| 1019 | try dtz.dumpBody(condbr.else_body, writer); | |
| 1020 | dtz.indent = old_indent; | |
| 1021 | ||
| 1022 | try writer.writeByteNTimes(' ', old_indent); | |
| 1023 | try writer.writeAll("})\n"); | |
| 1024 | }, | |
| 1025 | ||
| 1026 | .switchbr => { | |
| 1027 | const switchbr = inst.castTag(.switchbr).?; | |
| 1028 | ||
| 1029 | const condition_kinky = try dtz.writeInst(writer, switchbr.target); | |
| 1030 | if (condition_kinky != null) { | |
| 1031 | try writer.writeAll(", { // Instruction does not dominate all uses!\n"); | |
| 1032 | } else { | |
| 1033 | try writer.writeAll(", {\n"); | |
| 1034 | } | |
| 1035 | const old_indent = dtz.indent; | |
| 1036 | ||
| 1037 | if (switchbr.else_body.instructions.len != 0) { | |
| 1038 | dtz.indent += 2; | |
| 1039 | try dtz.dumpBody(switchbr.else_body, writer); | |
| 1040 | ||
| 1041 | try writer.writeByteNTimes(' ', old_indent); | |
| 1042 | try writer.writeAll("}, {\n"); | |
| 1043 | dtz.indent = old_indent; | |
| 1044 | } | |
| 1045 | for (switchbr.cases) |case| { | |
| 1046 | dtz.indent += 2; | |
| 1047 | try dtz.dumpBody(case.body, writer); | |
| 1048 | ||
| 1049 | try writer.writeByteNTimes(' ', old_indent); | |
| 1050 | try writer.writeAll("}, {\n"); | |
| 1051 | dtz.indent = old_indent; | |
| 1052 | } | |
| 1053 | ||
| 1054 | try writer.writeByteNTimes(' ', old_indent); | |
| 1055 | try writer.writeAll("})\n"); | |
| 1056 | }, | |
| 1057 | ||
| 1058 | .loop => { | |
| 1059 | const loop = inst.castTag(.loop).?; | |
| 1060 | ||
| 1061 | try writer.writeAll("{\n"); | |
| 1062 | ||
| 1063 | const old_indent = dtz.indent; | |
| 1064 | dtz.indent += 2; | |
| 1065 | try dtz.dumpBody(loop.body, writer); | |
| 1066 | dtz.indent = old_indent; | |
| 1067 | ||
| 1068 | try writer.writeByteNTimes(' ', dtz.indent); | |
| 1069 | try writer.writeAll("})\n"); | |
| 1070 | }, | |
| 1071 | ||
| 1072 | .call => { | |
| 1073 | const call = inst.castTag(.call).?; | |
| 1074 | ||
| 1075 | const args_kinky = try dtz.allocator.alloc(?usize, call.args.len); | |
| 1076 | defer dtz.allocator.free(args_kinky); | |
| 1077 | std.mem.set(?usize, args_kinky, null); | |
| 1078 | var any_kinky_args = false; | |
| 1079 | ||
| 1080 | const func_kinky = try dtz.writeInst(writer, call.func); | |
| 1081 | ||
| 1082 | for (call.args) |arg, i| { | |
| 1083 | try writer.writeAll(", "); | |
| 1084 | ||
| 1085 | args_kinky[i] = try dtz.writeInst(writer, arg); | |
| 1086 | any_kinky_args = any_kinky_args or args_kinky[i] != null; | |
| 1087 | } | |
| 1088 | ||
| 1089 | if (func_kinky != null or any_kinky_args) { | |
| 1090 | try writer.writeAll(") // Instruction does not dominate all uses!"); | |
| 1091 | if (func_kinky) |func_index| { | |
| 1092 | try writer.print(" %{d}", .{func_index}); | |
| 1093 | } | |
| 1094 | for (args_kinky) |arg_kinky| { | |
| 1095 | if (arg_kinky) |arg_index| { | |
| 1096 | try writer.print(" %{d}", .{arg_index}); | |
| 1097 | } | |
| 1098 | } | |
| 1099 | try writer.writeAll("\n"); | |
| 1100 | } else { | |
| 1101 | try writer.writeAll(")\n"); | |
| 1102 | } | |
| 1103 | }, | |
| 1104 | ||
| 1105 | .struct_field_ptr => { | |
| 1106 | const struct_field_ptr = inst.castTag(.struct_field_ptr).?; | |
| 1107 | const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr); | |
| 1108 | if (kinky != null) { | |
| 1109 | try writer.print("{d}) // Instruction does not dominate all uses!\n", .{ | |
| 1110 | struct_field_ptr.field_index, | |
| 1111 | }); | |
| 1112 | } else { | |
| 1113 | try writer.print("{d})\n", .{struct_field_ptr.field_index}); | |
| 1114 | } | |
| 1115 | }, | |
| 1116 | ||
| 1117 | // TODO fill out this debug printing | |
| 1118 | .assembly, | |
| 1119 | .constant, | |
| 1120 | .varptr, | |
| 1121 | => { | |
| 1122 | try writer.writeAll("!TODO!)\n"); | |
| 1123 | }, | |
| 1124 | } | |
| 1125 | } | |
| 1126 | } | |
| 1127 | ||
| 1128 | fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize { | |
| 1129 | if (dtz.partial_inst_table.get(inst)) |operand_index| { | |
| 1130 | try writer.print("%{d}", .{operand_index}); | |
| 1131 | return null; | |
| 1132 | } else if (dtz.const_table.get(inst)) |operand_index| { | |
| 1133 | try writer.print("@{d}", .{operand_index}); | |
| 1134 | return null; | |
| 1135 | } else if (dtz.inst_table.get(inst)) |operand_index| { | |
| 1136 | try writer.print("%{d}", .{operand_index}); | |
| 1137 | return operand_index; | |
| 1138 | } else { | |
| 1139 | try writer.writeAll("!BADREF!"); | |
| 1140 | return null; | |
| 1141 | } | |
| 1142 | } | |
| 1143 | ||
| 1144 | fn findConst(dtz: *DumpTzir, operand: *Inst) !void { | |
| 1145 | if (operand.tag == .constant) { | |
| 1146 | try dtz.const_table.put(operand, dtz.next_const_index); | |
| 1147 | dtz.next_const_index += 1; | |
| 1148 | } | |
| 1149 | } | |
| 1150 | }; |
src/link/Elf.zig+1-1| ... | ... | @@ -10,7 +10,7 @@ const log = std.log.scoped(.link); |
| 10 | 10 | const DW = std.dwarf; |
| 11 | 11 | const leb128 = std.leb; |
| 12 | 12 | |
| 13 | const ir = @import("../ir.zig"); | |
| 13 | const ir = @import("../air.zig"); | |
| 14 | 14 | const Module = @import("../Module.zig"); |
| 15 | 15 | const Compilation = @import("../Compilation.zig"); |
| 16 | 16 | const codegen = @import("../codegen.zig"); |
src/liveness.zig+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const ir = @import("ir.zig"); | |
| 2 | const ir = @import("air.zig"); | |
| 3 | 3 | const trace = @import("tracy.zig").trace; |
| 4 | 4 | const log = std.log.scoped(.liveness); |
| 5 | 5 |
src/register_manager.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ const math = std.math; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const Allocator = std.mem.Allocator; |
| 6 | const ir = @import("ir.zig"); | |
| 6 | const ir = @import("air.zig"); | |
| 7 | 7 | const Type = @import("type.zig").Type; |
| 8 | 8 | const Module = @import("Module.zig"); |
| 9 | 9 | const LazySrcLoc = Module.LazySrcLoc; |
src/value.zig+1-1| ... | ... | @@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable; |
| 7 | 7 | const Target = std.Target; |
| 8 | 8 | const Allocator = std.mem.Allocator; |
| 9 | 9 | const Module = @import("Module.zig"); |
| 10 | const ir = @import("ir.zig"); | |
| 10 | const ir = @import("air.zig"); | |
| 11 | 11 | |
| 12 | 12 | /// This is the raw data, with no bookkeeping, no memory awareness, |
| 13 | 13 | /// no de-duplication, and no type system awareness. |