• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
17 #include <algorithm>
18 #include <sstream>
19 #include <unordered_set>
20 
21 #include "src/debug.h"
22 
23 namespace tint {
24 namespace reader {
25 namespace spirv {
26 
27 namespace {
28 
29 const char* kWGSLReservedWords[] = {
30     // Please keep this list sorted
31     "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",
57     "workgroup",
58 };
59 
60 }  // namespace
61 
Namer(const FailStream & fail_stream)62 Namer::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 }
67 
68 Namer::~Namer() = default;
69 
Sanitize(const std::string & suggested_name)70 std::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 
GetMemberName(uint32_t struct_id,uint32_t member_index) const94 std::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 
FindUnusedDerivedName(const std::string & base_name) const107 std::string Namer::FindUnusedDerivedName(const std::string& base_name) const {
108   // Ensure uniqueness among names.
109   std::string derived_name;
110   int i = 0;
111   do {
112     std::stringstream new_name_stream;
113     new_name_stream << base_name;
114     if (i > 0) {
115       new_name_stream << "_" << i;
116     }
117     i++;
118     derived_name = new_name_stream.str();
119   } while (IsRegistered(derived_name));
120   return derived_name;
121 }
122 
MakeDerivedName(const std::string & base_name)123 std::string Namer::MakeDerivedName(const std::string& base_name) {
124   auto result = FindUnusedDerivedName(base_name);
125   const bool registered = RegisterWithoutId(result);
126   TINT_ASSERT(Reader, registered);
127   return result;
128 }
129 
Register(uint32_t id,const std::string & name)130 bool Namer::Register(uint32_t id, const std::string& name) {
131   if (HasName(id)) {
132     return Fail() << "internal error: ID " << id
133                   << " already has registered name: " << id_to_name_[id];
134   }
135   if (!RegisterWithoutId(name)) {
136     return false;
137   }
138   id_to_name_[id] = name;
139   name_to_id_[name] = id;
140   return true;
141 }
142 
RegisterWithoutId(const std::string & name)143 bool Namer::RegisterWithoutId(const std::string& name) {
144   if (IsRegistered(name)) {
145     return Fail() << "internal error: name already registered: " << name;
146   }
147   name_to_id_[name] = 0;
148   return true;
149 }
150 
SuggestSanitizedName(uint32_t id,const std::string & suggested_name)151 bool Namer::SuggestSanitizedName(uint32_t id,
152                                  const std::string& suggested_name) {
153   if (HasName(id)) {
154     return false;
155   }
156 
157   return Register(id, FindUnusedDerivedName(Sanitize(suggested_name)));
158 }
159 
SuggestSanitizedMemberName(uint32_t struct_id,uint32_t member_index,const std::string & suggested_name)160 bool Namer::SuggestSanitizedMemberName(uint32_t struct_id,
161                                        uint32_t member_index,
162                                        const std::string& suggested_name) {
163   // Creates an empty vector the first time we visit this struct.
164   auto& name_vector = struct_member_names_[struct_id];
165   // Resizing will set new entries to the empty string.
166   name_vector.resize(std::max(name_vector.size(), size_t(member_index + 1)));
167   auto& entry = name_vector[member_index];
168   if (entry.empty()) {
169     entry = Sanitize(suggested_name);
170     return true;
171   }
172   return false;
173 }
174 
ResolveMemberNamesForStruct(uint32_t struct_id,uint32_t num_members)175 void Namer::ResolveMemberNamesForStruct(uint32_t struct_id,
176                                         uint32_t num_members) {
177   auto& name_vector = struct_member_names_[struct_id];
178   // Resizing will set new entries to the empty string.
179   // It would have been an error if the client had registered a name for
180   // an out-of-bounds member index, so toss those away.
181   name_vector.resize(num_members);
182 
183   std::unordered_set<std::string> used_names;
184 
185   // Returns a name, based on the suggestion, which does not equal
186   // any name in the used_names set.
187   auto disambiguate_name =
188       [&used_names](const std::string& suggestion) -> std::string {
189     if (used_names.find(suggestion) == used_names.end()) {
190       // There is no collision.
191       return suggestion;
192     }
193 
194     uint32_t i = 1;
195     std::string new_name;
196     do {
197       std::stringstream new_name_stream;
198       new_name_stream << suggestion << "_" << i;
199       new_name = new_name_stream.str();
200       ++i;
201     } while (used_names.find(new_name) != used_names.end());
202     return new_name;
203   };
204 
205   // First ensure uniqueness among names for which we have already taken
206   // suggestions.
207   for (auto& name : name_vector) {
208     if (!name.empty()) {
209       // This modifies the names in-place, i.e. update the name_vector
210       // entries.
211       name = disambiguate_name(name);
212       used_names.insert(name);
213     }
214   }
215 
216   // Now ensure uniqueness among the rest.  Doing this in a second pass
217   // allows us to preserve suggestions as much as possible.  Otherwise
218   // a generated name such as 'field1' might collide with a user-suggested
219   // name of 'field1' attached to a later member.
220   uint32_t index = 0;
221   for (auto& name : name_vector) {
222     if (name.empty()) {
223       std::stringstream suggestion;
224       suggestion << "field" << index;
225       // Again, modify the name-vector in-place.
226       name = disambiguate_name(suggestion.str());
227       used_names.insert(name);
228     }
229     index++;
230   }
231 }
232 
233 }  // namespace spirv
234 }  // namespace reader
235 }  // namespace tint
236