| ... | @@ -442,6 +442,43 @@ pub const DeclGen = struct { | ... | @@ -442,6 +442,43 @@ pub const DeclGen = struct { |
| 442 | } | 442 | } |
| 443 | } | 443 | } |
| 444 | | 444 | |
| | 445 | /// Construct a struct at runtime. |
| | 446 | /// result_ty_ref must be a struct type. |
| | 447 | fn constructStruct(self: *DeclGen, result_ty_ref: SpvType.Ref, constituents: []const IdRef) !IdRef { |
| | 448 | // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' |
| | 449 | // operands are not constant. |
| | 450 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| | 451 | // For now, just initialize the struct by setting the fields manually... |
| | 452 | // TODO: Make this OpCompositeConstruct when we can |
| | 453 | const ptr_composite_id = try self.alloc(result_ty_ref, null); |
| | 454 | // Note: using 32-bit ints here because usize crashes the translator as well |
| | 455 | const index_ty_ref = try self.intType(.unsigned, 32); |
| | 456 | const spv_composite_ty = self.spv.typeRefType(result_ty_ref); |
| | 457 | const members = spv_composite_ty.payload(.@"struct").members; |
| | 458 | for (constituents, members, 0..) |constitent_id, member, index| { |
| | 459 | const index_id = try self.constInt(index_ty_ref, index); |
| | 460 | const ptr_id = self.spv.allocId(); |
| | 461 | const ptr_member_ty_ref = try self.spv.ptrType(member.ty, .Generic, 0); |
| | 462 | try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| | 463 | .id_result_type = self.typeId(ptr_member_ty_ref), |
| | 464 | .id_result = ptr_id, |
| | 465 | .base = ptr_composite_id, |
| | 466 | .indexes = &.{index_id}, |
| | 467 | }); |
| | 468 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| | 469 | .pointer = ptr_id, |
| | 470 | .object = constitent_id, |
| | 471 | }); |
| | 472 | } |
| | 473 | const result_id = self.spv.allocId(); |
| | 474 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| | 475 | .id_result_type = self.typeId(result_ty_ref), |
| | 476 | .id_result = result_id, |
| | 477 | .pointer = ptr_composite_id, |
| | 478 | }); |
| | 479 | return result_id; |
| | 480 | } |
| | 481 | |
| 445 | const IndirectConstantLowering = struct { | 482 | const IndirectConstantLowering = struct { |
| 446 | const undef = 0xAA; | 483 | const undef = 0xAA; |
| 447 | | 484 | |
| ... | @@ -1582,6 +1619,20 @@ pub const DeclGen = struct { | ... | @@ -1582,6 +1619,20 @@ pub const DeclGen = struct { |
| 1582 | } | 1619 | } |
| 1583 | } | 1620 | } |
| 1584 | | 1621 | |
| | 1622 | fn boolToInt(self: *DeclGen, result_ty_ref: SpvType.Ref, condition_id: IdRef) !IdRef { |
| | 1623 | const zero_id = try self.constInt(result_ty_ref, 0); |
| | 1624 | const one_id = try self.constInt(result_ty_ref, 1); |
| | 1625 | const result_id = self.spv.allocId(); |
| | 1626 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| | 1627 | .id_result_type = self.typeId(result_ty_ref), |
| | 1628 | .id_result = result_id, |
| | 1629 | .condition = condition_id, |
| | 1630 | .object_1 = one_id, |
| | 1631 | .object_2 = zero_id, |
| | 1632 | }); |
| | 1633 | return result_id; |
| | 1634 | } |
| | 1635 | |
| 1585 | /// Convert representation from indirect (in memory) to direct (in 'register') | 1636 | /// Convert representation from indirect (in memory) to direct (in 'register') |
| 1586 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). | 1637 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). |
| 1587 | fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { | 1638 | fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { |
| ... | @@ -1610,17 +1661,7 @@ pub const DeclGen = struct { | ... | @@ -1610,17 +1661,7 @@ pub const DeclGen = struct { |
| 1610 | return switch (ty.zigTypeTag()) { | 1661 | return switch (ty.zigTypeTag()) { |
| 1611 | .Bool => blk: { | 1662 | .Bool => blk: { |
| 1612 | const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); | 1663 | const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); |
| 1613 | const zero_id = try self.constInt(indirect_bool_ty_ref, 0); | 1664 | break :blk self.boolToInt(indirect_bool_ty_ref, operand_id); |
| 1614 | const one_id = try self.constInt(indirect_bool_ty_ref, 1); | | |
| 1615 | const result_id = self.spv.allocId(); | | |
| 1616 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ | | |
| 1617 | .id_result_type = self.typeId(indirect_bool_ty_ref), | | |
| 1618 | .id_result = result_id, | | |
| 1619 | .condition = operand_id, | | |
| 1620 | .object_1 = one_id, | | |
| 1621 | .object_2 = zero_id, | | |
| 1622 | }); | | |
| 1623 | break :blk result_id; | | |
| 1624 | }, | 1665 | }, |
| 1625 | else => operand_id, | 1666 | else => operand_id, |
| 1626 | }; | 1667 | }; |
| ... | @@ -1933,64 +1974,83 @@ pub const DeclGen = struct { | ... | @@ -1933,64 +1974,83 @@ pub const DeclGen = struct { |
| 1933 | .float, .bool => unreachable, | 1974 | .float, .bool => unreachable, |
| 1934 | } | 1975 | } |
| 1935 | | 1976 | |
| 1936 | // The operand type must be the same as the result type in SPIR-V. | 1977 | // The operand type must be the same as the result type in SPIR-V, which |
| | 1978 | // is the same as in Zig. |
| 1937 | const operand_ty_ref = try self.resolveType(operand_ty, .direct); | 1979 | const operand_ty_ref = try self.resolveType(operand_ty, .direct); |
| 1938 | const operand_ty_id = self.typeId(operand_ty_ref); | 1980 | const operand_ty_id = self.typeId(operand_ty_ref); |
| 1939 | | 1981 | |
| 1940 | const op_result_id = blk: { | 1982 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 1941 | // Construct the SPIR-V result type. | | |
| 1942 | // It is almost the same as the zig one, except that the fields must be the same type | | |
| 1943 | // and they must be unsigned. | | |
| 1944 | const overflow_result_ty_ref = try self.spv.simpleStructType(&.{ | | |
| 1945 | .{ .ty = operand_ty_ref, .name = "res" }, | | |
| 1946 | .{ .ty = operand_ty_ref, .name = "ov" }, | | |
| 1947 | }); | | |
| 1948 | const result_id = self.spv.allocId(); | | |
| 1949 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ | | |
| 1950 | .id_result_type = self.typeId(overflow_result_ty_ref), | | |
| 1951 | .id_result = result_id, | | |
| 1952 | .operand_1 = lhs, | | |
| 1953 | .operand_2 = rhs, | | |
| 1954 | }); | | |
| 1955 | break :blk result_id; | | |
| 1956 | }; | | |
| 1957 | | | |
| 1958 | // Now convert the SPIR-V flavor result into a Zig-flavor result. | | |
| 1959 | // First, extract the two fields. | | |
| 1960 | const unsigned_result = try self.extractField(operand_ty, op_result_id, 0); | | |
| 1961 | const overflow = try self.extractField(operand_ty, op_result_id, 1); | | |
| 1962 | | | |
| 1963 | // We need to convert the results to the types that Zig expects here. | | |
| 1964 | // The `result` is the same type except unsigned, so we can just bitcast that. | | |
| 1965 | // TODO: This can be removed in Kernels as there are only unsigned ints. Maybe for | | |
| 1966 | // shaders as well? | | |
| 1967 | const result = try self.bitcast(operand_ty_id, unsigned_result); | | |
| 1968 | | | |
| 1969 | // The overflow needs to be converted into whatever is used to represent it in Zig. | | |
| 1970 | const casted_overflow = blk: { | | |
| 1971 | const ov_ty = result_ty.tupleFields().types[1]; | | |
| 1972 | const ov_ty_id = try self.resolveTypeId(ov_ty); | | |
| 1973 | const result_id = self.spv.allocId(); | | |
| 1974 | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ | | |
| 1975 | .id_result_type = ov_ty_id, | | |
| 1976 | .id_result = result_id, | | |
| 1977 | .unsigned_value = overflow, | | |
| 1978 | }); | | |
| 1979 | break :blk result_id; | | |
| 1980 | }; | | |
| 1981 | | 1983 | |
| 1982 | // TODO: If copying this function for borrow, make sure to convert -1 to 1 as appropriate. | 1984 | const ov_ty = result_ty.tupleFields().types[1]; |
| | 1985 | // Note: result is stored in a struct, so indirect representation. |
| | 1986 | const ov_ty_ref = try self.resolveType(ov_ty, .indirect); |
| 1983 | | 1987 | |
| 1984 | // Finally, construct the Zig type. | 1988 | // TODO: Operations other than addition. |
| 1985 | // Layout is result, overflow. | 1989 | const value_id = self.spv.allocId(); |
| 1986 | const result_id = self.spv.allocId(); | 1990 | try self.func.body.emit(self.spv.gpa, .OpIAdd, .{ |
| 1987 | const constituents = [_]IdRef{ result, casted_overflow }; | | |
| 1988 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ | | |
| 1989 | .id_result_type = operand_ty_id, | 1991 | .id_result_type = operand_ty_id, |
| 1990 | .id_result = result_id, | 1992 | .id_result = value_id, |
| 1991 | .constituents = &constituents, | 1993 | .operand_1 = lhs, |
| | 1994 | .operand_2 = rhs, |
| | 1995 | }); |
| | 1996 | |
| | 1997 | const overflowed_id = switch (info.signedness) { |
| | 1998 | .unsigned => blk: { |
| | 1999 | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. |
| | 2000 | const overflowed_id = self.spv.allocId(); |
| | 2001 | try self.func.body.emit(self.spv.gpa, .OpULessThan, .{ |
| | 2002 | .id_result_type = self.typeId(bool_ty_ref), |
| | 2003 | .id_result = overflowed_id, |
| | 2004 | .operand_1 = value_id, |
| | 2005 | .operand_2 = lhs, |
| | 2006 | }); |
| | 2007 | break :blk overflowed_id; |
| | 2008 | }, |
| | 2009 | .signed => blk: { |
| | 2010 | // Overflow happened if: |
| | 2011 | // - rhs is negative and value > lhs |
| | 2012 | // - rhs is positive and value < lhs |
| | 2013 | // This can be shortened to: |
| | 2014 | // (rhs < 0 && value > lhs) || (rhs >= 0 && value <= lhs) |
| | 2015 | // = (rhs < 0) == (value > lhs) |
| | 2016 | // Note that signed overflow is also wrapping in spir-v. |
| | 2017 | |
| | 2018 | const rhs_lt_zero_id = self.spv.allocId(); |
| | 2019 | const zero_id = try self.constInt(operand_ty_ref, 0); |
| | 2020 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| | 2021 | .id_result_type = self.typeId(bool_ty_ref), |
| | 2022 | .id_result = rhs_lt_zero_id, |
| | 2023 | .operand_1 = rhs, |
| | 2024 | .operand_2 = zero_id, |
| | 2025 | }); |
| | 2026 | |
| | 2027 | const value_gt_lhs_id = self.spv.allocId(); |
| | 2028 | try self.func.body.emit(self.spv.gpa, .OpSGreaterThan, .{ |
| | 2029 | .id_result_type = self.typeId(bool_ty_ref), |
| | 2030 | .id_result = value_gt_lhs_id, |
| | 2031 | .operand_1 = value_id, |
| | 2032 | .operand_2 = lhs, |
| | 2033 | }); |
| | 2034 | |
| | 2035 | const overflowed_id = self.spv.allocId(); |
| | 2036 | try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ |
| | 2037 | .id_result_type = self.typeId(bool_ty_ref), |
| | 2038 | .id_result = overflowed_id, |
| | 2039 | .operand_1 = rhs_lt_zero_id, |
| | 2040 | .operand_2 = value_gt_lhs_id, |
| | 2041 | }); |
| | 2042 | break :blk overflowed_id; |
| | 2043 | }, |
| | 2044 | }; |
| | 2045 | |
| | 2046 | // Construct the struct that Zig wants as result. |
| | 2047 | // The value should already be the correct type. |
| | 2048 | const ov_id = try self.boolToInt(ov_ty_ref, overflowed_id); |
| | 2049 | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| | 2050 | return try self.constructStruct(result_ty_ref, &.{ |
| | 2051 | value_id, |
| | 2052 | ov_id, |
| 1992 | }); | 2053 | }); |
| 1993 | return result_id; | | |
| 1994 | } | 2054 | } |
| 1995 | | 2055 | |
| 1996 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 2056 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | @@ -2463,76 +2523,45 @@ pub const DeclGen = struct { | ... | @@ -2463,76 +2523,45 @@ pub const DeclGen = struct { |
| 2463 | return result_id; | 2523 | return result_id; |
| 2464 | } | 2524 | } |
| 2465 | | 2525 | |
| 2466 | fn variable( | 2526 | // Allocate a function-local variable, with possible initializer. |
| | 2527 | // This function returns a pointer to a variable of type `ty_ref`, |
| | 2528 | // which is in the Generic address space. The variable is actually |
| | 2529 | // placed in the Function address space. |
| | 2530 | fn alloc( |
| 2467 | self: *DeclGen, | 2531 | self: *DeclGen, |
| 2468 | comptime context: enum { function, global }, | 2532 | ty_ref: SpvType.Ref, |
| 2469 | result_id: IdRef, | | |
| 2470 | ptr_ty_ref: SpvType.Ref, | | |
| 2471 | initializer: ?IdRef, | 2533 | initializer: ?IdRef, |
| 2472 | ) !void { | 2534 | ) !IdRef { |
| 2473 | const storage_class = self.spv.typeRefType(ptr_ty_ref).payload(.pointer).storage_class; | 2535 | const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function, 0); |
| 2474 | const actual_storage_class = switch (storage_class) { | 2536 | const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, 0); |
| 2475 | .Generic => switch (context) { | | |
| 2476 | .function => .Function, | | |
| 2477 | .global => .CrossWorkgroup, | | |
| 2478 | }, | | |
| 2479 | else => storage_class, | | |
| 2480 | }; | | |
| 2481 | const actual_ptr_ty_ref = switch (storage_class) { | | |
| 2482 | .Generic => try self.spv.changePtrStorageClass(ptr_ty_ref, actual_storage_class), | | |
| 2483 | else => ptr_ty_ref, | | |
| 2484 | }; | | |
| 2485 | const alloc_result_id = switch (storage_class) { | | |
| 2486 | .Generic => self.spv.allocId(), | | |
| 2487 | else => result_id, | | |
| 2488 | }; | | |
| 2489 | | 2537 | |
| 2490 | const section = switch (actual_storage_class) { | 2538 | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to |
| 2491 | .Generic => unreachable, | 2539 | // directly generate them into func.prologue instead of the body. |
| 2492 | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to | 2540 | const var_id = self.spv.allocId(); |
| 2493 | // directly generate them into func.prologue instead of the body. | 2541 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 2494 | .Function => &self.func.prologue, | 2542 | .id_result_type = self.typeId(fn_ptr_ty_ref), |
| 2495 | else => &self.spv.sections.types_globals_constants, | 2543 | .id_result = var_id, |
| 2496 | }; | 2544 | .storage_class = .Function, |
| 2497 | try section.emit(self.spv.gpa, .OpVariable, .{ | | |
| 2498 | .id_result_type = self.typeId(actual_ptr_ty_ref), | | |
| 2499 | .id_result = alloc_result_id, | | |
| 2500 | .storage_class = actual_storage_class, | | |
| 2501 | .initializer = initializer, | 2545 | .initializer = initializer, |
| 2502 | }); | 2546 | }); |
| 2503 | | 2547 | |
| 2504 | if (storage_class != .Generic) { | 2548 | // Convert to a generic pointer |
| 2505 | return; | 2549 | const result_id = self.spv.allocId(); |
| 2506 | } | 2550 | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2507 | | 2551 | .id_result_type = self.typeId(general_ptr_ty_ref), |
| 2508 | // Now we need to convert the pointer. | 2552 | .id_result = result_id, |
| 2509 | // If this is a function local, we need to perform the conversion at runtime. Otherwise, we can do | 2553 | .pointer = var_id, |
| 2510 | // it ahead of time using OpSpecConstantOp. | 2554 | }); |
| 2511 | switch (actual_storage_class) { | 2555 | return result_id; |
| 2512 | .Function => try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ | | |
| 2513 | .id_result_type = self.typeId(ptr_ty_ref), | | |
| 2514 | .id_result = result_id, | | |
| 2515 | .pointer = alloc_result_id, | | |
| 2516 | }), | | |
| 2517 | // TODO: Can we do without this cast or move it to runtime? | | |
| 2518 | else => { | | |
| 2519 | const const_ptr_id = try self.makePointerConstant(section, actual_ptr_ty_ref, alloc_result_id); | | |
| 2520 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | | |
| 2521 | .id_result_type = self.typeId(ptr_ty_ref), | | |
| 2522 | .id_result = result_id, | | |
| 2523 | .pointer = const_ptr_id, | | |
| 2524 | }); | | |
| 2525 | }, | | |
| 2526 | } | | |
| 2527 | } | 2556 | } |
| 2528 | | 2557 | |
| 2529 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 2558 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2530 | if (self.liveness.isUnused(inst)) return null; | 2559 | if (self.liveness.isUnused(inst)) return null; |
| 2531 | const ty = self.air.typeOfIndex(inst); | 2560 | const ptr_ty = self.air.typeOfIndex(inst); |
| 2532 | const result_ty_ref = try self.resolveType(ty, .direct); | 2561 | assert(ptr_ty.ptrAddressSpace() == .generic); |
| 2533 | const result_id = self.spv.allocId(); | 2562 | const child_ty = ptr_ty.childType(); |
| 2534 | try self.variable(.function, result_id, result_ty_ref, null); | 2563 | const child_ty_ref = try self.resolveType(child_ty, .indirect); |
| 2535 | return result_id; | 2564 | return try self.alloc(child_ty_ref, null); |
| 2536 | } | 2565 | } |
| 2537 | | 2566 | |
| 2538 | fn airArg(self: *DeclGen) IdRef { | 2567 | fn airArg(self: *DeclGen) IdRef { |
| ... | @@ -2819,13 +2848,7 @@ pub const DeclGen = struct { | ... | @@ -2819,13 +2848,7 @@ pub const DeclGen = struct { |
| 2819 | } | 2848 | } |
| 2820 | | 2849 | |
| 2821 | const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); | 2850 | const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); |
| 2822 | const result_id = self.spv.allocId(); | 2851 | return try self.constructStruct(err_union_ty_ref, members.slice()); |
| 2823 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ | | |
| 2824 | .id_result_type = self.typeId(err_union_ty_ref), | | |
| 2825 | .id_result = result_id, | | |
| 2826 | .constituents = members.slice(), | | |
| 2827 | }); | | |
| 2828 | return result_id; | | |
| 2829 | } | 2852 | } |
| 2830 | | 2853 | |
| 2831 | fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef { | 2854 | fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef { |
| ... | @@ -2925,14 +2948,8 @@ pub const DeclGen = struct { | ... | @@ -2925,14 +2948,8 @@ pub const DeclGen = struct { |
| 2925 | } | 2948 | } |
| 2926 | | 2949 | |
| 2927 | const optional_ty_ref = try self.resolveType(optional_ty, .direct); | 2950 | const optional_ty_ref = try self.resolveType(optional_ty, .direct); |
| 2928 | const result_id = self.spv.allocId(); | | |
| 2929 | const members = [_]IdRef{ operand_id, try self.constBool(true, .indirect) }; | 2951 | const members = [_]IdRef{ operand_id, try self.constBool(true, .indirect) }; |
| 2930 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ | 2952 | return try self.constructStruct(optional_ty_ref, &members); |
| 2931 | .id_result_type = self.typeId(optional_ty_ref), | | |
| 2932 | .id_result = result_id, | | |
| 2933 | .constituents = &members, | | |
| 2934 | }); | | |
| 2935 | return result_id; | | |
| 2936 | } | 2953 | } |
| 2937 | | 2954 | |
| 2938 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { | 2955 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { |