mirror of
https://github.com/ziglang/zig.git
synced 2024-12-13 14:47:09 +00:00
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.
This commit is contained in:
parent
bfe2005167
commit
5d6f7b44c1
@ -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 {
|
@ -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"
|
||||
|
335
src/Air.zig
Normal file
335
src/Air.zig
Normal file
@ -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,
|
||||
};
|
||||
};
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user