Skip to content

Commit c04be63

Browse files
committed
Legalize: introduce a new pass before liveness
Each target can opt into different sets of legalize features. By performing these transformations before liveness, instructions that become unreferenced will have up-to-date liveness information.
1 parent f25212a commit c04be63

34 files changed

+562
-423
lines changed

CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,13 +512,15 @@ set(ZIG_STAGE2_SOURCES
512512
lib/std/zig/llvm/bitcode_writer.zig
513513
lib/std/zig/llvm/ir.zig
514514
src/Air.zig
515+
src/Air/Legalize.zig
516+
src/Air/Liveness.zig
517+
src/Air/Liveness/Verify.zig
518+
src/Air/types_resolved.zig
515519
src/Builtin.zig
516520
src/Compilation.zig
517521
src/Compilation/Config.zig
518522
src/DarwinPosixSpawn.zig
519523
src/InternPool.zig
520-
src/Liveness.zig
521-
src/Liveness/Verify.zig
522524
src/Package.zig
523525
src/Package/Fetch.zig
524526
src/Package/Fetch/git.zig

src/Air.zig

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@ const builtin = @import("builtin");
99
const assert = std.debug.assert;
1010

1111
const Air = @This();
12-
const Value = @import("Value.zig");
13-
const Type = @import("Type.zig");
1412
const InternPool = @import("InternPool.zig");
13+
const Type = @import("Type.zig");
14+
const Value = @import("Value.zig");
1515
const Zcu = @import("Zcu.zig");
1616
const types_resolved = @import("Air/types_resolved.zig");
1717

18+
pub const Legalize = @import("Air/Legalize.zig");
19+
pub const Liveness = @import("Air/Liveness.zig");
20+
1821
instructions: std.MultiArrayList(Inst).Slice,
1922
/// The meaning of this data is determined by `Inst.Tag` value.
2023
/// The first few indexes are reserved. See `ExtraIndex` for the values.
21-
extra: []const u32,
24+
extra: std.ArrayListUnmanaged(u32),
2225

2326
pub const ExtraIndex = enum(u32) {
2427
/// Payload index of the main `Block` in the `extra` array.
@@ -244,22 +247,27 @@ pub const Inst = struct {
244247
/// Uses the `bin_op` field.
245248
bit_or,
246249
/// Shift right. `>>`
250+
/// The rhs type may be a scalar version of the lhs type.
247251
/// Uses the `bin_op` field.
248252
shr,
249253
/// Shift right. The shift produces a poison value if it shifts out any non-zero bits.
254+
/// The rhs type may be a scalar version of the lhs type.
250255
/// Uses the `bin_op` field.
251256
shr_exact,
252257
/// Shift left. `<<`
258+
/// The rhs type may be a scalar version of the lhs type.
253259
/// Uses the `bin_op` field.
254260
shl,
255261
/// Shift left; For unsigned integers, the shift produces a poison value if it shifts
256262
/// out any non-zero bits. For signed integers, the shift produces a poison value if
257263
/// it shifts out any bits that disagree with the resultant sign bit.
264+
/// The rhs type may be a scalar version of the lhs type.
258265
/// Uses the `bin_op` field.
259266
shl_exact,
260267
/// Saturating integer shift left. `<<|`. The result is the same type as the `lhs`.
261268
/// The `rhs` must have the same vector shape as the `lhs`, but with any unsigned
262269
/// integer as the scalar type.
270+
/// The rhs type may be a scalar version of the lhs type.
263271
/// Uses the `bin_op` field.
264272
shl_sat,
265273
/// Bitwise XOR. `^`
@@ -1378,9 +1386,9 @@ pub const UnionInit = struct {
13781386
};
13791387

13801388
pub fn getMainBody(air: Air) []const Air.Inst.Index {
1381-
const body_index = air.extra[@intFromEnum(ExtraIndex.main_block)];
1389+
const body_index = air.extra.items[@intFromEnum(ExtraIndex.main_block)];
13821390
const extra = air.extraData(Block, body_index);
1383-
return @ptrCast(air.extra[extra.end..][0..extra.data.body_len]);
1391+
return @ptrCast(air.extra.items[extra.end..][0..extra.data.body_len]);
13841392
}
13851393

13861394
pub fn typeOf(air: *const Air, inst: Air.Inst.Ref, ip: *const InternPool) Type {
@@ -1656,9 +1664,9 @@ pub fn extraData(air: Air, comptime T: type, index: usize) struct { data: T, end
16561664
var result: T = undefined;
16571665
inline for (fields) |field| {
16581666
@field(result, field.name) = switch (field.type) {
1659-
u32 => air.extra[i],
1660-
InternPool.Index, Inst.Ref => @enumFromInt(air.extra[i]),
1661-
i32, CondBr.BranchHints => @bitCast(air.extra[i]),
1667+
u32 => air.extra.items[i],
1668+
InternPool.Index, Inst.Ref => @enumFromInt(air.extra.items[i]),
1669+
i32, CondBr.BranchHints => @bitCast(air.extra.items[i]),
16621670
else => @compileError("bad field type: " ++ @typeName(field.type)),
16631671
};
16641672
i += 1;
@@ -1671,7 +1679,7 @@ pub fn extraData(air: Air, comptime T: type, index: usize) struct { data: T, end
16711679

16721680
pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
16731681
air.instructions.deinit(gpa);
1674-
gpa.free(air.extra);
1682+
air.extra.deinit(gpa);
16751683
air.* = undefined;
16761684
}
16771685

@@ -1700,7 +1708,7 @@ pub const NullTerminatedString = enum(u32) {
17001708

17011709
pub fn toSlice(nts: NullTerminatedString, air: Air) [:0]const u8 {
17021710
if (nts == .none) return "";
1703-
const bytes = std.mem.sliceAsBytes(air.extra[@intFromEnum(nts)..]);
1711+
const bytes = std.mem.sliceAsBytes(air.extra.items[@intFromEnum(nts)..]);
17041712
return bytes[0..std.mem.indexOfScalar(u8, bytes, 0).? :0];
17051713
}
17061714
};
@@ -1943,7 +1951,7 @@ pub const UnwrappedSwitch = struct {
19431951
return us.getHintInner(us.cases_len);
19441952
}
19451953
fn getHintInner(us: UnwrappedSwitch, idx: u32) std.builtin.BranchHint {
1946-
const bag = us.air.extra[us.branch_hints_start..][idx / 10];
1954+
const bag = us.air.extra.items[us.branch_hints_start..][idx / 10];
19471955
const bits: u3 = @truncate(bag >> @intCast(3 * (idx % 10)));
19481956
return @enumFromInt(bits);
19491957
}
@@ -1971,13 +1979,13 @@ pub const UnwrappedSwitch = struct {
19711979

19721980
const extra = it.air.extraData(SwitchBr.Case, it.extra_index);
19731981
var extra_index = extra.end;
1974-
const items: []const Inst.Ref = @ptrCast(it.air.extra[extra_index..][0..extra.data.items_len]);
1982+
const items: []const Inst.Ref = @ptrCast(it.air.extra.items[extra_index..][0..extra.data.items_len]);
19751983
extra_index += items.len;
19761984
// TODO: ptrcast from []const Inst.Ref to []const [2]Inst.Ref when supported
1977-
const ranges_ptr: [*]const [2]Inst.Ref = @ptrCast(it.air.extra[extra_index..]);
1985+
const ranges_ptr: [*]const [2]Inst.Ref = @ptrCast(it.air.extra.items[extra_index..]);
19781986
const ranges: []const [2]Inst.Ref = ranges_ptr[0..extra.data.ranges_len];
19791987
extra_index += ranges.len * 2;
1980-
const body: []const Inst.Index = @ptrCast(it.air.extra[extra_index..][0..extra.data.body_len]);
1988+
const body: []const Inst.Index = @ptrCast(it.air.extra.items[extra_index..][0..extra.data.body_len]);
19811989
extra_index += body.len;
19821990
it.extra_index = @intCast(extra_index);
19831991

@@ -1992,7 +2000,7 @@ pub const UnwrappedSwitch = struct {
19922000
/// Returns the body of the "default" (`else`) case.
19932001
pub fn elseBody(it: *CaseIterator) []const Inst.Index {
19942002
assert(it.next_case == it.cases_len);
1995-
return @ptrCast(it.air.extra[it.extra_index..][0..it.else_body_len]);
2003+
return @ptrCast(it.air.extra.items[it.extra_index..][0..it.else_body_len]);
19962004
}
19972005
pub const Case = struct {
19982006
idx: u32,
@@ -2025,6 +2033,7 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
20252033
pub const typesFullyResolved = types_resolved.typesFullyResolved;
20262034
pub const typeFullyResolved = types_resolved.checkType;
20272035
pub const valFullyResolved = types_resolved.checkVal;
2036+
pub const legalize = Legalize.legalize;
20282037

20292038
pub const CoveragePoint = enum(u1) {
20302039
/// Indicates the block is not a place of interest corresponding to

src/Air/Legalize.zig

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
zcu: *const Zcu,
2+
air: Air,
3+
features: std.enums.EnumSet(Feature),
4+
5+
pub const Feature = enum {
6+
/// Legalize (shift lhs, (splat rhs)) -> (shift lhs, rhs)
7+
remove_shift_vector_rhs_splat,
8+
/// Legalize reduce of a one element vector to a bitcast
9+
reduce_one_elem_to_bitcast,
10+
};
11+
12+
pub const Features = std.enums.EnumFieldStruct(Feature, bool, false);
13+
14+
pub fn legalize(air: *Air, backend: std.builtin.CompilerBackend, zcu: *const Zcu) std.mem.Allocator.Error!void {
15+
var l: Legalize = .{
16+
.zcu = zcu,
17+
.air = air.*,
18+
.features = features: switch (backend) {
19+
.other, .stage1 => unreachable,
20+
inline .stage2_llvm,
21+
.stage2_c,
22+
.stage2_wasm,
23+
.stage2_arm,
24+
.stage2_x86_64,
25+
.stage2_aarch64,
26+
.stage2_x86,
27+
.stage2_riscv64,
28+
.stage2_sparc64,
29+
.stage2_spirv64,
30+
.stage2_powerpc,
31+
=> |ct_backend| {
32+
const Backend = codegen.importBackend(ct_backend) orelse break :features .initEmpty();
33+
break :features if (@hasDecl(Backend, "legalize_features"))
34+
.init(Backend.legalize_features)
35+
else
36+
.initEmpty();
37+
},
38+
_ => unreachable,
39+
},
40+
};
41+
defer air.* = l.air;
42+
if (!l.features.bits.eql(.initEmpty())) try l.legalizeBody(l.air.getMainBody());
43+
}
44+
45+
fn legalizeBody(l: *Legalize, body: []const Air.Inst.Index) std.mem.Allocator.Error!void {
46+
const zcu = l.zcu;
47+
const ip = &zcu.intern_pool;
48+
const tags = l.air.instructions.items(.tag);
49+
const data = l.air.instructions.items(.data);
50+
for (body) |inst| inst: switch (tags[@intFromEnum(inst)]) {
51+
else => {},
52+
53+
.shl,
54+
.shl_exact,
55+
.shl_sat,
56+
.shr,
57+
.shr_exact,
58+
=> |air_tag| if (l.features.contains(.remove_shift_vector_rhs_splat)) done: {
59+
const bin_op = data[@intFromEnum(inst)].bin_op;
60+
const ty = l.air.typeOf(bin_op.rhs, ip);
61+
if (!ty.isVector(zcu)) break :done;
62+
if (bin_op.rhs.toInterned()) |rhs_ip_index| switch (ip.indexToKey(rhs_ip_index)) {
63+
else => {},
64+
.aggregate => |aggregate| switch (aggregate.storage) {
65+
else => {},
66+
.repeated_elem => |splat| continue :inst l.replaceInst(inst, air_tag, .{ .bin_op = .{
67+
.lhs = bin_op.lhs,
68+
.rhs = Air.internedToRef(splat),
69+
} }),
70+
},
71+
} else {
72+
const rhs_inst = bin_op.rhs.toIndex().?;
73+
switch (tags[@intFromEnum(rhs_inst)]) {
74+
else => {},
75+
.splat => continue :inst l.replaceInst(inst, air_tag, .{ .bin_op = .{
76+
.lhs = bin_op.lhs,
77+
.rhs = data[@intFromEnum(rhs_inst)].ty_op.operand,
78+
} }),
79+
}
80+
}
81+
},
82+
83+
.reduce,
84+
.reduce_optimized,
85+
=> if (l.features.contains(.reduce_one_elem_to_bitcast)) done: {
86+
const reduce = data[@intFromEnum(inst)].reduce;
87+
const vector_ty = l.air.typeOf(reduce.operand, ip);
88+
switch (vector_ty.vectorLen(zcu)) {
89+
0 => unreachable,
90+
1 => continue :inst l.replaceInst(inst, .bitcast, .{ .ty_op = .{
91+
.ty = Air.internedToRef(vector_ty.scalarType(zcu).toIntern()),
92+
.operand = reduce.operand,
93+
} }),
94+
else => break :done,
95+
}
96+
},
97+
98+
.@"try", .try_cold => {
99+
const pl_op = data[@intFromEnum(inst)].pl_op;
100+
const extra = l.air.extraData(Air.Try, pl_op.payload);
101+
try l.legalizeBody(@ptrCast(l.air.extra.items[extra.end..][0..extra.data.body_len]));
102+
},
103+
.try_ptr, .try_ptr_cold => {
104+
const ty_pl = data[@intFromEnum(inst)].ty_pl;
105+
const extra = l.air.extraData(Air.TryPtr, ty_pl.payload);
106+
try l.legalizeBody(@ptrCast(l.air.extra.items[extra.end..][0..extra.data.body_len]));
107+
},
108+
.block, .loop => {
109+
const ty_pl = data[@intFromEnum(inst)].ty_pl;
110+
const extra = l.air.extraData(Air.Block, ty_pl.payload);
111+
try l.legalizeBody(@ptrCast(l.air.extra.items[extra.end..][0..extra.data.body_len]));
112+
},
113+
.dbg_inline_block => {
114+
const ty_pl = data[@intFromEnum(inst)].ty_pl;
115+
const extra = l.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
116+
try l.legalizeBody(@ptrCast(l.air.extra.items[extra.end..][0..extra.data.body_len]));
117+
},
118+
.cond_br => {
119+
const pl_op = data[@intFromEnum(inst)].pl_op;
120+
const extra = l.air.extraData(Air.CondBr, pl_op.payload);
121+
try l.legalizeBody(@ptrCast(l.air.extra.items[extra.end..][0..extra.data.then_body_len]));
122+
try l.legalizeBody(@ptrCast(l.air.extra.items[extra.end + extra.data.then_body_len ..][0..extra.data.else_body_len]));
123+
},
124+
.switch_br, .loop_switch_br => {
125+
const switch_br = l.air.unwrapSwitch(inst);
126+
var it = switch_br.iterateCases();
127+
while (it.next()) |case| try l.legalizeBody(case.body);
128+
try l.legalizeBody(it.elseBody());
129+
},
130+
};
131+
}
132+
133+
// inline to propagate comptime `tag`s
134+
inline fn replaceInst(l: *Legalize, inst: Air.Inst.Index, tag: Air.Inst.Tag, data: Air.Inst.Data) Air.Inst.Tag {
135+
const ip = &l.zcu.intern_pool;
136+
const orig_ty = if (std.debug.runtime_safety) l.air.typeOfIndex(inst, ip) else {};
137+
l.air.instructions.items(.tag)[@intFromEnum(inst)] = tag;
138+
l.air.instructions.items(.data)[@intFromEnum(inst)] = data;
139+
if (std.debug.runtime_safety) std.debug.assert(l.air.typeOfIndex(inst, ip).toIntern() == orig_ty.toIntern());
140+
return tag;
141+
}
142+
143+
const Air = @import("../Air.zig");
144+
const codegen = @import("../codegen.zig");
145+
const Legalize = @This();
146+
const std = @import("std");
147+
const Zcu = @import("../Zcu.zig");

0 commit comments

Comments
 (0)