| ... | ... | @@ -242,7 +242,7 @@ pub const DeclGen = struct { |
| 242 | 242 | return self.spv.declPtr(spv_decl_index).result_id; |
| 243 | 243 | } |
| 244 | 244 | |
| 245 | | return try self.constant(ty, val); |
| 245 | return try self.constant(ty, val, .direct); |
| 246 | 246 | } |
| 247 | 247 | const index = Air.refToIndex(inst).?; |
| 248 | 248 | return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage. |
| ... | ... | @@ -369,21 +369,11 @@ pub const DeclGen = struct { |
| 369 | 369 | .composite_integer, |
| 370 | 370 | }; |
| 371 | 371 | }, |
| 372 | | .Enum => blk: { |
| 373 | | var buffer: Type.Payload.Bits = undefined; |
| 374 | | const int_ty = ty.intTagType(&buffer); |
| 375 | | const int_info = int_ty.intInfo(target); |
| 376 | | break :blk ArithmeticTypeInfo{ |
| 377 | | .bits = int_info.bits, |
| 378 | | .is_vector = false, |
| 379 | | .signedness = int_info.signedness, |
| 380 | | .class = .integer, |
| 381 | | }; |
| 382 | | }, |
| 383 | 372 | // As of yet, there is no vector support in the self-hosted compiler. |
| 384 | 373 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), |
| 385 | 374 | // TODO: For which types is this the case? |
| 386 | | else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}), |
| 375 | // else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}), |
| 376 | else => unreachable, |
| 387 | 377 | }; |
| 388 | 378 | } |
| 389 | 379 | |
| ... | ... | @@ -452,6 +442,37 @@ pub const DeclGen = struct { |
| 452 | 442 | } |
| 453 | 443 | } |
| 454 | 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_member_ty_ref = try self.spv.ptrType(member.ty, .Generic, 0); |
| 461 | const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{index_id}); |
| 462 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 463 | .pointer = ptr_id, |
| 464 | .object = constitent_id, |
| 465 | }); |
| 466 | } |
| 467 | const result_id = self.spv.allocId(); |
| 468 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 469 | .id_result_type = self.typeId(result_ty_ref), |
| 470 | .id_result = result_id, |
| 471 | .pointer = ptr_composite_id, |
| 472 | }); |
| 473 | return result_id; |
| 474 | } |
| 475 | |
| 455 | 476 | const IndirectConstantLowering = struct { |
| 456 | 477 | const undef = 0xAA; |
| 457 | 478 | |
| ... | ... | @@ -705,6 +726,10 @@ pub const DeclGen = struct { |
| 705 | 726 | try self.lower(ptr_ty, slice.ptr); |
| 706 | 727 | try self.addInt(Type.usize, slice.len); |
| 707 | 728 | }, |
| 729 | .null_value, .zero => try self.addNullPtr(try dg.resolveType(ty, .indirect)), |
| 730 | .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { |
| 731 | try self.addInt(Type.usize, val); |
| 732 | }, |
| 708 | 733 | else => |tag| return dg.todo("pointer value of type {s}", .{@tagName(tag)}), |
| 709 | 734 | }, |
| 710 | 735 | .Struct => { |
| ... | ... | @@ -996,14 +1021,16 @@ pub const DeclGen = struct { |
| 996 | 1021 | /// the constant is more complicated however, it needs to be lowered to an indirect constant, which |
| 997 | 1022 | /// is then loaded using OpLoad. Such values are loaded into the UniformConstant storage class by default. |
| 998 | 1023 | /// This function should only be called during function code generation. |
| 999 | | fn constant(self: *DeclGen, ty: Type, val: Value) !IdRef { |
| 1024 | fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) !IdRef { |
| 1000 | 1025 | const target = self.getTarget(); |
| 1001 | 1026 | const section = &self.spv.sections.types_globals_constants; |
| 1002 | | const result_ty_ref = try self.resolveType(ty, .direct); |
| 1027 | const result_ty_ref = try self.resolveType(ty, repr); |
| 1003 | 1028 | const result_ty_id = self.typeId(result_ty_ref); |
| 1004 | | const result_id = self.spv.allocId(); |
| 1029 | |
| 1030 | log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) }); |
| 1005 | 1031 | |
| 1006 | 1032 | if (val.isUndef()) { |
| 1033 | const result_id = self.spv.allocId(); |
| 1007 | 1034 | try section.emit(self.spv.gpa, .OpUndef, .{ |
| 1008 | 1035 | .id_result_type = result_ty_id, |
| 1009 | 1036 | .id_result = result_id, |
| ... | ... | @@ -1014,24 +1041,76 @@ pub const DeclGen = struct { |
| 1014 | 1041 | switch (ty.zigTypeTag()) { |
| 1015 | 1042 | .Int => { |
| 1016 | 1043 | if (ty.isSignedInt()) { |
| 1017 | | try self.genConstInt(result_ty_ref, result_id, val.toSignedInt(target)); |
| 1044 | return try self.constInt(result_ty_ref, val.toSignedInt(target)); |
| 1018 | 1045 | } else { |
| 1019 | | try self.genConstInt(result_ty_ref, result_id, val.toUnsignedInt(target)); |
| 1046 | return try self.constInt(result_ty_ref, val.toUnsignedInt(target)); |
| 1020 | 1047 | } |
| 1021 | 1048 | }, |
| 1022 | | .Bool => { |
| 1023 | | const operands = .{ .id_result_type = result_ty_id, .id_result = result_id }; |
| 1024 | | if (val.toBool()) { |
| 1025 | | try section.emit(self.spv.gpa, .OpConstantTrue, operands); |
| 1049 | .Bool => switch (repr) { |
| 1050 | .direct => { |
| 1051 | const result_id = self.spv.allocId(); |
| 1052 | const operands = .{ .id_result_type = result_ty_id, .id_result = result_id }; |
| 1053 | if (val.toBool()) { |
| 1054 | try section.emit(self.spv.gpa, .OpConstantTrue, operands); |
| 1055 | } else { |
| 1056 | try section.emit(self.spv.gpa, .OpConstantFalse, operands); |
| 1057 | } |
| 1058 | return result_id; |
| 1059 | }, |
| 1060 | .indirect => return try self.constInt(result_ty_ref, @boolToInt(val.toBool())), |
| 1061 | }, |
| 1062 | .Float => { |
| 1063 | const result_id = self.spv.allocId(); |
| 1064 | switch (ty.floatBits(target)) { |
| 1065 | 16 => try self.spv.emitConstant(result_ty_id, result_id, .{ .float32 = val.toFloat(f16) }), |
| 1066 | 32 => try self.spv.emitConstant(result_ty_id, result_id, .{ .float32 = val.toFloat(f32) }), |
| 1067 | 64 => try self.spv.emitConstant(result_ty_id, result_id, .{ .float64 = val.toFloat(f64) }), |
| 1068 | 80, 128 => unreachable, // TODO |
| 1069 | else => unreachable, |
| 1070 | } |
| 1071 | return result_id; |
| 1072 | }, |
| 1073 | .ErrorSet => { |
| 1074 | const value = switch (val.tag()) { |
| 1075 | .@"error" => blk: { |
| 1076 | const err_name = val.castTag(.@"error").?.data.name; |
| 1077 | const kv = try self.module.getErrorValue(err_name); |
| 1078 | break :blk @intCast(u16, kv.value); |
| 1079 | }, |
| 1080 | .zero => 0, |
| 1081 | else => unreachable, |
| 1082 | }; |
| 1083 | |
| 1084 | return try self.constInt(result_ty_ref, value); |
| 1085 | }, |
| 1086 | .ErrorUnion => { |
| 1087 | const payload_ty = ty.errorUnionPayload(); |
| 1088 | const is_pl = val.errorUnionIsPayload(); |
| 1089 | const error_val = if (!is_pl) val else Value.initTag(.zero); |
| 1090 | |
| 1091 | const eu_layout = self.errorUnionLayout(payload_ty); |
| 1092 | if (!eu_layout.payload_has_bits) { |
| 1093 | return try self.constant(Type.anyerror, error_val, repr); |
| 1094 | } |
| 1095 | |
| 1096 | const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); |
| 1097 | |
| 1098 | var members: [2]IdRef = undefined; |
| 1099 | if (eu_layout.error_first) { |
| 1100 | members[0] = try self.constant(Type.anyerror, error_val, .indirect); |
| 1101 | members[1] = try self.constant(payload_ty, payload_val, .indirect); |
| 1026 | 1102 | } else { |
| 1027 | | try section.emit(self.spv.gpa, .OpConstantFalse, operands); |
| 1103 | members[0] = try self.constant(payload_ty, payload_val, .indirect); |
| 1104 | members[1] = try self.constant(Type.anyerror, error_val, .indirect); |
| 1028 | 1105 | } |
| 1106 | return try self.spv.constComposite(result_ty_ref, &members); |
| 1029 | 1107 | }, |
| 1030 | 1108 | // TODO: We can handle most pointers here (decl refs etc), because now they emit an extra |
| 1031 | 1109 | // OpVariable that is not really required. |
| 1032 | 1110 | else => { |
| 1033 | 1111 | // The value cannot be generated directly, so generate it as an indirect constant, |
| 1034 | 1112 | // and then perform an OpLoad. |
| 1113 | const result_id = self.spv.allocId(); |
| 1035 | 1114 | const alignment = ty.abiAlignment(target); |
| 1036 | 1115 | const spv_decl_index = try self.spv.allocDecl(.global); |
| 1037 | 1116 | |
| ... | ... | @@ -1053,10 +1132,9 @@ pub const DeclGen = struct { |
| 1053 | 1132 | }); |
| 1054 | 1133 | // TODO: Convert bools? This logic should hook into `load`. It should be a dead |
| 1055 | 1134 | // path though considering .Bool is handled above. |
| 1135 | return result_id; |
| 1056 | 1136 | }, |
| 1057 | 1137 | } |
| 1058 | | |
| 1059 | | return result_id; |
| 1060 | 1138 | } |
| 1061 | 1139 | |
| 1062 | 1140 | /// Turn a Zig type into a SPIR-V Type, and return its type result-id. |
| ... | ... | @@ -1592,10 +1670,23 @@ pub const DeclGen = struct { |
| 1592 | 1670 | } |
| 1593 | 1671 | } |
| 1594 | 1672 | |
| 1673 | fn boolToInt(self: *DeclGen, result_ty_ref: SpvType.Ref, condition_id: IdRef) !IdRef { |
| 1674 | const zero_id = try self.constInt(result_ty_ref, 0); |
| 1675 | const one_id = try self.constInt(result_ty_ref, 1); |
| 1676 | const result_id = self.spv.allocId(); |
| 1677 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 1678 | .id_result_type = self.typeId(result_ty_ref), |
| 1679 | .id_result = result_id, |
| 1680 | .condition = condition_id, |
| 1681 | .object_1 = one_id, |
| 1682 | .object_2 = zero_id, |
| 1683 | }); |
| 1684 | return result_id; |
| 1685 | } |
| 1686 | |
| 1595 | 1687 | /// Convert representation from indirect (in memory) to direct (in 'register') |
| 1596 | 1688 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). |
| 1597 | 1689 | fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { |
| 1598 | | // const direct_ty_ref = try self.resolveType(ty, .direct); |
| 1599 | 1690 | return switch (ty.zigTypeTag()) { |
| 1600 | 1691 | .Bool => blk: { |
| 1601 | 1692 | const direct_bool_ty_ref = try self.resolveType(ty, .direct); |
| ... | ... | @@ -1620,17 +1711,7 @@ pub const DeclGen = struct { |
| 1620 | 1711 | return switch (ty.zigTypeTag()) { |
| 1621 | 1712 | .Bool => blk: { |
| 1622 | 1713 | const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); |
| 1623 | | const zero_id = try self.constInt(indirect_bool_ty_ref, 0); |
| 1624 | | const one_id = try self.constInt(indirect_bool_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(indirect_bool_ty_ref), |
| 1628 | | .id_result = result_id, |
| 1629 | | .condition = operand_id, |
| 1630 | | .object_1 = one_id, |
| 1631 | | .object_2 = zero_id, |
| 1632 | | }); |
| 1633 | | break :blk result_id; |
| 1714 | break :blk self.boolToInt(indirect_bool_ty_ref, operand_id); |
| 1634 | 1715 | }, |
| 1635 | 1716 | else => operand_id, |
| 1636 | 1717 | }; |
| ... | ... | @@ -1714,6 +1795,9 @@ pub const DeclGen = struct { |
| 1714 | 1795 | |
| 1715 | 1796 | .shuffle => try self.airShuffle(inst), |
| 1716 | 1797 | |
| 1798 | .ptr_add => try self.airPtrAdd(inst), |
| 1799 | .ptr_sub => try self.airPtrSub(inst), |
| 1800 | |
| 1717 | 1801 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), |
| 1718 | 1802 | .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), |
| 1719 | 1803 | .xor => try self.airBinOpSimple(inst, .OpBitwiseXor), |
| ... | ... | @@ -1722,8 +1806,8 @@ pub const DeclGen = struct { |
| 1722 | 1806 | |
| 1723 | 1807 | .shl => try self.airShift(inst, .OpShiftLeftLogical), |
| 1724 | 1808 | |
| 1725 | | .bitcast => try self.airBitcast(inst), |
| 1726 | | .intcast, .trunc => try self.airIntcast(inst), |
| 1809 | .bitcast => try self.airBitCast(inst), |
| 1810 | .intcast, .trunc => try self.airIntCast(inst), |
| 1727 | 1811 | .ptrtoint => try self.airPtrToInt(inst), |
| 1728 | 1812 | .int_to_float => try self.airIntToFloat(inst), |
| 1729 | 1813 | .float_to_int => try self.airFloatToInt(inst), |
| ... | ... | @@ -1734,6 +1818,7 @@ pub const DeclGen = struct { |
| 1734 | 1818 | .slice_elem_ptr => try self.airSliceElemPtr(inst), |
| 1735 | 1819 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 1736 | 1820 | .ptr_elem_ptr => try self.airPtrElemPtr(inst), |
| 1821 | .ptr_elem_val => try self.airPtrElemVal(inst), |
| 1737 | 1822 | |
| 1738 | 1823 | .get_union_tag => try self.airGetUnionTag(inst), |
| 1739 | 1824 | .struct_field_val => try self.airStructFieldVal(inst), |
| ... | ... | @@ -1743,12 +1828,12 @@ pub const DeclGen = struct { |
| 1743 | 1828 | .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), |
| 1744 | 1829 | .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), |
| 1745 | 1830 | |
| 1746 | | .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual), |
| 1747 | | .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual), |
| 1748 | | .cmp_gt => try self.airCmp(inst, .OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan), |
| 1749 | | .cmp_gte => try self.airCmp(inst, .OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual), |
| 1750 | | .cmp_lt => try self.airCmp(inst, .OpFOrdLessThan, .OpSLessThan, .OpULessThan), |
| 1751 | | .cmp_lte => try self.airCmp(inst, .OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual), |
| 1831 | .cmp_eq => try self.airCmp(inst, .eq), |
| 1832 | .cmp_neq => try self.airCmp(inst, .neq), |
| 1833 | .cmp_gt => try self.airCmp(inst, .gt), |
| 1834 | .cmp_gte => try self.airCmp(inst, .gte), |
| 1835 | .cmp_lt => try self.airCmp(inst, .lt), |
| 1836 | .cmp_lte => try self.airCmp(inst, .lte), |
| 1752 | 1837 | |
| 1753 | 1838 | .arg => self.airArg(), |
| 1754 | 1839 | .alloc => try self.airAlloc(inst), |
| ... | ... | @@ -1944,64 +2029,83 @@ pub const DeclGen = struct { |
| 1944 | 2029 | .float, .bool => unreachable, |
| 1945 | 2030 | } |
| 1946 | 2031 | |
| 1947 | | // The operand type must be the same as the result type in SPIR-V. |
| 2032 | // The operand type must be the same as the result type in SPIR-V, which |
| 2033 | // is the same as in Zig. |
| 1948 | 2034 | const operand_ty_ref = try self.resolveType(operand_ty, .direct); |
| 1949 | 2035 | const operand_ty_id = self.typeId(operand_ty_ref); |
| 1950 | 2036 | |
| 1951 | | const op_result_id = blk: { |
| 1952 | | // Construct the SPIR-V result type. |
| 1953 | | // It is almost the same as the zig one, except that the fields must be the same type |
| 1954 | | // and they must be unsigned. |
| 1955 | | const overflow_result_ty_ref = try self.spv.simpleStructType(&.{ |
| 1956 | | .{ .ty = operand_ty_ref, .name = "res" }, |
| 1957 | | .{ .ty = operand_ty_ref, .name = "ov" }, |
| 1958 | | }); |
| 1959 | | const result_id = self.spv.allocId(); |
| 1960 | | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ |
| 1961 | | .id_result_type = self.typeId(overflow_result_ty_ref), |
| 1962 | | .id_result = result_id, |
| 1963 | | .operand_1 = lhs, |
| 1964 | | .operand_2 = rhs, |
| 1965 | | }); |
| 1966 | | break :blk result_id; |
| 1967 | | }; |
| 1968 | | |
| 1969 | | // Now convert the SPIR-V flavor result into a Zig-flavor result. |
| 1970 | | // First, extract the two fields. |
| 1971 | | const unsigned_result = try self.extractField(operand_ty, op_result_id, 0); |
| 1972 | | const overflow = try self.extractField(operand_ty, op_result_id, 1); |
| 1973 | | |
| 1974 | | // We need to convert the results to the types that Zig expects here. |
| 1975 | | // The `result` is the same type except unsigned, so we can just bitcast that. |
| 1976 | | // TODO: This can be removed in Kernels as there are only unsigned ints. Maybe for |
| 1977 | | // shaders as well? |
| 1978 | | const result = try self.bitcast(operand_ty_id, unsigned_result); |
| 1979 | | |
| 1980 | | // The overflow needs to be converted into whatever is used to represent it in Zig. |
| 1981 | | const casted_overflow = blk: { |
| 1982 | | const ov_ty = result_ty.tupleFields().types[1]; |
| 1983 | | const ov_ty_id = try self.resolveTypeId(ov_ty); |
| 1984 | | const result_id = self.spv.allocId(); |
| 1985 | | try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ |
| 1986 | | .id_result_type = ov_ty_id, |
| 1987 | | .id_result = result_id, |
| 1988 | | .unsigned_value = overflow, |
| 1989 | | }); |
| 1990 | | break :blk result_id; |
| 1991 | | }; |
| 2037 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 1992 | 2038 | |
| 1993 | | // TODO: If copying this function for borrow, make sure to convert -1 to 1 as appropriate. |
| 2039 | const ov_ty = result_ty.tupleFields().types[1]; |
| 2040 | // Note: result is stored in a struct, so indirect representation. |
| 2041 | const ov_ty_ref = try self.resolveType(ov_ty, .indirect); |
| 1994 | 2042 | |
| 1995 | | // Finally, construct the Zig type. |
| 1996 | | // Layout is result, overflow. |
| 1997 | | const result_id = self.spv.allocId(); |
| 1998 | | const constituents = [_]IdRef{ result, casted_overflow }; |
| 1999 | | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 2043 | // TODO: Operations other than addition. |
| 2044 | const value_id = self.spv.allocId(); |
| 2045 | try self.func.body.emit(self.spv.gpa, .OpIAdd, .{ |
| 2000 | 2046 | .id_result_type = operand_ty_id, |
| 2001 | | .id_result = result_id, |
| 2002 | | .constituents = &constituents, |
| 2047 | .id_result = value_id, |
| 2048 | .operand_1 = lhs, |
| 2049 | .operand_2 = rhs, |
| 2050 | }); |
| 2051 | |
| 2052 | const overflowed_id = switch (info.signedness) { |
| 2053 | .unsigned => blk: { |
| 2054 | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. |
| 2055 | const overflowed_id = self.spv.allocId(); |
| 2056 | try self.func.body.emit(self.spv.gpa, .OpULessThan, .{ |
| 2057 | .id_result_type = self.typeId(bool_ty_ref), |
| 2058 | .id_result = overflowed_id, |
| 2059 | .operand_1 = value_id, |
| 2060 | .operand_2 = lhs, |
| 2061 | }); |
| 2062 | break :blk overflowed_id; |
| 2063 | }, |
| 2064 | .signed => blk: { |
| 2065 | // Overflow happened if: |
| 2066 | // - rhs is negative and value > lhs |
| 2067 | // - rhs is positive and value < lhs |
| 2068 | // This can be shortened to: |
| 2069 | // (rhs < 0 && value > lhs) || (rhs >= 0 && value <= lhs) |
| 2070 | // = (rhs < 0) == (value > lhs) |
| 2071 | // Note that signed overflow is also wrapping in spir-v. |
| 2072 | |
| 2073 | const rhs_lt_zero_id = self.spv.allocId(); |
| 2074 | const zero_id = try self.constInt(operand_ty_ref, 0); |
| 2075 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2076 | .id_result_type = self.typeId(bool_ty_ref), |
| 2077 | .id_result = rhs_lt_zero_id, |
| 2078 | .operand_1 = rhs, |
| 2079 | .operand_2 = zero_id, |
| 2080 | }); |
| 2081 | |
| 2082 | const value_gt_lhs_id = self.spv.allocId(); |
| 2083 | try self.func.body.emit(self.spv.gpa, .OpSGreaterThan, .{ |
| 2084 | .id_result_type = self.typeId(bool_ty_ref), |
| 2085 | .id_result = value_gt_lhs_id, |
| 2086 | .operand_1 = value_id, |
| 2087 | .operand_2 = lhs, |
| 2088 | }); |
| 2089 | |
| 2090 | const overflowed_id = self.spv.allocId(); |
| 2091 | try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ |
| 2092 | .id_result_type = self.typeId(bool_ty_ref), |
| 2093 | .id_result = overflowed_id, |
| 2094 | .operand_1 = rhs_lt_zero_id, |
| 2095 | .operand_2 = value_gt_lhs_id, |
| 2096 | }); |
| 2097 | break :blk overflowed_id; |
| 2098 | }, |
| 2099 | }; |
| 2100 | |
| 2101 | // Construct the struct that Zig wants as result. |
| 2102 | // The value should already be the correct type. |
| 2103 | const ov_id = try self.boolToInt(ov_ty_ref, overflowed_id); |
| 2104 | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| 2105 | return try self.constructStruct(result_ty_ref, &.{ |
| 2106 | value_id, |
| 2107 | ov_id, |
| 2003 | 2108 | }); |
| 2004 | | return result_id; |
| 2005 | 2109 | } |
| 2006 | 2110 | |
| 2007 | 2111 | fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2040,85 +2144,262 @@ pub const DeclGen = struct { |
| 2040 | 2144 | return result_id; |
| 2041 | 2145 | } |
| 2042 | 2146 | |
| 2043 | | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !?IdRef { |
| 2044 | | if (self.liveness.isUnused(inst)) return null; |
| 2045 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2046 | | var lhs_id = try self.resolve(bin_op.lhs); |
| 2047 | | var rhs_id = try self.resolve(bin_op.rhs); |
| 2147 | /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective |
| 2148 | /// difference lies in whether the resulting type of the first dereference will be the |
| 2149 | /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain |
| 2150 | /// is the latter and PtrAccessChain is the former. |
| 2151 | fn accessChain( |
| 2152 | self: *DeclGen, |
| 2153 | result_ty_ref: SpvType.Ref, |
| 2154 | base: IdRef, |
| 2155 | indexes: []const IdRef, |
| 2156 | ) !IdRef { |
| 2048 | 2157 | const result_id = self.spv.allocId(); |
| 2049 | | const result_type_id = try self.resolveTypeId(Type.bool); |
| 2050 | | const op_ty = self.air.typeOf(bin_op.lhs); |
| 2051 | | assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module)); |
| 2158 | try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| 2159 | .id_result_type = self.typeId(result_ty_ref), |
| 2160 | .id_result = result_id, |
| 2161 | .base = base, |
| 2162 | .indexes = indexes, |
| 2163 | }); |
| 2164 | return result_id; |
| 2165 | } |
| 2052 | 2166 | |
| 2053 | | // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, |
| 2054 | | // but int and float versions of operations require different opcodes. |
| 2055 | | const info = try self.arithmeticTypeInfo(op_ty); |
| 2167 | fn ptrAccessChain( |
| 2168 | self: *DeclGen, |
| 2169 | result_ty_ref: SpvType.Ref, |
| 2170 | base: IdRef, |
| 2171 | element: IdRef, |
| 2172 | indexes: []const IdRef, |
| 2173 | ) !IdRef { |
| 2174 | const result_id = self.spv.allocId(); |
| 2175 | try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ |
| 2176 | .id_result_type = self.typeId(result_ty_ref), |
| 2177 | .id_result = result_id, |
| 2178 | .base = base, |
| 2179 | .element = element, |
| 2180 | .indexes = indexes, |
| 2181 | }); |
| 2182 | return result_id; |
| 2183 | } |
| 2056 | 2184 | |
| 2057 | | const opcode_index: usize = switch (info.class) { |
| 2058 | | .composite_integer => { |
| 2059 | | return self.todo("binary operations for composite integers", .{}); |
| 2185 | fn ptrAdd(self: *DeclGen, result_ty: Type, ptr_ty: Type, ptr_id: IdRef, offset_id: IdRef) !IdRef { |
| 2186 | const result_ty_ref = try self.resolveType(result_ty, .direct); |
| 2187 | |
| 2188 | switch (ptr_ty.ptrSize()) { |
| 2189 | .One => { |
| 2190 | // Pointer to array |
| 2191 | // TODO: Is this correct? |
| 2192 | return try self.accessChain(result_ty_ref, ptr_id, &.{offset_id}); |
| 2060 | 2193 | }, |
| 2061 | | .float => 0, |
| 2062 | | .bool => 1, |
| 2063 | | .strange_integer => blk: { |
| 2064 | | const op_ty_ref = try self.resolveType(op_ty, .direct); |
| 2065 | | lhs_id = try self.maskStrangeInt(op_ty_ref, lhs_id, info.bits); |
| 2066 | | rhs_id = try self.maskStrangeInt(op_ty_ref, rhs_id, info.bits); |
| 2067 | | break :blk switch (info.signedness) { |
| 2068 | | .signed => @as(usize, 1), |
| 2069 | | .unsigned => @as(usize, 2), |
| 2070 | | }; |
| 2194 | .C, .Many => { |
| 2195 | return try self.ptrAccessChain(result_ty_ref, ptr_id, offset_id, &.{}); |
| 2071 | 2196 | }, |
| 2072 | | .integer => switch (info.signedness) { |
| 2073 | | .signed => @as(usize, 1), |
| 2074 | | .unsigned => @as(usize, 2), |
| 2197 | .Slice => { |
| 2198 | // TODO: This is probably incorrect. A slice should be returned here, though this is what llvm does. |
| 2199 | const slice_ptr_id = try self.extractField(result_ty, ptr_id, 0); |
| 2200 | return try self.ptrAccessChain(result_ty_ref, slice_ptr_id, offset_id, &.{}); |
| 2075 | 2201 | }, |
| 2076 | | }; |
| 2202 | } |
| 2203 | } |
| 2077 | 2204 | |
| 2078 | | const operands = .{ |
| 2079 | | .id_result_type = result_type_id, |
| 2080 | | .id_result = result_id, |
| 2081 | | .operand_1 = lhs_id, |
| 2082 | | .operand_2 = rhs_id, |
| 2083 | | }; |
| 2205 | fn airPtrAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2206 | if (self.liveness.isUnused(inst)) return null; |
| 2207 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2208 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2209 | const ptr_id = try self.resolve(bin_op.lhs); |
| 2210 | const offset_id = try self.resolve(bin_op.rhs); |
| 2211 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2212 | const result_ty = self.air.typeOfIndex(inst); |
| 2084 | 2213 | |
| 2085 | | switch (opcode_index) { |
| 2086 | | 0 => try self.func.body.emit(self.spv.gpa, fop, operands), |
| 2087 | | 1 => try self.func.body.emit(self.spv.gpa, sop, operands), |
| 2088 | | 2 => try self.func.body.emit(self.spv.gpa, uop, operands), |
| 2089 | | else => unreachable, |
| 2090 | | } |
| 2214 | return try self.ptrAdd(result_ty, ptr_ty, ptr_id, offset_id); |
| 2215 | } |
| 2216 | |
| 2217 | fn airPtrSub(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2218 | if (self.liveness.isUnused(inst)) return null; |
| 2219 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2220 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2221 | const ptr_id = try self.resolve(bin_op.lhs); |
| 2222 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2223 | const offset_id = try self.resolve(bin_op.rhs); |
| 2224 | const offset_ty = self.air.typeOf(bin_op.rhs); |
| 2225 | const offset_ty_ref = try self.resolveType(offset_ty, .direct); |
| 2226 | const result_ty = self.air.typeOfIndex(inst); |
| 2227 | |
| 2228 | const negative_offset_id = self.spv.allocId(); |
| 2229 | try self.func.body.emit(self.spv.gpa, .OpSNegate, .{ |
| 2230 | .id_result_type = self.typeId(offset_ty_ref), |
| 2231 | .id_result = negative_offset_id, |
| 2232 | .operand = offset_id, |
| 2233 | }); |
| 2234 | return try self.ptrAdd(result_ty, ptr_ty, ptr_id, negative_offset_id); |
| 2235 | } |
| 2236 | |
| 2237 | fn cmp( |
| 2238 | self: *DeclGen, |
| 2239 | comptime op: std.math.CompareOperator, |
| 2240 | bool_ty_id: IdRef, |
| 2241 | ty: Type, |
| 2242 | lhs_id: IdRef, |
| 2243 | rhs_id: IdRef, |
| 2244 | ) !IdRef { |
| 2245 | var cmp_lhs_id = lhs_id; |
| 2246 | var cmp_rhs_id = rhs_id; |
| 2247 | const opcode: Opcode = opcode: { |
| 2248 | var int_buffer: Type.Payload.Bits = undefined; |
| 2249 | const op_ty = switch (ty.zigTypeTag()) { |
| 2250 | .Int, .Bool, .Float => ty, |
| 2251 | .Enum => ty.intTagType(&int_buffer), |
| 2252 | .ErrorSet => Type.u16, |
| 2253 | .Pointer => blk: { |
| 2254 | // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are |
| 2255 | // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using |
| 2256 | // OpConvertPtrToU... |
| 2257 | cmp_lhs_id = self.spv.allocId(); |
| 2258 | cmp_rhs_id = self.spv.allocId(); |
| 2259 | |
| 2260 | const usize_ty_id = self.typeId(try self.sizeType()); |
| 2261 | |
| 2262 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2263 | .id_result_type = usize_ty_id, |
| 2264 | .id_result = cmp_lhs_id, |
| 2265 | .pointer = lhs_id, |
| 2266 | }); |
| 2267 | |
| 2268 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2269 | .id_result_type = usize_ty_id, |
| 2270 | .id_result = cmp_rhs_id, |
| 2271 | .pointer = rhs_id, |
| 2272 | }); |
| 2091 | 2273 | |
| 2274 | break :blk Type.usize; |
| 2275 | }, |
| 2276 | .Optional => unreachable, // TODO |
| 2277 | else => unreachable, |
| 2278 | }; |
| 2279 | |
| 2280 | const info = try self.arithmeticTypeInfo(op_ty); |
| 2281 | const signedness = switch (info.class) { |
| 2282 | .composite_integer => { |
| 2283 | return self.todo("binary operations for composite integers", .{}); |
| 2284 | }, |
| 2285 | .float => break :opcode switch (op) { |
| 2286 | .eq => .OpFOrdEqual, |
| 2287 | .neq => .OpFOrdNotEqual, |
| 2288 | .lt => .OpFOrdLessThan, |
| 2289 | .lte => .OpFOrdLessThanEqual, |
| 2290 | .gt => .OpFOrdGreaterThan, |
| 2291 | .gte => .OpFOrdGreaterThanEqual, |
| 2292 | }, |
| 2293 | .bool => break :opcode switch (op) { |
| 2294 | .eq => .OpIEqual, |
| 2295 | .neq => .OpINotEqual, |
| 2296 | else => unreachable, |
| 2297 | }, |
| 2298 | .strange_integer => sign: { |
| 2299 | const op_ty_ref = try self.resolveType(op_ty, .direct); |
| 2300 | // Mask operands before performing comparison. |
| 2301 | cmp_lhs_id = try self.maskStrangeInt(op_ty_ref, cmp_lhs_id, info.bits); |
| 2302 | cmp_rhs_id = try self.maskStrangeInt(op_ty_ref, cmp_rhs_id, info.bits); |
| 2303 | break :sign info.signedness; |
| 2304 | }, |
| 2305 | .integer => info.signedness, |
| 2306 | }; |
| 2307 | |
| 2308 | break :opcode switch (signedness) { |
| 2309 | .unsigned => switch (op) { |
| 2310 | .eq => .OpIEqual, |
| 2311 | .neq => .OpINotEqual, |
| 2312 | .lt => .OpULessThan, |
| 2313 | .lte => .OpULessThanEqual, |
| 2314 | .gt => .OpUGreaterThan, |
| 2315 | .gte => .OpUGreaterThanEqual, |
| 2316 | }, |
| 2317 | .signed => switch (op) { |
| 2318 | .eq => .OpIEqual, |
| 2319 | .neq => .OpINotEqual, |
| 2320 | .lt => .OpSLessThan, |
| 2321 | .lte => .OpSLessThanEqual, |
| 2322 | .gt => .OpSGreaterThan, |
| 2323 | .gte => .OpSGreaterThanEqual, |
| 2324 | }, |
| 2325 | }; |
| 2326 | }; |
| 2327 | |
| 2328 | const result_id = self.spv.allocId(); |
| 2329 | try self.func.body.emitRaw(self.spv.gpa, opcode, 4); |
| 2330 | self.func.body.writeOperand(spec.IdResultType, bool_ty_id); |
| 2331 | self.func.body.writeOperand(spec.IdResult, result_id); |
| 2332 | self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id); |
| 2333 | self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id); |
| 2092 | 2334 | return result_id; |
| 2093 | 2335 | } |
| 2094 | 2336 | |
| 2095 | | fn bitcast(self: *DeclGen, target_type_id: IdResultType, value_id: IdRef) !IdRef { |
| 2337 | fn airCmp( |
| 2338 | self: *DeclGen, |
| 2339 | inst: Air.Inst.Index, |
| 2340 | comptime op: std.math.CompareOperator, |
| 2341 | ) !?IdRef { |
| 2342 | if (self.liveness.isUnused(inst)) return null; |
| 2343 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2344 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2345 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2346 | const bool_ty_id = try self.resolveTypeId(Type.bool); |
| 2347 | const ty = self.air.typeOf(bin_op.lhs); |
| 2348 | assert(ty.eql(self.air.typeOf(bin_op.rhs), self.module)); |
| 2349 | |
| 2350 | return try self.cmp(op, bool_ty_id, ty, lhs_id, rhs_id); |
| 2351 | } |
| 2352 | |
| 2353 | fn bitCast( |
| 2354 | self: *DeclGen, |
| 2355 | dst_ty: Type, |
| 2356 | src_ty: Type, |
| 2357 | src_id: IdRef, |
| 2358 | ) !IdRef { |
| 2359 | const dst_ty_ref = try self.resolveType(dst_ty, .direct); |
| 2096 | 2360 | const result_id = self.spv.allocId(); |
| 2097 | | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 2098 | | .id_result_type = target_type_id, |
| 2099 | | .id_result = result_id, |
| 2100 | | .operand = value_id, |
| 2101 | | }); |
| 2361 | |
| 2362 | // TODO: Some more cases are missing here |
| 2363 | // See fn bitCast in llvm.zig |
| 2364 | |
| 2365 | if (src_ty.zigTypeTag() == .Int and dst_ty.isPtrAtRuntime()) { |
| 2366 | try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{ |
| 2367 | .id_result_type = self.typeId(dst_ty_ref), |
| 2368 | .id_result = result_id, |
| 2369 | .integer_value = src_id, |
| 2370 | }); |
| 2371 | } else { |
| 2372 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 2373 | .id_result_type = self.typeId(dst_ty_ref), |
| 2374 | .id_result = result_id, |
| 2375 | .operand = src_id, |
| 2376 | }); |
| 2377 | } |
| 2102 | 2378 | return result_id; |
| 2103 | 2379 | } |
| 2104 | 2380 | |
| 2105 | | fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2381 | fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2106 | 2382 | if (self.liveness.isUnused(inst)) return null; |
| 2107 | 2383 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2108 | 2384 | const operand_id = try self.resolve(ty_op.operand); |
| 2109 | | const result_type_id = try self.resolveTypeId(self.air.typeOfIndex(inst)); |
| 2110 | | return try self.bitcast(result_type_id, operand_id); |
| 2385 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 2386 | const result_ty = self.air.typeOfIndex(inst); |
| 2387 | return try self.bitCast(result_ty, operand_ty, operand_id); |
| 2111 | 2388 | } |
| 2112 | 2389 | |
| 2113 | | fn airIntcast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2390 | fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2114 | 2391 | if (self.liveness.isUnused(inst)) return null; |
| 2115 | 2392 | |
| 2116 | 2393 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2117 | 2394 | const operand_id = try self.resolve(ty_op.operand); |
| 2118 | 2395 | const dest_ty = self.air.typeOfIndex(inst); |
| 2119 | | const dest_info = try self.arithmeticTypeInfo(dest_ty); |
| 2120 | 2396 | const dest_ty_id = try self.resolveTypeId(dest_ty); |
| 2121 | 2397 | |
| 2398 | const target = self.getTarget(); |
| 2399 | const dest_info = dest_ty.intInfo(target); |
| 2400 | |
| 2401 | // TODO: Masking? |
| 2402 | |
| 2122 | 2403 | const result_id = self.spv.allocId(); |
| 2123 | 2404 | switch (dest_info.signedness) { |
| 2124 | 2405 | .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ |
| ... | ... | @@ -2221,11 +2502,7 @@ pub const DeclGen = struct { |
| 2221 | 2502 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2222 | 2503 | const field_ty = self.air.typeOfIndex(inst); |
| 2223 | 2504 | const operand_id = try self.resolve(ty_op.operand); |
| 2224 | | return try self.extractField( |
| 2225 | | field_ty, |
| 2226 | | operand_id, |
| 2227 | | field, |
| 2228 | | ); |
| 2505 | return try self.extractField(field_ty, operand_id, field); |
| 2229 | 2506 | } |
| 2230 | 2507 | |
| 2231 | 2508 | fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2233,30 +2510,14 @@ pub const DeclGen = struct { |
| 2233 | 2510 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2234 | 2511 | if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null; |
| 2235 | 2512 | |
| 2236 | | const slice = try self.resolve(bin_op.lhs); |
| 2237 | | const index = try self.resolve(bin_op.rhs); |
| 2238 | | |
| 2239 | | const spv_ptr_ty = try self.resolveTypeId(self.air.typeOfIndex(inst)); |
| 2513 | const slice_id = try self.resolve(bin_op.lhs); |
| 2514 | const index_id = try self.resolve(bin_op.rhs); |
| 2240 | 2515 | |
| 2241 | | const slice_ptr = blk: { |
| 2242 | | const result_id = self.spv.allocId(); |
| 2243 | | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ |
| 2244 | | .id_result_type = spv_ptr_ty, |
| 2245 | | .id_result = result_id, |
| 2246 | | .composite = slice, |
| 2247 | | .indexes = &.{0}, |
| 2248 | | }); |
| 2249 | | break :blk result_id; |
| 2250 | | }; |
| 2516 | const ptr_ty = self.air.typeOfIndex(inst); |
| 2517 | const ptr_ty_ref = try self.resolveType(ptr_ty, .direct); |
| 2251 | 2518 | |
| 2252 | | const result_id = self.spv.allocId(); |
| 2253 | | try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ |
| 2254 | | .id_result_type = spv_ptr_ty, |
| 2255 | | .id_result = result_id, |
| 2256 | | .base = slice_ptr, |
| 2257 | | .element = index, |
| 2258 | | }); |
| 2259 | | return result_id; |
| 2519 | const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); |
| 2520 | return try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{}); |
| 2260 | 2521 | } |
| 2261 | 2522 | |
| 2262 | 2523 | fn airSliceElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2264,61 +2525,64 @@ pub const DeclGen = struct { |
| 2264 | 2525 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2265 | 2526 | if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) return null; |
| 2266 | 2527 | |
| 2267 | | const slice = try self.resolve(bin_op.lhs); |
| 2268 | | const index = try self.resolve(bin_op.rhs); |
| 2528 | const slice_id = try self.resolve(bin_op.lhs); |
| 2529 | const index_id = try self.resolve(bin_op.rhs); |
| 2269 | 2530 | |
| 2270 | 2531 | var slice_buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2271 | | const ptr_ty_id = try self.resolveTypeId(slice_ty.slicePtrFieldType(&slice_buf)); |
| 2272 | | |
| 2273 | | const slice_ptr = blk: { |
| 2274 | | const result_id = self.spv.allocId(); |
| 2275 | | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ |
| 2276 | | .id_result_type = ptr_ty_id, |
| 2277 | | .id_result = result_id, |
| 2278 | | .composite = slice, |
| 2279 | | .indexes = &.{0}, |
| 2280 | | }); |
| 2281 | | break :blk result_id; |
| 2282 | | }; |
| 2283 | | |
| 2284 | | const elem_ptr = blk: { |
| 2285 | | const result_id = self.spv.allocId(); |
| 2286 | | try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ |
| 2287 | | .id_result_type = ptr_ty_id, |
| 2288 | | .id_result = result_id, |
| 2289 | | .base = slice_ptr, |
| 2290 | | .element = index, |
| 2291 | | }); |
| 2292 | | break :blk result_id; |
| 2293 | | }; |
| 2532 | const ptr_ty = slice_ty.slicePtrFieldType(&slice_buf); |
| 2533 | const ptr_ty_ref = try self.resolveType(ptr_ty, .direct); |
| 2294 | 2534 | |
| 2535 | const slice_ptr = try self.extractField(ptr_ty, slice_id, 0); |
| 2536 | const elem_ptr = try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{}); |
| 2295 | 2537 | return try self.load(slice_ty, elem_ptr); |
| 2296 | 2538 | } |
| 2297 | 2539 | |
| 2540 | fn ptrElemPtr(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef, index_id: IdRef) !IdRef { |
| 2541 | // Construct new pointer type for the resulting pointer |
| 2542 | const elem_ty = ptr_ty.elemType2(); // use elemType() so that we get T for *[N]T. |
| 2543 | const elem_ty_ref = try self.resolveType(elem_ty, .direct); |
| 2544 | const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace()), 0); |
| 2545 | if (ptr_ty.isSinglePointer()) { |
| 2546 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 2547 | // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. |
| 2548 | return try self.accessChain(elem_ptr_ty_ref, ptr_id, &.{index_id}); |
| 2549 | } else { |
| 2550 | // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain |
| 2551 | return try self.ptrAccessChain(elem_ptr_ty_ref, ptr_id, index_id, &.{}); |
| 2552 | } |
| 2553 | } |
| 2554 | |
| 2298 | 2555 | fn airPtrElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2299 | 2556 | if (self.liveness.isUnused(inst)) return null; |
| 2300 | 2557 | |
| 2301 | 2558 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2302 | 2559 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2303 | 2560 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2304 | | const result_ty = self.air.typeOfIndex(inst); |
| 2305 | 2561 | const elem_ty = ptr_ty.childType(); |
| 2306 | 2562 | // TODO: Make this return a null ptr or something |
| 2307 | 2563 | if (!elem_ty.hasRuntimeBitsIgnoreComptime()) return null; |
| 2308 | 2564 | |
| 2309 | | const result_type_id = try self.resolveTypeId(result_ty); |
| 2310 | | const base_ptr = try self.resolve(bin_op.lhs); |
| 2311 | | const rhs = try self.resolve(bin_op.rhs); |
| 2565 | const ptr_id = try self.resolve(bin_op.lhs); |
| 2566 | const index_id = try self.resolve(bin_op.rhs); |
| 2567 | return try self.ptrElemPtr(ptr_ty, ptr_id, index_id); |
| 2568 | } |
| 2312 | 2569 | |
| 2313 | | const result_id = self.spv.allocId(); |
| 2314 | | const indexes = [_]IdRef{rhs}; |
| 2315 | | try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| 2316 | | .id_result_type = result_type_id, |
| 2317 | | .id_result = result_id, |
| 2318 | | .base = base_ptr, |
| 2319 | | .indexes = &indexes, |
| 2320 | | }); |
| 2321 | | return result_id; |
| 2570 | fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2571 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2572 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2573 | const ptr_id = try self.resolve(bin_op.lhs); |
| 2574 | const index_id = try self.resolve(bin_op.rhs); |
| 2575 | |
| 2576 | const elem_ptr_id = try self.ptrElemPtr(ptr_ty, ptr_id, index_id); |
| 2577 | |
| 2578 | // If we have a pointer-to-array, construct an element pointer to use with load() |
| 2579 | // If we pass ptr_ty directly, it will attempt to load the entire array rather than |
| 2580 | // just an element. |
| 2581 | var elem_ptr_info = ptr_ty.ptrInfo(); |
| 2582 | elem_ptr_info.data.size = .One; |
| 2583 | const elem_ptr_ty = Type.initPayload(&elem_ptr_info.base); |
| 2584 | |
| 2585 | return try self.load(elem_ptr_ty, elem_ptr_id); |
| 2322 | 2586 | } |
| 2323 | 2587 | |
| 2324 | 2588 | fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| ... | ... | @@ -2344,24 +2608,15 @@ pub const DeclGen = struct { |
| 2344 | 2608 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 2345 | 2609 | |
| 2346 | 2610 | const struct_ty = self.air.typeOf(struct_field.struct_operand); |
| 2347 | | const object = try self.resolve(struct_field.struct_operand); |
| 2611 | const object_id = try self.resolve(struct_field.struct_operand); |
| 2348 | 2612 | const field_index = struct_field.field_index; |
| 2349 | 2613 | const field_ty = struct_ty.structFieldType(field_index); |
| 2350 | | const field_ty_id = try self.resolveTypeId(field_ty); |
| 2351 | 2614 | |
| 2352 | 2615 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) return null; |
| 2353 | 2616 | |
| 2354 | 2617 | assert(struct_ty.zigTypeTag() == .Struct); // Cannot do unions yet. |
| 2355 | 2618 | |
| 2356 | | const result_id = self.spv.allocId(); |
| 2357 | | const indexes = [_]u32{field_index}; |
| 2358 | | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ |
| 2359 | | .id_result_type = field_ty_id, |
| 2360 | | .id_result = result_id, |
| 2361 | | .composite = object, |
| 2362 | | .indexes = &indexes, |
| 2363 | | }); |
| 2364 | | return result_id; |
| 2619 | return try self.extractField(field_ty, object_id, field_index); |
| 2365 | 2620 | } |
| 2366 | 2621 | |
| 2367 | 2622 | fn structFieldPtr( |
| ... | ... | @@ -2379,16 +2634,8 @@ pub const DeclGen = struct { |
| 2379 | 2634 | const u32_ty_id = self.typeId(try self.intType(.unsigned, 32)); |
| 2380 | 2635 | const field_index_id = self.spv.allocId(); |
| 2381 | 2636 | try self.spv.emitConstant(u32_ty_id, field_index_id, .{ .uint32 = field_index }); |
| 2382 | | const result_id = self.spv.allocId(); |
| 2383 | | const result_type_id = try self.resolveTypeId(result_ptr_ty); |
| 2384 | | const indexes = [_]IdRef{field_index_id}; |
| 2385 | | try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ |
| 2386 | | .id_result_type = result_type_id, |
| 2387 | | .id_result = result_id, |
| 2388 | | .base = object_ptr, |
| 2389 | | .indexes = &indexes, |
| 2390 | | }); |
| 2391 | | return result_id; |
| 2637 | const result_ty_ref = try self.resolveType(result_ptr_ty, .direct); |
| 2638 | return try self.accessChain(result_ty_ref, object_ptr, &.{field_index_id}); |
| 2392 | 2639 | }, |
| 2393 | 2640 | }, |
| 2394 | 2641 | else => unreachable, // TODO |
| ... | ... | @@ -2422,76 +2669,45 @@ pub const DeclGen = struct { |
| 2422 | 2669 | return result_id; |
| 2423 | 2670 | } |
| 2424 | 2671 | |
| 2425 | | fn variable( |
| 2672 | // Allocate a function-local variable, with possible initializer. |
| 2673 | // This function returns a pointer to a variable of type `ty_ref`, |
| 2674 | // which is in the Generic address space. The variable is actually |
| 2675 | // placed in the Function address space. |
| 2676 | fn alloc( |
| 2426 | 2677 | self: *DeclGen, |
| 2427 | | comptime context: enum { function, global }, |
| 2428 | | result_id: IdRef, |
| 2429 | | ptr_ty_ref: SpvType.Ref, |
| 2678 | ty_ref: SpvType.Ref, |
| 2430 | 2679 | initializer: ?IdRef, |
| 2431 | | ) !void { |
| 2432 | | const storage_class = self.spv.typeRefType(ptr_ty_ref).payload(.pointer).storage_class; |
| 2433 | | const actual_storage_class = switch (storage_class) { |
| 2434 | | .Generic => switch (context) { |
| 2435 | | .function => .Function, |
| 2436 | | .global => .CrossWorkgroup, |
| 2437 | | }, |
| 2438 | | else => storage_class, |
| 2439 | | }; |
| 2440 | | const actual_ptr_ty_ref = switch (storage_class) { |
| 2441 | | .Generic => try self.spv.changePtrStorageClass(ptr_ty_ref, actual_storage_class), |
| 2442 | | else => ptr_ty_ref, |
| 2443 | | }; |
| 2444 | | const alloc_result_id = switch (storage_class) { |
| 2445 | | .Generic => self.spv.allocId(), |
| 2446 | | else => result_id, |
| 2447 | | }; |
| 2680 | ) !IdRef { |
| 2681 | const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function, 0); |
| 2682 | const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, 0); |
| 2448 | 2683 | |
| 2449 | | const section = switch (actual_storage_class) { |
| 2450 | | .Generic => unreachable, |
| 2451 | | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to |
| 2452 | | // directly generate them into func.prologue instead of the body. |
| 2453 | | .Function => &self.func.prologue, |
| 2454 | | else => &self.spv.sections.types_globals_constants, |
| 2455 | | }; |
| 2456 | | try section.emit(self.spv.gpa, .OpVariable, .{ |
| 2457 | | .id_result_type = self.typeId(actual_ptr_ty_ref), |
| 2458 | | .id_result = alloc_result_id, |
| 2459 | | .storage_class = actual_storage_class, |
| 2684 | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to |
| 2685 | // directly generate them into func.prologue instead of the body. |
| 2686 | const var_id = self.spv.allocId(); |
| 2687 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| 2688 | .id_result_type = self.typeId(fn_ptr_ty_ref), |
| 2689 | .id_result = var_id, |
| 2690 | .storage_class = .Function, |
| 2460 | 2691 | .initializer = initializer, |
| 2461 | 2692 | }); |
| 2462 | 2693 | |
| 2463 | | if (storage_class != .Generic) { |
| 2464 | | return; |
| 2465 | | } |
| 2466 | | |
| 2467 | | // Now we need to convert the pointer. |
| 2468 | | // If this is a function local, we need to perform the conversion at runtime. Otherwise, we can do |
| 2469 | | // it ahead of time using OpSpecConstantOp. |
| 2470 | | switch (actual_storage_class) { |
| 2471 | | .Function => try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2472 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 2473 | | .id_result = result_id, |
| 2474 | | .pointer = alloc_result_id, |
| 2475 | | }), |
| 2476 | | // TODO: Can we do without this cast or move it to runtime? |
| 2477 | | else => { |
| 2478 | | const const_ptr_id = try self.makePointerConstant(section, actual_ptr_ty_ref, alloc_result_id); |
| 2479 | | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2480 | | .id_result_type = self.typeId(ptr_ty_ref), |
| 2481 | | .id_result = result_id, |
| 2482 | | .pointer = const_ptr_id, |
| 2483 | | }); |
| 2484 | | }, |
| 2485 | | } |
| 2694 | // Convert to a generic pointer |
| 2695 | const result_id = self.spv.allocId(); |
| 2696 | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 2697 | .id_result_type = self.typeId(general_ptr_ty_ref), |
| 2698 | .id_result = result_id, |
| 2699 | .pointer = var_id, |
| 2700 | }); |
| 2701 | return result_id; |
| 2486 | 2702 | } |
| 2487 | 2703 | |
| 2488 | 2704 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2489 | 2705 | if (self.liveness.isUnused(inst)) return null; |
| 2490 | | const ty = self.air.typeOfIndex(inst); |
| 2491 | | const result_ty_ref = try self.resolveType(ty, .direct); |
| 2492 | | const result_id = self.spv.allocId(); |
| 2493 | | try self.variable(.function, result_id, result_ty_ref, null); |
| 2494 | | return result_id; |
| 2706 | const ptr_ty = self.air.typeOfIndex(inst); |
| 2707 | assert(ptr_ty.ptrAddressSpace() == .generic); |
| 2708 | const child_ty = ptr_ty.childType(); |
| 2709 | const child_ty_ref = try self.resolveType(child_ty, .indirect); |
| 2710 | return try self.alloc(child_ty_ref, null); |
| 2495 | 2711 | } |
| 2496 | 2712 | |
| 2497 | 2713 | fn airArg(self: *DeclGen) IdRef { |
| ... | ... | @@ -2778,13 +2994,7 @@ pub const DeclGen = struct { |
| 2778 | 2994 | } |
| 2779 | 2995 | |
| 2780 | 2996 | const err_union_ty_ref = try self.resolveType(err_union_ty, .direct); |
| 2781 | | const result_id = self.spv.allocId(); |
| 2782 | | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 2783 | | .id_result_type = self.typeId(err_union_ty_ref), |
| 2784 | | .id_result = result_id, |
| 2785 | | .constituents = members.slice(), |
| 2786 | | }); |
| 2787 | | return result_id; |
| 2997 | return try self.constructStruct(err_union_ty_ref, members.slice()); |
| 2788 | 2998 | } |
| 2789 | 2999 | |
| 2790 | 3000 | fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef { |
| ... | ... | @@ -2884,14 +3094,8 @@ pub const DeclGen = struct { |
| 2884 | 3094 | } |
| 2885 | 3095 | |
| 2886 | 3096 | const optional_ty_ref = try self.resolveType(optional_ty, .direct); |
| 2887 | | const result_id = self.spv.allocId(); |
| 2888 | 3097 | const members = [_]IdRef{ operand_id, try self.constBool(true, .indirect) }; |
| 2889 | | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| 2890 | | .id_result_type = self.typeId(optional_ty_ref), |
| 2891 | | .id_result = result_id, |
| 2892 | | .constituents = &members, |
| 2893 | | }); |
| 2894 | | return result_id; |
| 3098 | return try self.constructStruct(optional_ty_ref, &members); |
| 2895 | 3099 | } |
| 2896 | 3100 | |
| 2897 | 3101 | fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { |