Commit 5d6f7b44c1
Changed files (15)
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");
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;
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;
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");
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");
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,
+ };
+};
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");
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;
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");
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;
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;
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.
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.
src/air.zig โ 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;
-
-/// 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,
+ * be sure to test debug info of parameters
+
+
/// 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;
-
- 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;
+ /// 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,
- 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;
+ pub const Assembly = struct {
+ pub const base_tag = Tag.assembly;
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 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 operandCount(self: *const Assembly) usize {
+ return self.args.len;
}
- 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 {
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"