| ... | ... | @@ -45,6 +45,9 @@ const Token = struct { |
| 45 | 45 | pipe, |
| 46 | 46 | /// =. |
| 47 | 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 | 52 | fn name(self: Tag) []const u8 { |
| 50 | 53 | return switch (self) { |
| ... | ... | @@ -56,6 +59,7 @@ const Token = struct { |
| 56 | 59 | .string => "<string literal>", |
| 57 | 60 | .pipe => "'|'", |
| 58 | 61 | .equals => "'='", |
| 62 | .placeholder => "<placeholder>", |
| 59 | 63 | }; |
| 60 | 64 | } |
| 61 | 65 | }; |
| ... | ... | @@ -128,12 +132,19 @@ const AsmValue = union(enum) { |
| 128 | 132 | /// This result-value represents a type registered into the module's type system. |
| 129 | 133 | ty: IdRef, |
| 130 | 134 | |
| 135 | /// This is a pre-supplied constant integer value. |
| 136 | constant: u32, |
| 137 | |
| 131 | 138 | /// Retrieve the result-id of this AsmValue. Asserts that this AsmValue |
| 132 | 139 | /// is of a variant that allows the result to be obtained (not an unresolved |
| 133 | 140 | /// forward declaration, not in the process of being declared, etc). |
| 134 | 141 | pub fn resultId(self: AsmValue) IdRef { |
| 135 | 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 | 148 | .value => |result| result, |
| 138 | 149 | .ty => |result| result, |
| 139 | 150 | }; |
| ... | ... | @@ -383,16 +394,16 @@ fn processGenericInstruction(self: *Assembler) !?AsmValue { |
| 383 | 394 | .OpExecutionMode, .OpExecutionModeId => &self.spv.sections.execution_modes, |
| 384 | 395 | .OpVariable => switch (@as(spec.StorageClass, @enumFromInt(operands[2].value))) { |
| 385 | 396 | .Function => &self.func.prologue, |
| 386 | | // These don't need to be marked in the dependency system. |
| 387 | | // Probably we should add them anyway, then filter out PushConstant globals. |
| 388 | | .PushConstant => &self.spv.sections.types_globals_constants, |
| 389 | | else => section: { |
| 397 | .Input, .Output => section: { |
| 390 | 398 | maybe_spv_decl_index = try self.spv.allocDecl(.global); |
| 391 | 399 | try self.func.decl_deps.put(self.spv.gpa, maybe_spv_decl_index.?, {}); |
| 392 | 400 | // TODO: In theory this can be non-empty if there is an initializer which depends on another global... |
| 393 | 401 | try self.spv.declareDeclDeps(maybe_spv_decl_index.?, &.{}); |
| 394 | 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 | 408 | // Default case - to be worked out further. |
| 398 | 409 | else => &self.func.body, |
| ... | ... | @@ -665,6 +676,22 @@ fn parseRefId(self: *Assembler) !void { |
| 665 | 676 | |
| 666 | 677 | fn parseLiteralInteger(self: *Assembler) !void { |
| 667 | 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 | 695 | try self.expectToken(.value); |
| 669 | 696 | // According to the SPIR-V machine readable grammar, a LiteralInteger |
| 670 | 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 | 707 | |
| 681 | 708 | fn parseLiteralExtInstInteger(self: *Assembler) !void { |
| 682 | 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 | 726 | try self.expectToken(.value); |
| 684 | 727 | const text = self.tokenText(tok); |
| 685 | 728 | const value = std.fmt.parseInt(u32, text, 0) catch { |
| ... | ... | @@ -756,6 +799,22 @@ fn parseContextDependentNumber(self: *Assembler) !void { |
| 756 | 799 | |
| 757 | 800 | fn parseContextDependentInt(self: *Assembler, signedness: std.builtin.Signedness, width: u32) !void { |
| 758 | 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 | 818 | try self.expectToken(.value); |
| 760 | 819 | |
| 761 | 820 | if (width == 0 or width > 2 * @bitSizeOf(spec.Word)) { |
| ... | ... | @@ -903,6 +962,7 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { |
| 903 | 962 | string, |
| 904 | 963 | string_end, |
| 905 | 964 | escape, |
| 965 | placeholder, |
| 906 | 966 | } = .start; |
| 907 | 967 | var token_start = start_offset; |
| 908 | 968 | var offset = start_offset; |
| ... | ... | @@ -930,6 +990,10 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { |
| 930 | 990 | offset += 1; |
| 931 | 991 | break; |
| 932 | 992 | }, |
| 993 | '$' => { |
| 994 | state = .placeholder; |
| 995 | tag = .placeholder; |
| 996 | }, |
| 933 | 997 | else => { |
| 934 | 998 | state = .value; |
| 935 | 999 | tag = .value; |
| ... | ... | @@ -945,11 +1009,11 @@ fn nextToken(self: *Assembler, start_offset: u32) !Token { |
| 945 | 1009 | ' ', '\t', '\r', '\n', '=', '|' => break, |
| 946 | 1010 | else => {}, |
| 947 | 1011 | }, |
| 948 | | .result_id => switch (c) { |
| 1012 | .result_id, .placeholder => switch (c) { |
| 949 | 1013 | '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, |
| 950 | 1014 | ' ', '\t', '\r', '\n', '=', '|' => break, |
| 951 | 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 | 1017 | // Again, probably a forgotten delimiter here. |
| 954 | 1018 | break; |
| 955 | 1019 | }, |