From 5d6f7b44c19b064a543b0c1eecb6ef5c671b612e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Jul 2021 20:42:47 -0700 Subject: [PATCH] stage2: rework AIR memory layout This commit changes the AIR file and the documentation of the memory layout. The actual work of modifying the surrounding code (in Sema and codegen) is not yet done. --- src/air.zig => BRANCH_TODO | 544 +++---------------------------------- CMakeLists.txt | 2 +- src/Air.zig | 335 +++++++++++++++++++++++ src/Module.zig | 2 +- src/Sema.zig | 2 +- src/Zir.zig | 3 +- src/codegen.zig | 2 +- src/codegen/c.zig | 3 +- src/codegen/llvm.zig | 2 +- src/codegen/spirv.zig | 3 +- src/codegen/wasm.zig | 3 +- src/link/Elf.zig | 2 +- src/liveness.zig | 2 +- src/register_manager.zig | 2 +- src/value.zig | 2 +- 15 files changed, 390 insertions(+), 519 deletions(-) rename src/air.zig => BRANCH_TODO (68%) create mode 100644 src/Air.zig diff --git a/src/air.zig b/BRANCH_TODO similarity index 68% rename from src/air.zig rename to BRANCH_TODO index e73367945b04c3a3d4b5f7ea5260ff16b2b5264b..ed77d6bd03dc1a2e56d324e5d825a7c1640bba25 100644 --- a/src/air.zig +++ b/BRANCH_TODO @@ -1,18 +1,6 @@ -const std = @import("std"); -const Value = @import("value.zig").Value; -const Type = @import("type.zig").Type; -const Module = @import("Module.zig"); -const assert = std.debug.assert; -const codegen = @import("codegen.zig"); -const ast = std.zig.ast; + * be sure to test debug info of parameters + -/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation -/// of instructions that correspond to the ZIR text format. -/// This struct owns the `Value` and `Type` memory. When the struct is deallocated, -/// so are the `Value` and `Type`. The value of a constant must be copied into -/// a memory location for the value to survive after a const instruction. -pub const Inst = struct { - tag: Tag, /// Each bit represents the index of an `Inst` parameter in the `args` field. /// If a bit is set, it marks the end of the lifetime of the corresponding /// instruction parameter. For example, 0b101 means that the first and @@ -24,8 +12,7 @@ pub const Inst = struct { /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the /// lifetimes of operands are encoded elsewhere. deaths: DeathsInt = undefined, - ty: Type, - src: Module.LazySrcLoc, + pub const DeathsInt = u16; pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); @@ -50,96 +37,25 @@ pub const Inst = struct { return (self.deaths & (1 << deaths_bits)) != 0; } - pub const Tag = enum { - add, - addwrap, - alloc, - arg, - assembly, - bit_and, - bitcast, - bit_or, - block, - br, - /// Same as `br` except the operand is a list of instructions to be treated as - /// a flat block; that is there is only 1 break instruction from the block, and - /// it is implied to be after the last instruction, and the last instruction is - /// the break operand. - /// This instruction exists for late-stage semantic analysis patch ups, to - /// replace one br operand with multiple instructions, without moving anything else around. - br_block_flat, - breakpoint, - br_void, - call, - cmp_lt, - cmp_lte, - cmp_eq, - cmp_gte, - cmp_gt, - cmp_neq, - condbr, - constant, - dbg_stmt, - /// ?T => bool - is_null, - /// ?T => bool (inverted logic) - is_non_null, - /// *?T => bool - is_null_ptr, - /// *?T => bool (inverted logic) - is_non_null_ptr, - /// E!T => bool - is_err, - /// E!T => bool (inverted logic) - is_non_err, - /// *E!T => bool - is_err_ptr, - /// *E!T => bool (inverted logic) - is_non_err_ptr, - bool_and, - bool_or, - /// Read a value from a pointer. - load, - /// A labeled block of code that loops forever. At the end of the body it is implied - /// to repeat; no explicit "repeat" instruction terminates loop bodies. - loop, - ptrtoint, - ref, - ret, - retvoid, - varptr, - /// Write a value to a pointer. LHS is pointer, RHS is value. - store, - sub, - subwrap, - unreach, - mul, - mulwrap, - div, - not, - floatcast, - intcast, - /// ?T => T - optional_payload, - /// *?T => *T - optional_payload_ptr, - wrap_optional, - /// E!T -> T - unwrap_errunion_payload, - /// E!T -> E - unwrap_errunion_err, - /// *(E!T) -> *T - unwrap_errunion_payload_ptr, - /// *(E!T) -> E - unwrap_errunion_err_ptr, - /// wrap from T to E!T - wrap_errunion_payload, - /// wrap from E to E!T - wrap_errunion_err, - xor, - switchbr, - /// Given a pointer to a struct and a field index, returns a pointer to the field. - struct_field_ptr, + pub fn operandCount(base: *Inst) usize { + inline for (@typeInfo(Tag).Enum.fields) |field| { + const tag = @intToEnum(Tag, field.value); + if (tag == base.tag) { + return @fieldParentPtr(tag.Type(), "base", base).operandCount(); + } + } + unreachable; + } + + pub fn getOperand(base: *Inst, index: usize) ?*Inst { + inline for (@typeInfo(Tag).Enum.fields) |field| { + const tag = @intToEnum(Tag, field.value); + if (tag == base.tag) { + return @fieldParentPtr(tag.Type(), "base", base).getOperand(index); + } + } + unreachable; + } pub fn Type(tag: Tag) type { return switch (tag) { @@ -214,42 +130,6 @@ pub const Inst = struct { }; } - pub fn fromCmpOp(op: std.math.CompareOperator) Tag { - return switch (op) { - .lt => .cmp_lt, - .lte => .cmp_lte, - .eq => .cmp_eq, - .gte => .cmp_gte, - .gt => .cmp_gt, - .neq => .cmp_neq, - }; - } - }; - - /// Prefer `castTag` to this. - pub fn cast(base: *Inst, comptime T: type) ?*T { - if (@hasField(T, "base_tag")) { - return base.castTag(T.base_tag); - } - inline for (@typeInfo(Tag).Enum.fields) |field| { - const tag = @intToEnum(Tag, field.value); - if (base.tag == tag) { - if (T == tag.Type()) { - return @fieldParentPtr(T, "base", base); - } - return null; - } - } - unreachable; - } - - pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() { - if (base.tag == tag) { - return @fieldParentPtr(tag.Type(), "base", base); - } - return null; - } - pub fn Args(comptime T: type) type { return std.meta.fieldInfo(T, .args).field_type; } @@ -265,38 +145,6 @@ pub const Inst = struct { return inst.val; } - pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator { - return switch (base.tag) { - .cmp_lt => .lt, - .cmp_lte => .lte, - .cmp_eq => .eq, - .cmp_gte => .gte, - .cmp_gt => .gt, - .cmp_neq => .neq, - else => null, - }; - } - - pub fn operandCount(base: *Inst) usize { - inline for (@typeInfo(Tag).Enum.fields) |field| { - const tag = @intToEnum(Tag, field.value); - if (tag == base.tag) { - return @fieldParentPtr(tag.Type(), "base", base).operandCount(); - } - } - unreachable; - } - - pub fn getOperand(base: *Inst, index: usize) ?*Inst { - inline for (@typeInfo(Tag).Enum.fields) |field| { - const tag = @intToEnum(Tag, field.value); - if (tag == base.tag) { - return @fieldParentPtr(tag.Type(), "base", base).getOperand(index); - } - } - unreachable; - } - pub fn breakBlock(base: *Inst) ?*Block { return switch (base.tag) { .br => base.castTag(.br).?.block, @@ -306,76 +154,39 @@ pub const Inst = struct { }; } - pub const NoOp = struct { - base: Inst, + pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br)); + pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br)); + comptime { + assert(@offsetOf(BrBlockFlat, "base") == @offsetOf(Br, "base")); + } - pub fn operandCount(self: *const NoOp) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const NoOp, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; + pub const BrBlockFlat = struct { + pub const base_tag = Tag.br_block_flat; - pub const UnOp = struct { base: Inst, - operand: *Inst, + block: *Block, + body: Body, - pub fn operandCount(self: *const UnOp) usize { + pub fn operandCount(self: *const BrBlockFlat) usize { _ = self; - return 1; - } - pub fn getOperand(self: *const UnOp, index: usize) ?*Inst { - if (index == 0) - return self.operand; - return null; + return 0; } - }; - - pub const BinOp = struct { - base: Inst, - lhs: *Inst, - rhs: *Inst, - - pub fn operandCount(self: *const BinOp) usize { + pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst { _ = self; - return 2; - } - pub fn getOperand(self: *const BinOp, index: usize) ?*Inst { - var i = index; - - if (i < 1) - return self.lhs; - i -= 1; - - if (i < 1) - return self.rhs; - i -= 1; - + _ = index; return null; } }; - pub const Arg = struct { - pub const base_tag = Tag.arg; + /// Same as `br` except the operand is a list of instructions to be treated as + /// a flat block; that is there is only 1 break instruction from the block, and + /// it is implied to be after the last instruction, and the last instruction is + /// the break operand. + /// This instruction exists for late-stage semantic analysis patch ups, to + /// replace one br operand with multiple instructions, without moving anything else around. + br_block_flat, - base: Inst, - /// This exists to be emitted into debug info. - name: [*:0]const u8, - pub fn operandCount(self: *const Arg) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const Arg, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; pub const Assembly = struct { pub const base_tag = Tag.assembly; @@ -398,282 +209,11 @@ pub const Inst = struct { } }; - pub const Block = struct { - pub const base_tag = Tag.block; - - base: Inst, - body: Body, - - pub fn operandCount(self: *const Block) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const Block, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; - - pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br)); - pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br)); - comptime { - assert(@offsetOf(BrBlockFlat, "base") == @offsetOf(Br, "base")); - } - - pub const BrBlockFlat = struct { - pub const base_tag = Tag.br_block_flat; - - base: Inst, - block: *Block, - body: Body, - - pub fn operandCount(self: *const BrBlockFlat) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; - - pub const Br = struct { - pub const base_tag = Tag.br; - - base: Inst, - block: *Block, - operand: *Inst, - - pub fn operandCount(self: *const Br) usize { - _ = self; - return 1; - } - pub fn getOperand(self: *const Br, index: usize) ?*Inst { - _ = self; - if (index == 0) - return self.operand; - return null; - } - }; - - pub const BrVoid = struct { - pub const base_tag = Tag.br_void; - - base: Inst, - block: *Block, - - pub fn operandCount(self: *const BrVoid) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; - - pub const Call = struct { - pub const base_tag = Tag.call; - - base: Inst, - func: *Inst, - args: []const *Inst, - - pub fn operandCount(self: *const Call) usize { - return self.args.len + 1; - } - pub fn getOperand(self: *const Call, index: usize) ?*Inst { - var i = index; - - if (i < 1) - return self.func; - i -= 1; - - if (i < self.args.len) - return self.args[i]; - i -= self.args.len; - - return null; - } - }; - - pub const CondBr = struct { - pub const base_tag = Tag.condbr; - - base: Inst, - condition: *Inst, - then_body: Body, - else_body: Body, - /// Set of instructions whose lifetimes end at the start of one of the branches. - /// The `then` branch is first: `deaths[0..then_death_count]`. - /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`. - deaths: [*]*Inst = undefined, - then_death_count: u32 = 0, - else_death_count: u32 = 0, - - pub fn operandCount(self: *const CondBr) usize { - _ = self; - return 1; - } - pub fn getOperand(self: *const CondBr, index: usize) ?*Inst { - var i = index; - - if (i < 1) - return self.condition; - i -= 1; - - return null; - } - pub fn thenDeaths(self: *const CondBr) []*Inst { - return self.deaths[0..self.then_death_count]; - } - pub fn elseDeaths(self: *const CondBr) []*Inst { - return (self.deaths + self.then_death_count)[0..self.else_death_count]; - } - }; - - pub const Constant = struct { - pub const base_tag = Tag.constant; - - base: Inst, - val: Value, - - pub fn operandCount(self: *const Constant) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const Constant, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; - - pub const Loop = struct { - pub const base_tag = Tag.loop; - - base: Inst, - body: Body, - - pub fn operandCount(self: *const Loop) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const Loop, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; - - pub const VarPtr = struct { - pub const base_tag = Tag.varptr; - - base: Inst, - variable: *Module.Var, - - pub fn operandCount(self: *const VarPtr) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } - }; - pub const StructFieldPtr = struct { - pub const base_tag = Tag.struct_field_ptr; - - base: Inst, struct_ptr: *Inst, field_index: usize, - - pub fn operandCount(self: *const StructFieldPtr) usize { - _ = self; - return 1; - } - pub fn getOperand(self: *const StructFieldPtr, index: usize) ?*Inst { - _ = self; - _ = index; - var i = index; - - if (i < 1) - return self.struct_ptr; - i -= 1; - - return null; - } - }; - - pub const SwitchBr = struct { - pub const base_tag = Tag.switchbr; - - base: Inst, - target: *Inst, - cases: []Case, - /// Set of instructions whose lifetimes end at the start of one of the cases. - /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. - deaths: [*]*Inst = undefined, - else_index: u32 = 0, - else_deaths: u32 = 0, - else_body: Body, - - pub const Case = struct { - item: Value, - body: Body, - index: u32 = 0, - deaths: u32 = 0, - }; - - pub fn operandCount(self: *const SwitchBr) usize { - _ = self; - return 1; - } - pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst { - var i = index; - - if (i < 1) - return self.target; - i -= 1; - - return null; - } - pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst { - const case = self.cases[case_index]; - return (self.deaths + case.index)[0..case.deaths]; - } - pub fn elseDeaths(self: *const SwitchBr) []*Inst { - return (self.deaths + self.else_index)[0..self.else_deaths]; - } - }; - - pub const DbgStmt = struct { - pub const base_tag = Tag.dbg_stmt; - - base: Inst, - line: u32, - column: u32, - - pub fn operandCount(self: *const DbgStmt) usize { - _ = self; - return 0; - } - pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst { - _ = self; - _ = index; - return null; - } }; -}; -pub const Body = struct { - instructions: []*Inst, -}; /// For debugging purposes, prints a function representation to stderr. pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { diff --git a/CMakeLists.txt b/CMakeLists.txt index 44417e4159ba8b1a64f4e6e44cf3832584bce2a1..39db11773c8f6c48024416a34e43230d4e328e1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -564,7 +564,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/codegen/x86_64.zig" "${CMAKE_SOURCE_DIR}/src/glibc.zig" "${CMAKE_SOURCE_DIR}/src/introspect.zig" - "${CMAKE_SOURCE_DIR}/src/air.zig" + "${CMAKE_SOURCE_DIR}/src/Air.zig" "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" "${CMAKE_SOURCE_DIR}/src/libcxx.zig" "${CMAKE_SOURCE_DIR}/src/libtsan.zig" diff --git a/src/Air.zig b/src/Air.zig new file mode 100644 index 0000000000000000000000000000000000000000..97a5824abcc3b23b476ad0b6057f60ded3bdf02e --- /dev/null +++ b/src/Air.zig @@ -0,0 +1,335 @@ +//! Analyzed Intermediate Representation. +//! Sema inputs ZIR and outputs AIR. + +const std = @import("std"); +const Value = @import("value.zig").Value; +const Type = @import("type.zig").Type; +const Module = @import("Module.zig"); +const assert = std.debug.assert; +const Air = @This(); + +instructions: std.MultiArrayList(Inst).Slice, +/// The meaning of this data is determined by `Inst.Tag` value. +extra: []u32, +values: []Value, +variables: []*Module.Var, + +pub const Inst = struct { + tag: Tag, + data: Data, + + pub const Tag = enum(u8) { + /// Float or integer addition. For integers, wrapping is undefined behavior. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + add, + /// Integer addition. Wrapping is defined to be twos complement wrapping. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + addwrap, + /// Float or integer subtraction. For integers, wrapping is undefined behavior. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + sub, + /// Integer subtraction. Wrapping is defined to be twos complement wrapping. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + subwrap, + /// Float or integer multiplication. For integers, wrapping is undefined behavior. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + mul, + /// Integer multiplication. Wrapping is defined to be twos complement wrapping. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + mulwrap, + /// Integer or float division. For integers, wrapping is undefined behavior. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + div, + /// Allocates stack local memory. + /// Uses the `ty` field. + alloc, + /// TODO + assembly, + /// Bitwise AND. `&`. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + bit_and, + /// Bitwise OR. `|`. + /// Result type is the same as both operands. + /// Uses the `bin_op` field. + bit_or, + /// Bitwise XOR. `^` + /// Uses the `bin_op` field. + xor, + /// Boolean or binary NOT. + /// Uses the `ty_op` field. + not, + /// Reinterpret the memory representation of a value as a different type. + /// Uses the `ty_op` field. + bitcast, + /// Uses the `ty_pl` field with payload `Block`. + block, + /// Return from a block with a result. + /// Result type is always noreturn. + /// Uses the `br` field. + br, + /// Lowers to a hardware trap instruction, or the next best thing. + /// Result type is always void. + breakpoint, + /// Function call. + /// Result type is the return type of the function being called. + /// Uses the `pl_op` field with the `Call` payload. operand is the callee. + call, + /// `<`. Result type is always bool. + /// Uses the `bin_op` field. + cmp_lt, + /// `<=`. Result type is always bool. + /// Uses the `bin_op` field. + cmp_lte, + /// `==`. Result type is always bool. + /// Uses the `bin_op` field. + cmp_eq, + /// `>=`. Result type is always bool. + /// Uses the `bin_op` field. + cmp_gte, + /// `>`. Result type is always bool. + /// Uses the `bin_op` field. + cmp_gt, + /// `!=`. Result type is always bool. + /// Uses the `bin_op` field. + cmp_neq, + /// Conditional branch. + /// Result type is always noreturn. + /// Uses the `pl_op` field. Operand is the condition. Payload is `CondBr`. + cond_br, + /// Switch branch. + /// Result type is always noreturn. + /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`. + switch_br, + /// A comptime-known value. Uses the `ty_pl` field, payload is index of + /// `values` array. + constant, + /// Notes the beginning of a source code statement and marks the line and column. + /// Result type is always void. + /// Uses the `dbg_stmt` field. + dbg_stmt, + /// ?T => bool + /// Result type is always bool. + /// Uses the `un_op` field. + is_null, + /// ?T => bool (inverted logic) + /// Result type is always bool. + /// Uses the `un_op` field. + is_non_null, + /// *?T => bool + /// Result type is always bool. + /// Uses the `un_op` field. + is_null_ptr, + /// *?T => bool (inverted logic) + /// Result type is always bool. + /// Uses the `un_op` field. + is_non_null_ptr, + /// E!T => bool + /// Result type is always bool. + /// Uses the `un_op` field. + is_err, + /// E!T => bool (inverted logic) + /// Result type is always bool. + /// Uses the `un_op` field. + is_non_err, + /// *E!T => bool + /// Result type is always bool. + /// Uses the `un_op` field. + is_err_ptr, + /// *E!T => bool (inverted logic) + /// Result type is always bool. + /// Uses the `un_op` field. + is_non_err_ptr, + /// Result type is always bool. + /// Uses the `bin_op` field. + bool_and, + /// Result type is always bool. + /// Uses the `bin_op` field. + bool_or, + /// Read a value from a pointer. + /// Uses the `ty_op` field. + load, + /// A labeled block of code that loops forever. At the end of the body it is implied + /// to repeat; no explicit "repeat" instruction terminates loop bodies. + /// Result type is always noreturn. + /// Uses the `ty_pl` field. Payload is `Block`. + loop, + /// Converts a pointer to its address. Result type is always `usize`. + /// Uses the `un_op` field. + ptrtoint, + /// Stores a value onto the stack and returns a pointer to it. + /// TODO audit where this AIR instruction is emitted, maybe it should instead be emitting + /// alloca instruction and storing to the alloca. + /// Uses the `ty_op` field. + ref, + /// Return a value from a function. + /// Result type is always noreturn. + /// Uses the `un_op` field. + ret, + /// Returns a pointer to a global variable. + /// Uses the `ty_pl` field. Index is into the `variables` array. + varptr, + /// Write a value to a pointer. LHS is pointer, RHS is value. + /// Result type is always void. + /// Uses the `bin_op` field. + store, + /// Indicates the program counter will never get to this instruction. + /// Result type is always noreturn. + unreach, + /// Convert from one float type to another. + /// Uses the `ty_op` field. + floatcast, + /// TODO audit uses of this. We should have explicit instructions for integer + /// widening and truncating. + /// Uses the `ty_op` field. + intcast, + /// ?T => T. If the value is null, undefined behavior. + /// Uses the `ty_op` field. + optional_payload, + /// *?T => *T. If the value is null, undefined behavior. + /// Uses the `ty_op` field. + optional_payload_ptr, + /// Given a payload value, wraps it in an optional type. + /// Uses the `ty_op` field. + wrap_optional, + /// E!T -> T. If the value is an error, undefined behavior. + /// Uses the `ty_op` field. + unwrap_errunion_payload, + /// E!T -> E. If the value is not an error, undefined behavior. + /// Uses the `ty_op` field. + unwrap_errunion_err, + /// *(E!T) -> *T. If the value is an error, undefined behavior. + /// Uses the `ty_op` field. + unwrap_errunion_payload_ptr, + /// *(E!T) -> E. If the value is not an error, undefined behavior. + /// Uses the `ty_op` field. + unwrap_errunion_err_ptr, + /// wrap from T to E!T + /// Uses the `ty_op` field. + wrap_errunion_payload, + /// wrap from E to E!T + /// Uses the `ty_op` field. + wrap_errunion_err, + /// Given a pointer to a struct and a field index, returns a pointer to the field. + /// Uses the `ty_pl` field, payload is `StructField`. + struct_field_ptr, + + pub fn fromCmpOp(op: std.math.CompareOperator) Tag { + return switch (op) { + .lt => .cmp_lt, + .lte => .cmp_lte, + .eq => .cmp_eq, + .gte => .cmp_gte, + .gt => .cmp_gt, + .neq => .cmp_neq, + }; + } + }; + + /// The position of an AIR instruction within the `Air` instructions array. + pub const Index = u32; + + /// All instructions have an 8-byte payload, which is contained within + /// this union. `Tag` determines which union field is active, as well as + /// how to interpret the data within. + pub const Data = union { + un_op: Ref, + bin_op: struct { + lhs: Ref, + rhs: Ref, + }, + ty: Type, + ty_op: struct { + ty: Ref, + operand: Ref, + }, + ty_pl: struct { + ty: Ref, + // Index into a different array. + payload: u32, + }, + br: struct { + block_inst: Index, + operand: Ref, + }, + pl_op: struct { + operand: Ref, + payload: u32, + }, + constant: struct { + ty: Type, + val: Value, + }, + dbg_stmt: struct { + line: u32, + column: u32, + }, + + // Make sure we don't accidentally add a field to make this union + // bigger than expected. Note that in Debug builds, Zig is allowed + // to insert a secret field for safety checks. + comptime { + if (std.builtin.mode != .Debug) { + assert(@sizeOf(Data) == 8); + } + } + }; + + pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator { + return switch (base.tag) { + .cmp_lt => .lt, + .cmp_lte => .lte, + .cmp_eq => .eq, + .cmp_gte => .gte, + .cmp_gt => .gt, + .cmp_neq => .neq, + else => null, + }; + } + + /// Trailing is a list of instruction indexes for every `body_len`. + pub const Block = struct { + body_len: u32, + }; + + /// Trailing is a list of `Ref` for every `args_len`. + pub const Call = struct { + args_len: u32, + }; + + /// This data is stored inside extra, with two sets of trailing `Ref`: + /// * 0. the then body, according to `then_body_len`. + /// * 1. the else body, according to `else_body_len`. + pub const CondBr = struct { + condition: Ref, + then_body_len: u32, + else_body_len: u32, + }; + + /// Trailing: + /// * 0. `Case` for each `cases_len` + /// * 1. the else body, according to `else_body_len`. + pub const SwitchBr = struct { + cases_len: u32, + else_body_len: u32, + + /// Trailing: + /// * instruction index for each `body_len`. + pub const Case = struct { + item: Ref, + body_len: u32, + }; + }; + + pub const StructField = struct { + struct_ptr: Ref, + field_index: u32, + }; +}; diff --git a/src/Module.zig b/src/Module.zig index a1f6887fbd9d03793f03e4c87a16ac399e21ea8c..2f1dc0b33b2bdb00dcbf39b94570c9648da38032 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -21,7 +21,7 @@ const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); const Package = @import("Package.zig"); const link = @import("link.zig"); -const ir = @import("air.zig"); +const Air = @import("Air.zig"); const Zir = @import("Zir.zig"); const trace = @import("tracy.zig").trace; const AstGen = @import("AstGen.zig"); diff --git a/src/Sema.zig b/src/Sema.zig index d7ce9fdf4f02bd7b9a9894eaec79047a17553500..85cb4aa423475998f6488822b20da3a050942181 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -52,7 +52,7 @@ const Sema = @This(); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); -const ir = @import("air.zig"); +const Air = @import("Air.zig"); const Zir = @import("Zir.zig"); const Module = @import("Module.zig"); const Inst = ir.Inst; diff --git a/src/Zir.zig b/src/Zir.zig index db851cfa4bced9d7ee242f64ae284fd008866593..b975500e2f4eb5b08879a2112e660055b4e17990 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -22,7 +22,6 @@ const Zir = @This(); const Type = @import("type.zig").Type; const Value = @import("value.zig").Value; const TypedValue = @import("TypedValue.zig"); -const ir = @import("air.zig"); const Module = @import("Module.zig"); const LazySrcLoc = Module.LazySrcLoc; @@ -214,7 +213,7 @@ pub const Inst = struct { as_node, /// Bitwise AND. `&` bit_and, - /// Bitcast a value to a different type. + /// Reinterpret the memory representation of a value as a different type. /// Uses the pl_node field with payload `Bin`. bitcast, /// A typed result location pointer is bitcasted to a new result location pointer. diff --git a/src/codegen.zig b/src/codegen.zig index 6050fe0ed8574d50bea105884cf487c413a1661e..205bab755a4fbf43fe4729b3f88acff45caca3bb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2,7 +2,7 @@ const std = @import("std"); const mem = std.mem; const math = std.math; const assert = std.debug.assert; -const ir = @import("air.zig"); +const Air = @import("Air.zig"); const Type = @import("type.zig").Type; const Value = @import("value.zig").Value; const TypedValue = @import("TypedValue.zig"); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 391375c70966c81977d9710702a7e5c5c5c5051f..e3f2423746a4fe170add87f498d8fe6b8ad64f23 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6,8 +6,7 @@ const log = std.log.scoped(.c); const link = @import("../link.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); -const ir = @import("../air.zig"); -const Inst = ir.Inst; +const Air = @import("../Air.zig"); const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b8f96891f4aa436879cfe1ea60e33ac9a6caa15f..45ee2d9bb85a6aa62c979fccb9193a4bfdc78fc4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9,7 +9,7 @@ const math = std.math; const Module = @import("../Module.zig"); const TypedValue = @import("../TypedValue.zig"); -const ir = @import("../air.zig"); +const Air = @import("../Air.zig"); const Inst = ir.Inst; const Value = @import("../value.zig").Value; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 7fa813e56554634334463ae75cfb1ddee9a82c7b..60e9a962753238d89d8eded7efa35530fed53811 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -12,8 +12,7 @@ const Decl = Module.Decl; const Type = @import("../type.zig").Type; const Value = @import("../value.zig").Value; const LazySrcLoc = Module.LazySrcLoc; -const ir = @import("../air.zig"); -const Inst = ir.Inst; +const Air = @import("../Air.zig"); pub const Word = u32; pub const ResultId = u32; diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 3476ab2ce6b6d3d25ff5e89a68c2ca9977a3c76b..45b00ddfad8cdda1bf7d3080652fba500bd23365 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -9,8 +9,7 @@ const wasm = std.wasm; const Module = @import("../Module.zig"); const Decl = Module.Decl; -const ir = @import("../air.zig"); -const Inst = ir.Inst; +const Air = @import("../Air.zig"); const Type = @import("../type.zig").Type; const Value = @import("../value.zig").Value; const Compilation = @import("../Compilation.zig"); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d754b478b9b3dd477848c893d6c3e7721f624679..90224866baabdee1c1a66ea4f6f9779faeab2ee4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -10,7 +10,7 @@ const log = std.log.scoped(.link); const DW = std.dwarf; const leb128 = std.leb; -const ir = @import("../air.zig"); +const Air = @import("../Air.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const codegen = @import("../codegen.zig"); diff --git a/src/liveness.zig b/src/liveness.zig index d115af77ed3d1f2a1c6fa265ec7a3cdf964b1fa8..e6692e4fc3a9cda8bbb2e51c76a6a972ac328d26 100644 --- a/src/liveness.zig +++ b/src/liveness.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const ir = @import("air.zig"); +const Air = @import("Air.zig"); const trace = @import("tracy.zig").trace; const log = std.log.scoped(.liveness); const assert = std.debug.assert; diff --git a/src/register_manager.zig b/src/register_manager.zig index 96cf4f17b7f38e8f3d5bc66d6181cfa12287899e..9c61423706ec36b62db5b2701e70c08d251655ee 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -3,7 +3,7 @@ const math = std.math; const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; -const ir = @import("air.zig"); +const Air = @import("Air.zig"); const Type = @import("type.zig").Type; const Module = @import("Module.zig"); const LazySrcLoc = Module.LazySrcLoc; diff --git a/src/value.zig b/src/value.zig index b4cd63b8d3699c32d0385ecb42794889249fc3f9..48cd6fffc47686dc9b94015990c907cf3c027a03 100644 --- a/src/value.zig +++ b/src/value.zig @@ -7,7 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable; const Target = std.Target; const Allocator = std.mem.Allocator; const Module = @import("Module.zig"); -const ir = @import("air.zig"); +const Air = @import("Air.zig"); /// This is the raw data, with no bookkeeping, no memory awareness, /// no de-duplication, and no type system awareness. -- 2.54.0