| author | |
| committer | |
| log | 9461ed5037de8f3e4f03021c27d7458aa3d1a432 |
| tree | f6c637ae2a516a0f558fb7c982185d0028ebfe7c |
| parent | 879f0b9cee9b409160edf10d8b52f73be2bddb4f |
| parent | 3c4cc1eedb959aa804ca752e20268ccecc14ccef |
| signature |
SPIR-V Intern Pool5 files changed, 1354 insertions(+), 1327 deletions(-)
src/codegen/spirv.zig+172-226| ... | ... | @@ -22,8 +22,10 @@ const IdResultType = spec.IdResultType; |
| 22 | 22 | const StorageClass = spec.StorageClass; |
| 23 | 23 | |
| 24 | 24 | const SpvModule = @import("spirv/Module.zig"); |
| 25 | const CacheRef = SpvModule.CacheRef; | |
| 26 | const CacheString = SpvModule.CacheString; | |
| 27 | ||
| 25 | 28 | const SpvSection = @import("spirv/Section.zig"); |
| 26 | const SpvType = @import("spirv/type.zig").Type; | |
| 27 | 29 | const SpvAssembler = @import("spirv/Assembler.zig"); |
| 28 | 30 | |
| 29 | 31 | const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef); |
| ... | ... | @@ -377,74 +379,23 @@ pub const DeclGen = struct { |
| 377 | 379 | }; |
| 378 | 380 | } |
| 379 | 381 | |
| 380 | fn genConstInt(self: *DeclGen, ty_ref: SpvType.Ref, result_id: IdRef, value: anytype) !void { | |
| 381 | const ty = self.spv.typeRefType(ty_ref); | |
| 382 | const ty_id = self.typeId(ty_ref); | |
| 383 | ||
| 384 | const Lit = spec.LiteralContextDependentNumber; | |
| 385 | const literal = switch (ty.intSignedness()) { | |
| 386 | .signed => switch (ty.intFloatBits()) { | |
| 387 | 1...32 => Lit{ .int32 = @intCast(i32, value) }, | |
| 388 | 33...64 => Lit{ .int64 = @intCast(i64, value) }, | |
| 389 | else => unreachable, // TODO: composite integer literals | |
| 390 | }, | |
| 391 | .unsigned => switch (ty.intFloatBits()) { | |
| 392 | 1...32 => Lit{ .uint32 = @intCast(u32, value) }, | |
| 393 | 33...64 => Lit{ .uint64 = @intCast(u64, value) }, | |
| 394 | else => unreachable, | |
| 395 | }, | |
| 396 | }; | |
| 397 | ||
| 398 | try self.spv.emitConstant(ty_id, result_id, literal); | |
| 399 | } | |
| 400 | ||
| 401 | fn constInt(self: *DeclGen, ty_ref: SpvType.Ref, value: anytype) !IdRef { | |
| 402 | const result_id = self.spv.allocId(); | |
| 403 | try self.genConstInt(ty_ref, result_id, value); | |
| 404 | return result_id; | |
| 405 | } | |
| 406 | ||
| 407 | fn constUndef(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef { | |
| 408 | const result_id = self.spv.allocId(); | |
| 409 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpUndef, .{ | |
| 410 | .id_result_type = self.typeId(ty_ref), | |
| 411 | .id_result = result_id, | |
| 412 | }); | |
| 413 | return result_id; | |
| 414 | } | |
| 415 | ||
| 416 | fn constNull(self: *DeclGen, ty_ref: SpvType.Ref) !IdRef { | |
| 417 | const result_id = self.spv.allocId(); | |
| 418 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantNull, .{ | |
| 419 | .id_result_type = self.typeId(ty_ref), | |
| 420 | .id_result = result_id, | |
| 421 | }); | |
| 422 | return result_id; | |
| 423 | } | |
| 424 | ||
| 382 | /// Emits a bool constant in a particular representation. | |
| 425 | 383 | fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef { |
| 426 | 384 | switch (repr) { |
| 427 | 385 | .indirect => { |
| 428 | 386 | const int_ty_ref = try self.intType(.unsigned, 1); |
| 429 | return self.constInt(int_ty_ref, @boolToInt(value)); | |
| 387 | return self.spv.constInt(int_ty_ref, @boolToInt(value)); | |
| 430 | 388 | }, |
| 431 | 389 | .direct => { |
| 432 | 390 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 433 | const result_id = self.spv.allocId(); | |
| 434 | const operands = .{ .id_result_type = self.typeId(bool_ty_ref), .id_result = result_id }; | |
| 435 | if (value) { | |
| 436 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantTrue, operands); | |
| 437 | } else { | |
| 438 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpConstantFalse, operands); | |
| 439 | } | |
| 440 | return result_id; | |
| 391 | return self.spv.constBool(bool_ty_ref, value); | |
| 441 | 392 | }, |
| 442 | 393 | } |
| 443 | 394 | } |
| 444 | 395 | |
| 445 | 396 | /// Construct a struct at runtime. |
| 446 | 397 | /// result_ty_ref must be a struct type. |
| 447 | fn constructStruct(self: *DeclGen, result_ty_ref: SpvType.Ref, constituents: []const IdRef) !IdRef { | |
| 398 | fn constructStruct(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef { | |
| 448 | 399 | // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' |
| 449 | 400 | // operands are not constant. |
| 450 | 401 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| ... | ... | @@ -453,11 +404,13 @@ pub const DeclGen = struct { |
| 453 | 404 | const ptr_composite_id = try self.alloc(result_ty_ref, null); |
| 454 | 405 | // Note: using 32-bit ints here because usize crashes the translator as well |
| 455 | 406 | 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); | |
| 407 | ||
| 408 | const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type; | |
| 409 | const member_types = spv_composite_ty.member_types; | |
| 410 | ||
| 411 | for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| { | |
| 412 | const index_id = try self.spv.constInt(index_ty_ref, index); | |
| 413 | const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Generic); | |
| 461 | 414 | const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{index_id}); |
| 462 | 415 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 463 | 416 | .pointer = ptr_id, |
| ... | ... | @@ -478,11 +431,9 @@ pub const DeclGen = struct { |
| 478 | 431 | |
| 479 | 432 | dg: *DeclGen, |
| 480 | 433 | /// Cached reference of the u32 type. |
| 481 | u32_ty_ref: SpvType.Ref, | |
| 482 | /// Cached type id of the u32 type. | |
| 483 | u32_ty_id: IdRef, | |
| 434 | u32_ty_ref: CacheRef, | |
| 484 | 435 | /// The members of the resulting structure type |
| 485 | members: std.ArrayList(SpvType.Payload.Struct.Member), | |
| 436 | members: std.ArrayList(CacheRef), | |
| 486 | 437 | /// The initializers of each of the members. |
| 487 | 438 | initializers: std.ArrayList(IdRef), |
| 488 | 439 | /// The current size of the structure. Includes |
| ... | ... | @@ -513,10 +464,8 @@ pub const DeclGen = struct { |
| 513 | 464 | } |
| 514 | 465 | |
| 515 | 466 | const word = @bitCast(Word, self.partial_word.buffer); |
| 516 | const result_id = self.dg.spv.allocId(); | |
| 517 | // TODO: Integrate with caching mechanism | |
| 518 | try self.dg.spv.emitConstant(self.u32_ty_id, result_id, .{ .uint32 = word }); | |
| 519 | try self.members.append(.{ .ty = self.u32_ty_ref }); | |
| 467 | const result_id = try self.dg.spv.constInt(self.u32_ty_ref, word); | |
| 468 | try self.members.append(self.u32_ty_ref); | |
| 520 | 469 | try self.initializers.append(result_id); |
| 521 | 470 | |
| 522 | 471 | self.partial_word.len = 0; |
| ... | ... | @@ -552,7 +501,7 @@ pub const DeclGen = struct { |
| 552 | 501 | } |
| 553 | 502 | } |
| 554 | 503 | |
| 555 | fn addPtr(self: *@This(), ptr_ty_ref: SpvType.Ref, ptr_id: IdRef) !void { | |
| 504 | fn addPtr(self: *@This(), ptr_ty_ref: CacheRef, ptr_id: IdRef) !void { | |
| 556 | 505 | // TODO: Double check pointer sizes here. |
| 557 | 506 | // shared pointers might be u32... |
| 558 | 507 | const target = self.dg.getTarget(); |
| ... | ... | @@ -560,17 +509,13 @@ pub const DeclGen = struct { |
| 560 | 509 | if (self.size % width != 0) { |
| 561 | 510 | return self.dg.todo("misaligned pointer constants", .{}); |
| 562 | 511 | } |
| 563 | try self.members.append(.{ .ty = ptr_ty_ref }); | |
| 512 | try self.members.append(ptr_ty_ref); | |
| 564 | 513 | try self.initializers.append(ptr_id); |
| 565 | 514 | self.size += width; |
| 566 | 515 | } |
| 567 | 516 | |
| 568 | fn addNullPtr(self: *@This(), ptr_ty_ref: SpvType.Ref) !void { | |
| 569 | const result_id = self.dg.spv.allocId(); | |
| 570 | try self.dg.spv.sections.types_globals_constants.emit(self.dg.spv.gpa, .OpConstantNull, .{ | |
| 571 | .id_result_type = self.dg.typeId(ptr_ty_ref), | |
| 572 | .id_result = result_id, | |
| 573 | }); | |
| 517 | fn addNullPtr(self: *@This(), ptr_ty_ref: CacheRef) !void { | |
| 518 | const result_id = try self.dg.spv.constNull(ptr_ty_ref); | |
| 574 | 519 | try self.addPtr(ptr_ty_ref, result_id); |
| 575 | 520 | } |
| 576 | 521 | |
| ... | ... | @@ -928,7 +873,7 @@ pub const DeclGen = struct { |
| 928 | 873 | const section = &self.spv.globals.section; |
| 929 | 874 | |
| 930 | 875 | const ty_ref = try self.resolveType(ty, .indirect); |
| 931 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, 0); | |
| 876 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class); | |
| 932 | 877 | |
| 933 | 878 | // const target = self.getTarget(); |
| 934 | 879 | |
| ... | ... | @@ -956,8 +901,7 @@ pub const DeclGen = struct { |
| 956 | 901 | var icl = IndirectConstantLowering{ |
| 957 | 902 | .dg = self, |
| 958 | 903 | .u32_ty_ref = u32_ty_ref, |
| 959 | .u32_ty_id = self.typeId(u32_ty_ref), | |
| 960 | .members = std.ArrayList(SpvType.Payload.Struct.Member).init(self.gpa), | |
| 904 | .members = std.ArrayList(CacheRef).init(self.gpa), | |
| 961 | 905 | .initializers = std.ArrayList(IdRef).init(self.gpa), |
| 962 | 906 | .decl_deps = std.AutoArrayHashMap(SpvModule.Decl.Index, void).init(self.gpa), |
| 963 | 907 | }; |
| ... | ... | @@ -969,8 +913,10 @@ pub const DeclGen = struct { |
| 969 | 913 | try icl.lower(ty, val); |
| 970 | 914 | try icl.flush(); |
| 971 | 915 | |
| 972 | const constant_struct_ty_ref = try self.spv.simpleStructType(icl.members.items); | |
| 973 | const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class, 0); | |
| 916 | const constant_struct_ty_ref = try self.spv.resolve(.{ .struct_type = .{ | |
| 917 | .member_types = icl.members.items, | |
| 918 | } }); | |
| 919 | const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class); | |
| 974 | 920 | |
| 975 | 921 | const constant_struct_id = self.spv.allocId(); |
| 976 | 922 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ |
| ... | ... | @@ -1004,7 +950,7 @@ pub const DeclGen = struct { |
| 1004 | 950 | }); |
| 1005 | 951 | |
| 1006 | 952 | if (cast_to_generic) { |
| 1007 | const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic, 0); | |
| 953 | const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic); | |
| 1008 | 954 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1009 | 955 | .id_result_type = self.typeId(generic_ptr_ty_ref), |
| 1010 | 956 | .id_result = result_id, |
| ... | ... | @@ -1023,52 +969,32 @@ pub const DeclGen = struct { |
| 1023 | 969 | /// This function should only be called during function code generation. |
| 1024 | 970 | fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) !IdRef { |
| 1025 | 971 | const target = self.getTarget(); |
| 1026 | const section = &self.spv.sections.types_globals_constants; | |
| 1027 | 972 | const result_ty_ref = try self.resolveType(ty, repr); |
| 1028 | const result_ty_id = self.typeId(result_ty_ref); | |
| 1029 | 973 | |
| 1030 | 974 | log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) }); |
| 1031 | 975 | |
| 1032 | 976 | if (val.isUndef()) { |
| 1033 | const result_id = self.spv.allocId(); | |
| 1034 | try section.emit(self.spv.gpa, .OpUndef, .{ | |
| 1035 | .id_result_type = result_ty_id, | |
| 1036 | .id_result = result_id, | |
| 1037 | }); | |
| 1038 | return result_id; | |
| 977 | return self.spv.constUndef(result_ty_ref); | |
| 1039 | 978 | } |
| 1040 | 979 | |
| 1041 | 980 | switch (ty.zigTypeTag()) { |
| 1042 | 981 | .Int => { |
| 1043 | 982 | if (ty.isSignedInt()) { |
| 1044 | return try self.constInt(result_ty_ref, val.toSignedInt(target)); | |
| 983 | return try self.spv.constInt(result_ty_ref, val.toSignedInt(target)); | |
| 1045 | 984 | } else { |
| 1046 | return try self.constInt(result_ty_ref, val.toUnsignedInt(target)); | |
| 985 | return try self.spv.constInt(result_ty_ref, val.toUnsignedInt(target)); | |
| 1047 | 986 | } |
| 1048 | 987 | }, |
| 1049 | 988 | .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())), | |
| 989 | .direct => return try self.spv.constBool(result_ty_ref, val.toBool()), | |
| 990 | .indirect => return try self.spv.constInt(result_ty_ref, @boolToInt(val.toBool())), | |
| 1061 | 991 | }, |
| 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; | |
| 992 | .Float => return switch (ty.floatBits(target)) { | |
| 993 | 16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16) } } }), | |
| 994 | 32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32) } } }), | |
| 995 | 64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64) } } }), | |
| 996 | 80, 128 => unreachable, // TODO | |
| 997 | else => unreachable, | |
| 1072 | 998 | }, |
| 1073 | 999 | .ErrorSet => { |
| 1074 | 1000 | const value = switch (val.tag()) { |
| ... | ... | @@ -1081,7 +1007,7 @@ pub const DeclGen = struct { |
| 1081 | 1007 | else => unreachable, |
| 1082 | 1008 | }; |
| 1083 | 1009 | |
| 1084 | return try self.constInt(result_ty_ref, value); | |
| 1010 | return try self.spv.constInt(result_ty_ref, value); | |
| 1085 | 1011 | }, |
| 1086 | 1012 | .ErrorUnion => { |
| 1087 | 1013 | const payload_ty = ty.errorUnionPayload(); |
| ... | ... | @@ -1126,7 +1052,7 @@ pub const DeclGen = struct { |
| 1126 | 1052 | try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); |
| 1127 | 1053 | |
| 1128 | 1054 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 1129 | .id_result_type = result_ty_id, | |
| 1055 | .id_result_type = self.typeId(result_ty_ref), | |
| 1130 | 1056 | .id_result = result_id, |
| 1131 | 1057 | .pointer = self.spv.declPtr(spv_decl_index).result_id, |
| 1132 | 1058 | }); |
| ... | ... | @@ -1140,26 +1066,28 @@ pub const DeclGen = struct { |
| 1140 | 1066 | /// Turn a Zig type into a SPIR-V Type, and return its type result-id. |
| 1141 | 1067 | fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType { |
| 1142 | 1068 | const type_ref = try self.resolveType(ty, .direct); |
| 1143 | return self.typeId(type_ref); | |
| 1069 | return self.spv.resultId(type_ref); | |
| 1144 | 1070 | } |
| 1145 | 1071 | |
| 1146 | fn typeId(self: *DeclGen, ty_ref: SpvType.Ref) IdRef { | |
| 1147 | return self.spv.typeId(ty_ref); | |
| 1072 | fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef { | |
| 1073 | return self.spv.resultId(ty_ref); | |
| 1148 | 1074 | } |
| 1149 | 1075 | |
| 1150 | 1076 | /// Create an integer type suitable for storing at least 'bits' bits. |
| 1151 | fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !SpvType.Ref { | |
| 1077 | /// The integer type that is returned by this function is the type that is used to perform | |
| 1078 | /// actual operations (as well as store) a Zig type of a particular number of bits. To create | |
| 1079 | /// a type with an exact size, use SpvModule.intType. | |
| 1080 | fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !CacheRef { | |
| 1152 | 1081 | const backing_bits = self.backingIntBits(bits) orelse { |
| 1153 | 1082 | // TODO: Integers too big for any native type are represented as "composite integers": |
| 1154 | 1083 | // An array of largestSupportedIntBits. |
| 1155 | 1084 | return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits }); |
| 1156 | 1085 | }; |
| 1157 | ||
| 1158 | return try self.spv.resolveType(try SpvType.int(self.spv.arena, signedness, backing_bits)); | |
| 1086 | return self.spv.intType(signedness, backing_bits); | |
| 1159 | 1087 | } |
| 1160 | 1088 | |
| 1161 | 1089 | /// Create an integer type that represents 'usize'. |
| 1162 | fn sizeType(self: *DeclGen) !SpvType.Ref { | |
| 1090 | fn sizeType(self: *DeclGen) !CacheRef { | |
| 1163 | 1091 | return try self.intType(.unsigned, self.getTarget().ptrBitWidth()); |
| 1164 | 1092 | } |
| 1165 | 1093 | |
| ... | ... | @@ -1185,7 +1113,7 @@ pub const DeclGen = struct { |
| 1185 | 1113 | /// If any of the fields' size is 0, it will be omitted. |
| 1186 | 1114 | /// NOTE: When the active field is set to something other than the most aligned field, the |
| 1187 | 1115 | /// resulting struct will be *underaligned*. |
| 1188 | fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !SpvType.Ref { | |
| 1116 | fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { | |
| 1189 | 1117 | const target = self.getTarget(); |
| 1190 | 1118 | const layout = ty.unionGetLayout(target); |
| 1191 | 1119 | const union_ty = ty.cast(Type.Payload.Union).?.data; |
| ... | ... | @@ -1199,7 +1127,8 @@ pub const DeclGen = struct { |
| 1199 | 1127 | return try self.resolveType(union_ty.tag_ty, .indirect); |
| 1200 | 1128 | } |
| 1201 | 1129 | |
| 1202 | var members = std.BoundedArray(SpvType.Payload.Struct.Member, 4){}; | |
| 1130 | var member_types = std.BoundedArray(CacheRef, 4){}; | |
| 1131 | var member_names = std.BoundedArray(CacheString, 4){}; | |
| 1203 | 1132 | |
| 1204 | 1133 | const has_tag = layout.tag_size != 0; |
| 1205 | 1134 | const tag_first = layout.tag_align >= layout.payload_align; |
| ... | ... | @@ -1207,7 +1136,8 @@ pub const DeclGen = struct { |
| 1207 | 1136 | |
| 1208 | 1137 | if (has_tag and tag_first) { |
| 1209 | 1138 | const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect); |
| 1210 | members.appendAssumeCapacity(.{ .name = "tag", .ty = tag_ty_ref }); | |
| 1139 | member_types.appendAssumeCapacity(tag_ty_ref); | |
| 1140 | member_names.appendAssumeCapacity(try self.spv.resolveString("tag")); | |
| 1211 | 1141 | } |
| 1212 | 1142 | |
| 1213 | 1143 | const active_field = maybe_active_field orelse layout.most_aligned_field; |
| ... | ... | @@ -1215,40 +1145,44 @@ pub const DeclGen = struct { |
| 1215 | 1145 | |
| 1216 | 1146 | const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: { |
| 1217 | 1147 | const active_payload_ty_ref = try self.resolveType(active_field_ty, .indirect); |
| 1218 | members.appendAssumeCapacity(.{ .name = "payload", .ty = active_payload_ty_ref }); | |
| 1148 | member_types.appendAssumeCapacity(active_payload_ty_ref); | |
| 1149 | member_names.appendAssumeCapacity(try self.spv.resolveString("payload")); | |
| 1219 | 1150 | break :blk active_field_ty.abiSize(target); |
| 1220 | 1151 | } else 0; |
| 1221 | 1152 | |
| 1222 | 1153 | const payload_padding_len = layout.payload_size - active_field_size; |
| 1223 | 1154 | if (payload_padding_len != 0) { |
| 1224 | 1155 | const payload_padding_ty_ref = try self.spv.arrayType(@intCast(u32, payload_padding_len), u8_ty_ref); |
| 1225 | members.appendAssumeCapacity(.{ .name = "padding_payload", .ty = payload_padding_ty_ref }); | |
| 1156 | member_types.appendAssumeCapacity(payload_padding_ty_ref); | |
| 1157 | member_names.appendAssumeCapacity(try self.spv.resolveString("payload_padding")); | |
| 1226 | 1158 | } |
| 1227 | 1159 | |
| 1228 | 1160 | if (has_tag and !tag_first) { |
| 1229 | 1161 | const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect); |
| 1230 | members.appendAssumeCapacity(.{ .name = "tag", .ty = tag_ty_ref }); | |
| 1162 | member_types.appendAssumeCapacity(tag_ty_ref); | |
| 1163 | member_names.appendAssumeCapacity(try self.spv.resolveString("tag")); | |
| 1231 | 1164 | } |
| 1232 | 1165 | |
| 1233 | 1166 | if (layout.padding != 0) { |
| 1234 | 1167 | const padding_ty_ref = try self.spv.arrayType(layout.padding, u8_ty_ref); |
| 1235 | members.appendAssumeCapacity(.{ .name = "padding", .ty = padding_ty_ref }); | |
| 1168 | member_types.appendAssumeCapacity(padding_ty_ref); | |
| 1169 | member_names.appendAssumeCapacity(try self.spv.resolveString("padding")); | |
| 1236 | 1170 | } |
| 1237 | 1171 | |
| 1238 | return try self.spv.simpleStructType(members.slice()); | |
| 1172 | return try self.spv.resolve(.{ .struct_type = .{ | |
| 1173 | .member_types = member_types.slice(), | |
| 1174 | .member_names = member_names.slice(), | |
| 1175 | } }); | |
| 1239 | 1176 | } |
| 1240 | 1177 | |
| 1241 | 1178 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. |
| 1242 | fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!SpvType.Ref { | |
| 1179 | fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef { | |
| 1243 | 1180 | log.debug("resolveType: ty = {}", .{ty.fmt(self.module)}); |
| 1244 | 1181 | const target = self.getTarget(); |
| 1245 | 1182 | switch (ty.zigTypeTag()) { |
| 1246 | .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)), | |
| 1183 | .Void, .NoReturn => return try self.spv.resolve(.void_type), | |
| 1247 | 1184 | .Bool => switch (repr) { |
| 1248 | .direct => return try self.spv.resolveType(SpvType.initTag(.bool)), | |
| 1249 | // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored. | |
| 1250 | // This function returns the *stored* type, for values directly we convert this into a bool when | |
| 1251 | // it is loaded, and convert it back to this type when stored. | |
| 1185 | .direct => return try self.spv.resolve(.bool_type), | |
| 1252 | 1186 | .indirect => return try self.intType(.unsigned, 1), |
| 1253 | 1187 | }, |
| 1254 | 1188 | .Int => { |
| ... | ... | @@ -1276,15 +1210,15 @@ pub const DeclGen = struct { |
| 1276 | 1210 | return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits}); |
| 1277 | 1211 | } |
| 1278 | 1212 | |
| 1279 | return try self.spv.resolveType(SpvType.float(bits)); | |
| 1213 | return try self.spv.resolve(.{ .float_type = .{ .bits = bits } }); | |
| 1280 | 1214 | }, |
| 1281 | 1215 | .Array => { |
| 1282 | 1216 | const elem_ty = ty.childType(); |
| 1283 | const elem_ty_ref = try self.resolveType(elem_ty, .indirect); | |
| 1217 | const elem_ty_ref = try self.resolveType(elem_ty, .direct); | |
| 1284 | 1218 | const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse { |
| 1285 | 1219 | return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()}); |
| 1286 | 1220 | }; |
| 1287 | return try self.spv.arrayType(total_len, elem_ty_ref); | |
| 1221 | return self.spv.arrayType(total_len, elem_ty_ref); | |
| 1288 | 1222 | }, |
| 1289 | 1223 | .Fn => switch (repr) { |
| 1290 | 1224 | .direct => { |
| ... | ... | @@ -1292,18 +1226,17 @@ pub const DeclGen = struct { |
| 1292 | 1226 | if (ty.fnIsVarArgs()) |
| 1293 | 1227 | return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); |
| 1294 | 1228 | |
| 1295 | // TODO: Parameter passing convention etc. | |
| 1296 | ||
| 1297 | const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen()); | |
| 1298 | for (param_types, 0..) |*param, i| { | |
| 1299 | param.* = try self.resolveType(ty.fnParamType(i), .direct); | |
| 1229 | const param_ty_refs = try self.gpa.alloc(CacheRef, ty.fnParamLen()); | |
| 1230 | defer self.gpa.free(param_ty_refs); | |
| 1231 | for (param_ty_refs, 0..) |*param_type, i| { | |
| 1232 | param_type.* = try self.resolveType(ty.fnParamType(i), .direct); | |
| 1300 | 1233 | } |
| 1234 | const return_ty_ref = try self.resolveType(ty.fnReturnType(), .direct); | |
| 1301 | 1235 | |
| 1302 | const return_type = try self.resolveType(ty.fnReturnType(), .direct); | |
| 1303 | ||
| 1304 | const payload = try self.spv.arena.create(SpvType.Payload.Function); | |
| 1305 | payload.* = .{ .return_type = return_type, .parameters = param_types }; | |
| 1306 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | |
| 1236 | return try self.spv.resolve(.{ .function_type = .{ | |
| 1237 | .return_type = return_ty_ref, | |
| 1238 | .parameters = param_ty_refs, | |
| 1239 | } }); | |
| 1307 | 1240 | }, |
| 1308 | 1241 | .indirect => { |
| 1309 | 1242 | // TODO: Represent function pointers properly. |
| ... | ... | @@ -1316,16 +1249,22 @@ pub const DeclGen = struct { |
| 1316 | 1249 | |
| 1317 | 1250 | const storage_class = spvStorageClass(ptr_info.@"addrspace"); |
| 1318 | 1251 | const child_ty_ref = try self.resolveType(ptr_info.pointee_type, .indirect); |
| 1319 | const ptr_ty_ref = try self.spv.ptrType(child_ty_ref, storage_class, 0); | |
| 1320 | ||
| 1252 | const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{ | |
| 1253 | .storage_class = storage_class, | |
| 1254 | .child_type = child_ty_ref, | |
| 1255 | } }); | |
| 1321 | 1256 | if (ptr_info.size != .Slice) { |
| 1322 | 1257 | return ptr_ty_ref; |
| 1323 | 1258 | } |
| 1324 | 1259 | |
| 1325 | return try self.spv.simpleStructType(&.{ | |
| 1326 | .{ .ty = ptr_ty_ref, .name = "ptr" }, | |
| 1327 | .{ .ty = try self.sizeType(), .name = "len" }, | |
| 1328 | }); | |
| 1260 | const size_ty_ref = try self.sizeType(); | |
| 1261 | return self.spv.resolve(.{ .struct_type = .{ | |
| 1262 | .member_types = &.{ ptr_ty_ref, size_ty_ref }, | |
| 1263 | .member_names = &.{ | |
| 1264 | try self.spv.resolveString("ptr"), | |
| 1265 | try self.spv.resolveString("len"), | |
| 1266 | }, | |
| 1267 | } }); | |
| 1329 | 1268 | }, |
| 1330 | 1269 | .Vector => { |
| 1331 | 1270 | // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations |
| ... | ... | @@ -1337,60 +1276,60 @@ pub const DeclGen = struct { |
| 1337 | 1276 | |
| 1338 | 1277 | // TODO: Properly verify sizes and child type. |
| 1339 | 1278 | |
| 1340 | const payload = try self.spv.arena.create(SpvType.Payload.Vector); | |
| 1341 | payload.* = .{ | |
| 1279 | return try self.spv.resolve(.{ .vector_type = .{ | |
| 1342 | 1280 | .component_type = try self.resolveType(ty.elemType(), repr), |
| 1343 | 1281 | .component_count = @intCast(u32, ty.vectorLen()), |
| 1344 | }; | |
| 1345 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | |
| 1282 | } }); | |
| 1346 | 1283 | }, |
| 1347 | 1284 | .Struct => { |
| 1348 | 1285 | if (ty.isSimpleTupleOrAnonStruct()) { |
| 1349 | 1286 | const tuple = ty.tupleFields(); |
| 1350 | const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len); | |
| 1351 | var member_index: u32 = 0; | |
| 1287 | const member_types = try self.gpa.alloc(CacheRef, tuple.types.len); | |
| 1288 | defer self.gpa.free(member_types); | |
| 1289 | ||
| 1290 | var member_index: usize = 0; | |
| 1352 | 1291 | for (tuple.types, 0..) |field_ty, i| { |
| 1353 | 1292 | const field_val = tuple.values[i]; |
| 1354 | if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; | |
| 1355 | members[member_index] = .{ | |
| 1356 | .ty = try self.resolveType(field_ty, .indirect), | |
| 1357 | }; | |
| 1293 | if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue; | |
| 1294 | ||
| 1295 | member_types[member_index] = try self.resolveType(field_ty, .indirect); | |
| 1358 | 1296 | member_index += 1; |
| 1359 | 1297 | } |
| 1360 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); | |
| 1361 | payload.* = .{ | |
| 1362 | .members = members[0..member_index], | |
| 1363 | }; | |
| 1364 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | |
| 1298 | ||
| 1299 | return try self.spv.resolve(.{ .struct_type = .{ | |
| 1300 | .member_types = member_types[0..member_index], | |
| 1301 | } }); | |
| 1365 | 1302 | } |
| 1366 | 1303 | |
| 1367 | 1304 | const struct_ty = ty.castTag(.@"struct").?.data; |
| 1368 | 1305 | |
| 1369 | 1306 | if (struct_ty.layout == .Packed) { |
| 1370 | return try self.resolveType(struct_ty.backing_int_ty, .indirect); | |
| 1307 | return try self.resolveType(struct_ty.backing_int_ty, .direct); | |
| 1371 | 1308 | } |
| 1372 | 1309 | |
| 1373 | const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count()); | |
| 1310 | const member_types = try self.gpa.alloc(CacheRef, struct_ty.fields.count()); | |
| 1311 | defer self.gpa.free(member_types); | |
| 1312 | ||
| 1313 | const member_names = try self.gpa.alloc(CacheString, struct_ty.fields.count()); | |
| 1314 | defer self.gpa.free(member_names); | |
| 1315 | ||
| 1374 | 1316 | var member_index: usize = 0; |
| 1375 | 1317 | for (struct_ty.fields.values(), 0..) |field, i| { |
| 1376 | 1318 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| 1377 | 1319 | |
| 1378 | members[member_index] = .{ | |
| 1379 | .ty = try self.resolveType(field.ty, .indirect), | |
| 1380 | .name = struct_ty.fields.keys()[i], | |
| 1381 | }; | |
| 1320 | member_types[member_index] = try self.resolveType(field.ty, .indirect); | |
| 1321 | member_names[member_index] = try self.spv.resolveString(struct_ty.fields.keys()[i]); | |
| 1382 | 1322 | member_index += 1; |
| 1383 | 1323 | } |
| 1384 | 1324 | |
| 1385 | 1325 | const name = try struct_ty.getFullyQualifiedName(self.module); |
| 1386 | 1326 | defer self.module.gpa.free(name); |
| 1387 | 1327 | |
| 1388 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); | |
| 1389 | payload.* = .{ | |
| 1390 | .members = members[0..member_index], | |
| 1391 | .name = try self.spv.arena.dupe(u8, name), | |
| 1392 | }; | |
| 1393 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | |
| 1328 | return try self.spv.resolve(.{ .struct_type = .{ | |
| 1329 | .name = try self.spv.resolveString(name), | |
| 1330 | .member_types = member_types[0..member_index], | |
| 1331 | .member_names = member_names[0..member_index], | |
| 1332 | } }); | |
| 1394 | 1333 | }, |
| 1395 | 1334 | .Optional => { |
| 1396 | 1335 | var buf: Type.Payload.ElemType = undefined; |
| ... | ... | @@ -1398,7 +1337,7 @@ pub const DeclGen = struct { |
| 1398 | 1337 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1399 | 1338 | // Just use a bool. |
| 1400 | 1339 | // Note: Always generate the bool with indirect format, to save on some sanity |
| 1401 | // Perform the converison to a direct bool when the field is extracted. | |
| 1340 | // Perform the conversion to a direct bool when the field is extracted. | |
| 1402 | 1341 | return try self.resolveType(Type.bool, .indirect); |
| 1403 | 1342 | } |
| 1404 | 1343 | |
| ... | ... | @@ -1410,11 +1349,13 @@ pub const DeclGen = struct { |
| 1410 | 1349 | |
| 1411 | 1350 | const bool_ty_ref = try self.resolveType(Type.bool, .indirect); |
| 1412 | 1351 | |
| 1413 | // its an actual optional | |
| 1414 | return try self.spv.simpleStructType(&.{ | |
| 1415 | .{ .ty = payload_ty_ref, .name = "payload" }, | |
| 1416 | .{ .ty = bool_ty_ref, .name = "valid" }, | |
| 1417 | }); | |
| 1352 | return try self.spv.resolve(.{ .struct_type = .{ | |
| 1353 | .member_types = &.{ payload_ty_ref, bool_ty_ref }, | |
| 1354 | .member_names = &.{ | |
| 1355 | try self.spv.resolveString("payload"), | |
| 1356 | try self.spv.resolveString("valid"), | |
| 1357 | }, | |
| 1358 | } }); | |
| 1418 | 1359 | }, |
| 1419 | 1360 | .Union => return try self.resolveUnionType(ty, null), |
| 1420 | 1361 | .ErrorSet => return try self.intType(.unsigned, 16), |
| ... | ... | @@ -1429,20 +1370,30 @@ pub const DeclGen = struct { |
| 1429 | 1370 | |
| 1430 | 1371 | const payload_ty_ref = try self.resolveType(payload_ty, .indirect); |
| 1431 | 1372 | |
| 1432 | var members = std.BoundedArray(SpvType.Payload.Struct.Member, 2){}; | |
| 1373 | var member_types: [2]CacheRef = undefined; | |
| 1374 | var member_names: [2]CacheString = undefined; | |
| 1433 | 1375 | if (eu_layout.error_first) { |
| 1434 | 1376 | // Put the error first |
| 1435 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); | |
| 1436 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); | |
| 1377 | member_types = .{ error_ty_ref, payload_ty_ref }; | |
| 1378 | member_names = .{ | |
| 1379 | try self.spv.resolveString("error"), | |
| 1380 | try self.spv.resolveString("payload"), | |
| 1381 | }; | |
| 1437 | 1382 | // TODO: ABI padding? |
| 1438 | 1383 | } else { |
| 1439 | 1384 | // Put the payload first. |
| 1440 | members.appendAssumeCapacity(.{ .ty = payload_ty_ref, .name = "payload" }); | |
| 1441 | members.appendAssumeCapacity(.{ .ty = error_ty_ref, .name = "error" }); | |
| 1385 | member_types = .{ payload_ty_ref, error_ty_ref }; | |
| 1386 | member_names = .{ | |
| 1387 | try self.spv.resolveString("payload"), | |
| 1388 | try self.spv.resolveString("error"), | |
| 1389 | }; | |
| 1442 | 1390 | // TODO: ABI padding? |
| 1443 | 1391 | } |
| 1444 | 1392 | |
| 1445 | return try self.spv.simpleStructType(members.slice()); | |
| 1393 | return try self.spv.resolve(.{ .struct_type = .{ | |
| 1394 | .member_types = &member_types, | |
| 1395 | .member_names = &member_names, | |
| 1396 | } }); | |
| 1446 | 1397 | }, |
| 1447 | 1398 | |
| 1448 | 1399 | .Null, |
| ... | ... | @@ -1526,17 +1477,13 @@ pub const DeclGen = struct { |
| 1526 | 1477 | /// the name of an error in the text executor. |
| 1527 | 1478 | fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void { |
| 1528 | 1479 | const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct); |
| 1529 | const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup, 0); | |
| 1480 | const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup); | |
| 1530 | 1481 | const void_ty_ref = try self.resolveType(Type.void, .direct); |
| 1531 | 1482 | |
| 1532 | const kernel_proto_ty_ref = blk: { | |
| 1533 | const proto_payload = try self.spv.arena.create(SpvType.Payload.Function); | |
| 1534 | proto_payload.* = .{ | |
| 1535 | .return_type = void_ty_ref, | |
| 1536 | .parameters = try self.spv.arena.dupe(SpvType.Ref, &.{ptr_anyerror_ty_ref}), | |
| 1537 | }; | |
| 1538 | break :blk try self.spv.resolveType(SpvType.initPayload(&proto_payload.base)); | |
| 1539 | }; | |
| 1483 | const kernel_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ | |
| 1484 | .return_type = void_ty_ref, | |
| 1485 | .parameters = &.{ptr_anyerror_ty_ref}, | |
| 1486 | } }); | |
| 1540 | 1487 | |
| 1541 | 1488 | const test_id = self.spv.declPtr(spv_test_decl_index).result_id; |
| 1542 | 1489 | |
| ... | ... | @@ -1670,9 +1617,9 @@ pub const DeclGen = struct { |
| 1670 | 1617 | } |
| 1671 | 1618 | } |
| 1672 | 1619 | |
| 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); | |
| 1620 | fn boolToInt(self: *DeclGen, result_ty_ref: CacheRef, condition_id: IdRef) !IdRef { | |
| 1621 | const zero_id = try self.spv.constInt(result_ty_ref, 0); | |
| 1622 | const one_id = try self.spv.constInt(result_ty_ref, 1); | |
| 1676 | 1623 | const result_id = self.spv.allocId(); |
| 1677 | 1624 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| 1678 | 1625 | .id_result_type = self.typeId(result_ty_ref), |
| ... | ... | @@ -1691,7 +1638,7 @@ pub const DeclGen = struct { |
| 1691 | 1638 | .Bool => blk: { |
| 1692 | 1639 | const direct_bool_ty_ref = try self.resolveType(ty, .direct); |
| 1693 | 1640 | const indirect_bool_ty_ref = try self.resolveType(ty, .indirect); |
| 1694 | const zero_id = try self.constInt(indirect_bool_ty_ref, 0); | |
| 1641 | const zero_id = try self.spv.constInt(indirect_bool_ty_ref, 0); | |
| 1695 | 1642 | const result_id = self.spv.allocId(); |
| 1696 | 1643 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 1697 | 1644 | .id_result_type = self.typeId(direct_bool_ty_ref), |
| ... | ... | @@ -1929,10 +1876,10 @@ pub const DeclGen = struct { |
| 1929 | 1876 | return result_id; |
| 1930 | 1877 | } |
| 1931 | 1878 | |
| 1932 | fn maskStrangeInt(self: *DeclGen, ty_ref: SpvType.Ref, value_id: IdRef, bits: u16) !IdRef { | |
| 1879 | fn maskStrangeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, bits: u16) !IdRef { | |
| 1933 | 1880 | const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1; |
| 1934 | 1881 | const result_id = self.spv.allocId(); |
| 1935 | const mask_id = try self.constInt(ty_ref, mask_value); | |
| 1882 | const mask_id = try self.spv.constInt(ty_ref, mask_value); | |
| 1936 | 1883 | try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{ |
| 1937 | 1884 | .id_result_type = self.typeId(ty_ref), |
| 1938 | 1885 | .id_result = result_id, |
| ... | ... | @@ -2071,7 +2018,7 @@ pub const DeclGen = struct { |
| 2071 | 2018 | // Note that signed overflow is also wrapping in spir-v. |
| 2072 | 2019 | |
| 2073 | 2020 | const rhs_lt_zero_id = self.spv.allocId(); |
| 2074 | const zero_id = try self.constInt(operand_ty_ref, 0); | |
| 2021 | const zero_id = try self.spv.constInt(operand_ty_ref, 0); | |
| 2075 | 2022 | try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ |
| 2076 | 2023 | .id_result_type = self.typeId(bool_ty_ref), |
| 2077 | 2024 | .id_result = rhs_lt_zero_id, |
| ... | ... | @@ -2150,7 +2097,7 @@ pub const DeclGen = struct { |
| 2150 | 2097 | /// is the latter and PtrAccessChain is the former. |
| 2151 | 2098 | fn accessChain( |
| 2152 | 2099 | self: *DeclGen, |
| 2153 | result_ty_ref: SpvType.Ref, | |
| 2100 | result_ty_ref: CacheRef, | |
| 2154 | 2101 | base: IdRef, |
| 2155 | 2102 | indexes: []const IdRef, |
| 2156 | 2103 | ) !IdRef { |
| ... | ... | @@ -2166,7 +2113,7 @@ pub const DeclGen = struct { |
| 2166 | 2113 | |
| 2167 | 2114 | fn ptrAccessChain( |
| 2168 | 2115 | self: *DeclGen, |
| 2169 | result_ty_ref: SpvType.Ref, | |
| 2116 | result_ty_ref: CacheRef, | |
| 2170 | 2117 | base: IdRef, |
| 2171 | 2118 | element: IdRef, |
| 2172 | 2119 | indexes: []const IdRef, |
| ... | ... | @@ -2541,7 +2488,7 @@ pub const DeclGen = struct { |
| 2541 | 2488 | // Construct new pointer type for the resulting pointer |
| 2542 | 2489 | const elem_ty = ptr_ty.elemType2(); // use elemType() so that we get T for *[N]T. |
| 2543 | 2490 | 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); | |
| 2491 | const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace())); | |
| 2545 | 2492 | if (ptr_ty.isSinglePointer()) { |
| 2546 | 2493 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 2547 | 2494 | // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. |
| ... | ... | @@ -2631,9 +2578,8 @@ pub const DeclGen = struct { |
| 2631 | 2578 | .Struct => switch (object_ty.containerLayout()) { |
| 2632 | 2579 | .Packed => unreachable, // TODO |
| 2633 | 2580 | else => { |
| 2634 | const u32_ty_id = self.typeId(try self.intType(.unsigned, 32)); | |
| 2635 | const field_index_id = self.spv.allocId(); | |
| 2636 | try self.spv.emitConstant(u32_ty_id, field_index_id, .{ .uint32 = field_index }); | |
| 2581 | const field_index_ty_ref = try self.intType(.unsigned, 32); | |
| 2582 | const field_index_id = try self.spv.constInt(field_index_ty_ref, field_index); | |
| 2637 | 2583 | const result_ty_ref = try self.resolveType(result_ptr_ty, .direct); |
| 2638 | 2584 | return try self.accessChain(result_ty_ref, object_ptr, &.{field_index_id}); |
| 2639 | 2585 | }, |
| ... | ... | @@ -2657,7 +2603,7 @@ pub const DeclGen = struct { |
| 2657 | 2603 | fn makePointerConstant( |
| 2658 | 2604 | self: *DeclGen, |
| 2659 | 2605 | section: *SpvSection, |
| 2660 | ptr_ty_ref: SpvType.Ref, | |
| 2606 | ptr_ty_ref: CacheRef, | |
| 2661 | 2607 | ptr_id: IdRef, |
| 2662 | 2608 | ) !IdRef { |
| 2663 | 2609 | const result_id = self.spv.allocId(); |
| ... | ... | @@ -2675,11 +2621,11 @@ pub const DeclGen = struct { |
| 2675 | 2621 | // placed in the Function address space. |
| 2676 | 2622 | fn alloc( |
| 2677 | 2623 | self: *DeclGen, |
| 2678 | ty_ref: SpvType.Ref, | |
| 2624 | ty_ref: CacheRef, | |
| 2679 | 2625 | initializer: ?IdRef, |
| 2680 | 2626 | ) !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); | |
| 2627 | const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function); | |
| 2628 | const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic); | |
| 2683 | 2629 | |
| 2684 | 2630 | // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to |
| 2685 | 2631 | // directly generate them into func.prologue instead of the body. |
| ... | ... | @@ -2833,7 +2779,7 @@ pub const DeclGen = struct { |
| 2833 | 2779 | |
| 2834 | 2780 | const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; |
| 2835 | 2781 | if (val_is_undef) { |
| 2836 | const undef = try self.constUndef(ptr_ty_ref); | |
| 2782 | const undef = try self.spv.constUndef(ptr_ty_ref); | |
| 2837 | 2783 | try self.store(ptr_ty, ptr, undef); |
| 2838 | 2784 | } else { |
| 2839 | 2785 | try self.store(ptr_ty, ptr, value); |
| ... | ... | @@ -2904,7 +2850,7 @@ pub const DeclGen = struct { |
| 2904 | 2850 | else |
| 2905 | 2851 | err_union_id; |
| 2906 | 2852 | |
| 2907 | const zero_id = try self.constInt(err_ty_ref, 0); | |
| 2853 | const zero_id = try self.spv.constInt(err_ty_ref, 0); | |
| 2908 | 2854 | const is_err_id = self.spv.allocId(); |
| 2909 | 2855 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| 2910 | 2856 | .id_result_type = self.typeId(bool_ty_ref), |
| ... | ... | @@ -2953,7 +2899,7 @@ pub const DeclGen = struct { |
| 2953 | 2899 | |
| 2954 | 2900 | if (err_union_ty.errorUnionSet().errorSetIsEmpty()) { |
| 2955 | 2901 | // No error possible, so just return undefined. |
| 2956 | return try self.constUndef(err_ty_ref); | |
| 2902 | return try self.spv.constUndef(err_ty_ref); | |
| 2957 | 2903 | } |
| 2958 | 2904 | |
| 2959 | 2905 | const payload_ty = err_union_ty.errorUnionPayload(); |
| ... | ... | @@ -2982,7 +2928,7 @@ pub const DeclGen = struct { |
| 2982 | 2928 | |
| 2983 | 2929 | const payload_ty_ref = try self.resolveType(payload_ty, .indirect); |
| 2984 | 2930 | var members = std.BoundedArray(IdRef, 2){}; |
| 2985 | const payload_id = try self.constUndef(payload_ty_ref); | |
| 2931 | const payload_id = try self.spv.constUndef(payload_ty_ref); | |
| 2986 | 2932 | if (eu_layout.error_first) { |
| 2987 | 2933 | members.appendAssumeCapacity(operand_id); |
| 2988 | 2934 | members.appendAssumeCapacity(payload_id); |
| ... | ... | @@ -3024,7 +2970,7 @@ pub const DeclGen = struct { |
| 3024 | 2970 | operand_id; |
| 3025 | 2971 | |
| 3026 | 2972 | const payload_ty_ref = try self.resolveType(ptr_ty, .direct); |
| 3027 | const null_id = try self.constNull(payload_ty_ref); | |
| 2973 | const null_id = try self.spv.constNull(payload_ty_ref); | |
| 3028 | 2974 | const result_id = self.spv.allocId(); |
| 3029 | 2975 | const operands = .{ |
| 3030 | 2976 | .id_result_type = self.typeId(bool_ty_ref), |
src/codegen/spirv/Assembler.zig+36-134| ... | ... | @@ -11,7 +11,8 @@ const IdRef = spec.IdRef; |
| 11 | 11 | const IdResult = spec.IdResult; |
| 12 | 12 | |
| 13 | 13 | const SpvModule = @import("Module.zig"); |
| 14 | const SpvType = @import("type.zig").Type; | |
| 14 | const CacheRef = SpvModule.CacheRef; | |
| 15 | const CacheKey = SpvModule.CacheKey; | |
| 15 | 16 | |
| 16 | 17 | /// Represents a token in the assembly template. |
| 17 | 18 | const Token = struct { |
| ... | ... | @@ -126,7 +127,7 @@ const AsmValue = union(enum) { |
| 126 | 127 | value: IdRef, |
| 127 | 128 | |
| 128 | 129 | /// This result-value represents a type registered into the module's type system. |
| 129 | ty: SpvType.Ref, | |
| 130 | ty: CacheRef, | |
| 130 | 131 | |
| 131 | 132 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue |
| 132 | 133 | /// is of a variant that allows the result to be obtained (not an unresolved |
| ... | ... | @@ -135,7 +136,7 @@ const AsmValue = union(enum) { |
| 135 | 136 | return switch (self) { |
| 136 | 137 | .just_declared, .unresolved_forward_reference => unreachable, |
| 137 | 138 | .value => |result| result, |
| 138 | .ty => |ref| spv.typeId(ref), | |
| 139 | .ty => |ref| spv.resultId(ref), | |
| 139 | 140 | }; |
| 140 | 141 | } |
| 141 | 142 | }; |
| ... | ... | @@ -267,9 +268,9 @@ fn processInstruction(self: *Assembler) !void { |
| 267 | 268 | /// refers to the result. |
| 268 | 269 | fn processTypeInstruction(self: *Assembler) !AsmValue { |
| 269 | 270 | const operands = self.inst.operands.items; |
| 270 | const ty = switch (self.inst.opcode) { | |
| 271 | .OpTypeVoid => SpvType.initTag(.void), | |
| 272 | .OpTypeBool => SpvType.initTag(.bool), | |
| 271 | const ref = switch (self.inst.opcode) { | |
| 272 | .OpTypeVoid => try self.spv.resolve(.void_type), | |
| 273 | .OpTypeBool => try self.spv.resolve(.bool_type), | |
| 273 | 274 | .OpTypeInt => blk: { |
| 274 | 275 | const signedness: std.builtin.Signedness = switch (operands[2].literal32) { |
| 275 | 276 | 0 => .unsigned, |
| ... | ... | @@ -282,7 +283,7 @@ fn processTypeInstruction(self: *Assembler) !AsmValue { |
| 282 | 283 | const width = std.math.cast(u16, operands[1].literal32) orelse { |
| 283 | 284 | return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32}); |
| 284 | 285 | }; |
| 285 | break :blk try SpvType.int(self.spv.arena, signedness, width); | |
| 286 | break :blk try self.spv.intType(signedness, width); | |
| 286 | 287 | }, |
| 287 | 288 | .OpTypeFloat => blk: { |
| 288 | 289 | const bits = operands[1].literal32; |
| ... | ... | @@ -292,136 +293,36 @@ fn processTypeInstruction(self: *Assembler) !AsmValue { |
| 292 | 293 | return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits}); |
| 293 | 294 | }, |
| 294 | 295 | } |
| 295 | break :blk SpvType.float(@intCast(u16, bits)); | |
| 296 | }, | |
| 297 | .OpTypeVector => blk: { | |
| 298 | const payload = try self.spv.arena.create(SpvType.Payload.Vector); | |
| 299 | payload.* = .{ | |
| 300 | .component_type = try self.resolveTypeRef(operands[1].ref_id), | |
| 301 | .component_count = operands[2].literal32, | |
| 302 | }; | |
| 303 | break :blk SpvType.initPayload(&payload.base); | |
| 304 | }, | |
| 305 | .OpTypeMatrix => blk: { | |
| 306 | const payload = try self.spv.arena.create(SpvType.Payload.Matrix); | |
| 307 | payload.* = .{ | |
| 308 | .column_type = try self.resolveTypeRef(operands[1].ref_id), | |
| 309 | .column_count = operands[2].literal32, | |
| 310 | }; | |
| 311 | break :blk SpvType.initPayload(&payload.base); | |
| 312 | }, | |
| 313 | .OpTypeImage => blk: { | |
| 314 | const payload = try self.spv.arena.create(SpvType.Payload.Image); | |
| 315 | payload.* = .{ | |
| 316 | .sampled_type = try self.resolveTypeRef(operands[1].ref_id), | |
| 317 | .dim = @intToEnum(spec.Dim, operands[2].value), | |
| 318 | .depth = switch (operands[3].literal32) { | |
| 319 | 0 => .no, | |
| 320 | 1 => .yes, | |
| 321 | 2 => .maybe, | |
| 322 | else => { | |
| 323 | return self.fail(0, "'{}' is not a valid image depth (expected 0, 1 or 2)", .{operands[3].literal32}); | |
| 324 | }, | |
| 325 | }, | |
| 326 | .arrayed = switch (operands[4].literal32) { | |
| 327 | 0 => false, | |
| 328 | 1 => true, | |
| 329 | else => { | |
| 330 | return self.fail(0, "'{}' is not a valid image arrayed-ness (expected 0 or 1)", .{operands[4].literal32}); | |
| 331 | }, | |
| 332 | }, | |
| 333 | .multisampled = switch (operands[5].literal32) { | |
| 334 | 0 => false, | |
| 335 | 1 => true, | |
| 336 | else => { | |
| 337 | return self.fail(0, "'{}' is not a valid image multisampled-ness (expected 0 or 1)", .{operands[5].literal32}); | |
| 338 | }, | |
| 339 | }, | |
| 340 | .sampled = switch (operands[6].literal32) { | |
| 341 | 0 => .known_at_runtime, | |
| 342 | 1 => .with_sampler, | |
| 343 | 2 => .without_sampler, | |
| 344 | else => { | |
| 345 | return self.fail(0, "'{}' is not a valid image sampled-ness (expected 0, 1 or 2)", .{operands[6].literal32}); | |
| 346 | }, | |
| 347 | }, | |
| 348 | .format = @intToEnum(spec.ImageFormat, operands[7].value), | |
| 349 | .access_qualifier = if (operands.len > 8) | |
| 350 | @intToEnum(spec.AccessQualifier, operands[8].value) | |
| 351 | else | |
| 352 | null, | |
| 353 | }; | |
| 354 | break :blk SpvType.initPayload(&payload.base); | |
| 355 | }, | |
| 356 | .OpTypeSampler => SpvType.initTag(.sampler), | |
| 357 | .OpTypeSampledImage => blk: { | |
| 358 | const payload = try self.spv.arena.create(SpvType.Payload.SampledImage); | |
| 359 | payload.* = .{ | |
| 360 | .image_type = try self.resolveTypeRef(operands[1].ref_id), | |
| 361 | }; | |
| 362 | break :blk SpvType.initPayload(&payload.base); | |
| 296 | break :blk try self.spv.resolve(.{ .float_type = .{ .bits = @intCast(u16, bits) } }); | |
| 363 | 297 | }, |
| 298 | .OpTypeVector => try self.spv.resolve(.{ .vector_type = .{ | |
| 299 | .component_type = try self.resolveTypeRef(operands[1].ref_id), | |
| 300 | .component_count = operands[2].literal32, | |
| 301 | } }), | |
| 364 | 302 | .OpTypeArray => { |
| 365 | 303 | // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant), |
| 366 | 304 | // and so some consideration must be taken when entering this in the type system. |
| 367 | 305 | return self.todo("process OpTypeArray", .{}); |
| 368 | 306 | }, |
| 369 | .OpTypeRuntimeArray => blk: { | |
| 370 | const payload = try self.spv.arena.create(SpvType.Payload.RuntimeArray); | |
| 371 | payload.* = .{ | |
| 372 | .element_type = try self.resolveTypeRef(operands[1].ref_id), | |
| 373 | // TODO: Fetch array stride from decorations. | |
| 374 | .array_stride = 0, | |
| 375 | }; | |
| 376 | break :blk SpvType.initPayload(&payload.base); | |
| 377 | }, | |
| 378 | .OpTypeOpaque => blk: { | |
| 379 | const payload = try self.spv.arena.create(SpvType.Payload.Opaque); | |
| 380 | const name_offset = operands[1].string; | |
| 381 | payload.* = .{ | |
| 382 | .name = std.mem.sliceTo(self.inst.string_bytes.items[name_offset..], 0), | |
| 383 | }; | |
| 384 | break :blk SpvType.initPayload(&payload.base); | |
| 385 | }, | |
| 386 | .OpTypePointer => blk: { | |
| 387 | const payload = try self.spv.arena.create(SpvType.Payload.Pointer); | |
| 388 | payload.* = .{ | |
| 389 | .storage_class = @intToEnum(spec.StorageClass, operands[1].value), | |
| 390 | .child_type = try self.resolveTypeRef(operands[2].ref_id), | |
| 391 | // TODO: Fetch decorations | |
| 392 | }; | |
| 393 | break :blk SpvType.initPayload(&payload.base); | |
| 394 | }, | |
| 307 | .OpTypePointer => try self.spv.ptrType( | |
| 308 | try self.resolveTypeRef(operands[2].ref_id), | |
| 309 | @intToEnum(spec.StorageClass, operands[1].value), | |
| 310 | ), | |
| 395 | 311 | .OpTypeFunction => blk: { |
| 396 | 312 | const param_operands = operands[2..]; |
| 397 | const param_types = try self.spv.arena.alloc(SpvType.Ref, param_operands.len); | |
| 313 | const param_types = try self.spv.gpa.alloc(CacheRef, param_operands.len); | |
| 314 | defer self.spv.gpa.free(param_types); | |
| 398 | 315 | for (param_types, 0..) |*param, i| { |
| 399 | 316 | param.* = try self.resolveTypeRef(param_operands[i].ref_id); |
| 400 | 317 | } |
| 401 | const payload = try self.spv.arena.create(SpvType.Payload.Function); | |
| 402 | payload.* = .{ | |
| 318 | break :blk try self.spv.resolve(.{ .function_type = .{ | |
| 403 | 319 | .return_type = try self.resolveTypeRef(operands[1].ref_id), |
| 404 | 320 | .parameters = param_types, |
| 405 | }; | |
| 406 | break :blk SpvType.initPayload(&payload.base); | |
| 321 | } }); | |
| 407 | 322 | }, |
| 408 | .OpTypeEvent => SpvType.initTag(.event), | |
| 409 | .OpTypeDeviceEvent => SpvType.initTag(.device_event), | |
| 410 | .OpTypeReserveId => SpvType.initTag(.reserve_id), | |
| 411 | .OpTypeQueue => SpvType.initTag(.queue), | |
| 412 | .OpTypePipe => blk: { | |
| 413 | const payload = try self.spv.arena.create(SpvType.Payload.Pipe); | |
| 414 | payload.* = .{ | |
| 415 | .qualifier = @intToEnum(spec.AccessQualifier, operands[1].value), | |
| 416 | }; | |
| 417 | break :blk SpvType.initPayload(&payload.base); | |
| 418 | }, | |
| 419 | .OpTypePipeStorage => SpvType.initTag(.pipe_storage), | |
| 420 | .OpTypeNamedBarrier => SpvType.initTag(.named_barrier), | |
| 421 | 323 | else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}), |
| 422 | 324 | }; |
| 423 | 325 | |
| 424 | const ref = try self.spv.resolveType(ty); | |
| 425 | 326 | return AsmValue{ .ty = ref }; |
| 426 | 327 | } |
| 427 | 328 | |
| ... | ... | @@ -528,7 +429,7 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue { |
| 528 | 429 | } |
| 529 | 430 | |
| 530 | 431 | /// Resolve a value reference as type. |
| 531 | fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !SpvType.Ref { | |
| 432 | fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !CacheRef { | |
| 532 | 433 | const value = try self.resolveRef(ref); |
| 533 | 434 | switch (value) { |
| 534 | 435 | .just_declared, .unresolved_forward_reference => unreachable, |
| ... | ... | @@ -761,19 +662,20 @@ fn parseContextDependentNumber(self: *Assembler) !void { |
| 761 | 662 | |
| 762 | 663 | const tok = self.currentToken(); |
| 763 | 664 | const result_type_ref = try self.resolveTypeRef(self.inst.operands.items[0].ref_id); |
| 764 | const result_type = self.spv.type_cache.keys()[@enumToInt(result_type_ref)]; | |
| 765 | if (result_type.isInt()) { | |
| 766 | try self.parseContextDependentInt(result_type.intSignedness(), result_type.intFloatBits()); | |
| 767 | } else if (result_type.isFloat()) { | |
| 768 | const width = result_type.intFloatBits(); | |
| 769 | switch (width) { | |
| 770 | 16 => try self.parseContextDependentFloat(16), | |
| 771 | 32 => try self.parseContextDependentFloat(32), | |
| 772 | 64 => try self.parseContextDependentFloat(64), | |
| 773 | else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{width}), | |
| 774 | } | |
| 775 | } else { | |
| 776 | return self.fail(tok.start, "cannot parse literal constant {s}", .{@tagName(result_type.tag())}); | |
| 665 | const result_type = self.spv.cache.lookup(result_type_ref); | |
| 666 | switch (result_type) { | |
| 667 | .int_type => |int| { | |
| 668 | try self.parseContextDependentInt(int.signedness, int.bits); | |
| 669 | }, | |
| 670 | .float_type => |float| { | |
| 671 | switch (float.bits) { | |
| 672 | 16 => try self.parseContextDependentFloat(16), | |
| 673 | 32 => try self.parseContextDependentFloat(32), | |
| 674 | 64 => try self.parseContextDependentFloat(64), | |
| 675 | else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{float.bits}), | |
| 676 | } | |
| 677 | }, | |
| 678 | else => return self.fail(tok.start, "cannot parse literal constant", .{}), | |
| 777 | 679 | } |
| 778 | 680 | } |
| 779 | 681 |
src/codegen/spirv/Cache.zig created+1046| ... | ... | @@ -0,0 +1,1046 @@ |
| 1 | //! This file implements an InternPool-like structure that caches | |
| 2 | //! SPIR-V types and constants. Instead of generating type and | |
| 3 | //! constant instructions directly, we first keep a representation | |
| 4 | //! in a compressed database. This is then only later turned into | |
| 5 | //! actual SPIR-V instructions. | |
| 6 | //! Note: This cache is insertion-ordered. This means that we | |
| 7 | //! can materialize the SPIR-V instructions in the proper order, | |
| 8 | //! as SPIR-V requires that the type is emitted before use. | |
| 9 | //! Note: According to SPIR-V spec section 2.8, Types and Variables, | |
| 10 | //! non-pointer non-aggrerate types (which includes matrices and | |
| 11 | //! vectors) must have a _unique_ representation in the final binary. | |
| 12 | ||
| 13 | const std = @import("std"); | |
| 14 | const assert = std.debug.assert; | |
| 15 | const Allocator = std.mem.Allocator; | |
| 16 | ||
| 17 | const Section = @import("Section.zig"); | |
| 18 | const Module = @import("Module.zig"); | |
| 19 | ||
| 20 | const spec = @import("spec.zig"); | |
| 21 | const Opcode = spec.Opcode; | |
| 22 | const IdResult = spec.IdResult; | |
| 23 | const StorageClass = spec.StorageClass; | |
| 24 | ||
| 25 | const Self = @This(); | |
| 26 | ||
| 27 | map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, | |
| 28 | items: std.MultiArrayList(Item) = .{}, | |
| 29 | extra: std.ArrayListUnmanaged(u32) = .{}, | |
| 30 | ||
| 31 | string_bytes: std.ArrayListUnmanaged(u8) = .{}, | |
| 32 | strings: std.AutoArrayHashMapUnmanaged(void, u32) = .{}, | |
| 33 | ||
| 34 | const Item = struct { | |
| 35 | tag: Tag, | |
| 36 | /// The result-id that this item uses. | |
| 37 | result_id: IdResult, | |
| 38 | /// The Tag determines how this should be interpreted. | |
| 39 | data: u32, | |
| 40 | }; | |
| 41 | ||
| 42 | const Tag = enum { | |
| 43 | // -- Types | |
| 44 | /// Simple type that has no additional data. | |
| 45 | /// data is SimpleType. | |
| 46 | type_simple, | |
| 47 | /// Signed integer type | |
| 48 | /// data is number of bits | |
| 49 | type_int_signed, | |
| 50 | /// Unsigned integer type | |
| 51 | /// data is number of bits | |
| 52 | type_int_unsigned, | |
| 53 | /// Floating point type | |
| 54 | /// data is number of bits | |
| 55 | type_float, | |
| 56 | /// Vector type | |
| 57 | /// data is payload to VectorType | |
| 58 | type_vector, | |
| 59 | /// Array type | |
| 60 | /// data is payload to ArrayType | |
| 61 | type_array, | |
| 62 | /// Function (proto)type | |
| 63 | /// data is payload to FunctionType | |
| 64 | type_function, | |
| 65 | /// Pointer type in the CrossWorkgroup storage class | |
| 66 | /// data is child type | |
| 67 | type_ptr_generic, | |
| 68 | /// Pointer type in the CrossWorkgroup storage class | |
| 69 | /// data is child type | |
| 70 | type_ptr_crosswgp, | |
| 71 | /// Pointer type in the Function storage class | |
| 72 | /// data is child type | |
| 73 | type_ptr_function, | |
| 74 | /// Simple pointer type that does not have any decorations. | |
| 75 | /// data is payload to SimplePointerType | |
| 76 | type_ptr_simple, | |
| 77 | /// Simple structure type that does not have any decorations. | |
| 78 | /// data is payload to SimpleStructType | |
| 79 | type_struct_simple, | |
| 80 | /// Simple structure type that does not have any decorations, but does | |
| 81 | /// have member names trailing. | |
| 82 | /// data is payload to SimpleStructType | |
| 83 | type_struct_simple_with_member_names, | |
| 84 | ||
| 85 | // -- Values | |
| 86 | /// Value of type u8 | |
| 87 | /// data is value | |
| 88 | uint8, | |
| 89 | /// Value of type u32 | |
| 90 | /// data is value | |
| 91 | uint32, | |
| 92 | // TODO: More specialized tags here. | |
| 93 | /// Integer value for signed values that are smaller than 32 bits. | |
| 94 | /// data is pointer to Int32 | |
| 95 | int_small, | |
| 96 | /// Integer value for unsigned values that are smaller than 32 bits. | |
| 97 | /// data is pointer to UInt32 | |
| 98 | uint_small, | |
| 99 | /// Integer value for signed values that are beteen 32 and 64 bits. | |
| 100 | /// data is pointer to Int64 | |
| 101 | int_large, | |
| 102 | /// Integer value for unsinged values that are beteen 32 and 64 bits. | |
| 103 | /// data is pointer to UInt64 | |
| 104 | uint_large, | |
| 105 | /// Value of type f16 | |
| 106 | /// data is value | |
| 107 | float16, | |
| 108 | /// Value of type f32 | |
| 109 | /// data is value | |
| 110 | float32, | |
| 111 | /// Value of type f64 | |
| 112 | /// data is payload to Float16 | |
| 113 | float64, | |
| 114 | /// Undefined value | |
| 115 | /// data is type | |
| 116 | undef, | |
| 117 | /// Null value | |
| 118 | /// data is type | |
| 119 | null, | |
| 120 | /// Bool value that is true | |
| 121 | /// data is (bool) type | |
| 122 | bool_true, | |
| 123 | /// Bool value that is false | |
| 124 | /// data is (bool) type | |
| 125 | bool_false, | |
| 126 | ||
| 127 | const SimpleType = enum { void, bool }; | |
| 128 | ||
| 129 | const VectorType = Key.VectorType; | |
| 130 | const ArrayType = Key.ArrayType; | |
| 131 | ||
| 132 | // Trailing: | |
| 133 | // - [param_len]Ref: parameter types. | |
| 134 | const FunctionType = struct { | |
| 135 | param_len: u32, | |
| 136 | return_type: Ref, | |
| 137 | }; | |
| 138 | ||
| 139 | const SimplePointerType = struct { | |
| 140 | storage_class: StorageClass, | |
| 141 | child_type: Ref, | |
| 142 | }; | |
| 143 | ||
| 144 | /// Trailing: | |
| 145 | /// - [members_len]Ref: Member types. | |
| 146 | /// - [members_len]String: Member names, -- ONLY if the tag is type_struct_simple_with_member_names | |
| 147 | const SimpleStructType = struct { | |
| 148 | /// (optional) The name of the struct. | |
| 149 | name: String, | |
| 150 | /// Number of members that this struct has. | |
| 151 | members_len: u32, | |
| 152 | }; | |
| 153 | ||
| 154 | const Float64 = struct { | |
| 155 | // Low-order 32 bits of the value. | |
| 156 | low: u32, | |
| 157 | // High-order 32 bits of the value. | |
| 158 | high: u32, | |
| 159 | ||
| 160 | fn encode(value: f64) Float64 { | |
| 161 | const bits = @bitCast(u64, value); | |
| 162 | return .{ | |
| 163 | .low = @truncate(u32, bits), | |
| 164 | .high = @truncate(u32, bits >> 32), | |
| 165 | }; | |
| 166 | } | |
| 167 | ||
| 168 | fn decode(self: Float64) f64 { | |
| 169 | const bits = @as(u64, self.low) | (@as(u64, self.high) << 32); | |
| 170 | return @bitCast(f64, bits); | |
| 171 | } | |
| 172 | }; | |
| 173 | ||
| 174 | const Int32 = struct { | |
| 175 | ty: Ref, | |
| 176 | value: i32, | |
| 177 | }; | |
| 178 | ||
| 179 | const UInt32 = struct { | |
| 180 | ty: Ref, | |
| 181 | value: u32, | |
| 182 | }; | |
| 183 | ||
| 184 | const UInt64 = struct { | |
| 185 | ty: Ref, | |
| 186 | low: u32, | |
| 187 | high: u32, | |
| 188 | ||
| 189 | fn encode(ty: Ref, value: u64) Int64 { | |
| 190 | return .{ | |
| 191 | .ty = ty, | |
| 192 | .low = @truncate(u32, value), | |
| 193 | .high = @truncate(u32, value >> 32), | |
| 194 | }; | |
| 195 | } | |
| 196 | ||
| 197 | fn decode(self: UInt64) u64 { | |
| 198 | return @as(u64, self.low) | (@as(u64, self.high) << 32); | |
| 199 | } | |
| 200 | }; | |
| 201 | ||
| 202 | const Int64 = struct { | |
| 203 | ty: Ref, | |
| 204 | low: u32, | |
| 205 | high: u32, | |
| 206 | ||
| 207 | fn encode(ty: Ref, value: i64) Int64 { | |
| 208 | return .{ | |
| 209 | .ty = ty, | |
| 210 | .low = @truncate(u32, @bitCast(u64, value)), | |
| 211 | .high = @truncate(u32, @bitCast(u64, value) >> 32), | |
| 212 | }; | |
| 213 | } | |
| 214 | ||
| 215 | fn decode(self: Int64) i64 { | |
| 216 | return @bitCast(i64, @as(u64, self.low) | (@as(u64, self.high) << 32)); | |
| 217 | } | |
| 218 | }; | |
| 219 | }; | |
| 220 | ||
| 221 | pub const Ref = enum(u32) { _ }; | |
| 222 | ||
| 223 | /// This union represents something that can be interned. This includes | |
| 224 | /// types and constants. This structure is used for interfacing with the | |
| 225 | /// database: Values described for this structure are ephemeral and stored | |
| 226 | /// in a more memory-efficient manner internally. | |
| 227 | pub const Key = union(enum) { | |
| 228 | // -- Types | |
| 229 | void_type, | |
| 230 | bool_type, | |
| 231 | int_type: IntType, | |
| 232 | float_type: FloatType, | |
| 233 | vector_type: VectorType, | |
| 234 | array_type: ArrayType, | |
| 235 | function_type: FunctionType, | |
| 236 | ptr_type: PointerType, | |
| 237 | struct_type: StructType, | |
| 238 | ||
| 239 | // -- values | |
| 240 | int: Int, | |
| 241 | float: Float, | |
| 242 | undef: Undef, | |
| 243 | null: Null, | |
| 244 | bool: Bool, | |
| 245 | ||
| 246 | pub const IntType = std.builtin.Type.Int; | |
| 247 | pub const FloatType = std.builtin.Type.Float; | |
| 248 | ||
| 249 | pub const VectorType = struct { | |
| 250 | component_type: Ref, | |
| 251 | component_count: u32, | |
| 252 | }; | |
| 253 | ||
| 254 | pub const ArrayType = struct { | |
| 255 | /// Child type of this array. | |
| 256 | element_type: Ref, | |
| 257 | /// Reference to a constant. | |
| 258 | length: Ref, | |
| 259 | /// Type has the 'ArrayStride' decoration. | |
| 260 | /// If zero, no stride is present. | |
| 261 | stride: u32 = 0, | |
| 262 | }; | |
| 263 | ||
| 264 | pub const FunctionType = struct { | |
| 265 | return_type: Ref, | |
| 266 | parameters: []const Ref, | |
| 267 | }; | |
| 268 | ||
| 269 | pub const PointerType = struct { | |
| 270 | storage_class: StorageClass, | |
| 271 | child_type: Ref, | |
| 272 | // TODO: Decorations: | |
| 273 | // - Alignment | |
| 274 | // - ArrayStride, | |
| 275 | // - MaxByteOffset, | |
| 276 | }; | |
| 277 | ||
| 278 | pub const StructType = struct { | |
| 279 | // TODO: Decorations. | |
| 280 | /// The name of the structure. Can be `.none`. | |
| 281 | name: String = .none, | |
| 282 | /// The type of each member. | |
| 283 | member_types: []const Ref, | |
| 284 | /// Name for each member. May be omitted. | |
| 285 | member_names: ?[]const String = null, | |
| 286 | ||
| 287 | fn memberNames(self: @This()) []const String { | |
| 288 | return if (self.member_names) |member_names| member_names else &.{}; | |
| 289 | } | |
| 290 | }; | |
| 291 | ||
| 292 | pub const Int = struct { | |
| 293 | /// The type: any bitness integer. | |
| 294 | ty: Ref, | |
| 295 | /// The actual value. Only uint64 and int64 types | |
| 296 | /// are available here: Smaller types should use these | |
| 297 | /// fields. | |
| 298 | value: Value, | |
| 299 | ||
| 300 | pub const Value = union(enum) { | |
| 301 | uint64: u64, | |
| 302 | int64: i64, | |
| 303 | }; | |
| 304 | ||
| 305 | /// Turns this value into the corresponding 32-bit literal, 2s complement signed. | |
| 306 | fn toBits32(self: Int) u32 { | |
| 307 | return switch (self.value) { | |
| 308 | .uint64 => |val| @intCast(u32, val), | |
| 309 | .int64 => |val| if (val < 0) @bitCast(u32, @intCast(i32, val)) else @intCast(u32, val), | |
| 310 | }; | |
| 311 | } | |
| 312 | ||
| 313 | fn toBits64(self: Int) u64 { | |
| 314 | return switch (self.value) { | |
| 315 | .uint64 => |val| val, | |
| 316 | .int64 => |val| @bitCast(u64, val), | |
| 317 | }; | |
| 318 | } | |
| 319 | ||
| 320 | fn to(self: Int, comptime T: type) T { | |
| 321 | return switch (self.value) { | |
| 322 | inline else => |val| @intCast(T, val), | |
| 323 | }; | |
| 324 | } | |
| 325 | }; | |
| 326 | ||
| 327 | /// Represents a numberic value of some type. | |
| 328 | pub const Float = struct { | |
| 329 | /// The type: 16, 32, or 64-bit float. | |
| 330 | ty: Ref, | |
| 331 | /// The actual value. | |
| 332 | value: Value, | |
| 333 | ||
| 334 | pub const Value = union(enum) { | |
| 335 | float16: f16, | |
| 336 | float32: f32, | |
| 337 | float64: f64, | |
| 338 | }; | |
| 339 | }; | |
| 340 | ||
| 341 | pub const Undef = struct { | |
| 342 | ty: Ref, | |
| 343 | }; | |
| 344 | ||
| 345 | pub const Null = struct { | |
| 346 | ty: Ref, | |
| 347 | }; | |
| 348 | ||
| 349 | pub const Bool = struct { | |
| 350 | ty: Ref, | |
| 351 | value: bool, | |
| 352 | }; | |
| 353 | ||
| 354 | fn hash(self: Key) u32 { | |
| 355 | var hasher = std.hash.Wyhash.init(0); | |
| 356 | switch (self) { | |
| 357 | .float => |float| { | |
| 358 | std.hash.autoHash(&hasher, float.ty); | |
| 359 | switch (float.value) { | |
| 360 | .float16 => |value| std.hash.autoHash(&hasher, @bitCast(u16, value)), | |
| 361 | .float32 => |value| std.hash.autoHash(&hasher, @bitCast(u32, value)), | |
| 362 | .float64 => |value| std.hash.autoHash(&hasher, @bitCast(u64, value)), | |
| 363 | } | |
| 364 | }, | |
| 365 | .function_type => |func| { | |
| 366 | std.hash.autoHash(&hasher, func.return_type); | |
| 367 | for (func.parameters) |param_type| { | |
| 368 | std.hash.autoHash(&hasher, param_type); | |
| 369 | } | |
| 370 | }, | |
| 371 | .struct_type => |struct_type| { | |
| 372 | std.hash.autoHash(&hasher, struct_type.name); | |
| 373 | for (struct_type.member_types) |member_type| { | |
| 374 | std.hash.autoHash(&hasher, member_type); | |
| 375 | } | |
| 376 | for (struct_type.memberNames()) |member_name| { | |
| 377 | std.hash.autoHash(&hasher, member_name); | |
| 378 | } | |
| 379 | }, | |
| 380 | inline else => |key| std.hash.autoHash(&hasher, key), | |
| 381 | } | |
| 382 | return @truncate(u32, hasher.final()); | |
| 383 | } | |
| 384 | ||
| 385 | fn eql(a: Key, b: Key) bool { | |
| 386 | const KeyTag = @typeInfo(Key).Union.tag_type.?; | |
| 387 | const a_tag: KeyTag = a; | |
| 388 | const b_tag: KeyTag = b; | |
| 389 | if (a_tag != b_tag) { | |
| 390 | return false; | |
| 391 | } | |
| 392 | return switch (a) { | |
| 393 | .function_type => |a_func| { | |
| 394 | const b_func = b.function_type; | |
| 395 | return a_func.return_type == b_func.return_type and | |
| 396 | std.mem.eql(Ref, a_func.parameters, b_func.parameters); | |
| 397 | }, | |
| 398 | .struct_type => |a_struct| { | |
| 399 | const b_struct = b.struct_type; | |
| 400 | return a_struct.name == b_struct.name and | |
| 401 | std.mem.eql(Ref, a_struct.member_types, b_struct.member_types) and | |
| 402 | std.mem.eql(String, a_struct.memberNames(), b_struct.memberNames()); | |
| 403 | }, | |
| 404 | // TODO: Unroll? | |
| 405 | else => std.meta.eql(a, b), | |
| 406 | }; | |
| 407 | } | |
| 408 | ||
| 409 | pub const Adapter = struct { | |
| 410 | self: *const Self, | |
| 411 | ||
| 412 | pub fn eql(ctx: @This(), a: Key, b_void: void, b_index: usize) bool { | |
| 413 | _ = b_void; | |
| 414 | return ctx.self.lookup(@intToEnum(Ref, b_index)).eql(a); | |
| 415 | } | |
| 416 | ||
| 417 | pub fn hash(ctx: @This(), a: Key) u32 { | |
| 418 | _ = ctx; | |
| 419 | return a.hash(); | |
| 420 | } | |
| 421 | }; | |
| 422 | ||
| 423 | fn toSimpleType(self: Key) Tag.SimpleType { | |
| 424 | return switch (self) { | |
| 425 | .void_type => .void, | |
| 426 | .bool_type => .bool, | |
| 427 | else => unreachable, | |
| 428 | }; | |
| 429 | } | |
| 430 | }; | |
| 431 | ||
| 432 | pub fn deinit(self: *Self, spv: *const Module) void { | |
| 433 | self.map.deinit(spv.gpa); | |
| 434 | self.items.deinit(spv.gpa); | |
| 435 | self.extra.deinit(spv.gpa); | |
| 436 | self.string_bytes.deinit(spv.gpa); | |
| 437 | self.strings.deinit(spv.gpa); | |
| 438 | } | |
| 439 | ||
| 440 | /// Actually materialize the database into spir-v instructions. | |
| 441 | /// This function returns a spir-v section of (only) constant and type instructions. | |
| 442 | /// Additionally, decorations, debug names, etc, are all directly emitted into the | |
| 443 | /// `spv` module. The section is allocated with `spv.gpa`. | |
| 444 | pub fn materialize(self: *const Self, spv: *Module) !Section { | |
| 445 | var section = Section{}; | |
| 446 | errdefer section.deinit(spv.gpa); | |
| 447 | for (self.items.items(.result_id), 0..) |result_id, index| { | |
| 448 | try self.emit(spv, result_id, @intToEnum(Ref, index), &section); | |
| 449 | } | |
| 450 | return section; | |
| 451 | } | |
| 452 | ||
| 453 | fn emit( | |
| 454 | self: *const Self, | |
| 455 | spv: *Module, | |
| 456 | result_id: IdResult, | |
| 457 | ref: Ref, | |
| 458 | section: *Section, | |
| 459 | ) !void { | |
| 460 | const key = self.lookup(ref); | |
| 461 | const Lit = spec.LiteralContextDependentNumber; | |
| 462 | switch (key) { | |
| 463 | .void_type => { | |
| 464 | try section.emit(spv.gpa, .OpTypeVoid, .{ .id_result = result_id }); | |
| 465 | try spv.debugName(result_id, "void", .{}); | |
| 466 | }, | |
| 467 | .bool_type => { | |
| 468 | try section.emit(spv.gpa, .OpTypeBool, .{ .id_result = result_id }); | |
| 469 | try spv.debugName(result_id, "bool", .{}); | |
| 470 | }, | |
| 471 | .int_type => |int| { | |
| 472 | try section.emit(spv.gpa, .OpTypeInt, .{ | |
| 473 | .id_result = result_id, | |
| 474 | .width = int.bits, | |
| 475 | .signedness = switch (int.signedness) { | |
| 476 | .unsigned => @as(spec.Word, 0), | |
| 477 | .signed => 1, | |
| 478 | }, | |
| 479 | }); | |
| 480 | const ui: []const u8 = switch (int.signedness) { | |
| 481 | .unsigned => "u", | |
| 482 | .signed => "i", | |
| 483 | }; | |
| 484 | try spv.debugName(result_id, "{s}{}", .{ ui, int.bits }); | |
| 485 | }, | |
| 486 | .float_type => |float| { | |
| 487 | try section.emit(spv.gpa, .OpTypeFloat, .{ | |
| 488 | .id_result = result_id, | |
| 489 | .width = float.bits, | |
| 490 | }); | |
| 491 | try spv.debugName(result_id, "f{}", .{float.bits}); | |
| 492 | }, | |
| 493 | .vector_type => |vector| { | |
| 494 | try section.emit(spv.gpa, .OpTypeVector, .{ | |
| 495 | .id_result = result_id, | |
| 496 | .component_type = self.resultId(vector.component_type), | |
| 497 | .component_count = vector.component_count, | |
| 498 | }); | |
| 499 | }, | |
| 500 | .array_type => |array| { | |
| 501 | try section.emit(spv.gpa, .OpTypeArray, .{ | |
| 502 | .id_result = result_id, | |
| 503 | .element_type = self.resultId(array.element_type), | |
| 504 | .length = self.resultId(array.length), | |
| 505 | }); | |
| 506 | if (array.stride != 0) { | |
| 507 | try spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = array.stride } }); | |
| 508 | } | |
| 509 | }, | |
| 510 | .function_type => |function| { | |
| 511 | try section.emitRaw(spv.gpa, .OpTypeFunction, 2 + function.parameters.len); | |
| 512 | section.writeOperand(IdResult, result_id); | |
| 513 | section.writeOperand(IdResult, self.resultId(function.return_type)); | |
| 514 | for (function.parameters) |param_type| { | |
| 515 | section.writeOperand(IdResult, self.resultId(param_type)); | |
| 516 | } | |
| 517 | }, | |
| 518 | .ptr_type => |ptr| { | |
| 519 | try section.emit(spv.gpa, .OpTypePointer, .{ | |
| 520 | .id_result = result_id, | |
| 521 | .storage_class = ptr.storage_class, | |
| 522 | .type = self.resultId(ptr.child_type), | |
| 523 | }); | |
| 524 | // TODO: Decorations? | |
| 525 | }, | |
| 526 | .struct_type => |struct_type| { | |
| 527 | try section.emitRaw(spv.gpa, .OpTypeStruct, 1 + struct_type.member_types.len); | |
| 528 | section.writeOperand(IdResult, result_id); | |
| 529 | for (struct_type.member_types) |member_type| { | |
| 530 | section.writeOperand(IdResult, self.resultId(member_type)); | |
| 531 | } | |
| 532 | if (self.getString(struct_type.name)) |name| { | |
| 533 | try spv.debugName(result_id, "{s}", .{name}); | |
| 534 | } | |
| 535 | for (struct_type.memberNames(), 0..) |member_name, i| { | |
| 536 | if (self.getString(member_name)) |name| { | |
| 537 | try spv.memberDebugName(result_id, @intCast(u32, i), "{s}", .{name}); | |
| 538 | } | |
| 539 | } | |
| 540 | // TODO: Decorations? | |
| 541 | }, | |
| 542 | .int => |int| { | |
| 543 | const int_type = self.lookup(int.ty).int_type; | |
| 544 | const ty_id = self.resultId(int.ty); | |
| 545 | const lit: Lit = switch (int_type.bits) { | |
| 546 | 1...32 => .{ .uint32 = int.toBits32() }, | |
| 547 | 33...64 => .{ .uint64 = int.toBits64() }, | |
| 548 | else => unreachable, | |
| 549 | }; | |
| 550 | ||
| 551 | try section.emit(spv.gpa, .OpConstant, .{ | |
| 552 | .id_result_type = ty_id, | |
| 553 | .id_result = result_id, | |
| 554 | .value = lit, | |
| 555 | }); | |
| 556 | }, | |
| 557 | .float => |float| { | |
| 558 | const ty_id = self.resultId(float.ty); | |
| 559 | const lit: Lit = switch (float.value) { | |
| 560 | .float16 => |value| .{ .uint32 = @bitCast(u16, value) }, | |
| 561 | .float32 => |value| .{ .float32 = value }, | |
| 562 | .float64 => |value| .{ .float64 = value }, | |
| 563 | }; | |
| 564 | try section.emit(spv.gpa, .OpConstant, .{ | |
| 565 | .id_result_type = ty_id, | |
| 566 | .id_result = result_id, | |
| 567 | .value = lit, | |
| 568 | }); | |
| 569 | }, | |
| 570 | .undef => |undef| { | |
| 571 | try section.emit(spv.gpa, .OpUndef, .{ | |
| 572 | .id_result_type = self.resultId(undef.ty), | |
| 573 | .id_result = result_id, | |
| 574 | }); | |
| 575 | }, | |
| 576 | .null => |null_info| { | |
| 577 | try section.emit(spv.gpa, .OpConstantNull, .{ | |
| 578 | .id_result_type = self.resultId(null_info.ty), | |
| 579 | .id_result = result_id, | |
| 580 | }); | |
| 581 | }, | |
| 582 | .bool => |bool_info| switch (bool_info.value) { | |
| 583 | true => { | |
| 584 | try section.emit(spv.gpa, .OpConstantTrue, .{ | |
| 585 | .id_result_type = self.resultId(bool_info.ty), | |
| 586 | .id_result = result_id, | |
| 587 | }); | |
| 588 | }, | |
| 589 | false => { | |
| 590 | try section.emit(spv.gpa, .OpConstantFalse, .{ | |
| 591 | .id_result_type = self.resultId(bool_info.ty), | |
| 592 | .id_result = result_id, | |
| 593 | }); | |
| 594 | }, | |
| 595 | }, | |
| 596 | } | |
| 597 | } | |
| 598 | ||
| 599 | /// Add a key to this cache. Returns a reference to the key that | |
| 600 | /// was added. The corresponding result-id can be queried using | |
| 601 | /// self.resultId with the result. | |
| 602 | pub fn resolve(self: *Self, spv: *Module, key: Key) !Ref { | |
| 603 | const adapter: Key.Adapter = .{ .self = self }; | |
| 604 | const entry = try self.map.getOrPutAdapted(spv.gpa, key, adapter); | |
| 605 | if (entry.found_existing) { | |
| 606 | return @intToEnum(Ref, entry.index); | |
| 607 | } | |
| 608 | const result_id = spv.allocId(); | |
| 609 | const item: Item = switch (key) { | |
| 610 | inline .void_type, .bool_type => .{ | |
| 611 | .tag = .type_simple, | |
| 612 | .result_id = result_id, | |
| 613 | .data = @enumToInt(key.toSimpleType()), | |
| 614 | }, | |
| 615 | .int_type => |int| blk: { | |
| 616 | const t: Tag = switch (int.signedness) { | |
| 617 | .signed => .type_int_signed, | |
| 618 | .unsigned => .type_int_unsigned, | |
| 619 | }; | |
| 620 | break :blk .{ | |
| 621 | .tag = t, | |
| 622 | .result_id = result_id, | |
| 623 | .data = int.bits, | |
| 624 | }; | |
| 625 | }, | |
| 626 | .float_type => |float| .{ | |
| 627 | .tag = .type_float, | |
| 628 | .result_id = result_id, | |
| 629 | .data = float.bits, | |
| 630 | }, | |
| 631 | .vector_type => |vector| .{ | |
| 632 | .tag = .type_vector, | |
| 633 | .result_id = result_id, | |
| 634 | .data = try self.addExtra(spv, vector), | |
| 635 | }, | |
| 636 | .array_type => |array| .{ | |
| 637 | .tag = .type_array, | |
| 638 | .result_id = result_id, | |
| 639 | .data = try self.addExtra(spv, array), | |
| 640 | }, | |
| 641 | .function_type => |function| blk: { | |
| 642 | const extra = try self.addExtra(spv, Tag.FunctionType{ | |
| 643 | .param_len = @intCast(u32, function.parameters.len), | |
| 644 | .return_type = function.return_type, | |
| 645 | }); | |
| 646 | try self.extra.appendSlice(spv.gpa, @ptrCast([]const u32, function.parameters)); | |
| 647 | break :blk .{ | |
| 648 | .tag = .type_function, | |
| 649 | .result_id = result_id, | |
| 650 | .data = extra, | |
| 651 | }; | |
| 652 | }, | |
| 653 | .ptr_type => |ptr| switch (ptr.storage_class) { | |
| 654 | .Generic => Item{ | |
| 655 | .tag = .type_ptr_generic, | |
| 656 | .result_id = result_id, | |
| 657 | .data = @enumToInt(ptr.child_type), | |
| 658 | }, | |
| 659 | .CrossWorkgroup => Item{ | |
| 660 | .tag = .type_ptr_crosswgp, | |
| 661 | .result_id = result_id, | |
| 662 | .data = @enumToInt(ptr.child_type), | |
| 663 | }, | |
| 664 | .Function => Item{ | |
| 665 | .tag = .type_ptr_function, | |
| 666 | .result_id = result_id, | |
| 667 | .data = @enumToInt(ptr.child_type), | |
| 668 | }, | |
| 669 | else => |storage_class| Item{ | |
| 670 | .tag = .type_ptr_simple, | |
| 671 | .result_id = result_id, | |
| 672 | .data = try self.addExtra(spv, Tag.SimplePointerType{ | |
| 673 | .storage_class = storage_class, | |
| 674 | .child_type = ptr.child_type, | |
| 675 | }), | |
| 676 | }, | |
| 677 | }, | |
| 678 | .struct_type => |struct_type| blk: { | |
| 679 | const extra = try self.addExtra(spv, Tag.SimpleStructType{ | |
| 680 | .name = struct_type.name, | |
| 681 | .members_len = @intCast(u32, struct_type.member_types.len), | |
| 682 | }); | |
| 683 | try self.extra.appendSlice(spv.gpa, @ptrCast([]const u32, struct_type.member_types)); | |
| 684 | ||
| 685 | if (struct_type.member_names) |member_names| { | |
| 686 | try self.extra.appendSlice(spv.gpa, @ptrCast([]const u32, member_names)); | |
| 687 | break :blk Item{ | |
| 688 | .tag = .type_struct_simple_with_member_names, | |
| 689 | .result_id = result_id, | |
| 690 | .data = extra, | |
| 691 | }; | |
| 692 | } else { | |
| 693 | break :blk Item{ | |
| 694 | .tag = .type_struct_simple, | |
| 695 | .result_id = result_id, | |
| 696 | .data = extra, | |
| 697 | }; | |
| 698 | } | |
| 699 | }, | |
| 700 | .int => |int| blk: { | |
| 701 | const int_type = self.lookup(int.ty).int_type; | |
| 702 | if (int_type.signedness == .unsigned and int_type.bits == 8) { | |
| 703 | break :blk .{ | |
| 704 | .tag = .uint8, | |
| 705 | .result_id = result_id, | |
| 706 | .data = int.to(u8), | |
| 707 | }; | |
| 708 | } else if (int_type.signedness == .unsigned and int_type.bits == 32) { | |
| 709 | break :blk .{ | |
| 710 | .tag = .uint32, | |
| 711 | .result_id = result_id, | |
| 712 | .data = int.to(u32), | |
| 713 | }; | |
| 714 | } | |
| 715 | ||
| 716 | switch (int.value) { | |
| 717 | inline else => |val| { | |
| 718 | if (val >= 0 and val <= std.math.maxInt(u32)) { | |
| 719 | break :blk .{ | |
| 720 | .tag = .uint_small, | |
| 721 | .result_id = result_id, | |
| 722 | .data = try self.addExtra(spv, Tag.UInt32{ | |
| 723 | .ty = int.ty, | |
| 724 | .value = @intCast(u32, val), | |
| 725 | }), | |
| 726 | }; | |
| 727 | } else if (val >= std.math.minInt(i32) and val <= std.math.maxInt(i32)) { | |
| 728 | break :blk .{ | |
| 729 | .tag = .int_small, | |
| 730 | .result_id = result_id, | |
| 731 | .data = try self.addExtra(spv, Tag.Int32{ | |
| 732 | .ty = int.ty, | |
| 733 | .value = @intCast(i32, val), | |
| 734 | }), | |
| 735 | }; | |
| 736 | } else if (val < 0) { | |
| 737 | break :blk .{ | |
| 738 | .tag = .int_large, | |
| 739 | .result_id = result_id, | |
| 740 | .data = try self.addExtra(spv, Tag.Int64.encode(int.ty, @intCast(i64, val))), | |
| 741 | }; | |
| 742 | } else { | |
| 743 | break :blk .{ | |
| 744 | .tag = .uint_large, | |
| 745 | .result_id = result_id, | |
| 746 | .data = try self.addExtra(spv, Tag.UInt64.encode(int.ty, @intCast(u64, val))), | |
| 747 | }; | |
| 748 | } | |
| 749 | }, | |
| 750 | } | |
| 751 | }, | |
| 752 | .float => |float| switch (self.lookup(float.ty).float_type.bits) { | |
| 753 | 16 => .{ | |
| 754 | .tag = .float16, | |
| 755 | .result_id = result_id, | |
| 756 | .data = @bitCast(u16, float.value.float16), | |
| 757 | }, | |
| 758 | 32 => .{ | |
| 759 | .tag = .float32, | |
| 760 | .result_id = result_id, | |
| 761 | .data = @bitCast(u32, float.value.float32), | |
| 762 | }, | |
| 763 | 64 => .{ | |
| 764 | .tag = .float64, | |
| 765 | .result_id = result_id, | |
| 766 | .data = try self.addExtra(spv, Tag.Float64.encode(float.value.float64)), | |
| 767 | }, | |
| 768 | else => unreachable, | |
| 769 | }, | |
| 770 | .undef => |undef| .{ | |
| 771 | .tag = .undef, | |
| 772 | .result_id = result_id, | |
| 773 | .data = @enumToInt(undef.ty), | |
| 774 | }, | |
| 775 | .null => |null_info| .{ | |
| 776 | .tag = .null, | |
| 777 | .result_id = result_id, | |
| 778 | .data = @enumToInt(null_info.ty), | |
| 779 | }, | |
| 780 | .bool => |bool_info| .{ | |
| 781 | .tag = switch (bool_info.value) { | |
| 782 | true => Tag.bool_true, | |
| 783 | false => Tag.bool_false, | |
| 784 | }, | |
| 785 | .result_id = result_id, | |
| 786 | .data = @enumToInt(bool_info.ty), | |
| 787 | }, | |
| 788 | }; | |
| 789 | try self.items.append(spv.gpa, item); | |
| 790 | ||
| 791 | return @intToEnum(Ref, entry.index); | |
| 792 | } | |
| 793 | ||
| 794 | /// Turn a Ref back into a Key. | |
| 795 | /// The Key is valid until the next call to resolve(). | |
| 796 | pub fn lookup(self: *const Self, ref: Ref) Key { | |
| 797 | const item = self.items.get(@enumToInt(ref)); | |
| 798 | const data = item.data; | |
| 799 | return switch (item.tag) { | |
| 800 | .type_simple => switch (@intToEnum(Tag.SimpleType, data)) { | |
| 801 | .void => .void_type, | |
| 802 | .bool => .bool_type, | |
| 803 | }, | |
| 804 | .type_int_signed => .{ .int_type = .{ | |
| 805 | .signedness = .signed, | |
| 806 | .bits = @intCast(u16, data), | |
| 807 | } }, | |
| 808 | .type_int_unsigned => .{ .int_type = .{ | |
| 809 | .signedness = .unsigned, | |
| 810 | .bits = @intCast(u16, data), | |
| 811 | } }, | |
| 812 | .type_float => .{ .float_type = .{ | |
| 813 | .bits = @intCast(u16, data), | |
| 814 | } }, | |
| 815 | .type_vector => .{ .vector_type = self.extraData(Tag.VectorType, data) }, | |
| 816 | .type_array => .{ .array_type = self.extraData(Tag.ArrayType, data) }, | |
| 817 | .type_function => { | |
| 818 | const payload = self.extraDataTrail(Tag.FunctionType, data); | |
| 819 | return .{ | |
| 820 | .function_type = .{ | |
| 821 | .return_type = payload.data.return_type, | |
| 822 | .parameters = @ptrCast([]const Ref, self.extra.items[payload.trail..][0..payload.data.param_len]), | |
| 823 | }, | |
| 824 | }; | |
| 825 | }, | |
| 826 | .type_ptr_generic => .{ | |
| 827 | .ptr_type = .{ | |
| 828 | .storage_class = .Generic, | |
| 829 | .child_type = @intToEnum(Ref, data), | |
| 830 | }, | |
| 831 | }, | |
| 832 | .type_ptr_crosswgp => .{ | |
| 833 | .ptr_type = .{ | |
| 834 | .storage_class = .CrossWorkgroup, | |
| 835 | .child_type = @intToEnum(Ref, data), | |
| 836 | }, | |
| 837 | }, | |
| 838 | .type_ptr_function => .{ | |
| 839 | .ptr_type = .{ | |
| 840 | .storage_class = .Function, | |
| 841 | .child_type = @intToEnum(Ref, data), | |
| 842 | }, | |
| 843 | }, | |
| 844 | .type_ptr_simple => { | |
| 845 | const payload = self.extraData(Tag.SimplePointerType, data); | |
| 846 | return .{ | |
| 847 | .ptr_type = .{ | |
| 848 | .storage_class = payload.storage_class, | |
| 849 | .child_type = payload.child_type, | |
| 850 | }, | |
| 851 | }; | |
| 852 | }, | |
| 853 | .type_struct_simple => { | |
| 854 | const payload = self.extraDataTrail(Tag.SimpleStructType, data); | |
| 855 | const member_types = @ptrCast([]const Ref, self.extra.items[payload.trail..][0..payload.data.members_len]); | |
| 856 | return .{ | |
| 857 | .struct_type = .{ | |
| 858 | .name = payload.data.name, | |
| 859 | .member_types = member_types, | |
| 860 | .member_names = null, | |
| 861 | }, | |
| 862 | }; | |
| 863 | }, | |
| 864 | .type_struct_simple_with_member_names => { | |
| 865 | const payload = self.extraDataTrail(Tag.SimpleStructType, data); | |
| 866 | const trailing = self.extra.items[payload.trail..]; | |
| 867 | const member_types = @ptrCast([]const Ref, trailing[0..payload.data.members_len]); | |
| 868 | const member_names = @ptrCast([]const String, trailing[payload.data.members_len..][0..payload.data.members_len]); | |
| 869 | return .{ | |
| 870 | .struct_type = .{ | |
| 871 | .name = payload.data.name, | |
| 872 | .member_types = member_types, | |
| 873 | .member_names = member_names, | |
| 874 | }, | |
| 875 | }; | |
| 876 | }, | |
| 877 | .float16 => .{ .float = .{ | |
| 878 | .ty = self.get(.{ .float_type = .{ .bits = 16 } }), | |
| 879 | .value = .{ .float16 = @bitCast(f16, @intCast(u16, data)) }, | |
| 880 | } }, | |
| 881 | .float32 => .{ .float = .{ | |
| 882 | .ty = self.get(.{ .float_type = .{ .bits = 32 } }), | |
| 883 | .value = .{ .float32 = @bitCast(f32, data) }, | |
| 884 | } }, | |
| 885 | .float64 => .{ .float = .{ | |
| 886 | .ty = self.get(.{ .float_type = .{ .bits = 64 } }), | |
| 887 | .value = .{ .float64 = self.extraData(Tag.Float64, data).decode() }, | |
| 888 | } }, | |
| 889 | .uint8 => .{ .int = .{ | |
| 890 | .ty = self.get(.{ .int_type = .{ .signedness = .unsigned, .bits = 8 } }), | |
| 891 | .value = .{ .uint64 = data }, | |
| 892 | } }, | |
| 893 | .uint32 => .{ .int = .{ | |
| 894 | .ty = self.get(.{ .int_type = .{ .signedness = .unsigned, .bits = 32 } }), | |
| 895 | .value = .{ .uint64 = data }, | |
| 896 | } }, | |
| 897 | .int_small => { | |
| 898 | const payload = self.extraData(Tag.Int32, data); | |
| 899 | return .{ .int = .{ | |
| 900 | .ty = payload.ty, | |
| 901 | .value = .{ .int64 = payload.value }, | |
| 902 | } }; | |
| 903 | }, | |
| 904 | .uint_small => { | |
| 905 | const payload = self.extraData(Tag.UInt32, data); | |
| 906 | return .{ .int = .{ | |
| 907 | .ty = payload.ty, | |
| 908 | .value = .{ .uint64 = payload.value }, | |
| 909 | } }; | |
| 910 | }, | |
| 911 | .int_large => { | |
| 912 | const payload = self.extraData(Tag.Int64, data); | |
| 913 | return .{ .int = .{ | |
| 914 | .ty = payload.ty, | |
| 915 | .value = .{ .int64 = payload.decode() }, | |
| 916 | } }; | |
| 917 | }, | |
| 918 | .uint_large => { | |
| 919 | const payload = self.extraData(Tag.UInt64, data); | |
| 920 | return .{ .int = .{ | |
| 921 | .ty = payload.ty, | |
| 922 | .value = .{ .uint64 = payload.decode() }, | |
| 923 | } }; | |
| 924 | }, | |
| 925 | .undef => .{ .undef = .{ | |
| 926 | .ty = @intToEnum(Ref, data), | |
| 927 | } }, | |
| 928 | .null => .{ .null = .{ | |
| 929 | .ty = @intToEnum(Ref, data), | |
| 930 | } }, | |
| 931 | .bool_true => .{ .bool = .{ | |
| 932 | .ty = @intToEnum(Ref, data), | |
| 933 | .value = true, | |
| 934 | } }, | |
| 935 | .bool_false => .{ .bool = .{ | |
| 936 | .ty = @intToEnum(Ref, data), | |
| 937 | .value = false, | |
| 938 | } }, | |
| 939 | }; | |
| 940 | } | |
| 941 | ||
| 942 | /// Look op the result-id that corresponds to a particular | |
| 943 | /// ref. | |
| 944 | pub fn resultId(self: Self, ref: Ref) IdResult { | |
| 945 | return self.items.items(.result_id)[@enumToInt(ref)]; | |
| 946 | } | |
| 947 | ||
| 948 | /// Get the ref for a key that has already been added to the cache. | |
| 949 | fn get(self: *const Self, key: Key) Ref { | |
| 950 | const adapter: Key.Adapter = .{ .self = self }; | |
| 951 | const index = self.map.getIndexAdapted(key, adapter).?; | |
| 952 | return @intToEnum(Ref, index); | |
| 953 | } | |
| 954 | ||
| 955 | fn addExtra(self: *Self, spv: *Module, extra: anytype) !u32 { | |
| 956 | const fields = @typeInfo(@TypeOf(extra)).Struct.fields; | |
| 957 | try self.extra.ensureUnusedCapacity(spv.gpa, fields.len); | |
| 958 | return try self.addExtraAssumeCapacity(extra); | |
| 959 | } | |
| 960 | ||
| 961 | fn addExtraAssumeCapacity(self: *Self, extra: anytype) !u32 { | |
| 962 | const payload_offset = @intCast(u32, self.extra.items.len); | |
| 963 | inline for (@typeInfo(@TypeOf(extra)).Struct.fields) |field| { | |
| 964 | const field_val = @field(extra, field.name); | |
| 965 | const word = switch (field.type) { | |
| 966 | u32 => field_val, | |
| 967 | i32 => @bitCast(u32, field_val), | |
| 968 | Ref => @enumToInt(field_val), | |
| 969 | StorageClass => @enumToInt(field_val), | |
| 970 | String => @enumToInt(field_val), | |
| 971 | else => @compileError("Invalid type: " ++ @typeName(field.type)), | |
| 972 | }; | |
| 973 | self.extra.appendAssumeCapacity(word); | |
| 974 | } | |
| 975 | return payload_offset; | |
| 976 | } | |
| 977 | ||
| 978 | fn extraData(self: Self, comptime T: type, offset: u32) T { | |
| 979 | return self.extraDataTrail(T, offset).data; | |
| 980 | } | |
| 981 | ||
| 982 | fn extraDataTrail(self: Self, comptime T: type, offset: u32) struct { data: T, trail: u32 } { | |
| 983 | var result: T = undefined; | |
| 984 | const fields = @typeInfo(T).Struct.fields; | |
| 985 | inline for (fields, 0..) |field, i| { | |
| 986 | const word = self.extra.items[offset + i]; | |
| 987 | @field(result, field.name) = switch (field.type) { | |
| 988 | u32 => word, | |
| 989 | i32 => @bitCast(i32, word), | |
| 990 | Ref => @intToEnum(Ref, word), | |
| 991 | StorageClass => @intToEnum(StorageClass, word), | |
| 992 | String => @intToEnum(String, word), | |
| 993 | else => @compileError("Invalid type: " ++ @typeName(field.type)), | |
| 994 | }; | |
| 995 | } | |
| 996 | return .{ | |
| 997 | .data = result, | |
| 998 | .trail = offset + @intCast(u32, fields.len), | |
| 999 | }; | |
| 1000 | } | |
| 1001 | ||
| 1002 | /// Represents a reference to some null-terminated string. | |
| 1003 | pub const String = enum(u32) { | |
| 1004 | none = std.math.maxInt(u32), | |
| 1005 | _, | |
| 1006 | ||
| 1007 | pub const Adapter = struct { | |
| 1008 | self: *const Self, | |
| 1009 | ||
| 1010 | pub fn eql(ctx: @This(), a: []const u8, _: void, b_index: usize) bool { | |
| 1011 | const offset = ctx.self.strings.values()[b_index]; | |
| 1012 | const b = std.mem.sliceTo(ctx.self.string_bytes.items[offset..], 0); | |
| 1013 | return std.mem.eql(u8, a, b); | |
| 1014 | } | |
| 1015 | ||
| 1016 | pub fn hash(ctx: @This(), a: []const u8) u32 { | |
| 1017 | _ = ctx; | |
| 1018 | var hasher = std.hash.Wyhash.init(0); | |
| 1019 | hasher.update(a); | |
| 1020 | return @truncate(u32, hasher.final()); | |
| 1021 | } | |
| 1022 | }; | |
| 1023 | }; | |
| 1024 | ||
| 1025 | /// Add a string to the cache. Must not contain any 0 values. | |
| 1026 | pub fn addString(self: *Self, spv: *Module, str: []const u8) !String { | |
| 1027 | assert(std.mem.indexOfScalar(u8, str, 0) == null); | |
| 1028 | const adapter = String.Adapter{ .self = self }; | |
| 1029 | const entry = try self.strings.getOrPutAdapted(spv.gpa, str, adapter); | |
| 1030 | if (!entry.found_existing) { | |
| 1031 | const offset = self.string_bytes.items.len; | |
| 1032 | try self.string_bytes.ensureUnusedCapacity(spv.gpa, 1 + str.len); | |
| 1033 | self.string_bytes.appendSliceAssumeCapacity(str); | |
| 1034 | self.string_bytes.appendAssumeCapacity(0); | |
| 1035 | entry.value_ptr.* = @intCast(u32, offset); | |
| 1036 | } | |
| 1037 | ||
| 1038 | return @intToEnum(String, entry.index); | |
| 1039 | } | |
| 1040 | ||
| 1041 | pub fn getString(self: *const Self, ref: String) ?[]const u8 { | |
| 1042 | return switch (ref) { | |
| 1043 | .none => null, | |
| 1044 | else => std.mem.sliceTo(self.string_bytes.items[self.strings.values()[@enumToInt(ref)]..], 0), | |
| 1045 | }; | |
| 1046 | } |
src/codegen/spirv/Module.zig+100-400| ... | ... | @@ -20,11 +20,13 @@ const IdResult = spec.IdResult; |
| 20 | 20 | const IdResultType = spec.IdResultType; |
| 21 | 21 | |
| 22 | 22 | const Section = @import("Section.zig"); |
| 23 | const Type = @import("type.zig").Type; | |
| 24 | 23 | |
| 25 | const TypeCache = std.ArrayHashMapUnmanaged(Type, IdResultType, Type.ShallowHashContext32, true); | |
| 24 | const Cache = @import("Cache.zig"); | |
| 25 | pub const CacheKey = Cache.Key; | |
| 26 | pub const CacheRef = Cache.Ref; | |
| 27 | pub const CacheString = Cache.String; | |
| 26 | 28 | |
| 27 | /// This structure represents a function that is in-progress of being emitted. | |
| 29 | /// This structure represents a function that isc in-progress of being emitted. | |
| 28 | 30 | /// Commonly, the contents of this structure will be merged with the appropriate |
| 29 | 31 | /// sections of the module and re-used. Note that the SPIR-V module system makes |
| 30 | 32 | /// no attempt of compacting result-id's, so any Fn instance should ultimately |
| ... | ... | @@ -126,7 +128,13 @@ sections: struct { |
| 126 | 128 | /// Annotation instructions (OpDecorate etc). |
| 127 | 129 | annotations: Section = .{}, |
| 128 | 130 | /// Type declarations, constants, global variables |
| 129 | /// Below this section, OpLine and OpNoLine is allowed. | |
| 131 | /// From this section, OpLine and OpNoLine is allowed. | |
| 132 | /// According to the SPIR-V documentation, this section normally | |
| 133 | /// also holds type and constant instructions. These are managed | |
| 134 | /// via the cache instead, which is the sole structure that | |
| 135 | /// manages that section. These will be inserted between this and | |
| 136 | /// the previous section when emitting the final binary. | |
| 137 | /// TODO: Do we need this section? Globals are also managed with another mechanism. | |
| 130 | 138 | types_globals_constants: Section = .{}, |
| 131 | 139 | // Functions without a body - skip for now. |
| 132 | 140 | /// Regular function definitions. |
| ... | ... | @@ -141,11 +149,9 @@ next_result_id: Word, |
| 141 | 149 | /// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource. |
| 142 | 150 | source_file_names: std.StringHashMapUnmanaged(IdRef) = .{}, |
| 143 | 151 | |
| 144 | /// SPIR-V type cache. Note that according to SPIR-V spec section 2.8, Types and Variables, non-pointer | |
| 145 | /// non-aggrerate types (which includes matrices and vectors) must have a _unique_ representation in | |
| 146 | /// the final binary. | |
| 147 | /// Note: Uses ArrayHashMap which is insertion ordered, so that we may refer to other types by index (Type.Ref). | |
| 148 | type_cache: TypeCache = .{}, | |
| 152 | /// SPIR-V type- and constant cache. This structure is used to store information about these in a more | |
| 153 | /// efficient manner. | |
| 154 | cache: Cache = .{}, | |
| 149 | 155 | |
| 150 | 156 | /// Set of Decls, referred to by Decl.Index. |
| 151 | 157 | decls: std.ArrayListUnmanaged(Decl) = .{}, |
| ... | ... | @@ -163,7 +169,7 @@ globals: struct { |
| 163 | 169 | globals: std.AutoArrayHashMapUnmanaged(Decl.Index, Global) = .{}, |
| 164 | 170 | /// This pseudo-section contains the initialization code for all the globals. Instructions from |
| 165 | 171 | /// here are reordered when flushing the module. Its contents should be part of the |
| 166 | /// `types_globals_constants` SPIR-V section. | |
| 172 | /// `types_globals_constants` SPIR-V section when the module is emitted. | |
| 167 | 173 | section: Section = .{}, |
| 168 | 174 | } = .{}, |
| 169 | 175 | |
| ... | ... | @@ -182,11 +188,10 @@ pub fn deinit(self: *Module) void { |
| 182 | 188 | self.sections.debug_strings.deinit(self.gpa); |
| 183 | 189 | self.sections.debug_names.deinit(self.gpa); |
| 184 | 190 | self.sections.annotations.deinit(self.gpa); |
| 185 | self.sections.types_globals_constants.deinit(self.gpa); | |
| 186 | 191 | self.sections.functions.deinit(self.gpa); |
| 187 | 192 | |
| 188 | 193 | self.source_file_names.deinit(self.gpa); |
| 189 | self.type_cache.deinit(self.gpa); | |
| 194 | self.cache.deinit(self); | |
| 190 | 195 | |
| 191 | 196 | self.decls.deinit(self.gpa); |
| 192 | 197 | self.decl_deps.deinit(self.gpa); |
| ... | ... | @@ -213,6 +218,22 @@ pub fn idBound(self: Module) Word { |
| 213 | 218 | return self.next_result_id; |
| 214 | 219 | } |
| 215 | 220 | |
| 221 | pub fn resolve(self: *Module, key: CacheKey) !CacheRef { | |
| 222 | return self.cache.resolve(self, key); | |
| 223 | } | |
| 224 | ||
| 225 | pub fn resultId(self: *const Module, ref: CacheRef) IdResult { | |
| 226 | return self.cache.resultId(ref); | |
| 227 | } | |
| 228 | ||
| 229 | pub fn resolveId(self: *Module, key: CacheKey) !IdResult { | |
| 230 | return self.resultId(try self.resolve(key)); | |
| 231 | } | |
| 232 | ||
| 233 | pub fn resolveString(self: *Module, str: []const u8) !CacheString { | |
| 234 | return try self.cache.addString(self, str); | |
| 235 | } | |
| 236 | ||
| 216 | 237 | fn orderGlobalsInto( |
| 217 | 238 | self: *Module, |
| 218 | 239 | decl_index: Decl.Index, |
| ... | ... | @@ -324,6 +345,9 @@ pub fn flush(self: *Module, file: std.fs.File) !void { |
| 324 | 345 | var entry_points = try self.entryPoints(); |
| 325 | 346 | defer entry_points.deinit(self.gpa); |
| 326 | 347 | |
| 348 | var types_constants = try self.cache.materialize(self); | |
| 349 | defer types_constants.deinit(self.gpa); | |
| 350 | ||
| 327 | 351 | // Note: needs to be kept in order according to section 2.3! |
| 328 | 352 | const buffers = &[_][]const Word{ |
| 329 | 353 | &header, |
| ... | ... | @@ -334,6 +358,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void { |
| 334 | 358 | self.sections.debug_strings.toWords(), |
| 335 | 359 | self.sections.debug_names.toWords(), |
| 336 | 360 | self.sections.annotations.toWords(), |
| 361 | types_constants.toWords(), | |
| 337 | 362 | self.sections.types_globals_constants.toWords(), |
| 338 | 363 | globals.toWords(), |
| 339 | 364 | self.sections.functions.toWords(), |
| ... | ... | @@ -386,417 +411,73 @@ pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef { |
| 386 | 411 | return result.value_ptr.*; |
| 387 | 412 | } |
| 388 | 413 | |
| 389 | /// Fetch a result-id for a spir-v type. This function deduplicates the type as appropriate, | |
| 390 | /// and returns a cached version if that exists. | |
| 391 | /// Note: This function does not attempt to perform any validation on the type. | |
| 392 | /// The type is emitted in a shallow fashion; any child types should already | |
| 393 | /// be emitted at this point. | |
| 394 | pub fn resolveType(self: *Module, ty: Type) !Type.Ref { | |
| 395 | const result = try self.type_cache.getOrPut(self.gpa, ty); | |
| 396 | const index = @intToEnum(Type.Ref, result.index); | |
| 397 | ||
| 398 | if (!result.found_existing) { | |
| 399 | const ref = try self.emitType(ty); | |
| 400 | self.type_cache.values()[result.index] = ref; | |
| 401 | } | |
| 402 | ||
| 403 | return index; | |
| 404 | } | |
| 405 | ||
| 406 | pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType { | |
| 407 | const ty_ref = try self.resolveType(ty); | |
| 408 | return self.typeId(ty_ref); | |
| 414 | pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !CacheRef { | |
| 415 | return try self.resolve(.{ .int_type = .{ | |
| 416 | .signedness = signedness, | |
| 417 | .bits = bits, | |
| 418 | } }); | |
| 409 | 419 | } |
| 410 | 420 | |
| 411 | pub fn typeRefType(self: Module, ty_ref: Type.Ref) Type { | |
| 412 | return self.type_cache.keys()[@enumToInt(ty_ref)]; | |
| 421 | pub fn arrayType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef { | |
| 422 | const len_ty_ref = try self.resolve(.{ .int_type = .{ | |
| 423 | .signedness = .unsigned, | |
| 424 | .bits = 32, | |
| 425 | } }); | |
| 426 | const len_ref = try self.resolve(.{ .int = .{ | |
| 427 | .ty = len_ty_ref, | |
| 428 | .value = .{ .uint64 = len }, | |
| 429 | } }); | |
| 430 | return try self.resolve(.{ .array_type = .{ | |
| 431 | .element_type = elem_ty_ref, | |
| 432 | .length = len_ref, | |
| 433 | } }); | |
| 413 | 434 | } |
| 414 | 435 | |
| 415 | /// Get the result-id of a particular type, by reference. Asserts type_ref is valid. | |
| 416 | pub fn typeId(self: Module, ty_ref: Type.Ref) IdResultType { | |
| 417 | return self.type_cache.values()[@enumToInt(ty_ref)]; | |
| 436 | pub fn ptrType( | |
| 437 | self: *Module, | |
| 438 | child: CacheRef, | |
| 439 | storage_class: spec.StorageClass, | |
| 440 | ) !CacheRef { | |
| 441 | return try self.resolve(.{ .ptr_type = .{ | |
| 442 | .storage_class = storage_class, | |
| 443 | .child_type = child, | |
| 444 | } }); | |
| 418 | 445 | } |
| 419 | 446 | |
| 420 | /// Unconditionally emit a spir-v type into the appropriate section. | |
| 421 | /// Note: If this function is called with a type that is already generated, it may yield an invalid module | |
| 422 | /// as non-pointer non-aggregrate types must me unique! | |
| 423 | /// Note: This function does not attempt to perform any validation on the type. | |
| 424 | /// The type is emitted in a shallow fashion; any child types should already | |
| 425 | /// be emitted at this point. | |
| 426 | pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType { | |
| 427 | const result_id = self.allocId(); | |
| 428 | const ref_id = result_id; | |
| 429 | const types = &self.sections.types_globals_constants; | |
| 430 | const debug_names = &self.sections.debug_names; | |
| 431 | const result_id_operand = .{ .id_result = result_id }; | |
| 432 | ||
| 433 | switch (ty.tag()) { | |
| 434 | .void => { | |
| 435 | try types.emit(self.gpa, .OpTypeVoid, result_id_operand); | |
| 436 | try debug_names.emit(self.gpa, .OpName, .{ | |
| 437 | .target = result_id, | |
| 438 | .name = "void", | |
| 439 | }); | |
| 447 | pub fn constInt(self: *Module, ty_ref: CacheRef, value: anytype) !IdRef { | |
| 448 | const ty = self.cache.lookup(ty_ref).int_type; | |
| 449 | const Value = Cache.Key.Int.Value; | |
| 450 | return try self.resolveId(.{ .int = .{ | |
| 451 | .ty = ty_ref, | |
| 452 | .value = switch (ty.signedness) { | |
| 453 | .signed => Value{ .int64 = @intCast(i64, value) }, | |
| 454 | .unsigned => Value{ .uint64 = @intCast(u64, value) }, | |
| 440 | 455 | }, |
| 441 | .bool => { | |
| 442 | try types.emit(self.gpa, .OpTypeBool, result_id_operand); | |
| 443 | try debug_names.emit(self.gpa, .OpName, .{ | |
| 444 | .target = result_id, | |
| 445 | .name = "bool", | |
| 446 | }); | |
| 447 | }, | |
| 448 | .u8, | |
| 449 | .u16, | |
| 450 | .u32, | |
| 451 | .u64, | |
| 452 | .i8, | |
| 453 | .i16, | |
| 454 | .i32, | |
| 455 | .i64, | |
| 456 | .int, | |
| 457 | => { | |
| 458 | // TODO: Kernels do not support OpTypeInt that is signed. We can probably | |
| 459 | // can get rid of the signedness all together, in Shaders also. | |
| 460 | const bits = ty.intFloatBits(); | |
| 461 | const signedness: spec.LiteralInteger = switch (ty.intSignedness()) { | |
| 462 | .unsigned => 0, | |
| 463 | .signed => 1, | |
| 464 | }; | |
| 465 | ||
| 466 | try types.emit(self.gpa, .OpTypeInt, .{ | |
| 467 | .id_result = result_id, | |
| 468 | .width = bits, | |
| 469 | .signedness = signedness, | |
| 470 | }); | |
| 471 | ||
| 472 | const ui: []const u8 = switch (signedness) { | |
| 473 | 0 => "u", | |
| 474 | 1 => "i", | |
| 475 | else => unreachable, | |
| 476 | }; | |
| 477 | const name = try std.fmt.allocPrint(self.gpa, "{s}{}", .{ ui, bits }); | |
| 478 | defer self.gpa.free(name); | |
| 479 | ||
| 480 | try debug_names.emit(self.gpa, .OpName, .{ | |
| 481 | .target = result_id, | |
| 482 | .name = name, | |
| 483 | }); | |
| 484 | }, | |
| 485 | .f16, .f32, .f64 => { | |
| 486 | const bits = ty.intFloatBits(); | |
| 487 | try types.emit(self.gpa, .OpTypeFloat, .{ | |
| 488 | .id_result = result_id, | |
| 489 | .width = bits, | |
| 490 | }); | |
| 491 | ||
| 492 | const name = try std.fmt.allocPrint(self.gpa, "f{}", .{bits}); | |
| 493 | defer self.gpa.free(name); | |
| 494 | try debug_names.emit(self.gpa, .OpName, .{ | |
| 495 | .target = result_id, | |
| 496 | .name = name, | |
| 497 | }); | |
| 498 | }, | |
| 499 | .vector => try types.emit(self.gpa, .OpTypeVector, .{ | |
| 500 | .id_result = result_id, | |
| 501 | .component_type = self.typeId(ty.childType()), | |
| 502 | .component_count = ty.payload(.vector).component_count, | |
| 503 | }), | |
| 504 | .matrix => try types.emit(self.gpa, .OpTypeMatrix, .{ | |
| 505 | .id_result = result_id, | |
| 506 | .column_type = self.typeId(ty.childType()), | |
| 507 | .column_count = ty.payload(.matrix).column_count, | |
| 508 | }), | |
| 509 | .image => { | |
| 510 | const info = ty.payload(.image); | |
| 511 | try types.emit(self.gpa, .OpTypeImage, .{ | |
| 512 | .id_result = result_id, | |
| 513 | .sampled_type = self.typeId(ty.childType()), | |
| 514 | .dim = info.dim, | |
| 515 | .depth = @enumToInt(info.depth), | |
| 516 | .arrayed = @boolToInt(info.arrayed), | |
| 517 | .ms = @boolToInt(info.multisampled), | |
| 518 | .sampled = @enumToInt(info.sampled), | |
| 519 | .image_format = info.format, | |
| 520 | .access_qualifier = info.access_qualifier, | |
| 521 | }); | |
| 522 | }, | |
| 523 | .sampler => try types.emit(self.gpa, .OpTypeSampler, result_id_operand), | |
| 524 | .sampled_image => try types.emit(self.gpa, .OpTypeSampledImage, .{ | |
| 525 | .id_result = result_id, | |
| 526 | .image_type = self.typeId(ty.childType()), | |
| 527 | }), | |
| 528 | .array => { | |
| 529 | const info = ty.payload(.array); | |
| 530 | assert(info.length != 0); | |
| 531 | ||
| 532 | const size_type = Type.initTag(.u32); | |
| 533 | const size_type_id = try self.resolveTypeId(size_type); | |
| 534 | const length_id = self.allocId(); | |
| 535 | try self.emitConstant(size_type_id, length_id, .{ .uint32 = info.length }); | |
| 536 | ||
| 537 | try types.emit(self.gpa, .OpTypeArray, .{ | |
| 538 | .id_result = result_id, | |
| 539 | .element_type = self.typeId(ty.childType()), | |
| 540 | .length = length_id, | |
| 541 | }); | |
| 542 | if (info.array_stride != 0) { | |
| 543 | try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); | |
| 544 | } | |
| 545 | }, | |
| 546 | .runtime_array => { | |
| 547 | const info = ty.payload(.runtime_array); | |
| 548 | try types.emit(self.gpa, .OpTypeRuntimeArray, .{ | |
| 549 | .id_result = result_id, | |
| 550 | .element_type = self.typeId(ty.childType()), | |
| 551 | }); | |
| 552 | if (info.array_stride != 0) { | |
| 553 | try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); | |
| 554 | } | |
| 555 | }, | |
| 556 | .@"struct" => { | |
| 557 | const info = ty.payload(.@"struct"); | |
| 558 | try types.emitRaw(self.gpa, .OpTypeStruct, 1 + info.members.len); | |
| 559 | types.writeOperand(IdResult, result_id); | |
| 560 | for (info.members) |member| { | |
| 561 | types.writeOperand(IdRef, self.typeId(member.ty)); | |
| 562 | } | |
| 563 | try self.decorateStruct(ref_id, info); | |
| 564 | }, | |
| 565 | .@"opaque" => try types.emit(self.gpa, .OpTypeOpaque, .{ | |
| 566 | .id_result = result_id, | |
| 567 | .literal_string = ty.payload(.@"opaque").name, | |
| 568 | }), | |
| 569 | .pointer => { | |
| 570 | const info = ty.payload(.pointer); | |
| 571 | try types.emit(self.gpa, .OpTypePointer, .{ | |
| 572 | .id_result = result_id, | |
| 573 | .storage_class = info.storage_class, | |
| 574 | .type = self.typeId(ty.childType()), | |
| 575 | }); | |
| 576 | if (info.array_stride != 0) { | |
| 577 | try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); | |
| 578 | } | |
| 579 | if (info.alignment != 0) { | |
| 580 | try self.decorate(ref_id, .{ .Alignment = .{ .alignment = info.alignment } }); | |
| 581 | } | |
| 582 | if (info.max_byte_offset) |max_byte_offset| { | |
| 583 | try self.decorate(ref_id, .{ .MaxByteOffset = .{ .max_byte_offset = max_byte_offset } }); | |
| 584 | } | |
| 585 | }, | |
| 586 | .function => { | |
| 587 | const info = ty.payload(.function); | |
| 588 | try types.emitRaw(self.gpa, .OpTypeFunction, 2 + info.parameters.len); | |
| 589 | types.writeOperand(IdResult, result_id); | |
| 590 | types.writeOperand(IdRef, self.typeId(info.return_type)); | |
| 591 | for (info.parameters) |parameter_type| { | |
| 592 | types.writeOperand(IdRef, self.typeId(parameter_type)); | |
| 593 | } | |
| 594 | }, | |
| 595 | .event => try types.emit(self.gpa, .OpTypeEvent, result_id_operand), | |
| 596 | .device_event => try types.emit(self.gpa, .OpTypeDeviceEvent, result_id_operand), | |
| 597 | .reserve_id => try types.emit(self.gpa, .OpTypeReserveId, result_id_operand), | |
| 598 | .queue => try types.emit(self.gpa, .OpTypeQueue, result_id_operand), | |
| 599 | .pipe => try types.emit(self.gpa, .OpTypePipe, .{ | |
| 600 | .id_result = result_id, | |
| 601 | .qualifier = ty.payload(.pipe).qualifier, | |
| 602 | }), | |
| 603 | .pipe_storage => try types.emit(self.gpa, .OpTypePipeStorage, result_id_operand), | |
| 604 | .named_barrier => try types.emit(self.gpa, .OpTypeNamedBarrier, result_id_operand), | |
| 605 | } | |
| 606 | ||
| 607 | return result_id; | |
| 456 | } }); | |
| 608 | 457 | } |
| 609 | 458 | |
| 610 | fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void { | |
| 611 | const debug_names = &self.sections.debug_names; | |
| 612 | ||
| 613 | if (info.name.len != 0) { | |
| 614 | try debug_names.emit(self.gpa, .OpName, .{ | |
| 615 | .target = target, | |
| 616 | .name = info.name, | |
| 617 | }); | |
| 618 | } | |
| 619 | ||
| 620 | // Decorations for the struct type itself. | |
| 621 | if (info.decorations.block) | |
| 622 | try self.decorate(target, .Block); | |
| 623 | if (info.decorations.buffer_block) | |
| 624 | try self.decorate(target, .BufferBlock); | |
| 625 | if (info.decorations.glsl_shared) | |
| 626 | try self.decorate(target, .GLSLShared); | |
| 627 | if (info.decorations.glsl_packed) | |
| 628 | try self.decorate(target, .GLSLPacked); | |
| 629 | if (info.decorations.c_packed) | |
| 630 | try self.decorate(target, .CPacked); | |
| 631 | ||
| 632 | // Decorations for the struct members. | |
| 633 | const extra = info.member_decoration_extra; | |
| 634 | var extra_i: u32 = 0; | |
| 635 | for (info.members, 0..) |member, i| { | |
| 636 | const d = member.decorations; | |
| 637 | const index = @intCast(Word, i); | |
| 638 | ||
| 639 | if (member.name.len != 0) { | |
| 640 | try debug_names.emit(self.gpa, .OpMemberName, .{ | |
| 641 | .type = target, | |
| 642 | .member = index, | |
| 643 | .name = member.name, | |
| 644 | }); | |
| 645 | } | |
| 646 | ||
| 647 | switch (member.offset) { | |
| 648 | .none => {}, | |
| 649 | else => try self.decorateMember( | |
| 650 | target, | |
| 651 | index, | |
| 652 | .{ .Offset = .{ .byte_offset = @enumToInt(member.offset) } }, | |
| 653 | ), | |
| 654 | } | |
| 655 | ||
| 656 | switch (d.matrix_layout) { | |
| 657 | .row_major => try self.decorateMember(target, index, .RowMajor), | |
| 658 | .col_major => try self.decorateMember(target, index, .ColMajor), | |
| 659 | .none => {}, | |
| 660 | } | |
| 661 | if (d.matrix_layout != .none) { | |
| 662 | try self.decorateMember(target, index, .{ | |
| 663 | .MatrixStride = .{ .matrix_stride = extra[extra_i] }, | |
| 664 | }); | |
| 665 | extra_i += 1; | |
| 666 | } | |
| 667 | ||
| 668 | if (d.no_perspective) | |
| 669 | try self.decorateMember(target, index, .NoPerspective); | |
| 670 | if (d.flat) | |
| 671 | try self.decorateMember(target, index, .Flat); | |
| 672 | if (d.patch) | |
| 673 | try self.decorateMember(target, index, .Patch); | |
| 674 | if (d.centroid) | |
| 675 | try self.decorateMember(target, index, .Centroid); | |
| 676 | if (d.sample) | |
| 677 | try self.decorateMember(target, index, .Sample); | |
| 678 | if (d.invariant) | |
| 679 | try self.decorateMember(target, index, .Invariant); | |
| 680 | if (d.@"volatile") | |
| 681 | try self.decorateMember(target, index, .Volatile); | |
| 682 | if (d.coherent) | |
| 683 | try self.decorateMember(target, index, .Coherent); | |
| 684 | if (d.non_writable) | |
| 685 | try self.decorateMember(target, index, .NonWritable); | |
| 686 | if (d.non_readable) | |
| 687 | try self.decorateMember(target, index, .NonReadable); | |
| 688 | ||
| 689 | if (d.builtin) { | |
| 690 | try self.decorateMember(target, index, .{ | |
| 691 | .BuiltIn = .{ .built_in = @intToEnum(spec.BuiltIn, extra[extra_i]) }, | |
| 692 | }); | |
| 693 | extra_i += 1; | |
| 694 | } | |
| 695 | if (d.stream) { | |
| 696 | try self.decorateMember(target, index, .{ | |
| 697 | .Stream = .{ .stream_number = extra[extra_i] }, | |
| 698 | }); | |
| 699 | extra_i += 1; | |
| 700 | } | |
| 701 | if (d.location) { | |
| 702 | try self.decorateMember(target, index, .{ | |
| 703 | .Location = .{ .location = extra[extra_i] }, | |
| 704 | }); | |
| 705 | extra_i += 1; | |
| 706 | } | |
| 707 | if (d.component) { | |
| 708 | try self.decorateMember(target, index, .{ | |
| 709 | .Component = .{ .component = extra[extra_i] }, | |
| 710 | }); | |
| 711 | extra_i += 1; | |
| 712 | } | |
| 713 | if (d.xfb_buffer) { | |
| 714 | try self.decorateMember(target, index, .{ | |
| 715 | .XfbBuffer = .{ .xfb_buffer_number = extra[extra_i] }, | |
| 716 | }); | |
| 717 | extra_i += 1; | |
| 718 | } | |
| 719 | if (d.xfb_stride) { | |
| 720 | try self.decorateMember(target, index, .{ | |
| 721 | .XfbStride = .{ .xfb_stride = extra[extra_i] }, | |
| 722 | }); | |
| 723 | extra_i += 1; | |
| 724 | } | |
| 725 | if (d.user_semantic) { | |
| 726 | const len = extra[extra_i]; | |
| 727 | extra_i += 1; | |
| 728 | const semantic = @ptrCast([*]const u8, &extra[extra_i])[0..len]; | |
| 729 | try self.decorateMember(target, index, .{ | |
| 730 | .UserSemantic = .{ .semantic = semantic }, | |
| 731 | }); | |
| 732 | extra_i += std.math.divCeil(u32, extra_i, @sizeOf(u32)) catch unreachable; | |
| 733 | } | |
| 734 | } | |
| 735 | } | |
| 736 | ||
| 737 | pub fn simpleStructType(self: *Module, members: []const Type.Payload.Struct.Member) !Type.Ref { | |
| 738 | const payload = try self.arena.create(Type.Payload.Struct); | |
| 739 | payload.* = .{ | |
| 740 | .members = try self.arena.dupe(Type.Payload.Struct.Member, members), | |
| 741 | .decorations = .{}, | |
| 742 | }; | |
| 743 | return try self.resolveType(Type.initPayload(&payload.base)); | |
| 744 | } | |
| 745 | ||
| 746 | pub fn arrayType(self: *Module, len: u32, ty: Type.Ref) !Type.Ref { | |
| 747 | const payload = try self.arena.create(Type.Payload.Array); | |
| 748 | payload.* = .{ | |
| 749 | .element_type = ty, | |
| 750 | .length = len, | |
| 751 | }; | |
| 752 | return try self.resolveType(Type.initPayload(&payload.base)); | |
| 459 | pub fn constUndef(self: *Module, ty_ref: CacheRef) !IdRef { | |
| 460 | return try self.resolveId(.{ .undef = .{ .ty = ty_ref } }); | |
| 753 | 461 | } |
| 754 | 462 | |
| 755 | pub fn ptrType( | |
| 756 | self: *Module, | |
| 757 | child: Type.Ref, | |
| 758 | storage_class: spec.StorageClass, | |
| 759 | alignment: u32, | |
| 760 | ) !Type.Ref { | |
| 761 | const ptr_payload = try self.arena.create(Type.Payload.Pointer); | |
| 762 | ptr_payload.* = .{ | |
| 763 | .storage_class = storage_class, | |
| 764 | .child_type = child, | |
| 765 | .alignment = alignment, | |
| 766 | }; | |
| 767 | return try self.resolveType(Type.initPayload(&ptr_payload.base)); | |
| 463 | pub fn constNull(self: *Module, ty_ref: CacheRef) !IdRef { | |
| 464 | return try self.resolveId(.{ .null = .{ .ty = ty_ref } }); | |
| 768 | 465 | } |
| 769 | 466 | |
| 770 | pub fn changePtrStorageClass(self: *Module, ptr_ty_ref: Type.Ref, new_storage_class: spec.StorageClass) !Type.Ref { | |
| 771 | const payload = try self.arena.create(Type.Payload.Pointer); | |
| 772 | payload.* = self.typeRefType(ptr_ty_ref).payload(.pointer).*; | |
| 773 | payload.storage_class = new_storage_class; | |
| 774 | return try self.resolveType(Type.initPayload(&payload.base)); | |
| 467 | pub fn constBool(self: *Module, ty_ref: CacheRef, value: bool) !IdRef { | |
| 468 | return try self.resolveId(.{ .bool = .{ .ty = ty_ref, .value = value } }); | |
| 775 | 469 | } |
| 776 | 470 | |
| 777 | pub fn constComposite(self: *Module, ty_ref: Type.Ref, members: []const IdRef) !IdRef { | |
| 471 | pub fn constComposite(self: *Module, ty_ref: CacheRef, members: []const IdRef) !IdRef { | |
| 778 | 472 | const result_id = self.allocId(); |
| 779 | 473 | try self.sections.types_globals_constants.emit(self.gpa, .OpSpecConstantComposite, .{ |
| 780 | .id_result_type = self.typeId(ty_ref), | |
| 474 | .id_result_type = self.resultId(ty_ref), | |
| 781 | 475 | .id_result = result_id, |
| 782 | 476 | .constituents = members, |
| 783 | 477 | }); |
| 784 | 478 | return result_id; |
| 785 | 479 | } |
| 786 | 480 | |
| 787 | pub fn emitConstant( | |
| 788 | self: *Module, | |
| 789 | ty_id: IdRef, | |
| 790 | result_id: IdRef, | |
| 791 | value: spec.LiteralContextDependentNumber, | |
| 792 | ) !void { | |
| 793 | try self.sections.types_globals_constants.emit(self.gpa, .OpConstant, .{ | |
| 794 | .id_result_type = ty_id, | |
| 795 | .id_result = result_id, | |
| 796 | .value = value, | |
| 797 | }); | |
| 798 | } | |
| 799 | ||
| 800 | 481 | /// Decorate a result-id. |
| 801 | 482 | pub fn decorate( |
| 802 | 483 | self: *Module, |
| ... | ... | @@ -883,3 +564,22 @@ pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8 |
| 883 | 564 | .name = try self.arena.dupe(u8, name), |
| 884 | 565 | }); |
| 885 | 566 | } |
| 567 | ||
| 568 | pub fn debugName(self: *Module, target: IdResult, comptime fmt: []const u8, args: anytype) !void { | |
| 569 | const name = try std.fmt.allocPrint(self.gpa, fmt, args); | |
| 570 | defer self.gpa.free(name); | |
| 571 | try self.sections.debug_names.emit(self.gpa, .OpName, .{ | |
| 572 | .target = target, | |
| 573 | .name = name, | |
| 574 | }); | |
| 575 | } | |
| 576 | ||
| 577 | pub fn memberDebugName(self: *Module, target: IdResult, member: u32, comptime fmt: []const u8, args: anytype) !void { | |
| 578 | const name = try std.fmt.allocPrint(self.gpa, fmt, args); | |
| 579 | defer self.gpa.free(name); | |
| 580 | try self.sections.debug_names.emit(self.gpa, .OpMemberName, .{ | |
| 581 | .type = target, | |
| 582 | .member = member, | |
| 583 | .name = name, | |
| 584 | }); | |
| 585 | } |
src/codegen/spirv/type.zig deleted-567| ... | ... | @@ -1,567 +0,0 @@ |
| 1 | //! This module models a SPIR-V Type. These are distinct from Zig types, with some types | |
| 2 | //! which are not representable by Zig directly. | |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | const assert = std.debug.assert; | |
| 6 | const Signedness = std.builtin.Signedness; | |
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | ||
| 9 | const spec = @import("spec.zig"); | |
| 10 | ||
| 11 | pub const Type = extern union { | |
| 12 | tag_if_small_enough: Tag, | |
| 13 | ptr_otherwise: *Payload, | |
| 14 | ||
| 15 | /// A reference to another SPIR-V type. | |
| 16 | pub const Ref = enum(u32) { _ }; | |
| 17 | ||
| 18 | pub fn initTag(comptime small_tag: Tag) Type { | |
| 19 | comptime assert(@enumToInt(small_tag) < Tag.no_payload_count); | |
| 20 | return .{ .tag_if_small_enough = small_tag }; | |
| 21 | } | |
| 22 | ||
| 23 | pub fn initPayload(pl: *Payload) Type { | |
| 24 | assert(@enumToInt(pl.tag) >= Tag.no_payload_count); | |
| 25 | return .{ .ptr_otherwise = pl }; | |
| 26 | } | |
| 27 | ||
| 28 | pub fn int(arena: Allocator, signedness: Signedness, bits: u16) !Type { | |
| 29 | const bits_and_signedness = switch (signedness) { | |
| 30 | .signed => -@as(i32, bits), | |
| 31 | .unsigned => @as(i32, bits), | |
| 32 | }; | |
| 33 | ||
| 34 | return switch (bits_and_signedness) { | |
| 35 | 8 => initTag(.u8), | |
| 36 | 16 => initTag(.u16), | |
| 37 | 32 => initTag(.u32), | |
| 38 | 64 => initTag(.u64), | |
| 39 | -8 => initTag(.i8), | |
| 40 | -16 => initTag(.i16), | |
| 41 | -32 => initTag(.i32), | |
| 42 | -64 => initTag(.i64), | |
| 43 | else => { | |
| 44 | const int_payload = try arena.create(Payload.Int); | |
| 45 | int_payload.* = .{ | |
| 46 | .width = bits, | |
| 47 | .signedness = signedness, | |
| 48 | }; | |
| 49 | return initPayload(&int_payload.base); | |
| 50 | }, | |
| 51 | }; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn float(bits: u16) Type { | |
| 55 | return switch (bits) { | |
| 56 | 16 => initTag(.f16), | |
| 57 | 32 => initTag(.f32), | |
| 58 | 64 => initTag(.f64), | |
| 59 | else => unreachable, // Enable more types if required. | |
| 60 | }; | |
| 61 | } | |
| 62 | ||
| 63 | pub fn tag(self: Type) Tag { | |
| 64 | if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count) { | |
| 65 | return self.tag_if_small_enough; | |
| 66 | } else { | |
| 67 | return self.ptr_otherwise.tag; | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() { | |
| 72 | if (@enumToInt(self.tag_if_small_enough) < Tag.no_payload_count) | |
| 73 | return null; | |
| 74 | ||
| 75 | if (self.ptr_otherwise.tag == t) | |
| 76 | return self.payload(t); | |
| 77 | ||
| 78 | return null; | |
| 79 | } | |
| 80 | ||
| 81 | /// Access the payload of a type directly. | |
| 82 | pub fn payload(self: Type, comptime t: Tag) *t.Type() { | |
| 83 | assert(self.tag() == t); | |
| 84 | return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise); | |
| 85 | } | |
| 86 | ||
| 87 | /// Perform a shallow equality test, comparing two types while assuming that any child types | |
| 88 | /// are equal only if their references are equal. | |
| 89 | pub fn eqlShallow(a: Type, b: Type) bool { | |
| 90 | if (a.tag_if_small_enough == b.tag_if_small_enough) | |
| 91 | return true; | |
| 92 | ||
| 93 | const tag_a = a.tag(); | |
| 94 | const tag_b = b.tag(); | |
| 95 | if (tag_a != tag_b) | |
| 96 | return false; | |
| 97 | ||
| 98 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 99 | const t = @field(Tag, field.name); | |
| 100 | if (t == tag_a) { | |
| 101 | return eqlPayloads(t, a, b); | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | unreachable; | |
| 106 | } | |
| 107 | ||
| 108 | /// Compare the payload of two compatible tags, given that we already know the tag of both types. | |
| 109 | fn eqlPayloads(comptime t: Tag, a: Type, b: Type) bool { | |
| 110 | switch (t) { | |
| 111 | .void, | |
| 112 | .bool, | |
| 113 | .sampler, | |
| 114 | .event, | |
| 115 | .device_event, | |
| 116 | .reserve_id, | |
| 117 | .queue, | |
| 118 | .pipe_storage, | |
| 119 | .named_barrier, | |
| 120 | .u8, | |
| 121 | .u16, | |
| 122 | .u32, | |
| 123 | .u64, | |
| 124 | .i8, | |
| 125 | .i16, | |
| 126 | .i32, | |
| 127 | .i64, | |
| 128 | .f16, | |
| 129 | .f32, | |
| 130 | .f64, | |
| 131 | => return true, | |
| 132 | .int, | |
| 133 | .vector, | |
| 134 | .matrix, | |
| 135 | .sampled_image, | |
| 136 | .array, | |
| 137 | .runtime_array, | |
| 138 | .@"opaque", | |
| 139 | .pointer, | |
| 140 | .pipe, | |
| 141 | .image, | |
| 142 | => return std.meta.eql(a.payload(t).*, b.payload(t).*), | |
| 143 | .@"struct" => { | |
| 144 | const struct_a = a.payload(.@"struct"); | |
| 145 | const struct_b = b.payload(.@"struct"); | |
| 146 | if (struct_a.members.len != struct_b.members.len) | |
| 147 | return false; | |
| 148 | for (struct_a.members, 0..) |mem_a, i| { | |
| 149 | if (!std.meta.eql(mem_a, struct_b.members[i])) | |
| 150 | return false; | |
| 151 | } | |
| 152 | return true; | |
| 153 | }, | |
| 154 | .function => { | |
| 155 | const fn_a = a.payload(.function); | |
| 156 | const fn_b = b.payload(.function); | |
| 157 | if (fn_a.return_type != fn_b.return_type) | |
| 158 | return false; | |
| 159 | return std.mem.eql(Ref, fn_a.parameters, fn_b.parameters); | |
| 160 | }, | |
| 161 | } | |
| 162 | } | |
| 163 | ||
| 164 | /// Perform a shallow hash, which hashes the reference value of child types instead of recursing. | |
| 165 | pub fn hashShallow(self: Type) u64 { | |
| 166 | var hasher = std.hash.Wyhash.init(0); | |
| 167 | const t = self.tag(); | |
| 168 | std.hash.autoHash(&hasher, t); | |
| 169 | ||
| 170 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 171 | if (@field(Tag, field.name) == t) { | |
| 172 | switch (@field(Tag, field.name)) { | |
| 173 | .void, | |
| 174 | .bool, | |
| 175 | .sampler, | |
| 176 | .event, | |
| 177 | .device_event, | |
| 178 | .reserve_id, | |
| 179 | .queue, | |
| 180 | .pipe_storage, | |
| 181 | .named_barrier, | |
| 182 | .u8, | |
| 183 | .u16, | |
| 184 | .u32, | |
| 185 | .u64, | |
| 186 | .i8, | |
| 187 | .i16, | |
| 188 | .i32, | |
| 189 | .i64, | |
| 190 | .f16, | |
| 191 | .f32, | |
| 192 | .f64, | |
| 193 | => {}, | |
| 194 | else => self.hashPayload(@field(Tag, field.name), &hasher), | |
| 195 | } | |
| 196 | } | |
| 197 | } | |
| 198 | ||
| 199 | return hasher.final(); | |
| 200 | } | |
| 201 | ||
| 202 | /// Perform a shallow hash, given that we know the tag of the field ahead of time. | |
| 203 | fn hashPayload(self: Type, comptime t: Tag, hasher: *std.hash.Wyhash) void { | |
| 204 | const fields = @typeInfo(t.Type()).Struct.fields; | |
| 205 | const pl = self.payload(t); | |
| 206 | comptime assert(std.mem.eql(u8, fields[0].name, "base")); | |
| 207 | inline for (fields[1..]) |field| { // Skip the 'base' field. | |
| 208 | std.hash.autoHashStrat(hasher, @field(pl, field.name), .DeepRecursive); | |
| 209 | } | |
| 210 | } | |
| 211 | ||
| 212 | /// Hash context that hashes and compares types in a shallow fashion, useful for type caches. | |
| 213 | pub const ShallowHashContext32 = struct { | |
| 214 | pub fn hash(self: @This(), t: Type) u32 { | |
| 215 | _ = self; | |
| 216 | return @truncate(u32, t.hashShallow()); | |
| 217 | } | |
| 218 | pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool { | |
| 219 | _ = self; | |
| 220 | _ = b_index; | |
| 221 | return a.eqlShallow(b); | |
| 222 | } | |
| 223 | }; | |
| 224 | ||
| 225 | /// Return the reference to any child type. Asserts the type is one of: | |
| 226 | /// - Vectors | |
| 227 | /// - Matrices | |
| 228 | /// - Images | |
| 229 | /// - SampledImages, | |
| 230 | /// - Arrays | |
| 231 | /// - RuntimeArrays | |
| 232 | /// - Pointers | |
| 233 | pub fn childType(self: Type) Ref { | |
| 234 | return switch (self.tag()) { | |
| 235 | .vector => self.payload(.vector).component_type, | |
| 236 | .matrix => self.payload(.matrix).column_type, | |
| 237 | .image => self.payload(.image).sampled_type, | |
| 238 | .sampled_image => self.payload(.sampled_image).image_type, | |
| 239 | .array => self.payload(.array).element_type, | |
| 240 | .runtime_array => self.payload(.runtime_array).element_type, | |
| 241 | .pointer => self.payload(.pointer).child_type, | |
| 242 | else => unreachable, | |
| 243 | }; | |
| 244 | } | |
| 245 | ||
| 246 | pub fn isInt(self: Type) bool { | |
| 247 | return switch (self.tag()) { | |
| 248 | .u8, | |
| 249 | .u16, | |
| 250 | .u32, | |
| 251 | .u64, | |
| 252 | .i8, | |
| 253 | .i16, | |
| 254 | .i32, | |
| 255 | .i64, | |
| 256 | .int, | |
| 257 | => true, | |
| 258 | else => false, | |
| 259 | }; | |
| 260 | } | |
| 261 | ||
| 262 | pub fn isFloat(self: Type) bool { | |
| 263 | return switch (self.tag()) { | |
| 264 | .f16, .f32, .f64 => true, | |
| 265 | else => false, | |
| 266 | }; | |
| 267 | } | |
| 268 | ||
| 269 | /// Returns the number of bits that make up an int or float type. | |
| 270 | /// Asserts type is either int or float. | |
| 271 | pub fn intFloatBits(self: Type) u16 { | |
| 272 | return switch (self.tag()) { | |
| 273 | .u8, .i8 => 8, | |
| 274 | .u16, .i16, .f16 => 16, | |
| 275 | .u32, .i32, .f32 => 32, | |
| 276 | .u64, .i64, .f64 => 64, | |
| 277 | .int => self.payload(.int).width, | |
| 278 | else => unreachable, | |
| 279 | }; | |
| 280 | } | |
| 281 | ||
| 282 | /// Returns the signedness of an integer type. | |
| 283 | /// Asserts that the type is an int. | |
| 284 | pub fn intSignedness(self: Type) Signedness { | |
| 285 | return switch (self.tag()) { | |
| 286 | .u8, .u16, .u32, .u64 => .unsigned, | |
| 287 | .i8, .i16, .i32, .i64 => .signed, | |
| 288 | .int => self.payload(.int).signedness, | |
| 289 | else => unreachable, | |
| 290 | }; | |
| 291 | } | |
| 292 | ||
| 293 | pub const Tag = enum(usize) { | |
| 294 | void, | |
| 295 | bool, | |
| 296 | sampler, | |
| 297 | event, | |
| 298 | device_event, | |
| 299 | reserve_id, | |
| 300 | queue, | |
| 301 | pipe_storage, | |
| 302 | named_barrier, | |
| 303 | u8, | |
| 304 | u16, | |
| 305 | u32, | |
| 306 | u64, | |
| 307 | i8, | |
| 308 | i16, | |
| 309 | i32, | |
| 310 | i64, | |
| 311 | f16, | |
| 312 | f32, | |
| 313 | f64, | |
| 314 | ||
| 315 | // After this, the tag requires a payload. | |
| 316 | int, | |
| 317 | vector, | |
| 318 | matrix, | |
| 319 | image, | |
| 320 | sampled_image, | |
| 321 | array, | |
| 322 | runtime_array, | |
| 323 | @"struct", | |
| 324 | @"opaque", | |
| 325 | pointer, | |
| 326 | function, | |
| 327 | pipe, | |
| 328 | ||
| 329 | pub const last_no_payload_tag = Tag.f64; | |
| 330 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | |
| 331 | ||
| 332 | pub fn Type(comptime t: Tag) type { | |
| 333 | return switch (t) { | |
| 334 | .void, | |
| 335 | .bool, | |
| 336 | .sampler, | |
| 337 | .event, | |
| 338 | .device_event, | |
| 339 | .reserve_id, | |
| 340 | .queue, | |
| 341 | .pipe_storage, | |
| 342 | .named_barrier, | |
| 343 | .u8, | |
| 344 | .u16, | |
| 345 | .u32, | |
| 346 | .u64, | |
| 347 | .i8, | |
| 348 | .i16, | |
| 349 | .i32, | |
| 350 | .i64, | |
| 351 | .f16, | |
| 352 | .f32, | |
| 353 | .f64, | |
| 354 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), | |
| 355 | .int => Payload.Int, | |
| 356 | .vector => Payload.Vector, | |
| 357 | .matrix => Payload.Matrix, | |
| 358 | .image => Payload.Image, | |
| 359 | .sampled_image => Payload.SampledImage, | |
| 360 | .array => Payload.Array, | |
| 361 | .runtime_array => Payload.RuntimeArray, | |
| 362 | .@"struct" => Payload.Struct, | |
| 363 | .@"opaque" => Payload.Opaque, | |
| 364 | .pointer => Payload.Pointer, | |
| 365 | .function => Payload.Function, | |
| 366 | .pipe => Payload.Pipe, | |
| 367 | }; | |
| 368 | } | |
| 369 | }; | |
| 370 | ||
| 371 | pub const Payload = struct { | |
| 372 | tag: Tag, | |
| 373 | ||
| 374 | pub const Int = struct { | |
| 375 | base: Payload = .{ .tag = .int }, | |
| 376 | width: u16, | |
| 377 | signedness: Signedness, | |
| 378 | }; | |
| 379 | ||
| 380 | pub const Vector = struct { | |
| 381 | base: Payload = .{ .tag = .vector }, | |
| 382 | component_type: Ref, | |
| 383 | component_count: u32, | |
| 384 | }; | |
| 385 | ||
| 386 | pub const Matrix = struct { | |
| 387 | base: Payload = .{ .tag = .matrix }, | |
| 388 | column_type: Ref, | |
| 389 | column_count: u32, | |
| 390 | }; | |
| 391 | ||
| 392 | pub const Image = struct { | |
| 393 | base: Payload = .{ .tag = .image }, | |
| 394 | sampled_type: Ref, | |
| 395 | dim: spec.Dim, | |
| 396 | depth: enum(u2) { | |
| 397 | no = 0, | |
| 398 | yes = 1, | |
| 399 | maybe = 2, | |
| 400 | }, | |
| 401 | arrayed: bool, | |
| 402 | multisampled: bool, | |
| 403 | sampled: enum(u2) { | |
| 404 | known_at_runtime = 0, | |
| 405 | with_sampler = 1, | |
| 406 | without_sampler = 2, | |
| 407 | }, | |
| 408 | format: spec.ImageFormat, | |
| 409 | access_qualifier: ?spec.AccessQualifier, | |
| 410 | }; | |
| 411 | ||
| 412 | pub const SampledImage = struct { | |
| 413 | base: Payload = .{ .tag = .sampled_image }, | |
| 414 | image_type: Ref, | |
| 415 | }; | |
| 416 | ||
| 417 | pub const Array = struct { | |
| 418 | base: Payload = .{ .tag = .array }, | |
| 419 | element_type: Ref, | |
| 420 | /// Note: Must be emitted as constant, not as literal! | |
| 421 | length: u32, | |
| 422 | /// Type has the 'ArrayStride' decoration. | |
| 423 | /// If zero, no stride is present. | |
| 424 | array_stride: u32 = 0, | |
| 425 | }; | |
| 426 | ||
| 427 | pub const RuntimeArray = struct { | |
| 428 | base: Payload = .{ .tag = .runtime_array }, | |
| 429 | element_type: Ref, | |
| 430 | /// Type has the 'ArrayStride' decoration. | |
| 431 | /// If zero, no stride is present. | |
| 432 | array_stride: u32 = 0, | |
| 433 | }; | |
| 434 | ||
| 435 | pub const Struct = struct { | |
| 436 | base: Payload = .{ .tag = .@"struct" }, | |
| 437 | members: []Member, | |
| 438 | name: []const u8 = "", | |
| 439 | decorations: StructDecorations = .{}, | |
| 440 | ||
| 441 | /// Extra information for decorations, packed for efficiency. Fields are stored sequentially by | |
| 442 | /// order of the `members` slice and `MemberDecorations` struct. | |
| 443 | member_decoration_extra: []u32 = &.{}, | |
| 444 | ||
| 445 | pub const Member = struct { | |
| 446 | ty: Ref, | |
| 447 | name: []const u8 = "", | |
| 448 | offset: MemberOffset = .none, | |
| 449 | decorations: MemberDecorations = .{}, | |
| 450 | }; | |
| 451 | ||
| 452 | pub const MemberOffset = enum(u32) { none = 0xFFFF_FFFF, _ }; | |
| 453 | ||
| 454 | pub const StructDecorations = packed struct { | |
| 455 | /// Type has the 'Block' decoration. | |
| 456 | block: bool = false, | |
| 457 | /// Type has the 'BufferBlock' decoration. | |
| 458 | buffer_block: bool = false, | |
| 459 | /// Type has the 'GLSLShared' decoration. | |
| 460 | glsl_shared: bool = false, | |
| 461 | /// Type has the 'GLSLPacked' decoration. | |
| 462 | glsl_packed: bool = false, | |
| 463 | /// Type has the 'CPacked' decoration. | |
| 464 | c_packed: bool = false, | |
| 465 | }; | |
| 466 | ||
| 467 | pub const MemberDecorations = packed struct { | |
| 468 | /// Matrix layout for (arrays of) matrices. If this field is not .none, | |
| 469 | /// then there is also an extra field containing the matrix stride corresponding | |
| 470 | /// to the 'MatrixStride' decoration. | |
| 471 | matrix_layout: enum(u2) { | |
| 472 | /// Member has the 'RowMajor' decoration. The member type | |
| 473 | /// must be a matrix or an array of matrices. | |
| 474 | row_major, | |
| 475 | /// Member has the 'ColMajor' decoration. The member type | |
| 476 | /// must be a matrix or an array of matrices. | |
| 477 | col_major, | |
| 478 | /// Member is not a matrix or array of matrices. | |
| 479 | none, | |
| 480 | } = .none, | |
| 481 | ||
| 482 | // Regular decorations, these do not imply extra fields. | |
| 483 | ||
| 484 | /// Member has the 'NoPerspective' decoration. | |
| 485 | no_perspective: bool = false, | |
| 486 | /// Member has the 'Flat' decoration. | |
| 487 | flat: bool = false, | |
| 488 | /// Member has the 'Patch' decoration. | |
| 489 | patch: bool = false, | |
| 490 | /// Member has the 'Centroid' decoration. | |
| 491 | centroid: bool = false, | |
| 492 | /// Member has the 'Sample' decoration. | |
| 493 | sample: bool = false, | |
| 494 | /// Member has the 'Invariant' decoration. | |
| 495 | /// Note: requires parent struct to have 'Block'. | |
| 496 | invariant: bool = false, | |
| 497 | /// Member has the 'Volatile' decoration. | |
| 498 | @"volatile": bool = false, | |
| 499 | /// Member has the 'Coherent' decoration. | |
| 500 | coherent: bool = false, | |
| 501 | /// Member has the 'NonWritable' decoration. | |
| 502 | non_writable: bool = false, | |
| 503 | /// Member has the 'NonReadable' decoration. | |
| 504 | non_readable: bool = false, | |
| 505 | ||
| 506 | // The following decorations all imply extra field(s). | |
| 507 | ||
| 508 | /// Member has the 'BuiltIn' decoration. | |
| 509 | /// This decoration has an extra field of type `spec.BuiltIn`. | |
| 510 | /// Note: If any member of a struct has the BuiltIn decoration, all members must have one. | |
| 511 | /// Note: Each builtin may only be reachable once for a particular entry point. | |
| 512 | /// Note: The member type may be constrained by a particular built-in, defined in the client API specification. | |
| 513 | builtin: bool = false, | |
| 514 | /// Member has the 'Stream' decoration. | |
| 515 | /// This member has an extra field of type `u32`. | |
| 516 | stream: bool = false, | |
| 517 | /// Member has the 'Location' decoration. | |
| 518 | /// This member has an extra field of type `u32`. | |
| 519 | location: bool = false, | |
| 520 | /// Member has the 'Component' decoration. | |
| 521 | /// This member has an extra field of type `u32`. | |
| 522 | component: bool = false, | |
| 523 | /// Member has the 'XfbBuffer' decoration. | |
| 524 | /// This member has an extra field of type `u32`. | |
| 525 | xfb_buffer: bool = false, | |
| 526 | /// Member has the 'XfbStride' decoration. | |
| 527 | /// This member has an extra field of type `u32`. | |
| 528 | xfb_stride: bool = false, | |
| 529 | /// Member has the 'UserSemantic' decoration. | |
| 530 | /// This member has an extra field of type `[]u8`, which is encoded | |
| 531 | /// by an `u32` containing the number of chars exactly, and then the string padded to | |
| 532 | /// a multiple of 4 bytes with zeroes. | |
| 533 | user_semantic: bool = false, | |
| 534 | }; | |
| 535 | }; | |
| 536 | ||
| 537 | pub const Opaque = struct { | |
| 538 | base: Payload = .{ .tag = .@"opaque" }, | |
| 539 | name: []u8, | |
| 540 | }; | |
| 541 | ||
| 542 | pub const Pointer = struct { | |
| 543 | base: Payload = .{ .tag = .pointer }, | |
| 544 | storage_class: spec.StorageClass, | |
| 545 | child_type: Ref, | |
| 546 | /// Type has the 'ArrayStride' decoration. | |
| 547 | /// This is valid for pointers to elements of an array. | |
| 548 | /// If zero, no stride is present. | |
| 549 | array_stride: u32 = 0, | |
| 550 | /// If nonzero, type has the 'Alignment' decoration. | |
| 551 | alignment: u32 = 0, | |
| 552 | /// Type has the 'MaxByteOffset' decoration. | |
| 553 | max_byte_offset: ?u32 = null, | |
| 554 | }; | |
| 555 | ||
| 556 | pub const Function = struct { | |
| 557 | base: Payload = .{ .tag = .function }, | |
| 558 | return_type: Ref, | |
| 559 | parameters: []Ref, | |
| 560 | }; | |
| 561 | ||
| 562 | pub const Pipe = struct { | |
| 563 | base: Payload = .{ .tag = .pipe }, | |
| 564 | qualifier: spec.AccessQualifier, | |
| 565 | }; | |
| 566 | }; | |
| 567 | }; |