| author | |
| committer | |
| log | 0c41945a01a3a5254e8b4266a0677f2d8224aa1a |
| tree | 5d096152fa91ad2990c1563c8d783a8dcedd3849 |
| parent | 0552a8b11f973fc9621971e2130c25ad3a4af0ad |
| signature |
3 files changed, 1047 insertions(+), 1047 deletions(-)
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+1-1| ... | @@ -21,7 +21,7 @@ const IdResultType = spec.IdResultType; | ... | @@ -21,7 +21,7 @@ const IdResultType = spec.IdResultType; |
| 21 | 21 | ||
| 22 | const Section = @import("Section.zig"); | 22 | const Section = @import("Section.zig"); |
| 23 | 23 | ||
| 24 | const Cache = @import("TypeConstantCache.zig"); | 24 | const Cache = @import("Cache.zig"); |
| 25 | pub const CacheKey = Cache.Key; | 25 | pub const CacheKey = Cache.Key; |
| 26 | pub const CacheRef = Cache.Ref; | 26 | pub const CacheRef = Cache.Ref; |
| 27 | pub const CacheString = Cache.String; | 27 | pub const CacheString = Cache.String; |
src/codegen/spirv/TypeConstantCache.zig deleted-1046| ... | @@ -1,1046 +0,0 @@ | ||
| 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 | } | ||