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
37namespace llvm {
38namespace yaml {
39template <> 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
52template <> 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
63LLVM_YAML_IS_SEQUENCE_VECTOR(glow::DeviceConfigHelper);
64
65LLVM_YAML_IS_STRING_MAP(std::string);
66
67namespace glow {
68llvm::raw_ostream &operator<<(llvm::raw_ostream &os, void *ptr) {
69 std::ostringstream stringstream;
70 stringstream << ptr;
71 return os << stringstream.str();
72}
73
74llvm::raw_ostream &outs() { return llvm::outs(); }
75
76llvm::raw_ostream &errs() { return llvm::errs(); }
77
78llvm::raw_ostream &dbgs() { return llvm::dbgs(); }
79
80std::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}
104std::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}
109std::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
151void report(const char *msg) { errs() << msg; }
152
153const 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.
175const 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
199std::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.
234const 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
246template <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
262std::vector<DeviceConfigHelper>
263deserializeDeviceConfigFromYaml(llvm::StringRef fileName) {
264 return deserializeFromYaml<std::vector<DeviceConfigHelper>>(fileName);
265}
266
267std::map<std::string, std::string>
268deserializeStrStrMapFromYaml(llvm::StringRef fileName) {
269 return deserializeFromYaml<std::map<std::string, std::string>>(fileName);
270}
271
272Expected<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
282Expected<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