authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-06-15 11:36:15-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-06-15 11:42:03-04:00
log6e72026e3b04586cf44deadeddeb8bf44a42ee99
tree03524f47e38bd7d150bff083b52aca7c7700073a
parent1ca213dab02db42c63a6b7e191044c0124f9bfb2

Legalize: make the feature set comptime-known in zig1

This allows legalizations to be added that aren't used by zig1 without affecting the size of zig1.

2 files changed, 78 insertions(+), 42 deletions(-)

src/Air/Legalize.zig+65-36
......@@ -1,7 +1,36 @@
11pt: Zcu.PerThread,
22air_instructions: std.MultiArrayList(Air.Inst),
33air_extra: std.ArrayListUnmanaged(u32),
4features: *const Features,
4features: if (switch (dev.env) {
5 .bootstrap => @import("../codegen/c.zig").legalizeFeatures(undefined),
6 else => null,
7}) |bootstrap_features| struct {
8 fn init(features: *const Features) @This() {
9 assert(features.eql(bootstrap_features.*));
10 return .{};
11 }
12 /// `inline` to propagate comptime-known result.
13 inline fn has(_: @This(), comptime feature: Feature) bool {
14 return comptime bootstrap_features.contains(feature);
15 }
16 /// `inline` to propagate comptime-known result.
17 fn hasAny(_: @This(), comptime features: []const Feature) bool {
18 return comptime !bootstrap_features.intersectWith(.initMany(features)).eql(.initEmpty());
19 }
20} else struct {
21 features: *const Features,
22 /// `inline` to propagate whether `dev.check` returns.
23 inline fn init(features: *const Features) @This() {
24 dev.check(.legalize);
25 return .{ .features = features };
26 }
27 fn has(rt: @This(), comptime feature: Feature) bool {
28 return rt.features.contains(feature);
29 }
30 fn hasAny(rt: @This(), comptime features: []const Feature) bool {
31 return !rt.features.intersectWith(comptime .initMany(features)).eql(comptime .initEmpty());
32 }
33},
534
635pub const Feature = enum {
736 scalarize_add,
......@@ -199,7 +228,7 @@ pub const Feature = enum {
199228 .float_from_int => .scalarize_float_from_int,
200229 .shuffle_one => .scalarize_shuffle_one,
201230 .shuffle_two => .scalarize_shuffle_two,
202 .select => .scalarize_selects,
231 .select => .scalarize_select,
203232 .mul_add => .scalarize_mul_add,
204233 };
205234 }
......@@ -210,13 +239,12 @@ pub const Features = std.enums.EnumSet(Feature);
210239pub const Error = std.mem.Allocator.Error;
211240
212241pub fn legalize(air: *Air, pt: Zcu.PerThread, features: *const Features) Error!void {
213 dev.check(.legalize);
214242 assert(!features.eql(comptime .initEmpty())); // backend asked to run legalize, but no features were enabled
215243 var l: Legalize = .{
216244 .pt = pt,
217245 .air_instructions = air.instructions.toMultiArrayList(),
218246 .air_extra = air.extra,
219 .features = features,
247 .features = .init(features),
220248 };
221249 defer air.* = l.getTmpAir();
222250 const main_extra = l.extraData(Air.Block, l.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)]);
......@@ -278,28 +306,28 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
278306 .bit_and,
279307 .bit_or,
280308 .xor,
281 => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) {
309 => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) {
282310 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
283311 if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op);
284312 },
285 .add_safe => if (l.features.contains(.expand_add_safe)) {
286 assert(!l.features.contains(.scalarize_add_safe)); // it doesn't make sense to do both
313 .add_safe => if (l.features.has(.expand_add_safe)) {
314 assert(!l.features.has(.scalarize_add_safe)); // it doesn't make sense to do both
287315 continue :inst l.replaceInst(inst, .block, try l.safeArithmeticBlockPayload(inst, .add_with_overflow));
288 } else if (l.features.contains(.scalarize_add_safe)) {
316 } else if (l.features.has(.scalarize_add_safe)) {
289317 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
290318 if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op);
291319 },
292 .sub_safe => if (l.features.contains(.expand_sub_safe)) {
293 assert(!l.features.contains(.scalarize_sub_safe)); // it doesn't make sense to do both
320 .sub_safe => if (l.features.has(.expand_sub_safe)) {
321 assert(!l.features.has(.scalarize_sub_safe)); // it doesn't make sense to do both
294322 continue :inst l.replaceInst(inst, .block, try l.safeArithmeticBlockPayload(inst, .sub_with_overflow));
295 } else if (l.features.contains(.scalarize_sub_safe)) {
323 } else if (l.features.has(.scalarize_sub_safe)) {
296324 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
297325 if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op);
298326 },
299 .mul_safe => if (l.features.contains(.expand_mul_safe)) {
300 assert(!l.features.contains(.scalarize_mul_safe)); // it doesn't make sense to do both
327 .mul_safe => if (l.features.has(.expand_mul_safe)) {
328 assert(!l.features.has(.scalarize_mul_safe)); // it doesn't make sense to do both
301329 continue :inst l.replaceInst(inst, .block, try l.safeArithmeticBlockPayload(inst, .mul_with_overflow));
302 } else if (l.features.contains(.scalarize_mul_safe)) {
330 } else if (l.features.has(.scalarize_mul_safe)) {
303331 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
304332 if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op);
305333 },
......@@ -308,7 +336,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
308336 .sub_with_overflow,
309337 .mul_with_overflow,
310338 .shl_with_overflow,
311 => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) {
339 => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) {
312340 const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl;
313341 if (ty_pl.ty.toType().fieldType(0, zcu).isVector(zcu)) continue :inst l.replaceInst(inst, .block, try l.scalarizeOverflowBlockPayload(inst));
314342 },
......@@ -320,13 +348,13 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
320348 .shl,
321349 .shl_exact,
322350 .shl_sat,
323 => |air_tag| if (!l.features.intersectWith(comptime .initMany(&.{
351 => |air_tag| if (l.features.hasAny(&.{
324352 .unsplat_shift_rhs,
325353 .scalarize(air_tag),
326 })).eql(comptime .initEmpty())) {
354 })) {
327355 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
328356 if (l.typeOf(bin_op.rhs).isVector(zcu)) {
329 if (l.features.contains(.unsplat_shift_rhs)) {
357 if (l.features.has(.unsplat_shift_rhs)) {
330358 if (bin_op.rhs.toInterned()) |rhs_ip_index| switch (ip.indexToKey(rhs_ip_index)) {
331359 else => {},
332360 .aggregate => |aggregate| switch (aggregate.storage) {
......@@ -347,7 +375,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
347375 }
348376 }
349377 }
350 if (l.features.contains(comptime .scalarize(air_tag))) continue :inst try l.scalarize(inst, .bin_op);
378 if (l.features.has(comptime .scalarize(air_tag))) continue :inst try l.scalarize(inst, .bin_op);
351379 }
352380 },
353381 inline .not,
......@@ -364,11 +392,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
364392 .int_from_float,
365393 .int_from_float_optimized,
366394 .float_from_int,
367 => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) {
395 => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) {
368396 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
369397 if (ty_op.ty.toType().isVector(zcu)) continue :inst try l.scalarize(inst, .ty_op);
370398 },
371 .bitcast => if (l.features.contains(.scalarize_bitcast)) {
399 .bitcast => if (l.features.has(.scalarize_bitcast)) {
372400 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
373401
374402 const to_ty = ty_op.ty.toType();
......@@ -404,10 +432,10 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
404432 };
405433 if (!from_ty_legal) continue :inst l.replaceInst(inst, .block, try l.scalarizeBitcastOperandBlockPayload(inst));
406434 },
407 .intcast_safe => if (l.features.contains(.expand_intcast_safe)) {
408 assert(!l.features.contains(.scalarize_intcast_safe)); // it doesn't make sense to do both
435 .intcast_safe => if (l.features.has(.expand_intcast_safe)) {
436 assert(!l.features.has(.scalarize_intcast_safe)); // it doesn't make sense to do both
409437 continue :inst l.replaceInst(inst, .block, try l.safeIntcastBlockPayload(inst));
410 } else if (l.features.contains(.scalarize_intcast_safe)) {
438 } else if (l.features.has(.scalarize_intcast_safe)) {
411439 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
412440 if (ty_op.ty.toType().isVector(zcu)) continue :inst try l.scalarize(inst, .ty_op);
413441 },
......@@ -442,7 +470,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
442470 .trunc_float,
443471 .neg,
444472 .neg_optimized,
445 => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) {
473 => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) {
446474 const un_op = l.air_instructions.items(.data)[@intFromEnum(inst)].un_op;
447475 if (l.typeOf(un_op).isVector(zcu)) continue :inst try l.scalarize(inst, .un_op);
448476 },
......@@ -459,7 +487,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
459487 .cmp_neq,
460488 .cmp_neq_optimized,
461489 => {},
462 inline .cmp_vector, .cmp_vector_optimized => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) {
490 inline .cmp_vector, .cmp_vector_optimized => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) {
463491 const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl;
464492 if (ty_pl.ty.toType().isVector(zcu)) continue :inst try l.scalarize(inst, .cmp_vector);
465493 },
......@@ -513,13 +541,13 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
513541 .bool_and,
514542 .bool_or,
515543 => {},
516 .load => if (l.features.contains(.expand_packed_load)) {
544 .load => if (l.features.has(.expand_packed_load)) {
517545 const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op;
518546 const ptr_info = l.typeOf(ty_op.operand).ptrInfo(zcu);
519547 if (ptr_info.packed_offset.host_size > 0 and ptr_info.flags.vector_index == .none) continue :inst l.replaceInst(inst, .block, try l.packedLoadBlockPayload(inst));
520548 },
521549 .ret, .ret_safe, .ret_load => {},
522 .store, .store_safe => if (l.features.contains(.expand_packed_store)) {
550 .store, .store_safe => if (l.features.has(.expand_packed_store)) {
523551 const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op;
524552 const ptr_info = l.typeOf(bin_op.lhs).ptrInfo(zcu);
525553 if (ptr_info.packed_offset.host_size > 0 and ptr_info.flags.vector_index == .none) continue :inst l.replaceInst(inst, .block, try l.packedStoreBlockPayload(inst));
......@@ -542,7 +570,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
542570 .struct_field_ptr_index_2,
543571 .struct_field_ptr_index_3,
544572 => {},
545 .struct_field_val => if (l.features.contains(.expand_packed_struct_field_val)) {
573 .struct_field_val => if (l.features.has(.expand_packed_struct_field_val)) {
546574 const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl;
547575 const extra = l.extraData(Air.StructField, ty_pl.payload).data;
548576 switch (l.typeOf(extra.struct_operand).containerLayout(zcu)) {
......@@ -564,7 +592,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
564592 .ptr_elem_ptr,
565593 .array_to_slice,
566594 => {},
567 .reduce, .reduce_optimized => if (l.features.contains(.reduce_one_elem_to_bitcast)) {
595 .reduce, .reduce_optimized => if (l.features.has(.reduce_one_elem_to_bitcast)) {
568596 const reduce = l.air_instructions.items(.data)[@intFromEnum(inst)].reduce;
569597 const vector_ty = l.typeOf(reduce.operand);
570598 switch (vector_ty.vectorLen(zcu)) {
......@@ -577,9 +605,9 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
577605 }
578606 },
579607 .splat => {},
580 .shuffle_one => if (l.features.contains(.scalarize_shuffle_one)) continue :inst try l.scalarize(inst, .shuffle_one),
581 .shuffle_two => if (l.features.contains(.scalarize_shuffle_two)) continue :inst try l.scalarize(inst, .shuffle_two),
582 .select => if (l.features.contains(.scalarize_select)) continue :inst try l.scalarize(inst, .select),
608 .shuffle_one => if (l.features.has(.scalarize_shuffle_one)) continue :inst try l.scalarize(inst, .shuffle_one),
609 .shuffle_two => if (l.features.has(.scalarize_shuffle_two)) continue :inst try l.scalarize(inst, .shuffle_two),
610 .select => if (l.features.has(.scalarize_select)) continue :inst try l.scalarize(inst, .select),
583611 .memset,
584612 .memset_safe,
585613 .memcpy,
......@@ -597,7 +625,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
597625 .error_name,
598626 .error_set_has_value,
599627 => {},
600 .aggregate_init => if (l.features.contains(.expand_packed_aggregate_init)) {
628 .aggregate_init => if (l.features.has(.expand_packed_aggregate_init)) {
601629 const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl;
602630 const agg_ty = ty_pl.ty.toType();
603631 switch (agg_ty.zigTypeTag(zcu)) {
......@@ -609,7 +637,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
609637 }
610638 },
611639 .union_init, .prefetch => {},
612 .mul_add => if (l.features.contains(.scalarize_mul_add)) {
640 .mul_add => if (l.features.has(.scalarize_mul_add)) {
613641 const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op;
614642 if (l.typeOf(pl_op.operand).isVector(zcu)) continue :inst try l.scalarize(inst, .pl_op_bin);
615643 },
......@@ -636,6 +664,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
636664}
637665
638666const ScalarizeForm = enum { un_op, ty_op, bin_op, pl_op_bin, bitcast, cmp_vector, shuffle_one, shuffle_two, select };
667/// inline to propagate comptime-known `replaceInst` result.
639668inline fn scalarize(l: *Legalize, orig_inst: Air.Inst.Index, comptime form: ScalarizeForm) Error!Air.Inst.Tag {
640669 return l.replaceInst(orig_inst, .block, try l.scalarizeBlockPayload(orig_inst, form));
641670}
......@@ -2691,7 +2720,7 @@ fn addBlockBody(l: *Legalize, body: []const Air.Inst.Index) Error!u32 {
26912720}
26922721
26932722/// Returns `tag` to remind the caller to `continue :inst` the result.
2694/// This is inline to propagate the comptime-known `tag`.
2723/// `inline` to propagate the comptime-known `tag` result.
26952724inline fn replaceInst(l: *Legalize, inst: Air.Inst.Index, comptime tag: Air.Inst.Tag, data: Air.Inst.Data) Air.Inst.Tag {
26962725 const orig_ty = if (std.debug.runtime_safety) l.typeOfIndex(inst) else {};
26972726 l.air_instructions.set(@intFromEnum(inst), .{ .tag = tag, .data = data });
src/codegen/c.zig+13-6
......@@ -23,12 +23,19 @@ const BigIntLimb = std.math.big.Limb;
2323const BigInt = std.math.big.int;
2424
2525pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
26 return if (dev.env.supports(.legalize)) comptime &.initMany(&.{
27 .expand_intcast_safe,
28 .expand_add_safe,
29 .expand_sub_safe,
30 .expand_mul_safe,
31 }) else null; // we don't currently ask zig1 to use safe optimization modes
26 return comptime switch (dev.env.supports(.legalize)) {
27 inline false, true => |supports_legalize| &.init(.{
28 // we don't currently ask zig1 to use safe optimization modes
29 .expand_intcast_safe = supports_legalize,
30 .expand_add_safe = supports_legalize,
31 .expand_sub_safe = supports_legalize,
32 .expand_mul_safe = supports_legalize,
33
34 .expand_packed_load = true,
35 .expand_packed_store = true,
36 .expand_packed_struct_field_val = true,
37 }),
38 };
3239}
3340
3441/// For most backends, MIR is basically a sequence of machine code instructions, perhaps with some