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;
99const Word = spec.Word;
1010const IdRef = spec.IdRef;
1111const IdResult = spec.IdResult;
12const StorageClass = spec.StorageClass;
1213
1314const SpvModule = @import("Module.zig");
14const CacheRef = SpvModule.CacheRef;
15const CacheKey = SpvModule.CacheKey;
1615
1716/// Represents a token in the assembly template.
1817const Token = struct {
......@@ -127,16 +126,16 @@ const AsmValue = union(enum) {
127126 value: IdRef,
128127
129128 /// This result-value represents a type registered into the module's type system.
130 ty: CacheRef,
129 ty: IdRef,
131130
132131 /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue
133132 /// is of a variant that allows the result to be obtained (not an unresolved
134133 /// 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 {
136135 return switch (self) {
137136 .just_declared, .unresolved_forward_reference => unreachable,
138137 .value => |result| result,
139 .ty => |ref| spv.resultId(ref),
138 .ty => |result| result,
140139 };
141140 }
142141};
......@@ -292,23 +291,23 @@ fn processInstruction(self: *Assembler) !void {
292291/// refers to the result.
293292fn processTypeInstruction(self: *Assembler) !AsmValue {
294293 const operands = self.inst.operands.items;
295 const ref = switch (self.inst.opcode) {
296 .OpTypeVoid => try self.spv.resolve(.void_type),
297 .OpTypeBool => try self.spv.resolve(.bool_type),
294 const section = &self.spv.sections.types_globals_constants;
295 const id = switch (self.inst.opcode) {
296 .OpTypeVoid => try self.spv.voidType(),
297 .OpTypeBool => try self.spv.boolType(),
298298 .OpTypeInt => blk: {
299 // const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
300 // 0 => .unsigned,
301 // 1 => .signed,
302 // else => {
303 // // TODO: Improve source location.
304 // return self.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
305 // },
306 // };
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});
309 // };
310 // break :blk try self.spv.intType(signedness, width);
311 break :blk @as(CacheRef, @enumFromInt(0)); // TODO(robin): fix
299 const signedness: std.builtin.Signedness = switch (operands[2].literal32) {
300 0 => .unsigned,
301 1 => .signed,
302 else => {
303 // TODO: Improve source location.
304 return self.fail(0, "{} is not a valid signedness (expected 0 or 1)", .{operands[2].literal32});
305 },
306 };
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});
309 };
310 break :blk try self.spv.intType(signedness, width);
312311 },
313312 .OpTypeFloat => blk: {
314313 const bits = operands[1].literal32;
......@@ -318,43 +317,49 @@ fn processTypeInstruction(self: *Assembler) !AsmValue {
318317 return self.fail(0, "{} is not a valid bit count for floats (expected 16, 32 or 64)", .{bits});
319318 },
320319 }
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);
322325 },
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 } }),
327326 .OpTypeArray => {
328327 // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant),
329328 // and so some consideration must be taken when entering this in the type system.
330329 return self.todo("process OpTypeArray", .{});
331330 },
332331 .OpTypePointer => blk: {
333 break :blk try self.spv.resolve(.{
334 .ptr_type = .{
335 .storage_class = @enumFromInt(operands[1].value),
336 .child_type = try self.resolveTypeRef(operands[2].ref_id),
337 // TODO: This should be a proper reference resolved via OpTypeForwardPointer
338 .fwd = @enumFromInt(std.math.maxInt(u32)),
339 },
332 const storage_class: StorageClass = @enumFromInt(operands[1].value);
333 const child_type = try self.resolveRefId(operands[2].ref_id);
334 const result_id = self.spv.allocId();
335 try section.emit(self.spv.gpa, .OpTypePointer, .{
336 .id_result = result_id,
337 .storage_class = storage_class,
338 .type = child_type,
340339 });
340 break :blk result_id;
341341 },
342342 .OpTypeFunction => blk: {
343343 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);
345347 defer self.spv.gpa.free(param_types);
346 for (param_types, 0..) |*param, i| {
347 param.* = try self.resolveTypeRef(param_operands[i].ref_id);
348 for (param_types, param_operands) |*param, operand| {
349 param.* = try self.resolveRefId(operand.ref_id);
348350 }
349 break :blk try self.spv.resolve(.{ .function_type = .{
350 .return_type = try self.resolveTypeRef(operands[1].ref_id),
351 .parameters = param_types,
352 } });
351 const result_id = self.spv.allocId();
352 try section.emit(self.spv.gpa, .OpTypeFunction, .{
353 .id_result = result_id,
354 .return_type = return_type,
355 .id_ref_2 = param_types,
356 });
357 break :blk result_id;
353358 },
354359 else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}),
355360 };
356361
357 return AsmValue{ .ty = ref };
362 return AsmValue{ .ty = id };
358363}
359364
360365/// Emit `self.inst` into `self.spv` and `self.func`, and return the AsmValue
......@@ -411,7 +416,7 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue {
411416 .ref_id => |index| {
412417 const result = try self.resolveRef(index);
413418 try section.ensureUnusedCapacity(self.spv.gpa, 1);
414 section.writeOperand(spec.IdRef, result.resultId(self.spv));
419 section.writeOperand(spec.IdRef, result.resultId());
415420 },
416421 .string => |offset| {
417422 const text = std.mem.sliceTo(self.inst.string_bytes.items[offset..], 0);
......@@ -460,18 +465,9 @@ fn resolveRef(self: *Assembler, ref: AsmValue.Ref) !AsmValue {
460465 }
461466}
462467
463/// Resolve a value reference as type.
464fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !CacheRef {
468fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef {
465469 const value = try self.resolveRef(ref);
466 switch (value) {
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 }
470 return value.resultId();
475471}
476472
477473/// Attempt to parse an instruction into `self.inst`.
......@@ -710,22 +706,41 @@ fn parseContextDependentNumber(self: *Assembler) !void {
710706 assert(self.inst.opcode == .OpConstant or self.inst.opcode == .OpSpecConstant);
711707
712708 const tok = self.currentToken();
713 const result_type_ref = try self.resolveTypeRef(self.inst.operands.items[0].ref_id);
714 const result_type = self.spv.cache.lookup(result_type_ref);
715 switch (result_type) {
716 .int_type => |int| {
717 try self.parseContextDependentInt(int.signedness, int.bits);
718 },
719 .float_type => |float| {
720 switch (float.bits) {
709 const result = try self.resolveRef(self.inst.operands.items[0].ref_id);
710 const result_id = result.resultId();
711 // We are going to cheat a little bit: The types we are interested in, int and float,
712 // are added to the module and cached via self.spv.intType and self.spv.floatType. Therefore,
713 // we can determine the width of these types by directly checking the cache.
714 // This only works if the Assembler and codegen both use spv.intType and spv.floatType though.
715 // We don't expect there to be many of these types, so just look it up every time.
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) {
721735 16 => try self.parseContextDependentFloat(16),
722736 32 => try self.parseContextDependentFloat(32),
723737 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}),
725739 }
726 },
727 else => return self.fail(tok.start, "cannot parse literal constant", .{}),
740 }
728741 }
742
743 return self.fail(tok.start, "cannot parse literal constant", .{});
729744}
730745
731746fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void {