| 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,7 +554,7 @@ set(ZIG_STAGE2_SOURCES |
| 554 | "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" | 554 | "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" |
| 555 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" | 555 | "${CMAKE_SOURCE_DIR}/src/glibc.zig" |
| 556 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" | 556 | "${CMAKE_SOURCE_DIR}/src/introspect.zig" |
| 557 | "${CMAKE_SOURCE_DIR}/src/ir.zig" | 557 | "${CMAKE_SOURCE_DIR}/src/air.zig" |
| 558 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" | 558 | "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" |
| 559 | "${CMAKE_SOURCE_DIR}/src/libcxx.zig" | 559 | "${CMAKE_SOURCE_DIR}/src/libcxx.zig" |
| 560 | "${CMAKE_SOURCE_DIR}/src/libtsan.zig" | 560 | "${CMAKE_SOURCE_DIR}/src/libtsan.zig" |
src/Module.zig+1-1| ... | @@ -21,7 +21,7 @@ const Type = @import("type.zig").Type; | ... | @@ -21,7 +21,7 @@ const Type = @import("type.zig").Type; |
| 21 | const TypedValue = @import("TypedValue.zig"); | 21 | const TypedValue = @import("TypedValue.zig"); |
| 22 | const Package = @import("Package.zig"); | 22 | const Package = @import("Package.zig"); |
| 23 | const link = @import("link.zig"); | 23 | const link = @import("link.zig"); |
| 24 | const ir = @import("ir.zig"); | 24 | const ir = @import("air.zig"); |
| 25 | const Zir = @import("Zir.zig"); | 25 | const Zir = @import("Zir.zig"); |
| 26 | const trace = @import("tracy.zig").trace; | 26 | const trace = @import("tracy.zig").trace; |
| 27 | const AstGen = @import("AstGen.zig"); | 27 | const AstGen = @import("AstGen.zig"); |
src/Sema.zig+1-1| ... | @@ -52,7 +52,7 @@ const Sema = @This(); | ... | @@ -52,7 +52,7 @@ const Sema = @This(); |
| 52 | const Value = @import("value.zig").Value; | 52 | const Value = @import("value.zig").Value; |
| 53 | const Type = @import("type.zig").Type; | 53 | const Type = @import("type.zig").Type; |
| 54 | const TypedValue = @import("TypedValue.zig"); | 54 | const TypedValue = @import("TypedValue.zig"); |
| 55 | const ir = @import("ir.zig"); | 55 | const ir = @import("air.zig"); |
| 56 | const Zir = @import("Zir.zig"); | 56 | const Zir = @import("Zir.zig"); |
| 57 | const Module = @import("Module.zig"); | 57 | const Module = @import("Module.zig"); |
| 58 | const Inst = ir.Inst; | 58 | const Inst = ir.Inst; |
src/Zir.zig+1-1| ... | @@ -22,7 +22,7 @@ const Zir = @This(); | ... | @@ -22,7 +22,7 @@ const Zir = @This(); |
| 22 | const Type = @import("type.zig").Type; | 22 | const Type = @import("type.zig").Type; |
| 23 | const Value = @import("value.zig").Value; | 23 | const Value = @import("value.zig").Value; |
| 24 | const TypedValue = @import("TypedValue.zig"); | 24 | const TypedValue = @import("TypedValue.zig"); |
| 25 | const ir = @import("ir.zig"); | 25 | const ir = @import("air.zig"); |
| 26 | const Module = @import("Module.zig"); | 26 | const Module = @import("Module.zig"); |
| 27 | const LazySrcLoc = Module.LazySrcLoc; | 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,7 +2,7 @@ const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const ir = @import("ir.zig"); | 5 | const ir = @import("air.zig"); |
| 6 | const Type = @import("type.zig").Type; | 6 | const Type = @import("type.zig").Type; |
| 7 | const Value = @import("value.zig").Value; | 7 | const Value = @import("value.zig").Value; |
| 8 | const TypedValue = @import("TypedValue.zig"); | 8 | const TypedValue = @import("TypedValue.zig"); |
src/codegen/c.zig+1-1| ... | @@ -6,7 +6,7 @@ const log = std.log.scoped(.c); | ... | @@ -6,7 +6,7 @@ const log = std.log.scoped(.c); |
| 6 | const link = @import("../link.zig"); | 6 | const link = @import("../link.zig"); |
| 7 | const Module = @import("../Module.zig"); | 7 | const Module = @import("../Module.zig"); |
| 8 | const Compilation = @import("../Compilation.zig"); | 8 | const Compilation = @import("../Compilation.zig"); |
| 9 | const ir = @import("../ir.zig"); | 9 | const ir = @import("../air.zig"); |
| 10 | const Inst = ir.Inst; | 10 | const Inst = ir.Inst; |
| 11 | const Value = @import("../value.zig").Value; | 11 | const Value = @import("../value.zig").Value; |
| 12 | const Type = @import("../type.zig").Type; | 12 | const Type = @import("../type.zig").Type; |
src/codegen/llvm.zig+1-1| ... | @@ -9,7 +9,7 @@ const math = std.math; | ... | @@ -9,7 +9,7 @@ const math = std.math; |
| 9 | 9 | ||
| 10 | const Module = @import("../Module.zig"); | 10 | const Module = @import("../Module.zig"); |
| 11 | const TypedValue = @import("../TypedValue.zig"); | 11 | const TypedValue = @import("../TypedValue.zig"); |
| 12 | const ir = @import("../ir.zig"); | 12 | const ir = @import("../air.zig"); |
| 13 | const Inst = ir.Inst; | 13 | const Inst = ir.Inst; |
| 14 | 14 | ||
| 15 | const Value = @import("../value.zig").Value; | 15 | const Value = @import("../value.zig").Value; |
src/codegen/spirv.zig+1-1| ... | @@ -11,7 +11,7 @@ const Decl = Module.Decl; | ... | @@ -11,7 +11,7 @@ const Decl = Module.Decl; |
| 11 | const Type = @import("../type.zig").Type; | 11 | const Type = @import("../type.zig").Type; |
| 12 | const Value = @import("../value.zig").Value; | 12 | const Value = @import("../value.zig").Value; |
| 13 | const LazySrcLoc = Module.LazySrcLoc; | 13 | const LazySrcLoc = Module.LazySrcLoc; |
| 14 | const ir = @import("../ir.zig"); | 14 | const ir = @import("../air.zig"); |
| 15 | const Inst = ir.Inst; | 15 | const Inst = ir.Inst; |
| 16 | 16 | ||
| 17 | pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); | 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,7 +9,7 @@ const wasm = std.wasm; |
| 9 | 9 | ||
| 10 | const Module = @import("../Module.zig"); | 10 | const Module = @import("../Module.zig"); |
| 11 | const Decl = Module.Decl; | 11 | const Decl = Module.Decl; |
| 12 | const ir = @import("../ir.zig"); | 12 | const ir = @import("../air.zig"); |
| 13 | const Inst = ir.Inst; | 13 | const Inst = ir.Inst; |
| 14 | const Type = @import("../type.zig").Type; | 14 | const Type = @import("../type.zig").Type; |
| 15 | const Value = @import("../value.zig").Value; | 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,7 +10,7 @@ const log = std.log.scoped(.link); |
| 10 | const DW = std.dwarf; | 10 | const DW = std.dwarf; |
| 11 | const leb128 = std.leb; | 11 | const leb128 = std.leb; |
| 12 | 12 | ||
| 13 | const ir = @import("../ir.zig"); | 13 | const ir = @import("../air.zig"); |
| 14 | const Module = @import("../Module.zig"); | 14 | const Module = @import("../Module.zig"); |
| 15 | const Compilation = @import("../Compilation.zig"); | 15 | const Compilation = @import("../Compilation.zig"); |
| 16 | const codegen = @import("../codegen.zig"); | 16 | const codegen = @import("../codegen.zig"); |
src/liveness.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const ir = @import("ir.zig"); | 2 | const ir = @import("air.zig"); |
| 3 | const trace = @import("tracy.zig").trace; | 3 | const trace = @import("tracy.zig").trace; |
| 4 | const log = std.log.scoped(.liveness); | 4 | const log = std.log.scoped(.liveness); |
| 5 | 5 |
src/register_manager.zig+1-1| ... | @@ -3,7 +3,7 @@ const math = std.math; | ... | @@ -3,7 +3,7 @@ const math = std.math; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const Allocator = std.mem.Allocator; | 5 | const Allocator = std.mem.Allocator; |
| 6 | const ir = @import("ir.zig"); | 6 | const ir = @import("air.zig"); |
| 7 | const Type = @import("type.zig").Type; | 7 | const Type = @import("type.zig").Type; |
| 8 | const Module = @import("Module.zig"); | 8 | const Module = @import("Module.zig"); |
| 9 | const LazySrcLoc = Module.LazySrcLoc; | 9 | const LazySrcLoc = Module.LazySrcLoc; |
src/value.zig+1-1| ... | @@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable; | ... | @@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable; |
| 7 | const Target = std.Target; | 7 | const Target = std.Target; |
| 8 | const Allocator = std.mem.Allocator; | 8 | const Allocator = std.mem.Allocator; |
| 9 | const Module = @import("Module.zig"); | 9 | const Module = @import("Module.zig"); |
| 10 | const ir = @import("ir.zig"); | 10 | const ir = @import("air.zig"); |
| 11 | 11 | ||
| 12 | /// This is the raw data, with no bookkeeping, no memory awareness, | 12 | /// This is the raw data, with no bookkeeping, no memory awareness, |
| 13 | /// no de-duplication, and no type system awareness. | 13 | /// no de-duplication, and no type system awareness. |