| ... | ... | @@ -9,10 +9,9 @@ const Opcode = spec.Opcode; |
| 9 | 9 | const Word = spec.Word; |
| 10 | 10 | const IdRef = spec.IdRef; |
| 11 | 11 | const IdResult = spec.IdResult; |
| 12 | const StorageClass = spec.StorageClass; |
| 12 | 13 | |
| 13 | 14 | const SpvModule = @import("Module.zig"); |
| 14 | | const CacheRef = SpvModule.CacheRef; |
| 15 | | const CacheKey = SpvModule.CacheKey; |
| 16 | 15 | |
| 17 | 16 | /// Represents a token in the assembly template. |
| 18 | 17 | const Token = struct { |
| ... | ... | @@ -127,16 +126,16 @@ const AsmValue = union(enum) { |
| 127 | 126 | value: IdRef, |
| 128 | 127 | |
| 129 | 128 | /// This result-value represents a type registered into the module's type system. |
| 130 | | ty: CacheRef, |
| 129 | ty: IdRef, |
| 131 | 130 | |
| 132 | 131 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue |
| 133 | 132 | /// is of a variant that allows the result to be obtained (not an unresolved |
| 134 | 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 | 135 | return switch (self) { |
| 137 | 136 | .just_declared, .unresolved_forward_reference => unreachable, |
| 138 | 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 | 291 | /// refers to the result. |
| 293 | 292 | fn processTypeInstruction(self: *Assembler) !AsmValue { |
| 294 | 293 | 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(), |
| 298 | 298 | .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); |
| 312 | 311 | }, |
| 313 | 312 | .OpTypeFloat => blk: { |
| 314 | 313 | const bits = operands[1].literal32; |
| ... | ... | @@ -318,43 +317,49 @@ fn processTypeInstruction(self: *Assembler) !AsmValue { |
| 318 | 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 | 326 | .OpTypeArray => { |
| 328 | 327 | // TODO: The length of an OpTypeArray is determined by a constant (which may be a spec constant), |
| 329 | 328 | // and so some consideration must be taken when entering this in the type system. |
| 330 | 329 | return self.todo("process OpTypeArray", .{}); |
| 331 | 330 | }, |
| 332 | 331 | .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, |
| 340 | 339 | }); |
| 340 | break :blk result_id; |
| 341 | 341 | }, |
| 342 | 342 | .OpTypeFunction => blk: { |
| 343 | 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 | 347 | 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); |
| 348 | 350 | } |
| 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; |
| 353 | 358 | }, |
| 354 | 359 | else => return self.todo("process type instruction {s}", .{@tagName(self.inst.opcode)}), |
| 355 | 360 | }; |
| 356 | 361 | |
| 357 | | return AsmValue{ .ty = ref }; |
| 362 | return AsmValue{ .ty = id }; |
| 358 | 363 | } |
| 359 | 364 | |
| 360 | 365 | /// Emit `self.inst` into `self.spv` and `self.func`, and return the AsmValue |
| ... | ... | @@ -411,7 +416,7 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue { |
| 411 | 416 | .ref_id => |index| { |
| 412 | 417 | const result = try self.resolveRef(index); |
| 413 | 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 | 421 | .string => |offset| { |
| 417 | 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 | 465 | } |
| 461 | 466 | } |
| 462 | 467 | |
| 463 | | /// Resolve a value reference as type. |
| 464 | | fn resolveTypeRef(self: *Assembler, ref: AsmValue.Ref) !CacheRef { |
| 468 | fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef { |
| 465 | 469 | 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(); |
| 475 | 471 | } |
| 476 | 472 | |
| 477 | 473 | /// Attempt to parse an instruction into `self.inst`. |
| ... | ... | @@ -710,22 +706,41 @@ fn parseContextDependentNumber(self: *Assembler) !void { |
| 710 | 706 | assert(self.inst.opcode == .OpConstant or self.inst.opcode == .OpSpecConstant); |
| 711 | 707 | |
| 712 | 708 | 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) { |
| 721 | 735 | 16 => try self.parseContextDependentFloat(16), |
| 722 | 736 | 32 => try self.parseContextDependentFloat(32), |
| 723 | 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 | | }, |
| 727 | | else => return self.fail(tok.start, "cannot parse literal constant", .{}), |
| 740 | } |
| 728 | 741 | } |
| 742 | |
| 743 | return self.fail(tok.start, "cannot parse literal constant", .{}); |
| 729 | 744 | } |
| 730 | 745 | |
| 731 | 746 | fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { |