authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-11 22:13:54+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-05-11 20:31:51+02:00
log83ab1ba8fdb6900e63e1e0aa3a5e1ab966fc2022
treee9ad849f2c9aa875b12e05219208443eca3b626d
parent435a5660ce62c9c54f8ac9bf0a325811a6b684e6
signaturelock-open Commit is signed but in an unrecognized format.

spirv: lower air is_null, is_non_null

Implements AIR lowering for is_null and is_non_null tags. Additionally this cleans up and centralizes the logic to convert from 'direct' representation to 'indirect' representation and vice-versa. The related functions, as well as the functions that use it, are all moved near eachother so that the conversion logic remains in a central place. Extracting/inserting fields and loading/storing pointers should go through these functions.

1 files changed, 186 insertions(+), 90 deletions(-)

src/codegen/spirv.zig+186-90
...@@ -402,9 +402,21 @@ pub const DeclGen = struct {...@@ -402,9 +402,21 @@ pub const DeclGen = struct {
402 return result_id;402 return result_id;
403 }403 }
404404
405 fn constUndef(self: *DeclGen, ty_ref: SpvType.Ref) Error!IdRef {405 fn constUndef(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef {
406 const result_id = self.spv.allocId();406 const result_id = self.spv.allocId();
407 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id });407 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpUndef, .{
408 .id_result_type = self.typeId(ty_ref),
409 .id_result = result_id,
410 });
411 return result_id;
412 }
413
414 fn constNull(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef {
415 const result_id = self.spv.allocId();
416 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantNull, .{
417 .id_result_type = self.typeId(ty_ref),
418 .id_result = result_id,
419 });
408 return result_id;420 return result_id;
409 }421 }
410422
...@@ -674,7 +686,7 @@ pub const DeclGen = struct {...@@ -674,7 +686,7 @@ pub const DeclGen = struct {
674 try self.addConstBool(has_payload);686 try self.addConstBool(has_payload);
675 return;687 return;
676 } else if (ty.optionalReprIsPayload()) {688 } else if (ty.optionalReprIsPayload()) {
677 // Optional representation is a nullable pointer.689 // Optional representation is a nullable pointer or slice.
678 if (val.castTag(.opt_payload)) |payload| {690 if (val.castTag(.opt_payload)) |payload| {
679 try self.lower(payload_ty, payload.data);691 try self.lower(payload_ty, payload.data);
680 } else if (has_payload) {692 } else if (has_payload) {
...@@ -1257,7 +1269,7 @@ pub const DeclGen = struct {...@@ -1257,7 +1269,7 @@ pub const DeclGen = struct {
12571269
1258 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);1270 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
1259 if (ty.optionalReprIsPayload()) {1271 if (ty.optionalReprIsPayload()) {
1260 // Optional is actually a pointer.1272 // Optional is actually a pointer or a slice.
1261 return payload_ty_ref;1273 return payload_ty_ref;
1262 }1274 }
12631275
...@@ -1523,6 +1535,93 @@ pub const DeclGen = struct {...@@ -1523,6 +1535,93 @@ pub const DeclGen = struct {
1523 }1535 }
1524 }1536 }
15251537
1538 /// Convert representation from indirect (in memory) to direct (in 'register')
1539 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).
1540 fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
1541 // const direct_ty_ref = try self.resolveType(ty, .direct);
1542 return switch (ty.zigTypeTag()) {
1543 .Bool => blk: {
1544 const direct_bool_ty_ref = try self.resolveType(ty, .direct);
1545 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect);
1546 const zero_id = try self.constInt(indirect_bool_ty_ref, 0);
1547 const result_id = self.spv.allocId();
1548 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
1549 .id_result_type = self.typeId(direct_bool_ty_ref),
1550 .id_result = result_id,
1551 .operand_1 = operand_id,
1552 .operand_2 = zero_id,
1553 });
1554 break :blk result_id;
1555 },
1556 else => operand_id,
1557 };
1558 }
1559
1560 /// Convert representation from direct (in 'register) to direct (in memory)
1561 /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect).
1562 fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
1563 return switch (ty.zigTypeTag()) {
1564 .Bool => blk: {
1565 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect);
1566 const zero_id = try self.constInt(indirect_bool_ty_ref, 0);
1567 const one_id = try self.constInt(indirect_bool_ty_ref, 1);
1568 const result_id = self.spv.allocId();
1569 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
1570 .id_result_type = self.typeId(indirect_bool_ty_ref),
1571 .id_result = result_id,
1572 .condition = operand_id,
1573 .object_1 = one_id,
1574 .object_2 = zero_id,
1575 });
1576 break :blk result_id;
1577 },
1578 else => operand_id,
1579 };
1580 }
1581
1582 fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef {
1583 const result_ty_ref = try self.resolveType(result_ty, .indirect);
1584 const result_id = self.spv.allocId();
1585 const indexes = [_]u32{field};
1586 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{
1587 .id_result_type = self.typeId(result_ty_ref),
1588 .id_result = result_id,
1589 .composite = object,
1590 .indexes = &indexes,
1591 });
1592 // Convert bools; direct structs have their field types as indirect values.
1593 return try self.convertToDirect(result_ty, result_id);
1594 }
1595
1596 fn load(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef) !IdRef {
1597 const value_ty = ptr_ty.childType();
1598 const indirect_value_ty_ref = try self.resolveType(value_ty, .indirect);
1599 const result_id = self.spv.allocId();
1600 const access = spec.MemoryAccess.Extended{
1601 .Volatile = ptr_ty.isVolatilePtr(),
1602 };
1603 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
1604 .id_result_type = self.typeId(indirect_value_ty_ref),
1605 .id_result = result_id,
1606 .pointer = ptr_id,
1607 .memory_access = access,
1608 });
1609 return try self.convertToDirect(value_ty, result_id);
1610 }
1611
1612 fn store(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef, value_id: IdRef) !void {
1613 const value_ty = ptr_ty.childType();
1614 const indirect_value_id = try self.convertToIndirect(value_ty, value_id);
1615 const access = spec.MemoryAccess.Extended{
1616 .Volatile = ptr_ty.isVolatilePtr(),
1617 };
1618 try self.func.body.emit(self.spv.gpa, .OpStore, .{
1619 .pointer = ptr_id,
1620 .object = indirect_value_id,
1621 .memory_access = access,
1622 });
1623 }
1624
1526 fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void {1625 fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void {
1527 for (body) |inst| {1626 for (body) |inst| {
1528 try self.genInst(inst);1627 try self.genInst(inst);
...@@ -1615,6 +1714,9 @@ pub const DeclGen = struct {...@@ -1615,6 +1714,9 @@ pub const DeclGen = struct {
1615 .unwrap_errunion_err => try self.airErrUnionErr(inst),1714 .unwrap_errunion_err => try self.airErrUnionErr(inst),
1616 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),1715 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
16171716
1717 .is_null => try self.airIsNull(inst, .is_null),
1718 .is_non_null => try self.airIsNull(inst, .is_non_null),
1719
1618 .assembly => try self.airAssembly(inst),1720 .assembly => try self.airAssembly(inst),
16191721
1620 .call => try self.airCall(inst, .auto),1722 .call => try self.airCall(inst, .auto),
...@@ -1776,18 +1878,17 @@ pub const DeclGen = struct {...@@ -1776,18 +1878,17 @@ pub const DeclGen = struct {
1776 .float, .bool => unreachable,1878 .float, .bool => unreachable,
1777 }1879 }
17781880
1779 const operand_ty_id = try self.resolveTypeId(operand_ty);1881 // The operand type must be the same as the result type in SPIR-V.
1780 const result_type_id = try self.resolveTypeId(result_ty);1882 const operand_ty_ref = try self.resolveType(operand_ty, .direct);
17811883 const operand_ty_id = self.typeId(operand_ty_ref);
1782 const overflow_member_ty_ref = try self.intType(.unsigned, info.bits);
17831884
1784 const op_result_id = blk: {1885 const op_result_id = blk: {
1785 // Construct the SPIR-V result type.1886 // Construct the SPIR-V result type.
1786 // It is almost the same as the zig one, except that the fields must be the same type1887 // It is almost the same as the zig one, except that the fields must be the same type
1787 // and they must be unsigned.1888 // and they must be unsigned.
1788 const overflow_result_ty_ref = try self.spv.simpleStructType(&.{1889 const overflow_result_ty_ref = try self.spv.simpleStructType(&.{
1789 .{ .ty = overflow_member_ty_ref, .name = "res" },1890 .{ .ty = operand_ty_ref, .name = "res" },
1790 .{ .ty = overflow_member_ty_ref, .name = "ov" },1891 .{ .ty = operand_ty_ref, .name = "ov" },
1791 });1892 });
1792 const result_id = self.spv.allocId();1893 const result_id = self.spv.allocId();
1793 try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{1894 try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{
...@@ -1801,11 +1902,13 @@ pub const DeclGen = struct {...@@ -1801,11 +1902,13 @@ pub const DeclGen = struct {
18011902
1802 // Now convert the SPIR-V flavor result into a Zig-flavor result.1903 // Now convert the SPIR-V flavor result into a Zig-flavor result.
1803 // First, extract the two fields.1904 // First, extract the two fields.
1804 const unsigned_result = try self.extractField(overflow_member_ty_ref, op_result_id, 0);1905 const unsigned_result = try self.extractField(operand_ty, op_result_id, 0);
1805 const overflow = try self.extractField(overflow_member_ty_ref, op_result_id, 1);1906 const overflow = try self.extractField(operand_ty, op_result_id, 1);
18061907
1807 // We need to convert the results to the types that Zig expects here.1908 // We need to convert the results to the types that Zig expects here.
1808 // The `result` is the same type except unsigned, so we can just bitcast that.1909 // The `result` is the same type except unsigned, so we can just bitcast that.
1910 // TODO: This can be removed in Kernels as there are only unsigned ints. Maybe for
1911 // shaders as well?
1809 const result = try self.bitcast(operand_ty_id, unsigned_result);1912 const result = try self.bitcast(operand_ty_id, unsigned_result);
18101913
1811 // The overflow needs to be converted into whatever is used to represent it in Zig.1914 // The overflow needs to be converted into whatever is used to represent it in Zig.
...@@ -1828,7 +1931,7 @@ pub const DeclGen = struct {...@@ -1828,7 +1931,7 @@ pub const DeclGen = struct {
1828 const result_id = self.spv.allocId();1931 const result_id = self.spv.allocId();
1829 const constituents = [_]IdRef{ result, casted_overflow };1932 const constituents = [_]IdRef{ result, casted_overflow };
1830 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{1933 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
1831 .id_result_type = result_type_id,1934 .id_result_type = operand_ty_id,
1832 .id_result = result_id,1935 .id_result = result_id,
1833 .constituents = &constituents,1936 .constituents = &constituents,
1834 });1937 });
...@@ -1980,25 +2083,14 @@ pub const DeclGen = struct {...@@ -1980,25 +2083,14 @@ pub const DeclGen = struct {
1980 return result_id;2083 return result_id;
1981 }2084 }
19822085
1983 fn extractField(self: *DeclGen, result_ty_ref: SpvType.Ref, object: IdRef, field: u32) !IdRef {
1984 const result_id = self.spv.allocId();
1985 const indexes = [_]u32{field};
1986 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{
1987 .id_result_type = self.typeId(result_ty_ref),
1988 .id_result = result_id,
1989 .composite = object,
1990 .indexes = &indexes,
1991 });
1992 // TODO: Convert bools, direct structs should have their field types as indirect values.
1993 return result_id;
1994 }
1995
1996 fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef {2086 fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef {
1997 if (self.liveness.isUnused(inst)) return null;2087 if (self.liveness.isUnused(inst)) return null;
1998 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2088 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2089 const field_ty = self.air.typeOfIndex(inst);
2090 const operand_id = try self.resolve(ty_op.operand);
1999 return try self.extractField(2091 return try self.extractField(
2000 try self.resolveType(self.air.typeOfIndex(inst), .direct),2092 field_ty,
2001 try self.resolve(ty_op.operand),2093 operand_id,
2002 field,2094 field,
2003 );2095 );
2004 }2096 }
...@@ -2367,35 +2459,6 @@ pub const DeclGen = struct {...@@ -2367,35 +2459,6 @@ pub const DeclGen = struct {
2367 return try self.load(ptr_ty, operand);2459 return try self.load(ptr_ty, operand);
2368 }2460 }
23692461
2370 fn load(self: *DeclGen, ptr_ty: Type, ptr: IdRef) !IdRef {
2371 const value_ty = ptr_ty.childType();
2372 const direct_result_ty_ref = try self.resolveType(value_ty, .direct);
2373 const indirect_result_ty_ref = try self.resolveType(value_ty, .indirect);
2374 const result_id = self.spv.allocId();
2375 const access = spec.MemoryAccess.Extended{
2376 .Volatile = ptr_ty.isVolatilePtr(),
2377 };
2378 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
2379 .id_result_type = self.typeId(indirect_result_ty_ref),
2380 .id_result = result_id,
2381 .pointer = ptr,
2382 .memory_access = access,
2383 });
2384 if (value_ty.zigTypeTag() == .Bool) {
2385 // Convert indirect bool to direct bool
2386 const zero_id = try self.constInt(indirect_result_ty_ref, 0);
2387 const casted_result_id = self.spv.allocId();
2388 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
2389 .id_result_type = self.typeId(direct_result_ty_ref),
2390 .id_result = casted_result_id,
2391 .operand_1 = result_id,
2392 .operand_2 = zero_id,
2393 });
2394 return casted_result_id;
2395 }
2396 return result_id;
2397 }
2398
2399 fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void {2462 fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void {
2400 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2463 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2401 const ptr_ty = self.air.typeOf(bin_op.lhs);2464 const ptr_ty = self.air.typeOf(bin_op.lhs);
...@@ -2405,35 +2468,6 @@ pub const DeclGen = struct {...@@ -2405,35 +2468,6 @@ pub const DeclGen = struct {
2405 try self.store(ptr_ty, ptr, value);2468 try self.store(ptr_ty, ptr, value);
2406 }2469 }
24072470
2408 fn store(self: *DeclGen, ptr_ty: Type, ptr: IdRef, value: IdRef) !void {
2409 const value_ty = ptr_ty.childType();
2410 const converted_value = switch (value_ty.zigTypeTag()) {
2411 .Bool => blk: {
2412 const indirect_bool_ty_ref = try self.resolveType(value_ty, .indirect);
2413 const result_id = self.spv.allocId();
2414 const zero = try self.constInt(indirect_bool_ty_ref, 0);
2415 const one = try self.constInt(indirect_bool_ty_ref, 1);
2416 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
2417 .id_result_type = self.typeId(indirect_bool_ty_ref),
2418 .id_result = result_id,
2419 .condition = value,
2420 .object_1 = one,
2421 .object_2 = zero,
2422 });
2423 break :blk result_id;
2424 },
2425 else => value,
2426 };
2427 const access = spec.MemoryAccess.Extended{
2428 .Volatile = ptr_ty.isVolatilePtr(),
2429 };
2430 try self.func.body.emit(self.spv.gpa, .OpStore, .{
2431 .pointer = ptr,
2432 .object = converted_value,
2433 .memory_access = access,
2434 });
2435 }
2436
2437 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {2471 fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {
2438 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2472 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2439 const loop = self.air.extraData(Air.Block, ty_pl.payload);2473 const loop = self.air.extraData(Air.Block, ty_pl.payload);
...@@ -2488,14 +2522,13 @@ pub const DeclGen = struct {...@@ -2488,14 +2522,13 @@ pub const DeclGen = struct {
2488 const payload_ty = self.air.typeOfIndex(inst);2522 const payload_ty = self.air.typeOfIndex(inst);
24892523
2490 const err_ty_ref = try self.resolveType(Type.anyerror, .direct);2524 const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
2491 const payload_ty_ref = try self.resolveType(payload_ty, .direct);
2492 const bool_ty_ref = try self.resolveType(Type.bool, .direct);2525 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
24932526
2494 const eu_layout = self.errorUnionLayout(payload_ty);2527 const eu_layout = self.errorUnionLayout(payload_ty);
24952528
2496 if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {2529 if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {
2497 const err_id = if (eu_layout.payload_has_bits)2530 const err_id = if (eu_layout.payload_has_bits)
2498 try self.extractField(err_ty_ref, err_union_id, eu_layout.errorFieldIndex())2531 try self.extractField(Type.anyerror, err_union_id, eu_layout.errorFieldIndex())
2499 else2532 else
2500 err_union_id;2533 err_union_id;
25012534
...@@ -2535,7 +2568,7 @@ pub const DeclGen = struct {...@@ -2535,7 +2568,7 @@ pub const DeclGen = struct {
2535 return null;2568 return null;
2536 }2569 }
25372570
2538 return try self.extractField(payload_ty_ref, err_union_id, eu_layout.payloadFieldIndex());2571 return try self.extractField(payload_ty, err_union_id, eu_layout.payloadFieldIndex());
2539 }2572 }
25402573
2541 fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {2574 fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -2559,7 +2592,7 @@ pub const DeclGen = struct {...@@ -2559,7 +2592,7 @@ pub const DeclGen = struct {
2559 return operand_id;2592 return operand_id;
2560 }2593 }
25612594
2562 return try self.extractField(err_ty_ref, operand_id, eu_layout.errorFieldIndex());2595 return try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex());
2563 }2596 }
25642597
2565 fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {2598 fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -2598,6 +2631,69 @@ pub const DeclGen = struct {...@@ -2598,6 +2631,69 @@ pub const DeclGen = struct {
2598 return result_id;2631 return result_id;
2599 }2632 }
26002633
2634 fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef {
2635 if (self.liveness.isUnused(inst)) return null;
2636
2637 const un_op = self.air.instructions.items(.data)[inst].un_op;
2638 const operand_id = try self.resolve(un_op);
2639 const optional_ty = self.air.typeOf(un_op);
2640
2641 var buf: Type.Payload.ElemType = undefined;
2642 const payload_ty = optional_ty.optionalChild(&buf);
2643
2644 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
2645
2646 if (optional_ty.optionalReprIsPayload()) {
2647 // Pointer payload represents nullability: pointer or slice.
2648
2649 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
2650 const ptr_ty = if (payload_ty.isSlice())
2651 payload_ty.slicePtrFieldType(&ptr_buf)
2652 else
2653 payload_ty;
2654
2655 const ptr_id = if (payload_ty.isSlice())
2656 try self.extractField(Type.bool, operand_id, 0)
2657 else
2658 operand_id;
2659
2660 const payload_ty_ref = try self.resolveType(ptr_ty, .direct);
2661 const null_id = try self.constNull(payload_ty_ref);
2662 const result_id = self.spv.allocId();
2663 const operands = .{
2664 .id_result_type = self.typeId(bool_ty_ref),
2665 .id_result = result_id,
2666 .operand_1 = ptr_id,
2667 .operand_2 = null_id,
2668 };
2669 switch (pred) {
2670 .is_null => try self.func.body.emit(self.spv.gpa, .OpPtrEqual, operands),
2671 .is_non_null => try self.func.body.emit(self.spv.gpa, .OpPtrNotEqual, operands),
2672 }
2673 return result_id;
2674 }
2675
2676 const is_non_null_id = if (optional_ty.hasRuntimeBitsIgnoreComptime())
2677 try self.extractField(Type.bool, operand_id, 1)
2678 else
2679 // Optional representation is bool indicating whether the optional is set
2680 operand_id;
2681
2682 return switch (pred) {
2683 .is_null => blk: {
2684 // Invert condition
2685 const result_id = self.spv.allocId();
2686 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{
2687 .id_result_type = self.typeId(bool_ty_ref),
2688 .id_result = result_id,
2689 .operand = is_non_null_id,
2690 });
2691 break :blk result_id;
2692 },
2693 .is_non_null => is_non_null_id,
2694 };
2695 }
2696
2601 fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void {2697 fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void {
2602 const target = self.getTarget();2698 const target = self.getTarget();
2603 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2699 const pl_op = self.air.instructions.items(.data)[inst].pl_op;