| ... | @@ -45,6 +45,9 @@ const Token = struct { | ... | @@ -45,6 +45,9 @@ const Token = struct { |
| 45 | pipe, | 45 | pipe, |
| 46 | /// =. | 46 | /// =. |
| 47 | equals, | 47 | equals, |
| | 48 | /// $identifier. This is used (for now) for constant values, like integers. |
| | 49 | /// These can be used in place of a normal `value`. |
| | 50 | placeholder, |
| 48 | | 51 | |
| 49 | fn name(self: Tag) []const u8 { | 52 | fn name(self: Tag) []const u8 { |
| 50 | return switch (self) { | 53 | return switch (self) { |
| ... | @@ -56,6 +59,7 @@ const Token = struct { | ... | @@ -56,6 +59,7 @@ const Token = struct { |
| 56 | .string => "<string literal>", | 59 | .string => "<string literal>", |
| 57 | .pipe => "'|'", | 60 | .pipe => "'|'", |
| 58 | .equals => "'='", | 61 | .equals => "'='", |
| | 62 | .placeholder => "<placeholder>", |
| 59 | }; | 63 | }; |
| 60 | } | 64 | } |
| 61 | }; | 65 | }; |
| ... | @@ -128,12 +132,19 @@ const AsmValue = union(enum) { | ... | @@ -128,12 +132,19 @@ const AsmValue = union(enum) { |
| 128 | /// This result-value represents a type registered into the module's type system. | 132 | /// This result-value represents a type registered into the module's type system. |
| 129 | ty: IdRef, | 133 | ty: IdRef, |
| 130 | | 134 | |
| | 135 | /// This is a pre-supplied constant integer value. |
| | 136 | constant: u32, |
| | 137 | |
| 131 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue | 138 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue |
| 132 | /// is of a variant that allows the result to be obtained (not an unresolved | 139 | /// is of a variant that allows the result to be obtained (not an unresolved |
| 133 | /// forward declaration, not in the process of being declared, etc). | 140 | /// forward declaration, not in the process of being declared, etc). |
| 134 | pub fn resultId(self: AsmValue) IdRef { | 141 | pub fn resultId(self: AsmValue) IdRef { |
| 135 | return switch (self) { | 142 | return switch (self) { |
| 136 | .just_declared, .unresolved_forward_reference => unreachable, | 143 | .just_declared, |
| | 144 | .unresolved_forward_reference, |
| | 145 | // TODO: Lower this value as constant? |
| | 146 | .constant, |
| | 147 | => unreachable, |
| 137 | .value => |result| result, | 148 | .value => |result| result, |
| 138 | .ty => |result| result, | 149 | .ty => |result| result, |
| 139 | }; | 150 | }; |
| ... | @@ -383,16 +394,16 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue { | ... | @@ -383,16 +394,16 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue { |
| 383 | .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes, | 394 | .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes, |
| 384 | .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) { | 395 | .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) { |
| 385 | .Function => &self.func.prologue, | 396 | .Function => &self.func.prologue, |
| 386 | // These don't need to be marked in the dependency system. | 397 | .Input, .Output => section: { |
| 387 | // Probably we should add them anyway, then filter out PushConstant globals. | | |
| 388 | .PushConstant => &self.spv.sections.types_globals_constants, | | |
| 389 | else => section: { | | |
| 390 | maybe_spv_decl_index = try self.spv.allocDecl(.global); | 398 | maybe_spv_decl_index = try self.spv.allocDecl(.global); |
| 391 | try self.func.decl_deps.put(self.spv.gpa, maybe_spv_decl_index.?, {}); | 399 | try self.func.decl_deps.put(self.spv.gpa, maybe_spv_decl_index.?, {}); |
| 392 | // TODO: In theory this can be non-empty if there is an initializer which depends on another global... | 400 | // TODO: In theory this can be non-empty if there is an initializer which depends on another global... |
| 393 | try self.spv.declareDeclDeps(maybe_spv_decl_index.?, &.{}); | 401 | try self.spv.declareDeclDeps(maybe_spv_decl_index.?, &.{}); |
| 394 | break :section &self.spv.sections.types_globals_constants; | 402 | break :section &self.spv.sections.types_globals_constants; |
| 395 | }, | 403 | }, |
| | 404 | // These don't need to be marked in the dependency system. |
| | 405 | // Probably we should add them anyway, then filter out PushConstant globals. |
| | 406 | else => &self.spv.sections.types_globals_constants, |
| 396 | }, | 407 | }, |
| 397 | // Default case - to be worked out further. | 408 | // Default case - to be worked out further. |
| 398 | else => &self.func.body, | 409 | else => &self.func.body, |
| ... | @@ -665,6 +676,22 @@ fn parseRefId(self: *Assembler) !void { | ... | @@ -665,6 +676,22 @@ fn parseRefId(self: *Assembler) !void { |
| 665 | | 676 | |
| 666 | fn parseLiteralInteger(self: *Assembler) !void { | 677 | fn parseLiteralInteger(self: *Assembler) !void { |
| 667 | const tok = self.currentToken(); | 678 | const tok = self.currentToken(); |
| | 679 | if (self.eatToken(.placeholder)) { |
| | 680 | const name = self.tokenText(tok)[1..]; |
| | 681 | const value = self.value_map.get(name) orelse { |
| | 682 | return self.fail(tok.start, "invalid placeholder '${s}'", .{name}); |
| | 683 | }; |
| | 684 | switch (value) { |
| | 685 | .constant => |literal32| { |
| | 686 | try self.inst.operands.append(self.gpa, .{ .literal32 = literal32 }); |
| | 687 | }, |
| | 688 | else => { |
| | 689 | return self.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name}); |
| | 690 | }, |
| | 691 | } |
| | 692 | return; |
| | 693 | } |
| | 694 | |
| 668 | try self.expectToken(.value); | 695 | try self.expectToken(.value); |
| 669 | // According to the SPIR-V machine readable grammar, a LiteralInteger | 696 | // According to the SPIR-V machine readable grammar, a LiteralInteger |
| 670 | // may consist of one or more words. From the SPIR-V docs it seems like there | 697 | // may consist of one or more words. From the SPIR-V docs it seems like there |
| ... | @@ -680,6 +707,22 @@ fn parseLiteralInteger(self: *Assembler) !void { | ... | @@ -680,6 +707,22 @@ fn parseLiteralInteger(self: *Assembler) !void { |
| 680 | | 707 | |
| 681 | fn parseLiteralExtInstInteger(self: *Assembler) !void { | 708 | fn parseLiteralExtInstInteger(self: *Assembler) !void { |
| 682 | const tok = self.currentToken(); | 709 | const tok = self.currentToken(); |
| | 710 | if (self.eatToken(.placeholder)) { |
| | 711 | const name = self.tokenText(tok)[1..]; |
| | 712 | const value = self.value_map.get(name) orelse { |
| | 713 | return self.fail(tok.start, "invalid placeholder '${s}'", .{name}); |
| | 714 | }; |
| | 715 | switch (value) { |
| | 716 | .constant => |literal32| { |
| | 717 | try self.inst.operands.append(self.gpa, .{ .literal32 = literal32 }); |
| | 718 | }, |
| | 719 | else => { |
| | 720 | return self.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name}); |
| | 721 | }, |
| | 722 | } |
| | 723 | return; |
| | 724 | } |
| | 725 | |
| 683 | try self.expectToken(.value); | 726 | try self.expectToken(.value); |
| 684 | const text = self.tokenText(tok); | 727 | const text = self.tokenText(tok); |
| 685 | const value = std.fmt.parseInt(u32, text, 0) catch { | 728 | const value = std.fmt.parseInt(u32, text, 0) catch { |
| ... | @@ -756,6 +799,22 @@ fn parseContextDependentNumber(self: *Assembler) !void { | ... | @@ -756,6 +799,22 @@ fn parseContextDependentNumber(self: *Assembler) !void { |
| 756 | | 799 | |
| 757 | fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { | 800 | fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { |
| 758 | const tok = self.currentToken(); | 801 | const tok = self.currentToken(); |
| | 802 | if (self.eatToken(.placeholder)) { |
| | 803 | const name = self.tokenText(tok)[1..]; |
| | 804 | const value = self.value_map.get(name) orelse { |
| | 805 | return self.fail(tok.start, "invalid placeholder '${s}'", .{name}); |
| | 806 | }; |
| | 807 | switch (value) { |
| | 808 | .constant => |literal32| { |
| | 809 | try self.inst.operands.append(self.gpa, .{ .literal32 = literal32 }); |
| | 810 | }, |
| | 811 | else => { |
| | 812 | return self.fail(tok.start, "value '{s}' cannot be used as placeholder", .{name}); |
| | 813 | }, |
| | 814 | } |
| | 815 | return; |
| | 816 | } |
| | 817 | |
| 759 | try self.expectToken(.value); | 818 | try self.expectToken(.value); |
| 760 | | 819 | |
| 761 | if (width == 0 or width > 2 * @bitSizeOf(spec.Word)) { | 820 | if (width == 0 or width > 2 * @bitSizeOf(spec.Word)) { |
| ... | @@ -903,6 +962,7 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { | ... | @@ -903,6 +962,7 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { |
| 903 | string, | 962 | string, |
| 904 | string_end, | 963 | string_end, |
| 905 | escape, | 964 | escape, |
| | 965 | placeholder, |
| 906 | } = .start; | 966 | } = .start; |
| 907 | var token_start = start_offset; | 967 | var token_start = start_offset; |
| 908 | var offset = start_offset; | 968 | var offset = start_offset; |
| ... | @@ -930,6 +990,10 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { | ... | @@ -930,6 +990,10 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { |
| 930 | offset += 1; | 990 | offset += 1; |
| 931 | break; | 991 | break; |
| 932 | }, | 992 | }, |
| | 993 | '$' => { |
| | 994 | state = .placeholder; |
| | 995 | tag = .placeholder; |
| | 996 | }, |
| 933 | else => { | 997 | else => { |
| 934 | state = .value; | 998 | state = .value; |
| 935 | tag = .value; | 999 | tag = .value; |
| ... | @@ -945,11 +1009,11 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { | ... | @@ -945,11 +1009,11 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { |
| 945 | ' ', '\t', '\r', '\n', '=', '|' => break, | 1009 | ' ', '\t', '\r', '\n', '=', '|' => break, |
| 946 | else => {}, | 1010 | else => {}, |
| 947 | }, | 1011 | }, |
| 948 | .result_id => switch (c) { | 1012 | .result_id, .placeholder => switch (c) { |
| 949 | '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, | 1013 | '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, |
| 950 | ' ', '\t', '\r', '\n', '=', '|' => break, | 1014 | ' ', '\t', '\r', '\n', '=', '|' => break, |
| 951 | else => { | 1015 | else => { |
| 952 | try self.addError(offset, "illegal character in result-id", .{}); | 1016 | try self.addError(offset, "illegal character in result-id or placeholder", .{}); |
| 953 | // Again, probably a forgotten delimiter here. | 1017 | // Again, probably a forgotten delimiter here. |
| 954 | break; | 1018 | break; |
| 955 | }, | 1019 | }, |