| ... | ... | @@ -402,9 +402,21 @@ pub const DeclGen = struct { |
| 402 | 402 | return result_id; |
| 403 | 403 | } |
| 404 | 404 | |
| 405 | | fn constUndef(self: *DeclGen, ty_ref: SpvType.Ref) Error!IdRef { |
| 405 | fn constUndef(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef { |
| 406 | 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 | 420 | return result_id; |
| 409 | 421 | } |
| 410 | 422 | |
| ... | ... | @@ -674,7 +686,7 @@ pub const DeclGen = struct { |
| 674 | 686 | try self.addConstBool(has_payload); |
| 675 | 687 | return; |
| 676 | 688 | } else if (ty.optionalReprIsPayload()) { |
| 677 | | // Optional representation is a nullable pointer. |
| 689 | // Optional representation is a nullable pointer or slice. |
| 678 | 690 | if (val.castTag(.opt_payload)) |payload| { |
| 679 | 691 | try self.lower(payload_ty, payload.data); |
| 680 | 692 | } else if (has_payload) { |
| ... | ... | @@ -1257,7 +1269,7 @@ pub const DeclGen = struct { |
| 1257 | 1269 | |
| 1258 | 1270 | const payload_ty_ref = try self.resolveType(payload_ty, .indirect); |
| 1259 | 1271 | if (ty.optionalReprIsPayload()) { |
| 1260 | | // Optional is actually a pointer. |
| 1272 | // Optional is actually a pointer or a slice. |
| 1261 | 1273 | return payload_ty_ref; |
| 1262 | 1274 | } |
| 1263 | 1275 | |
| ... | ... | @@ -1523,6 +1535,93 @@ pub const DeclGen = struct { |
| 1523 | 1535 | } |
| 1524 | 1536 | } |
| 1525 | 1537 | |
| 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 | 1625 | fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void { |
| 1527 | 1626 | for (body) |inst| { |
| 1528 | 1627 | try self.genInst(inst); |
| ... | ... | @@ -1615,6 +1714,9 @@ pub const DeclGen = struct { |
| 1615 | 1714 | .unwrap_errunion_err => try self.airErrUnionErr(inst), |
| 1616 | 1715 | .wrap_errunion_err => try self.airWrapErrUnionErr(inst), |
| 1617 | 1716 | |
| 1717 | .is_null => try self.airIsNull(inst, .is_null), |
| 1718 | .is_non_null => try self.airIsNull(inst, .is_non_null), |
| 1719 | |
| 1618 | 1720 | .assembly => try self.airAssembly(inst), |
| 1619 | 1721 | |
| 1620 | 1722 | .call => try self.airCall(inst, .auto), |
| ... | ... | @@ -1776,18 +1878,17 @@ pub const DeclGen = struct { |
| 1776 | 1878 | .float, .bool => unreachable, |
| 1777 | 1879 | } |
| 1778 | 1880 | |
| 1779 | | const operand_ty_id = try self.resolveTypeId(operand_ty); |
| 1780 | | const result_type_id = try self.resolveTypeId(result_ty); |
| 1781 | | |
| 1782 | | const overflow_member_ty_ref = try self.intType(.unsigned, info.bits); |
| 1881 | // The operand type must be the same as the result type in SPIR-V. |
| 1882 | const operand_ty_ref = try self.resolveType(operand_ty, .direct); |
| 1883 | const operand_ty_id = self.typeId(operand_ty_ref); |
| 1783 | 1884 | |
| 1784 | 1885 | const op_result_id = blk: { |
| 1785 | 1886 | // Construct the SPIR-V result type. |
| 1786 | 1887 | // It is almost the same as the zig one, except that the fields must be the same type |
| 1787 | 1888 | // and they must be unsigned. |
| 1788 | 1889 | const overflow_result_ty_ref = try self.spv.simpleStructType(&.{ |
| 1789 | | .{ .ty = overflow_member_ty_ref, .name = "res" }, |
| 1790 | | .{ .ty = overflow_member_ty_ref, .name = "ov" }, |
| 1890 | .{ .ty = operand_ty_ref, .name = "res" }, |
| 1891 | .{ .ty = operand_ty_ref, .name = "ov" }, |
| 1791 | 1892 | }); |
| 1792 | 1893 | const result_id = self.spv.allocId(); |
| 1793 | 1894 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ |
| ... | ... | @@ -1801,11 +1902,13 @@ pub const DeclGen = struct { |
| 1801 | 1902 | |
| 1802 | 1903 | // Now convert the SPIR-V flavor result into a Zig-flavor result. |
| 1803 | 1904 | // First, extract the two fields. |
| 1804 | | const unsigned_result = try self.extractField(overflow_member_ty_ref, op_result_id, 0); |
| 1805 | | const overflow = try self.extractField(overflow_member_ty_ref, op_result_id, 1); |
| 1905 | const unsigned_result = try self.extractField(operand_ty, op_result_id, 0); |
| 1906 | const overflow = try self.extractField(operand_ty, op_result_id, 1); |
| 1806 | 1907 | |
| 1807 | 1908 | // We need to convert the results to the types that Zig expects here. |
| 1808 | 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 | 1912 | const result = try self.bitcast(operand_ty_id, unsigned_result); |
| 1810 | 1913 | |
| 1811 | 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 | 1931 | const result_id = self.spv.allocId(); |
| 1829 | 1932 | const constituents = [_]IdRef{ result, casted_overflow }; |
| 1830 | 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 | 1935 | .id_result = result_id, |
| 1833 | 1936 | .constituents = &constituents, |
| 1834 | 1937 | }); |
| ... | ... | @@ -1980,25 +2083,14 @@ pub const DeclGen = struct { |
| 1980 | 2083 | return result_id; |
| 1981 | 2084 | } |
| 1982 | 2085 | |
| 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 | 2086 | fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { |
| 1997 | 2087 | if (self.liveness.isUnused(inst)) return null; |
| 1998 | 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 | 2091 | return try self.extractField( |
| 2000 | | try self.resolveType(self.air.typeOfIndex(inst), .direct), |
| 2001 | | try self.resolve(ty_op.operand), |
| 2092 | field_ty, |
| 2093 | operand_id, |
| 2002 | 2094 | field, |
| 2003 | 2095 | ); |
| 2004 | 2096 | } |
| ... | ... | @@ -2367,35 +2459,6 @@ pub const DeclGen = struct { |
| 2367 | 2459 | return try self.load(ptr_ty, operand); |
| 2368 | 2460 | } |
| 2369 | 2461 | |
| 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 | 2462 | fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 2400 | 2463 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2401 | 2464 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| ... | ... | @@ -2405,35 +2468,6 @@ pub const DeclGen = struct { |
| 2405 | 2468 | try self.store(ptr_ty, ptr, value); |
| 2406 | 2469 | } |
| 2407 | 2470 | |
| 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 | 2471 | fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 2438 | 2472 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2439 | 2473 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| ... | ... | @@ -2488,14 +2522,13 @@ pub const DeclGen = struct { |
| 2488 | 2522 | const payload_ty = self.air.typeOfIndex(inst); |
| 2489 | 2523 | |
| 2490 | 2524 | const err_ty_ref = try self.resolveType(Type.anyerror, .direct); |
| 2491 | | const payload_ty_ref = try self.resolveType(payload_ty, .direct); |
| 2492 | 2525 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2493 | 2526 | |
| 2494 | 2527 | const eu_layout = self.errorUnionLayout(payload_ty); |
| 2495 | 2528 | |
| 2496 | 2529 | if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { |
| 2497 | 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 | 2532 | else |
| 2500 | 2533 | err_union_id; |
| 2501 | 2534 | |
| ... | ... | @@ -2535,7 +2568,7 @@ pub const DeclGen = struct { |
| 2535 | 2568 | return null; |
| 2536 | 2569 | } |
| 2537 | 2570 | |
| 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 | } |
| 2540 | 2573 | |
| 2541 | 2574 | fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2559,7 +2592,7 @@ pub const DeclGen = struct { |
| 2559 | 2592 | return operand_id; |
| 2560 | 2593 | } |
| 2561 | 2594 | |
| 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 | } |
| 2564 | 2597 | |
| 2565 | 2598 | fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2598,6 +2631,69 @@ pub const DeclGen = struct { |
| 2598 | 2631 | return result_id; |
| 2599 | 2632 | } |
| 2600 | 2633 | |
| 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 | 2697 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 2602 | 2698 | const target = self.getTarget(); |
| 2603 | 2699 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |