Skip to content

Commit

Permalink
Air: store interned values in Air.Inst.Ref
Browse files Browse the repository at this point in the history
Previously, interned values were represented as AIR instructions using
the `interned` tag. Now, the AIR ref directly encodes the InternPool
index. The encoding works as follows:
* If the ref matches one of the static values, it corresponds to the same InternPool index.
* Otherwise, if the MSB is 0, the ref corresponds to an InternPool index.
* Otherwise, if the MSB is 1, the ref corresponds to an AIR instruction index (after removing the MSB).

Note that since most static InternPool indices are low values (the
exceptions being `.none` and `.var_args_param_type`), the first rule is
almost a nop.
  • Loading branch information
mlugg authored and andrewrk committed Jun 27, 2023
1 parent dae516d commit ff37ccd
Show file tree
Hide file tree
Showing 13 changed files with 204 additions and 324 deletions.
97 changes: 51 additions & 46 deletions src/Air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -438,9 +438,6 @@ pub const Inst = struct {
/// was executed on the operand.
/// Uses the `ty_pl` field. Payload is `TryPtr`.
try_ptr,
/// A comptime-known value via an index into the InternPool.
/// Uses the `interned` field.
interned,
/// Notes the beginning of a source code statement and marks the line and column.
/// Result type is always void.
/// Uses the `dbg_stmt` field.
Expand Down Expand Up @@ -879,6 +876,12 @@ pub const Inst = struct {
/// The position of an AIR instruction within the `Air` instructions array.
pub const Index = u32;

/// Either a reference to a value stored in the InternPool, or a reference to an AIR instruction.
/// The most-significant bit of the value is a tag bit. This bit is 1 if the value represents an
/// instruction index and 0 if it represents an InternPool index.
///
/// The hardcoded refs `none` and `var_args_param_type` are exceptions to this rule: they have
/// their tag bit set but refer to the InternPool.
pub const Ref = enum(u32) {
u0_type = @intFromEnum(InternPool.Index.u0_type),
i0_type = @intFromEnum(InternPool.Index.i0_type),
Expand Down Expand Up @@ -979,7 +982,6 @@ pub const Inst = struct {
pub const Data = union {
no_op: void,
un_op: Ref,
interned: InternPool.Index,

bin_op: struct {
lhs: Ref,
Expand Down Expand Up @@ -1216,11 +1218,11 @@ pub fn getMainBody(air: Air) []const Air.Inst.Index {
}

pub fn typeOf(air: *const Air, inst: Air.Inst.Ref, ip: *const InternPool) Type {
const ref_int = @intFromEnum(inst);
if (ref_int < InternPool.static_keys.len) {
return InternPool.static_keys[ref_int].typeOf().toType();
if (refToInterned(inst)) |ip_index| {
return ip.typeOf(ip_index).toType();
} else {
return air.typeOfIndex(refToIndex(inst).?, ip);
}
return air.typeOfIndex(ref_int - ref_start_index, ip);
}

pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) Type {
Expand Down Expand Up @@ -1342,8 +1344,6 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
.try_ptr,
=> return air.getRefType(datas[inst].ty_pl.ty),

.interned => return ip.typeOf(datas[inst].interned).toType(),

.not,
.bitcast,
.load,
Expand Down Expand Up @@ -1479,18 +1479,8 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
}

pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type {
const ref_int = @intFromEnum(ref);
if (ref_int < ref_start_index) {
const ip_index = @as(InternPool.Index, @enumFromInt(ref_int));
return ip_index.toType();
}
const inst_index = ref_int - ref_start_index;
const air_tags = air.instructions.items(.tag);
const air_datas = air.instructions.items(.data);
return switch (air_tags[inst_index]) {
.interned => air_datas[inst_index].interned.toType(),
else => unreachable,
};
_ = air; // TODO: remove this parameter
return refToInterned(ref).?.toType();
}

/// Returns the requested data, as well as the new index which is at the start of the
Expand Down Expand Up @@ -1521,40 +1511,56 @@ pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
air.* = undefined;
}

pub const ref_start_index: u32 = InternPool.static_len;
pub fn refToInternedAllowNone(ref: Inst.Ref) ?InternPool.Index {
return switch (ref) {
.var_args_param_type => .var_args_param_type,
.none => .none,
else => if (@intFromEnum(ref) >> 31 == 0) {
return @as(InternPool.Index, @enumFromInt(@intFromEnum(ref)));
} else null,
};
}

pub fn indexToRef(inst: Inst.Index) Inst.Ref {
return @as(Inst.Ref, @enumFromInt(ref_start_index + inst));
pub fn refToInterned(ref: Inst.Ref) ?InternPool.Index {
assert(ref != .none);
return refToInternedAllowNone(ref);
}

pub fn refToIndex(inst: Inst.Ref) ?Inst.Index {
assert(inst != .none);
const ref_int = @intFromEnum(inst);
if (ref_int >= ref_start_index) {
return ref_int - ref_start_index;
} else {
return null;
}
pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {
assert(@intFromEnum(ip_index) >> 31 == 0);
return switch (ip_index) {
.var_args_param_type => .var_args_param_type,
.none => .none,
else => @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index)))),
};
}

pub fn refToIndexAllowNone(ref: Inst.Ref) ?Inst.Index {
return switch (ref) {
.var_args_param_type, .none => null,
else => if (@intFromEnum(ref) >> 31 != 0) {
return @as(u31, @truncate(@intFromEnum(ref)));
} else null,
};
}

pub fn refToIndexAllowNone(inst: Inst.Ref) ?Inst.Index {
if (inst == .none) return null;
return refToIndex(inst);
pub fn refToIndex(ref: Inst.Ref) ?Inst.Index {
assert(ref != .none);
return refToIndexAllowNone(ref);
}

pub fn indexToRef(inst: Inst.Index) Inst.Ref {
assert(inst >> 31 == 0);
return @enumFromInt((1 << 31) | inst);
}

/// Returns `null` if runtime-known.
pub fn value(air: Air, inst: Inst.Ref, mod: *Module) !?Value {
const ref_int = @intFromEnum(inst);
if (ref_int < ref_start_index) {
const ip_index = @as(InternPool.Index, @enumFromInt(ref_int));
if (refToInterned(inst)) |ip_index| {
return ip_index.toValue();
}
const inst_index = @as(Air.Inst.Index, @intCast(ref_int - ref_start_index));
const air_datas = air.instructions.items(.data);
switch (air.instructions.items(.tag)[inst_index]) {
.interned => return air_datas[inst_index].interned.toValue(),
else => return air.typeOfIndex(inst_index, &mod.intern_pool).onePossibleValue(mod),
}
const index = refToIndex(inst).?;
return air.typeOfIndex(index, &mod.intern_pool).onePossibleValue(mod);
}

pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {
Expand Down Expand Up @@ -1709,7 +1715,6 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
.cmp_neq_optimized,
.cmp_vector,
.cmp_vector_optimized,
.interned,
.is_null,
.is_non_null,
.is_null_ptr,
Expand Down
15 changes: 1 addition & 14 deletions src/Liveness.zig
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,6 @@ pub fn categorizeOperand(
.inferred_alloc,
.inferred_alloc_comptime,
.ret_ptr,
.interned,
.trap,
.breakpoint,
.dbg_stmt,
Expand Down Expand Up @@ -981,7 +980,7 @@ fn analyzeInst(
.work_group_id,
=> return analyzeOperands(a, pass, data, inst, .{ .none, .none, .none }),

.inferred_alloc, .inferred_alloc_comptime, .interned => unreachable,
.inferred_alloc, .inferred_alloc_comptime => unreachable,

.trap,
.unreach,
Expand Down Expand Up @@ -1264,7 +1263,6 @@ fn analyzeOperands(
operands: [bpi - 1]Air.Inst.Ref,
) Allocator.Error!void {
const gpa = a.gpa;
const inst_tags = a.air.instructions.items(.tag);
const ip = a.intern_pool;

switch (pass) {
Expand All @@ -1273,10 +1271,6 @@ fn analyzeOperands(

for (operands) |op_ref| {
const operand = Air.refToIndexAllowNone(op_ref) orelse continue;

// Don't compute any liveness for constants
if (inst_tags[operand] == .interned) continue;

_ = try data.live_set.put(gpa, operand, {});
}
},
Expand Down Expand Up @@ -1307,9 +1301,6 @@ fn analyzeOperands(
const op_ref = operands[i];
const operand = Air.refToIndexAllowNone(op_ref) orelse continue;

// Don't compute any liveness for constants
if (inst_tags[operand] == .interned) continue;

const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i));

if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
Expand Down Expand Up @@ -1837,10 +1828,6 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {

const operand = Air.refToIndex(op_ref) orelse return;

// Don't compute any liveness for constants
const inst_tags = big.a.air.instructions.items(.tag);
if (inst_tags[operand] == .interned) return

// If our result is unused and the instruction doesn't need to be lowered, backends will
// skip the lowering of this instruction, so we don't want to record uses of operands.
// That way, we can mark as many instructions as possible unused.
Expand Down
6 changes: 0 additions & 6 deletions src/Liveness/Verify.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
.inferred_alloc,
.inferred_alloc_comptime,
.ret_ptr,
.interned,
.breakpoint,
.dbg_stmt,
.dbg_inline_begin,
Expand Down Expand Up @@ -559,10 +558,6 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies
assert(!dies);
return;
};
if (self.air.instructions.items(.tag)[operand] == .interned) {
assert(!dies);
return;
}
if (dies) {
if (!self.live.remove(operand)) return invalid("%{}: dead operand %{} reused and killed again", .{ inst, operand });
} else {
Expand All @@ -583,7 +578,6 @@ fn verifyInstOperands(
}

fn verifyInst(self: *Verify, inst: Air.Inst.Index) Error!void {
if (self.air.instructions.items(.tag)[inst] == .interned) return;
if (self.liveness.isUnused(inst)) {
assert(!self.live.contains(inst));
} else {
Expand Down
Loading

0 comments on commit ff37ccd

Please sign in to comment.