authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-04-06 02:41:56+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-04-06 13:37:40+02:00
logef638502d472e3c3430044c6ed908f30f17d2796
tree28a7ca9677f722b52f465477f8200341b648feda
parent97a67762ba1fcc363656a59af10a3031332cbd62
signaturebadge-check Signed by SSH key SHA256:ZS52FNyUv2WUXvO4njmVaFVO46RHojFuOrxRc4LuKzg

spirv: remove cache usage from assembler


1 files changed, 79 insertions(+), 64 deletions(-)

src/codegen/spirv/Assembler.zig+79-64
...@@ -9,10 +9,9 @@ const Opcode = spec.Opcode;...@@ -9,10 +9,9 @@ const Opcode = spec.Opcode;
9const Word = spec.Word;9const Word = spec.Word;
10const IdRef = spec.IdRef;10const IdRef = spec.IdRef;
11const IdResult = spec.IdResult;11const IdResult = spec.IdResult;
12const StorageClass = spec.StorageClass;
1213
13const SpvModule = @import("Module.zig");14const SpvModule = @import("Module.zig");
14const CacheRef = SpvModule.CacheRef;
15const CacheKey = SpvModule.CacheKey;
1615
17/// Represents a token in the assembly template.16/// Represents a token in the assembly template.
18const Token = struct {17const Token = struct {
...@@ -127,16 +126,16 @@ const AsmValue = union(enum) {...@@ -127,16 +126,16 @@ const AsmValue = union(enum) {
127 value: IdRef,126 value: IdRef,
128127
129 /// This result-value represents a type registered into the module's type system.128 /// This result-value represents a type registered into the module's type system.
130 ty: CacheRef,129 ty: IdRef,
131130
132 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue131 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
133 /// is of a variant that allows the result to be obtained (not an unresolved132 /// is of a variant that allows the result to be obtained (not an unresolved
134 /// forward declaration, not in the process of being declared, etc).133 /// forward declaration, not in the process of being declared, etc).
135 pub fn resultId(self: AsmValue, spv: *const SpvModule) IdRef {134 pub fn resultId(self: AsmValue) IdRef {
136 return switch (self) {135 return switch (self) {
137 .just_declared, .unresolved_forward_reference => unreachable,136 .just_declared, .unresolved_forward_reference => unreachable,
138 .value => |result| result,137 .value => |result| result,
139 .ty => |ref| spv.resultId(ref),138 .ty => |result| result,
140 };139 };
141 }140 }
142};141};
...@@ -292,23 +291,23 @@ fn processInstruction(self: *Assembler) !void {...@@ -292,23 +291,23 @@ fn processInstruction(self: *Assembler) !void {
292/// refers to the result.291/// refers to the result.
293fn processTypeInstruction(self: *Assembler) !AsmValue {292fn processTypeInstruction(self: *Assembler) !AsmValue {
294 const operands = self.inst.operands.items;293 const operands = self.inst.operands.items;
295 const ref = switch (self.inst.opcode) {294 const section = &self.spv.sections.types_globals_constants;
296 .OpTypeVoid => try self.spv.resolve(.void_type),295 const id = switch (self.inst.opcode) {
297 .OpTypeBool => try self.spv.resolve(.bool_type),296 .OpTypeVoid => try self.spv.voidType(),
297 .OpTypeBool => try self.spv.boolType(),
298 .OpTypeInt => blk: {298 .OpTypeInt => blk: {
299 // const signedness: std.builtin.Signedness = switch (operands[2].literal32) {299 const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
300 // 0 => .unsigned,300 0 => .unsigned,
301 // 1 => .signed,301 1 => .signed,
302 // else => {302 else => {
303 // // TODO: Improve source location.303 // TODO: Improve source location.
304 // return self.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});304 return self.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
305 // },305 },
306 // };306 };
307 // const width = std.math.cast(u16, operands[1].literal32) orelse {307 const width = std.math.cast(u16, operands[1].literal32) orelse {
308 // return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});308 return self.fail(0, "int type of {} bits is too large", .{operands[1].literal32});
309 // };309 };
310 // break :blk try self.spv.intType(signedness, width);310 break :blk try self.spv.intType(signedness, width);
311 break :blk @as(CacheRef, @enumFromInt(0)); // TODO(robin): fix
312 },311 },
313 .OpTypeFloat => blk: {312 .OpTypeFloat => blk: {
314 const bits = operands[1].literal32;313 const bits = operands[1].literal32;
...@@ -318,43 +317,49 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {...@@ -318,43 +317,49 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
318 return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});317 return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});
319 },318 },
320 }319 }
321 break :blk try self.spv.resolve(.{ .float_type = .{ .bits = @intCast(bits) } });320 break :blk try self.spv.floatType(@intCast(bits));
321 },
322 .OpTypeVector => blk: {
323 const child_type = try self.resolveRefId(operands[1].ref_id);
324 break :blk try self.spv.vectorType(operands[2].literal32, child_type);
322 },325 },
323 .OpTypeVector => try self.spv.resolve(.{ .vector_type = .{
324 .component_type = try self.resolveTypeRef(operands[1].ref_id),
325 .component_count = operands[2].literal32,
326 } }),
327 .OpTypeArray => {326 .OpTypeArray => {
328 // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant),327 // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant),
329 // and so some consideration must be taken when entering this in the type system.328 // and so some consideration must be taken when entering this in the type system.
330 return self.todo("process OpTypeArray", .{});329 return self.todo("process OpTypeArray", .{});
331 },330 },
332 .OpTypePointer => blk: {331 .OpTypePointer => blk: {
333 break :blk try self.spv.resolve(.{332 const storage_class: StorageClass = @enumFromInt(operands[1].value);
334 .ptr_type = .{333 const child_type = try self.resolveRefId(operands[2].ref_id);
335 .storage_class = @enumFromInt(operands[1].value),334 const result_id = self.spv.allocId();
336 .child_type = try self.resolveTypeRef(operands[2].ref_id),335 try section.emit(self.spv.gpa, .OpTypePointer, .{
337 // TODO: This should be a proper reference resolved via OpTypeForwardPointer336 .id_result = result_id,
338 .fwd = @enumFromInt(std.math.maxInt(u32)),337 .storage_class = storage_class,
339 },338 .type = child_type,
340 });339 });
340 break :blk result_id;
341 },341 },
342 .OpTypeFunction => blk: {342 .OpTypeFunction => blk: {
343 const param_operands = operands[2..];343 const param_operands = operands[2..];
344 const param_types = try self.spv.gpa.alloc(CacheRef, param_operands.len);344 const return_type = try self.resolveRefId(operands[1].ref_id);
345
346 const param_types = try self.spv.gpa.alloc(IdRef, param_operands.len);
345 defer self.spv.gpa.free(param_types);347 defer self.spv.gpa.free(param_types);
346 for (param_types, 0..) |*param, i| {348 for (param_types, param_operands) |*param, operand| {
347 param.* = try self.resolveTypeRef(param_operands[i].ref_id);349 param.* = try self.resolveRefId(operand.ref_id);
348 }350 }
349 break :blk try self.spv.resolve(.{ .function_type = .{351 const result_id = self.spv.allocId();
350 .return_type = try self.resolveTypeRef(operands[1].ref_id),352 try section.emit(self.spv.gpa, .OpTypeFunction, .{
351 .parameters = param_types,353 .id_result = result_id,
352 } });354 .return_type = return_type,
355 .id_ref_2 = param_types,
356 });
357 break :blk result_id;
353 },358 },
354 else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}),359 else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}),
355 };360 };
356361
357 return AsmValue{ .ty = ref };362 return AsmValue{ .ty = id };
358}363}
359364
360/// Emit `self.inst` into `self.spv` and `self.func`, and return the AsmValue365/// Emit `self.inst` into `self.spv` and `self.func`, and return the AsmValue
...@@ -411,7 +416,7 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {...@@ -411,7 +416,7 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
411 .ref_id => |index| {416 .ref_id => |index| {
412 const result = try self.resolveRef(index);417 const result = try self.resolveRef(index);
413 try section.ensureUnusedCapacity(self.spv.gpa, 1);418 try section.ensureUnusedCapacity(self.spv.gpa, 1);
414 section.writeOperand(spec.IdRef, result.resultId(self.spv));419 section.writeOperand(spec.IdRef, result.resultId());
415 },420 },
416 .string => |offset| {421 .string => |offset| {
417 const text = std.mem.sliceTo(self.inst.string_bytes.items[offset..], 0);422 const text = std.mem.sliceTo(self.inst.string_bytes.items[offset..], 0);
...@@ -460,18 +465,9 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {...@@ -460,18 +465,9 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {
460 }465 }
461}466}
462467
463/// Resolve a value reference as type.468fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef {
464fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !CacheRef {
465 const value = try self.resolveRef(ref);469 const value = try self.resolveRef(ref);
466 switch (value) {470 return value.resultId();
467 .just_declared, .unresolved_forward_reference => unreachable,
468 .ty => |ty_ref| return ty_ref,
469 else => {
470 const name = self.value_map.keys()[ref];
471 // TODO: Improve source location.
472 return self.fail(0, "expected operand %{s} to refer to a type", .{name});
473 },
474 }
475}471}
476472
477/// Attempt to parse an instruction into `self.inst`.473/// Attempt to parse an instruction into `self.inst`.
...@@ -710,22 +706,41 @@ fn parseContextDependentNumber(self: *Assembler) !void {...@@ -710,22 +706,41 @@ fn parseContextDependentNumber(self: *Assembler) !void {
710 assert(self.inst.opcode == .OpConstant or self.inst.opcode == .OpSpecConstant);706 assert(self.inst.opcode == .OpConstant or self.inst.opcode == .OpSpecConstant);
711707
712 const tok = self.currentToken();708 const tok = self.currentToken();
713 const result_type_ref = try self.resolveTypeRef(self.inst.operands.items[0].ref_id);709 const result = try self.resolveRef(self.inst.operands.items[0].ref_id);
714 const result_type = self.spv.cache.lookup(result_type_ref);710 const result_id = result.resultId();
715 switch (result_type) {711 // We are going to cheat a little bit: The types we are interested in, int and float,
716 .int_type => |int| {712 // are added to the module and cached via self.spv.intType and self.spv.floatType. Therefore,
717 try self.parseContextDependentInt(int.signedness, int.bits);713 // we can determine the width of these types by directly checking the cache.
718 },714 // This only works if the Assembler and codegen both use spv.intType and spv.floatType though.
719 .float_type => |float| {715 // We don't expect there to be many of these types, so just look it up every time.
720 switch (float.bits) {716 // TODO: Count be improved to be a little bit more efficent.
717
718 {
719 var it = self.spv.cache2.int_types.iterator();
720 while (it.next()) |entry| {
721 const id = entry.value_ptr.*;
722 if (id != result_id) continue;
723 const info = entry.key_ptr.*;
724 return try self.parseContextDependentInt(info.signedness, info.bits);
725 }
726 }
727
728 {
729 var it = self.spv.cache2.float_types.iterator();
730 while (it.next()) |entry| {
731 const id = entry.value_ptr.*;
732 if (id != result_id) continue;
733 const info = entry.key_ptr.*;
734 switch (info.bits) {
721 16 => try self.parseContextDependentFloat(16),735 16 => try self.parseContextDependentFloat(16),
722 32 => try self.parseContextDependentFloat(32),736 32 => try self.parseContextDependentFloat(32),
723 64 => try self.parseContextDependentFloat(64),737 64 => try self.parseContextDependentFloat(64),
724 else => return self.fail(tok.start, "cannot parse {}-bit float literal", .{float.bits}),738 else => return self.fail(tok.start, "cannot parse {}-bit info literal", .{info.bits}),
725 }739 }
726 },740 }
727 else => return self.fail(tok.start, "cannot parse literal constant", .{}),
728 }741 }
742
743 return self.fail(tok.start, "cannot parse literal constant", .{});
729}744}
730745
731fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void {746fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void {