diff --git a/src/air.zig b/BRANCH_TODO similarity index 68% rename from src/air.zig rename to BRANCH_TODO index e73367945b..ed77d6bd03 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,115 +154,6 @@ pub const Inst = struct { }; } - pub const NoOp = struct { - base: Inst, - - 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 UnOp = struct { - base: Inst, - operand: *Inst, - - pub fn operandCount(self: *const UnOp) usize { - _ = self; - return 1; - } - pub fn getOperand(self: *const UnOp, index: usize) ?*Inst { - if (index == 0) - return self.operand; - return null; - } - }; - - pub const BinOp = struct { - base: Inst, - lhs: *Inst, - rhs: *Inst, - - pub fn operandCount(self: *const BinOp) usize { - _ = 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; - - return null; - } - }; - - pub const Arg = struct { - pub const base_tag = Tag.arg; - - 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; - - base: Inst, - asm_source: []const u8, - is_volatile: bool, - output_constraint: ?[]const u8, - inputs: []const []const u8, - clobbers: []const []const u8, - args: []const *Inst, - - pub fn operandCount(self: *const Assembly) usize { - return self.args.len; - } - pub fn getOperand(self: *const Assembly, index: usize) ?*Inst { - if (index < self.args.len) - return self.args[index]; - return null; - } - }; - - 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 { @@ -439,241 +178,42 @@ pub const Inst = struct { } }; - pub const Br = struct { - pub const base_tag = Tag.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, + + + + pub const Assembly = struct { + pub const base_tag = Tag.assembly; 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, + asm_source: []const u8, + is_volatile: bool, + output_constraint: ?[]const u8, + inputs: []const []const u8, + clobbers: []const []const u8, args: []const *Inst, - pub fn operandCount(self: *const Call) usize { - return self.args.len + 1; + pub fn operandCount(self: *const Assembly) usize { + return self.args.len; } - 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; + pub fn getOperand(self: *const Assembly, index: usize) ?*Inst { + if (index < self.args.len) + return self.args[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 44417e4159..39db11773c 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 0000000000..97a5824abc --- /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 a1f6887fbd..2f1dc0b33b 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 d7ce9fdf4f..85cb4aa423 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 db851cfa4b..b975500e2f 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 6050fe0ed8..205bab755a 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 391375c709..e3f2423746 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 b8f96891f4..45ee2d9bb8 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 7fa813e565..60e9a96275 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 3476ab2ce6..45b00ddfad 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 d754b478b9..90224866ba 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 d115af77ed..e6692e4fc3 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 96cf4f17b7..9c61423706 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 b4cd63b8d3..48cd6fffc4 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.