1 | /** |
2 | * Copyright (c) Glow Contributors. See CONTRIBUTORS file. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include "glow/Support/Support.h" |
18 | #include "llvm/Support/Debug.h" |
19 | |
20 | #include "llvm/Support/FileSystem.h" |
21 | #include "llvm/Support/YAMLParser.h" |
22 | #include "llvm/Support/YAMLTraits.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | |
25 | #include "folly/hash/SpookyHashV2.h" |
26 | |
27 | #include <atomic> |
28 | #include <cctype> |
29 | #include <cstdarg> |
30 | #include <mutex> |
31 | #include <shared_mutex> |
32 | #include <sstream> |
33 | #include <string> |
34 | |
35 | #include <unordered_map> |
36 | |
37 | namespace llvm { |
38 | namespace yaml { |
39 | template <> struct BlockScalarTraits<glow::MultiLineStr> { |
40 | static void output(const glow::MultiLineStr &Value, void *Ctxt, |
41 | llvm::raw_ostream &OS) { |
42 | OS << Value.str; |
43 | } |
44 | |
45 | static StringRef input(StringRef Scalar, void *Ctxt, |
46 | glow::MultiLineStr &Value) { |
47 | Value.str = Scalar.str(); |
48 | return StringRef(); |
49 | } |
50 | }; |
51 | |
52 | template <> struct MappingTraits<glow::DeviceConfigHelper> { |
53 | static void mapping(IO &io, glow::DeviceConfigHelper &info) { |
54 | io.mapRequired("name" , info.name_); |
55 | io.mapRequired("backendName" , info.backendName_); |
56 | io.mapRequired("parameters" , info.parameters_); |
57 | } |
58 | }; |
59 | |
60 | } // end namespace yaml |
61 | } // end namespace llvm |
62 | |
63 | LLVM_YAML_IS_SEQUENCE_VECTOR(glow::DeviceConfigHelper); |
64 | |
65 | LLVM_YAML_IS_STRING_MAP(std::string); |
66 | |
67 | namespace glow { |
68 | llvm::raw_ostream &operator<<(llvm::raw_ostream &os, void *ptr) { |
69 | std::ostringstream stringstream; |
70 | stringstream << ptr; |
71 | return os << stringstream.str(); |
72 | } |
73 | |
74 | llvm::raw_ostream &outs() { return llvm::outs(); } |
75 | |
76 | llvm::raw_ostream &errs() { return llvm::errs(); } |
77 | |
78 | llvm::raw_ostream &dbgs() { return llvm::dbgs(); } |
79 | |
80 | std::string separateString(const std::string &str, size_t length, |
81 | const std::string &delimiter) { |
82 | assert(length > 0 && "Separation block length must be greater than 0!" ); |
83 | size_t inpSize = str.size(); |
84 | size_t delSize = delimiter.size(); |
85 | size_t numBlocks = (inpSize + length - 1) / length; |
86 | size_t outSize = inpSize + (numBlocks - 1) * delSize; |
87 | std::string sepStr; |
88 | sepStr.reserve(outSize); |
89 | for (size_t blockIdx = 0; blockIdx < numBlocks; blockIdx++) { |
90 | // Append substring block. |
91 | size_t blockStart = blockIdx * length; |
92 | size_t blockSize = |
93 | (inpSize - blockStart) >= length ? length : (inpSize - blockStart); |
94 | blockSize = (blockSize >= length) ? length : blockSize; |
95 | sepStr.append(str, blockStart, blockSize); |
96 | // Append delimiter. |
97 | if (blockIdx < numBlocks - 1) { |
98 | sepStr.append(delimiter); |
99 | } |
100 | } |
101 | assert(sepStr.size() == outSize && "Inconsistent string separation!" ); |
102 | return sepStr; |
103 | } |
104 | std::string separateString(llvm::StringRef str, size_t length, |
105 | const std::string &delimiter) { |
106 | std::string str1 = str.str(); |
107 | return separateString(str1, length, delimiter); |
108 | } |
109 | std::string escapeDottyString(const std::string &str) { |
110 | std::string out; |
111 | out.reserve(str.capacity()); |
112 | for (unsigned char c : str) { |
113 | if (std::isprint(c) && c != '\\' && c != '"' && c != '<' && c != '>') { |
114 | out += c; |
115 | } else { |
116 | out += "\\" ; |
117 | switch (c) { |
118 | case '"': |
119 | out += "\"" ; |
120 | break; |
121 | case '<': |
122 | out += "<" ; |
123 | break; |
124 | case '>': |
125 | out += ">" ; |
126 | break; |
127 | case '\\': |
128 | out += "\\" ; |
129 | break; |
130 | case '\t': |
131 | out += 't'; |
132 | break; |
133 | case '\r': |
134 | out += 'r'; |
135 | break; |
136 | case '\n': |
137 | // The marker '\l' means left-justify linebreak. |
138 | out += 'l'; |
139 | break; |
140 | default: |
141 | char const *const hexdig = "0123456789ABCDEF" ; |
142 | out += 'x'; |
143 | out += hexdig[c >> 4]; |
144 | out += hexdig[c & 0xF]; |
145 | } |
146 | } |
147 | } |
148 | return out; |
149 | } |
150 | |
151 | void report(const char *msg) { errs() << msg; } |
152 | |
153 | const std::string strFormat(const char *format, ...) { |
154 | // Initialize use of varargs. |
155 | va_list vaArgs; |
156 | va_start(vaArgs, format); |
157 | |
158 | // Create a copy of the varargs. |
159 | va_list vaArgsCopy; |
160 | va_copy(vaArgsCopy, vaArgs); |
161 | // Compute the length of the output to be produced. |
162 | // The vsnprintf call does not actually write anything, but properly computes |
163 | // the amount of characters that would be written. |
164 | const int len = vsnprintf(NULL, 0, format, vaArgsCopy); |
165 | va_end(vaArgsCopy); |
166 | |
167 | // Create a formatted string without any risk of memory issues. |
168 | std::vector<char> str(len + 1); |
169 | std::vsnprintf(str.data(), str.size(), format, vaArgs); |
170 | va_end(vaArgs); |
171 | return std::string(str.data(), len); |
172 | } |
173 | |
174 | /// Create a formatted string that should live until the end of the execution. |
175 | const std::string &staticStrFormat(const char *format, ...) { |
176 | // The storage for strings that should live until the end of the execution. |
177 | static std::vector<std::string> staticStrings; |
178 | // Initialize use of varargs. |
179 | va_list vaArgs; |
180 | va_start(vaArgs, format); |
181 | |
182 | // Create a copy of the varargs. |
183 | va_list vaArgsCopy; |
184 | va_copy(vaArgsCopy, vaArgs); |
185 | // Compute the length of the output to be produced. |
186 | // The vsnprintf call does not actually write anything, but properly computes |
187 | // the amount of characters that would be written. |
188 | const int len = vsnprintf(NULL, 0, format, vaArgsCopy); |
189 | va_end(vaArgsCopy); |
190 | |
191 | // Create a formatted string without any risk of memory issues. |
192 | std::vector<char> str(len + 1); |
193 | std::vsnprintf(str.data(), str.size(), format, vaArgs); |
194 | va_end(vaArgs); |
195 | staticStrings.emplace_back(std::string(str.data(), len)); |
196 | return staticStrings.back(); |
197 | } |
198 | |
199 | std::string legalizeName(llvm::StringRef name, size_t maxLength) { |
200 | std::string legalName; |
201 | |
202 | // Legalize the name. |
203 | for (const char c : name) { |
204 | bool legal = isalpha(c) || isdigit(c) || c == '_'; |
205 | legalName.push_back(legal ? c : '_'); |
206 | } |
207 | |
208 | // Names must start with some alphabetic character or underscore and can't be |
209 | // empty. |
210 | if (legalName.empty() || isdigit(legalName[0])) { |
211 | legalName = "A" + legalName; |
212 | } |
213 | |
214 | if (maxLength == 0 || legalName.size() <= maxLength) { |
215 | return legalName; |
216 | } |
217 | |
218 | // Now we have to deal with long names. |
219 | |
220 | // Assuming that having long names is a rare case, |
221 | // therefore using a good hash function can produce unique enough names. |
222 | std::string truncationSuffix{"_trunc_" }; |
223 | truncationSuffix += std::to_string( |
224 | folly::hash::SpookyHashV2::Hash64(legalName.data(), legalName.size(), 0)); |
225 | |
226 | std::string truncatedName{legalName.data(), |
227 | maxLength - truncationSuffix.size()}; |
228 | truncatedName += truncationSuffix; |
229 | |
230 | return truncatedName; |
231 | } |
232 | |
233 | /// \returns the color based on \p index which is used in dot file. |
234 | const char *getDotFileNodeColor(size_t index) { |
235 | static const char *colorNames[] = { |
236 | "AliceBlue" , "CadetBlue1" , "Coral" , "DarkOliveGreen1" , |
237 | "DarkSeaGreen1" , "GhostWhite" , "Khaki1" , "LavenderBlush1" , |
238 | "LemonChiffon1" , "LightSkyBlue" , "MistyRose1" , "MistyRose2" , |
239 | "PaleTurquoise2" , "PeachPuff1" , "PowderBlue" , "Salmon" , |
240 | "Thistle1" , "Thistle3" , "Wheat1" , "Yellow2" , |
241 | }; |
242 | unsigned arrayLen = sizeof(colorNames) / sizeof(colorNames[0]); |
243 | return colorNames[index % arrayLen]; |
244 | } |
245 | |
246 | template <typename T> static T deserializeFromYaml(llvm::StringRef fileName) { |
247 | T result; |
248 | llvm::outs() << "Deserialize from " << fileName << "\n" ; |
249 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> text = |
250 | llvm::MemoryBuffer::getFileAsStream(fileName); |
251 | assert(!text.getError() && "Unable to open file" ); |
252 | |
253 | std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(*text); |
254 | llvm::yaml::Input yin(buffer->getBuffer()); |
255 | yin >> result; |
256 | |
257 | assert(!yin.error() && "Error reading yaml file" ); |
258 | |
259 | return result; |
260 | } |
261 | |
262 | std::vector<DeviceConfigHelper> |
263 | deserializeDeviceConfigFromYaml(llvm::StringRef fileName) { |
264 | return deserializeFromYaml<std::vector<DeviceConfigHelper>>(fileName); |
265 | } |
266 | |
267 | std::map<std::string, std::string> |
268 | deserializeStrStrMapFromYaml(llvm::StringRef fileName) { |
269 | return deserializeFromYaml<std::map<std::string, std::string>>(fileName); |
270 | } |
271 | |
272 | Expected<int> getIntFromStr(llvm::StringRef input) { |
273 | // StringRef not necessarily null terminated, so get a str from it. |
274 | const std::string inputStr = input.str(); |
275 | char *end; |
276 | int val = std::strtol(inputStr.data(), &end, 10); |
277 | RETURN_ERR_IF_NOT(!(end == inputStr.data() || *end != '\0'), |
278 | "Integer was not properly specified: " + inputStr); |
279 | return val; |
280 | } |
281 | |
282 | Expected<float> getFloatFromStr(llvm::StringRef input) { |
283 | // StringRef not necessarily null terminated, so get a str from it. |
284 | const std::string inputStr = input.str(); |
285 | char *end; |
286 | double val = std::strtod(inputStr.data(), &end); |
287 | RETURN_ERR_IF_NOT(!(end == inputStr.data() || *end != '\0'), |
288 | "Floating point number was not properly specified: " + |
289 | inputStr); |
290 | return (float)val; |
291 | } |
292 | |
293 | } // namespace glow |
294 | |