blob: c2b2e98751f8f97655ddacc6d320c4ff1d842a6a [file] [log] [blame]
David Netobe4c89a2020-03-20 19:24:23 +00001// Copyright 2020 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "src/reader/spirv/namer.h"
16
David Netobd1d1422020-03-23 16:42:21 +000017#include <algorithm>
David Neto8af12f72020-03-23 16:43:41 +000018#include <sstream>
David Neto23dceb42020-03-23 16:48:03 +000019#include <unordered_set>
David Netobd1d1422020-03-23 16:42:21 +000020
David Neto2c953ec2021-04-27 14:39:47 +000021#include "src/debug.h"
22
David Netobe4c89a2020-03-20 19:24:23 +000023namespace tint {
24namespace reader {
25namespace spirv {
26
David Neto018428d2020-07-13 14:15:52 +000027namespace {
28
29const char* kWGSLReservedWords[] = {
30 // Please keep this list sorted
James Pricee87ded82021-04-30 17:14:19 +000031 "array", "as", "asm",
32 "bf16", "binding", "block",
33 "bool", "break", "builtin",
34 "case", "cast", "compute",
35 "const", "continue", "default",
36 "discard", "do", "else",
37 "elseif", "entry_point", "enum",
38 "f16", "f32", "fallthrough",
39 "false", "fn", "for",
40 "fragment", "i16", "i32",
41 "i64", "i8", "if",
42 "image", "import", "in",
43 "let", "location", "loop",
44 "mat2x2", "mat2x3", "mat2x4",
45 "mat3x2", "mat3x3", "mat3x4",
46 "mat4x2", "mat4x3", "mat4x4",
47 "offset", "out", "override",
48 "premerge", "private", "ptr",
49 "regardless", "return", "set",
50 "storage", "struct", "switch",
51 "true", "type", "typedef",
52 "u16", "u32", "u64",
53 "u8", "uniform", "uniform_constant",
54 "unless", "using", "var",
55 "vec2", "vec3", "vec4",
56 "vertex", "void", "while",
David Neto018428d2020-07-13 14:15:52 +000057 "workgroup",
58};
dan sinclair23508f42020-07-25 14:33:35 +000059
60} // namespace
David Neto018428d2020-07-13 14:15:52 +000061
62Namer::Namer(const FailStream& fail_stream) : fail_stream_(fail_stream) {
63 for (const auto* reserved : kWGSLReservedWords) {
64 name_to_id_[std::string(reserved)] = 0;
65 }
66}
David Netobe4c89a2020-03-20 19:24:23 +000067
68Namer::~Namer() = default;
69
David Netobd1d1422020-03-23 16:42:21 +000070std::string Namer::Sanitize(const std::string& suggested_name) {
71 if (suggested_name.empty()) {
72 return "empty";
73 }
74 // Otherwise, replace invalid characters by '_'.
75 std::string result;
76 std::string invalid_as_first_char = "_0123456789";
77 std::string valid =
78 "abcdefghijklmnopqrstuvwxyz"
79 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
80 "_0123456789";
81 // If the first character is invalid for starting a WGSL identifier, then
82 // prefix the result with "x".
83 if ((std::string::npos != invalid_as_first_char.find(suggested_name[0])) ||
84 (std::string::npos == valid.find(suggested_name[0]))) {
85 result = "x";
86 }
87 std::transform(suggested_name.begin(), suggested_name.end(),
88 std::back_inserter(result), [&valid](const char c) {
89 return (std::string::npos == valid.find(c)) ? '_' : c;
90 });
91 return result;
92}
93
David Netoad90ee22020-03-23 16:45:57 +000094std::string Namer::GetMemberName(uint32_t struct_id,
95 uint32_t member_index) const {
96 std::string result;
97 auto where = struct_member_names_.find(struct_id);
98 if (where != struct_member_names_.end()) {
99 auto& member_names = where->second;
100 if (member_index < member_names.size()) {
101 result = member_names[member_index];
102 }
103 }
104 return result;
105}
106
Ben Claytonc00cc432022-01-05 20:42:50 +0000107std::string Namer::FindUnusedDerivedName(const std::string& base_name) {
David Neto8af12f72020-03-23 16:43:41 +0000108 // Ensure uniqueness among names.
109 std::string derived_name;
Ben Claytonc00cc432022-01-05 20:42:50 +0000110 uint32_t& i = next_unusued_derived_name_id_[base_name];
111 while (i != 0xffffffff) {
David Neto8af12f72020-03-23 16:43:41 +0000112 std::stringstream new_name_stream;
113 new_name_stream << base_name;
114 if (i > 0) {
115 new_name_stream << "_" << i;
116 }
117 derived_name = new_name_stream.str();
Ben Claytonc00cc432022-01-05 20:42:50 +0000118 if (!IsRegistered(derived_name)) {
119 return derived_name;
120 }
121 i++;
122 }
123 TINT_ASSERT(Reader, false /* FindUnusedDerivedName() overflowed u32 */);
124 return "<u32 overflow>";
David Neto8af12f72020-03-23 16:43:41 +0000125}
126
David Netoad2f7cc2020-06-16 01:02:59 +0000127std::string Namer::MakeDerivedName(const std::string& base_name) {
128 auto result = FindUnusedDerivedName(base_name);
David Neto2c953ec2021-04-27 14:39:47 +0000129 const bool registered = RegisterWithoutId(result);
Ben Claytonffd28e22021-06-24 11:27:36 +0000130 TINT_ASSERT(Reader, registered);
David Netoad2f7cc2020-06-16 01:02:59 +0000131 return result;
132}
133
David Neto2c953ec2021-04-27 14:39:47 +0000134bool Namer::Register(uint32_t id, const std::string& name) {
David Netobe4c89a2020-03-20 19:24:23 +0000135 if (HasName(id)) {
136 return Fail() << "internal error: ID " << id
137 << " already has registered name: " << id_to_name_[id];
138 }
David Neto2c953ec2021-04-27 14:39:47 +0000139 if (!RegisterWithoutId(name)) {
140 return false;
141 }
David Netobe4c89a2020-03-20 19:24:23 +0000142 id_to_name_[id] = name;
David Neto8af12f72020-03-23 16:43:41 +0000143 name_to_id_[name] = id;
David Netobe4c89a2020-03-20 19:24:23 +0000144 return true;
145}
146
David Neto2c953ec2021-04-27 14:39:47 +0000147bool Namer::RegisterWithoutId(const std::string& name) {
148 if (IsRegistered(name)) {
149 return Fail() << "internal error: name already registered: " << name;
150 }
151 name_to_id_[name] = 0;
152 return true;
153}
154
David Netoca8e6dd2020-03-23 16:45:01 +0000155bool Namer::SuggestSanitizedName(uint32_t id,
156 const std::string& suggested_name) {
157 if (HasName(id)) {
158 return false;
159 }
160
David Neto2c953ec2021-04-27 14:39:47 +0000161 return Register(id, FindUnusedDerivedName(Sanitize(suggested_name)));
David Netoca8e6dd2020-03-23 16:45:01 +0000162}
163
David Netoad90ee22020-03-23 16:45:57 +0000164bool Namer::SuggestSanitizedMemberName(uint32_t struct_id,
165 uint32_t member_index,
166 const std::string& suggested_name) {
167 // Creates an empty vector the first time we visit this struct.
168 auto& name_vector = struct_member_names_[struct_id];
169 // Resizing will set new entries to the empty string.
170 name_vector.resize(std::max(name_vector.size(), size_t(member_index + 1)));
171 auto& entry = name_vector[member_index];
172 if (entry.empty()) {
173 entry = Sanitize(suggested_name);
174 return true;
175 }
176 return false;
177}
178
David Neto23dceb42020-03-23 16:48:03 +0000179void Namer::ResolveMemberNamesForStruct(uint32_t struct_id,
180 uint32_t num_members) {
181 auto& name_vector = struct_member_names_[struct_id];
182 // Resizing will set new entries to the empty string.
183 // It would have been an error if the client had registered a name for
184 // an out-of-bounds member index, so toss those away.
185 name_vector.resize(num_members);
186
187 std::unordered_set<std::string> used_names;
188
189 // Returns a name, based on the suggestion, which does not equal
190 // any name in the used_names set.
191 auto disambiguate_name =
192 [&used_names](const std::string& suggestion) -> std::string {
193 if (used_names.find(suggestion) == used_names.end()) {
194 // There is no collision.
195 return suggestion;
196 }
197
198 uint32_t i = 1;
199 std::string new_name;
200 do {
201 std::stringstream new_name_stream;
202 new_name_stream << suggestion << "_" << i;
203 new_name = new_name_stream.str();
204 ++i;
205 } while (used_names.find(new_name) != used_names.end());
206 return new_name;
207 };
208
209 // First ensure uniqueness among names for which we have already taken
210 // suggestions.
211 for (auto& name : name_vector) {
212 if (!name.empty()) {
213 // This modifies the names in-place, i.e. update the name_vector
214 // entries.
215 name = disambiguate_name(name);
216 used_names.insert(name);
217 }
218 }
219
220 // Now ensure uniqueness among the rest. Doing this in a second pass
221 // allows us to preserve suggestions as much as possible. Otherwise
222 // a generated name such as 'field1' might collide with a user-suggested
223 // name of 'field1' attached to a later member.
224 uint32_t index = 0;
225 for (auto& name : name_vector) {
226 if (name.empty()) {
227 std::stringstream suggestion;
228 suggestion << "field" << index;
229 // Again, modify the name-vector in-place.
230 name = disambiguate_name(suggestion.str());
231 used_names.insert(name);
232 }
233 index++;
234 }
235}
236
David Netobe4c89a2020-03-20 19:24:23 +0000237} // namespace spirv
238} // namespace reader
239} // namespace tint