| ... | ... | @@ -1,7 +1,36 @@ |
| 1 | 1 | pt: Zcu.PerThread, |
| 2 | 2 | air_instructions: std.MultiArrayList(Air.Inst), |
| 3 | 3 | air_extra: std.ArrayListUnmanaged(u32), |
| 4 | | features: *const Features, |
| 4 | features: 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 | }, |
| 5 | 34 | |
| 6 | 35 | pub const Feature = enum { |
| 7 | 36 | scalarize_add, |
| ... | ... | @@ -199,7 +228,7 @@ pub const Feature = enum { |
| 199 | 228 | .float_from_int => .scalarize_float_from_int, |
| 200 | 229 | .shuffle_one => .scalarize_shuffle_one, |
| 201 | 230 | .shuffle_two => .scalarize_shuffle_two, |
| 202 | | .select => .scalarize_selects, |
| 231 | .select => .scalarize_select, |
| 203 | 232 | .mul_add => .scalarize_mul_add, |
| 204 | 233 | }; |
| 205 | 234 | } |
| ... | ... | @@ -210,13 +239,12 @@ pub const Features = std.enums.EnumSet(Feature); |
| 210 | 239 | pub const Error = std.mem.Allocator.Error; |
| 211 | 240 | |
| 212 | 241 | pub fn legalize(air: *Air, pt: Zcu.PerThread, features: *const Features) Error!void { |
| 213 | | dev.check(.legalize); |
| 214 | 242 | assert(!features.eql(comptime .initEmpty())); // backend asked to run legalize, but no features were enabled |
| 215 | 243 | var l: Legalize = .{ |
| 216 | 244 | .pt = pt, |
| 217 | 245 | .air_instructions = air.instructions.toMultiArrayList(), |
| 218 | 246 | .air_extra = air.extra, |
| 219 | | .features = features, |
| 247 | .features = .init(features), |
| 220 | 248 | }; |
| 221 | 249 | defer air.* = l.getTmpAir(); |
| 222 | 250 | 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 { |
| 278 | 306 | .bit_and, |
| 279 | 307 | .bit_or, |
| 280 | 308 | .xor, |
| 281 | | => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) { |
| 309 | => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) { |
| 282 | 310 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 283 | 311 | if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op); |
| 284 | 312 | }, |
| 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 |
| 287 | 315 | 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)) { |
| 289 | 317 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 290 | 318 | if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op); |
| 291 | 319 | }, |
| 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 |
| 294 | 322 | 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)) { |
| 296 | 324 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 297 | 325 | if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op); |
| 298 | 326 | }, |
| 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 |
| 301 | 329 | 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)) { |
| 303 | 331 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 304 | 332 | if (l.typeOf(bin_op.lhs).isVector(zcu)) continue :inst try l.scalarize(inst, .bin_op); |
| 305 | 333 | }, |
| ... | ... | @@ -308,7 +336,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 308 | 336 | .sub_with_overflow, |
| 309 | 337 | .mul_with_overflow, |
| 310 | 338 | .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))) { |
| 312 | 340 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 313 | 341 | if (ty_pl.ty.toType().fieldType(0, zcu).isVector(zcu)) continue :inst l.replaceInst(inst, .block, try l.scalarizeOverflowBlockPayload(inst)); |
| 314 | 342 | }, |
| ... | ... | @@ -320,13 +348,13 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 320 | 348 | .shl, |
| 321 | 349 | .shl_exact, |
| 322 | 350 | .shl_sat, |
| 323 | | => |air_tag| if (!l.features.intersectWith(comptime .initMany(&.{ |
| 351 | => |air_tag| if (l.features.hasAny(&.{ |
| 324 | 352 | .unsplat_shift_rhs, |
| 325 | 353 | .scalarize(air_tag), |
| 326 | | })).eql(comptime .initEmpty())) { |
| 354 | })) { |
| 327 | 355 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 328 | 356 | if (l.typeOf(bin_op.rhs).isVector(zcu)) { |
| 329 | | if (l.features.contains(.unsplat_shift_rhs)) { |
| 357 | if (l.features.has(.unsplat_shift_rhs)) { |
| 330 | 358 | if (bin_op.rhs.toInterned()) |rhs_ip_index| switch (ip.indexToKey(rhs_ip_index)) { |
| 331 | 359 | else => {}, |
| 332 | 360 | .aggregate => |aggregate| switch (aggregate.storage) { |
| ... | ... | @@ -347,7 +375,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 347 | 375 | } |
| 348 | 376 | } |
| 349 | 377 | } |
| 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); |
| 351 | 379 | } |
| 352 | 380 | }, |
| 353 | 381 | inline .not, |
| ... | ... | @@ -364,11 +392,11 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 364 | 392 | .int_from_float, |
| 365 | 393 | .int_from_float_optimized, |
| 366 | 394 | .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))) { |
| 368 | 396 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 369 | 397 | if (ty_op.ty.toType().isVector(zcu)) continue :inst try l.scalarize(inst, .ty_op); |
| 370 | 398 | }, |
| 371 | | .bitcast => if (l.features.contains(.scalarize_bitcast)) { |
| 399 | .bitcast => if (l.features.has(.scalarize_bitcast)) { |
| 372 | 400 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 373 | 401 | |
| 374 | 402 | const to_ty = ty_op.ty.toType(); |
| ... | ... | @@ -404,10 +432,10 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 404 | 432 | }; |
| 405 | 433 | if (!from_ty_legal) continue :inst l.replaceInst(inst, .block, try l.scalarizeBitcastOperandBlockPayload(inst)); |
| 406 | 434 | }, |
| 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 |
| 409 | 437 | 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)) { |
| 411 | 439 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 412 | 440 | if (ty_op.ty.toType().isVector(zcu)) continue :inst try l.scalarize(inst, .ty_op); |
| 413 | 441 | }, |
| ... | ... | @@ -442,7 +470,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 442 | 470 | .trunc_float, |
| 443 | 471 | .neg, |
| 444 | 472 | .neg_optimized, |
| 445 | | => |air_tag| if (l.features.contains(comptime .scalarize(air_tag))) { |
| 473 | => |air_tag| if (l.features.has(comptime .scalarize(air_tag))) { |
| 446 | 474 | const un_op = l.air_instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 447 | 475 | if (l.typeOf(un_op).isVector(zcu)) continue :inst try l.scalarize(inst, .un_op); |
| 448 | 476 | }, |
| ... | ... | @@ -459,7 +487,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 459 | 487 | .cmp_neq, |
| 460 | 488 | .cmp_neq_optimized, |
| 461 | 489 | => {}, |
| 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))) { |
| 463 | 491 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 464 | 492 | if (ty_pl.ty.toType().isVector(zcu)) continue :inst try l.scalarize(inst, .cmp_vector); |
| 465 | 493 | }, |
| ... | ... | @@ -513,13 +541,13 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 513 | 541 | .bool_and, |
| 514 | 542 | .bool_or, |
| 515 | 543 | => {}, |
| 516 | | .load => if (l.features.contains(.expand_packed_load)) { |
| 544 | .load => if (l.features.has(.expand_packed_load)) { |
| 517 | 545 | const ty_op = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 518 | 546 | const ptr_info = l.typeOf(ty_op.operand).ptrInfo(zcu); |
| 519 | 547 | 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)); |
| 520 | 548 | }, |
| 521 | 549 | .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)) { |
| 523 | 551 | const bin_op = l.air_instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 524 | 552 | const ptr_info = l.typeOf(bin_op.lhs).ptrInfo(zcu); |
| 525 | 553 | 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 { |
| 542 | 570 | .struct_field_ptr_index_2, |
| 543 | 571 | .struct_field_ptr_index_3, |
| 544 | 572 | => {}, |
| 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)) { |
| 546 | 574 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 547 | 575 | const extra = l.extraData(Air.StructField, ty_pl.payload).data; |
| 548 | 576 | switch (l.typeOf(extra.struct_operand).containerLayout(zcu)) { |
| ... | ... | @@ -564,7 +592,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 564 | 592 | .ptr_elem_ptr, |
| 565 | 593 | .array_to_slice, |
| 566 | 594 | => {}, |
| 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)) { |
| 568 | 596 | const reduce = l.air_instructions.items(.data)[@intFromEnum(inst)].reduce; |
| 569 | 597 | const vector_ty = l.typeOf(reduce.operand); |
| 570 | 598 | switch (vector_ty.vectorLen(zcu)) { |
| ... | ... | @@ -577,9 +605,9 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 577 | 605 | } |
| 578 | 606 | }, |
| 579 | 607 | .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), |
| 583 | 611 | .memset, |
| 584 | 612 | .memset_safe, |
| 585 | 613 | .memcpy, |
| ... | ... | @@ -597,7 +625,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 597 | 625 | .error_name, |
| 598 | 626 | .error_set_has_value, |
| 599 | 627 | => {}, |
| 600 | | .aggregate_init => if (l.features.contains(.expand_packed_aggregate_init)) { |
| 628 | .aggregate_init => if (l.features.has(.expand_packed_aggregate_init)) { |
| 601 | 629 | const ty_pl = l.air_instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 602 | 630 | const agg_ty = ty_pl.ty.toType(); |
| 603 | 631 | switch (agg_ty.zigTypeTag(zcu)) { |
| ... | ... | @@ -609,7 +637,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 609 | 637 | } |
| 610 | 638 | }, |
| 611 | 639 | .union_init, .prefetch => {}, |
| 612 | | .mul_add => if (l.features.contains(.scalarize_mul_add)) { |
| 640 | .mul_add => if (l.features.has(.scalarize_mul_add)) { |
| 613 | 641 | const pl_op = l.air_instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 614 | 642 | if (l.typeOf(pl_op.operand).isVector(zcu)) continue :inst try l.scalarize(inst, .pl_op_bin); |
| 615 | 643 | }, |
| ... | ... | @@ -636,6 +664,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { |
| 636 | 664 | } |
| 637 | 665 | |
| 638 | 666 | const 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. |
| 639 | 668 | inline fn scalarize(l: *Legalize, orig_inst: Air.Inst.Index, comptime form: ScalarizeForm) Error!Air.Inst.Tag { |
| 640 | 669 | return l.replaceInst(orig_inst, .block, try l.scalarizeBlockPayload(orig_inst, form)); |
| 641 | 670 | } |
| ... | ... | @@ -2691,7 +2720,7 @@ fn addBlockBody(l: *Legalize, body: []const Air.Inst.Index) Error!u32 { |
| 2691 | 2720 | } |
| 2692 | 2721 | |
| 2693 | 2722 | /// 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. |
| 2695 | 2724 | inline fn replaceInst(l: *Legalize, inst: Air.Inst.Index, comptime tag: Air.Inst.Tag, data: Air.Inst.Data) Air.Inst.Tag { |
| 2696 | 2725 | const orig_ty = if (std.debug.runtime_safety) l.typeOfIndex(inst) else {}; |
| 2697 | 2726 | l.air_instructions.set(@intFromEnum(inst), .{ .tag = tag, .data = data }); |