| ... | @@ -9,10 +9,9 @@ const Opcode = spec.Opcode; | ... | @@ -9,10 +9,9 @@ const Opcode = spec.Opcode; |
| 9 | const Word = spec.Word; | 9 | const Word = spec.Word; |
| 10 | const IdRef = spec.IdRef; | 10 | const IdRef = spec.IdRef; |
| 11 | const IdResult = spec.IdResult; | 11 | const IdResult = spec.IdResult; |
| | 12 | const StorageClass = spec.StorageClass; |
| 12 | | 13 | |
| 13 | const SpvModule = @import("Module.zig"); | 14 | const SpvModule = @import("Module.zig"); |
| 14 | const CacheRef = SpvModule.CacheRef; | | |
| 15 | const CacheKey = SpvModule.CacheKey; | | |
| 16 | | 15 | |
| 17 | /// Represents a token in the assembly template. | 16 | /// Represents a token in the assembly template. |
| 18 | const Token = struct { | 17 | const Token = struct { |
| ... | @@ -127,16 +126,16 @@ const AsmValue = union(enum) { | ... | @@ -127,16 +126,16 @@ const AsmValue = union(enum) { |
| 127 | value: IdRef, | 126 | value: IdRef, |
| 128 | | 127 | |
| 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, |
| 131 | | 130 | |
| 132 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue | 131 | /// 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 unresolved | 132 | /// 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. |
| 293 | fn processTypeInstruction(self: *Assembler) !AsmValue { | 292 | fn 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 OpTypeForwardPointer | 336 | .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 | }; |
| 356 | | 361 | |
| 357 | return AsmValue{ .ty = ref }; | 362 | return AsmValue{ .ty = id }; |
| 358 | } | 363 | } |
| 359 | | 364 | |
| 360 | /// Emit `self.inst` into `self.spv` and `self.func`, and return the AsmValue | 365 | /// 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 | } |
| 462 | | 467 | |
| 463 | /// Resolve a value reference as type. | 468 | fn resolveRefId(self: *Assembler, ref: AsmValue.Ref) !IdRef { |
| 464 | fn 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 | } |
| 476 | | 472 | |
| 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); |
| 711 | | 707 | |
| 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 | } |
| 730 | | 745 | |
| 731 | fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { | 746 | fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { |