Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
6 : #define V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
7 :
8 : #include <cstdio>
9 : #include <cstring>
10 :
11 : #include "src/globals.h"
12 : #include "src/snapshot/snapshot.h"
13 : #include "src/source-position-table.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : enum DataDirective {
19 : kByte,
20 : kLong,
21 : kQuad,
22 : kOcta,
23 : };
24 :
25 : static constexpr char kDefaultEmbeddedVariant[] = "Default";
26 :
27 : // The platform-dependent logic for emitting assembly code for the generated
28 : // embedded.S file.
29 : class EmbeddedFileWriter;
30 : class PlatformDependentEmbeddedFileWriter final {
31 : public:
32 1 : void SetFile(FILE* fp) { fp_ = fp; }
33 :
34 : void SectionText();
35 : void SectionData();
36 : void SectionRoData();
37 :
38 : void AlignToCodeAlignment();
39 : void AlignToDataAlignment();
40 :
41 : void DeclareUint32(const char* name, uint32_t value);
42 : void DeclarePointerToSymbol(const char* name, const char* target);
43 :
44 : void DeclareLabel(const char* name);
45 :
46 : void SourceInfo(int fileid, int line);
47 : void DeclareFunctionBegin(const char* name);
48 : void DeclareFunctionEnd(const char* name);
49 :
50 : // Returns the number of printed characters.
51 : int HexLiteral(uint64_t value);
52 :
53 : void Comment(const char* string);
54 4266 : void Newline() { fprintf(fp_, "\n"); }
55 :
56 : void FilePrologue();
57 : void DeclareExternalFilename(int fileid, const char* filename);
58 : void FileEpilogue();
59 :
60 : int IndentedDataDirective(DataDirective directive);
61 :
62 172020 : FILE* fp() const { return fp_; }
63 :
64 : private:
65 : void DeclareSymbolGlobal(const char* name);
66 :
67 : private:
68 : FILE* fp_ = nullptr;
69 : };
70 :
71 : // When writing out compiled builtins to a file, we
72 : // Detailed source-code information about builtins can only be obtained by
73 : // registration on the isolate during compilation.
74 1 : class EmbeddedFileWriterInterface {
75 : public:
76 : // We maintain a database of filenames to synthetic IDs.
77 : virtual int LookupOrAddExternallyCompiledFilename(const char* filename) = 0;
78 : virtual const char* GetExternallyCompiledFilename(int index) const = 0;
79 : virtual int GetExternallyCompiledFilenameCount() const = 0;
80 :
81 : // The isolate will call the method below just prior to replacing the
82 : // compiled builtin Code objects with trampolines.
83 : virtual void PrepareBuiltinSourcePositionMap(Builtins* builtins) = 0;
84 : };
85 :
86 : // Generates the embedded.S file which is later compiled into the final v8
87 : // binary. Its contents are exported through two symbols:
88 : //
89 : // v8_<variant>_embedded_blob_ (intptr_t):
90 : // a pointer to the start of the embedded blob.
91 : // v8_<variant>_embedded_blob_size_ (uint32_t):
92 : // size of the embedded blob in bytes.
93 : //
94 : // The variant is usually "Default" but can be modified in multisnapshot builds.
95 1511 : class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
96 : public:
97 13592 : int LookupOrAddExternallyCompiledFilename(const char* filename) override {
98 : auto result = external_filenames_.find(filename);
99 13592 : if (result != external_filenames_.end()) {
100 13559 : return result->second;
101 : }
102 : int new_id =
103 33 : ExternalFilenameIndexToId(static_cast<int>(external_filenames_.size()));
104 66 : external_filenames_.insert(std::make_pair(filename, new_id));
105 33 : external_filenames_by_index_.push_back(filename);
106 : DCHECK_EQ(external_filenames_by_index_.size(), external_filenames_.size());
107 33 : return new_id;
108 : }
109 :
110 0 : const char* GetExternallyCompiledFilename(int fileid) const override {
111 0 : size_t index = static_cast<size_t>(ExternalFilenameIdToIndex(fileid));
112 : DCHECK_GE(index, 0);
113 : DCHECK_LT(index, external_filenames_by_index_.size());
114 :
115 0 : return external_filenames_by_index_[index];
116 : }
117 :
118 0 : int GetExternallyCompiledFilenameCount() const override {
119 0 : return static_cast<int>(external_filenames_.size());
120 : }
121 :
122 : void PrepareBuiltinSourcePositionMap(Builtins* builtins) override;
123 :
124 : void SetEmbeddedFile(const char* embedded_src_path) {
125 1 : embedded_src_path_ = embedded_src_path;
126 : }
127 :
128 : void SetEmbeddedVariant(const char* embedded_variant) {
129 1 : embedded_variant_ = embedded_variant;
130 : }
131 :
132 : void WriteEmbedded(const i::EmbeddedData* blob) const {
133 1 : MaybeWriteEmbeddedFile(blob);
134 : }
135 :
136 : private:
137 2 : void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const {
138 1 : if (embedded_src_path_ == nullptr) return;
139 :
140 1 : FILE* fp = GetFileDescriptorOrDie(embedded_src_path_);
141 :
142 1 : PlatformDependentEmbeddedFileWriter writer;
143 : writer.SetFile(fp);
144 :
145 1 : WriteFilePrologue(&writer);
146 1 : WriteExternalFilenames(&writer);
147 1 : WriteMetadataSection(&writer, blob);
148 1 : WriteInstructionStreams(&writer, blob);
149 1 : WriteFileEpilogue(&writer, blob);
150 :
151 1 : fclose(fp);
152 : }
153 :
154 1 : static FILE* GetFileDescriptorOrDie(const char* filename) {
155 1 : FILE* fp = v8::base::OS::FOpen(filename, "wb");
156 1 : if (fp == nullptr) {
157 0 : i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
158 0 : exit(1);
159 : }
160 1 : return fp;
161 : }
162 :
163 1 : void WriteFilePrologue(PlatformDependentEmbeddedFileWriter* w) const {
164 1 : w->Comment("Autogenerated file. Do not edit.");
165 : w->Newline();
166 1 : w->FilePrologue();
167 1 : }
168 :
169 1 : void WriteExternalFilenames(PlatformDependentEmbeddedFileWriter* w) const {
170 : w->Comment(
171 1 : "Source positions in the embedded blob refer to filenames by id.");
172 1 : w->Comment("Assembly directives here map the id to a filename.");
173 : w->Newline();
174 :
175 : // Write external filenames.
176 35 : int size = static_cast<int>(external_filenames_by_index_.size());
177 35 : for (int i = 0; i < size; i++) {
178 : w->DeclareExternalFilename(ExternalFilenameIndexToId(i),
179 99 : external_filenames_by_index_[i]);
180 : }
181 1 : }
182 :
183 : // Fairly arbitrary but should fit all symbol names.
184 : static constexpr int kTemporaryStringLength = 256;
185 :
186 1 : void WriteMetadataSection(PlatformDependentEmbeddedFileWriter* w,
187 1 : const i::EmbeddedData* blob) const {
188 : char embedded_blob_data_symbol[kTemporaryStringLength];
189 : i::SNPrintF(i::Vector<char>(embedded_blob_data_symbol),
190 2 : "v8_%s_embedded_blob_data_", embedded_variant_);
191 :
192 1 : w->Comment("The embedded blob starts here. Metadata comes first, followed");
193 1 : w->Comment("by builtin instruction streams.");
194 1 : w->SectionText();
195 1 : w->AlignToCodeAlignment();
196 1 : w->DeclareLabel(embedded_blob_data_symbol);
197 :
198 : WriteBinaryContentsAsInlineAssembly(w, blob->data(),
199 1 : i::EmbeddedData::RawDataOffset());
200 1 : }
201 :
202 1505 : void WriteBuiltin(PlatformDependentEmbeddedFileWriter* w,
203 : const i::EmbeddedData* blob, const int builtin_id) const {
204 : const bool is_default_variant =
205 1505 : std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0;
206 :
207 : char builtin_symbol[kTemporaryStringLength];
208 1505 : if (is_default_variant) {
209 : // Create nicer symbol names for the default mode.
210 : i::SNPrintF(i::Vector<char>(builtin_symbol), "Builtins_%s",
211 3010 : i::Builtins::name(builtin_id));
212 : } else {
213 : i::SNPrintF(i::Vector<char>(builtin_symbol), "%s_Builtins_%s",
214 0 : embedded_variant_, i::Builtins::name(builtin_id));
215 : }
216 :
217 : // Labels created here will show up in backtraces. We check in
218 : // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e.
219 : // that labels do not insert bytes into the middle of the blob byte
220 : // stream.
221 1505 : w->DeclareFunctionBegin(builtin_symbol);
222 1505 : const std::vector<byte>& current_positions = source_positions_[builtin_id];
223 :
224 : // The code below interleaves bytes of assembly code for the builtin
225 : // function with source positions at the appropriate offsets.
226 : Vector<const byte> vpos(current_positions.data(), current_positions.size());
227 : v8::internal::SourcePositionTableIterator positions(
228 1505 : vpos, SourcePositionTableIterator::kExternalOnly);
229 :
230 : const uint8_t* data = reinterpret_cast<const uint8_t*>(
231 1505 : blob->InstructionStartOfBuiltin(builtin_id));
232 : uint32_t size = blob->PaddedInstructionSizeOfBuiltin(builtin_id);
233 : uint32_t i = 0;
234 : uint32_t next_offset = static_cast<uint32_t>(
235 1505 : positions.done() ? size : positions.code_offset());
236 5455 : while (i < size) {
237 2445 : if (i == next_offset) {
238 : // Write source directive.
239 : w->SourceInfo(positions.source_position().ExternalFileId(),
240 1880 : positions.source_position().ExternalLine());
241 940 : positions.Advance();
242 : next_offset = static_cast<uint32_t>(
243 940 : positions.done() ? size : positions.code_offset());
244 : }
245 2445 : CHECK_GE(next_offset, i);
246 2445 : WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i);
247 : i = next_offset;
248 : }
249 :
250 1505 : w->DeclareFunctionEnd(builtin_symbol);
251 1505 : }
252 :
253 1 : void WriteInstructionStreams(PlatformDependentEmbeddedFileWriter* w,
254 : const i::EmbeddedData* blob) const {
255 1506 : for (int i = 0; i < i::Builtins::builtin_count; i++) {
256 1505 : if (!blob->ContainsBuiltin(i)) continue;
257 :
258 1505 : WriteBuiltin(w, blob, i);
259 : }
260 : w->Newline();
261 1 : }
262 :
263 1 : void WriteFileEpilogue(PlatformDependentEmbeddedFileWriter* w,
264 1 : const i::EmbeddedData* blob) const {
265 : {
266 : char embedded_blob_data_symbol[kTemporaryStringLength];
267 : i::SNPrintF(i::Vector<char>(embedded_blob_data_symbol),
268 2 : "v8_%s_embedded_blob_data_", embedded_variant_);
269 :
270 : char embedded_blob_symbol[kTemporaryStringLength];
271 : i::SNPrintF(i::Vector<char>(embedded_blob_symbol), "v8_%s_embedded_blob_",
272 2 : embedded_variant_);
273 :
274 1 : w->Comment("Pointer to the beginning of the embedded blob.");
275 1 : w->SectionData();
276 1 : w->AlignToDataAlignment();
277 : w->DeclarePointerToSymbol(embedded_blob_symbol,
278 1 : embedded_blob_data_symbol);
279 : w->Newline();
280 : }
281 :
282 : {
283 : char embedded_blob_size_symbol[kTemporaryStringLength];
284 : i::SNPrintF(i::Vector<char>(embedded_blob_size_symbol),
285 2 : "v8_%s_embedded_blob_size_", embedded_variant_);
286 :
287 1 : w->Comment("The size of the embedded blob in bytes.");
288 1 : w->SectionRoData();
289 1 : w->AlignToDataAlignment();
290 1 : w->DeclareUint32(embedded_blob_size_symbol, blob->size());
291 : w->Newline();
292 : }
293 :
294 1 : w->FileEpilogue();
295 1 : }
296 :
297 : #if defined(_MSC_VER) && !defined(__clang__)
298 : #define V8_COMPILER_IS_MSVC
299 : #endif
300 :
301 : #if defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
302 : // Windows MASM doesn't have an .octa directive, use QWORDs instead.
303 : // Note: MASM *really* does not like large data streams. It takes over 5
304 : // minutes to assemble the ~350K lines of embedded.S produced when using
305 : // BYTE directives in a debug build. QWORD produces roughly 120KLOC and
306 : // reduces assembly time to ~40 seconds. Still terrible, but much better
307 : // than before. See also: https://crbug.com/v8/8475.
308 :
309 : // GCC MASM on Aix doesn't have an .octa directive, use .llong instead.
310 :
311 : static constexpr DataDirective kByteChunkDirective = kQuad;
312 : static constexpr int kByteChunkSize = 8;
313 :
314 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
315 : int current_line_length, const uint8_t* data) {
316 : const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
317 : return current_line_length + w->HexLiteral(*quad_ptr);
318 : }
319 : #else // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
320 : static constexpr DataDirective kByteChunkDirective = kOcta;
321 : static constexpr int kByteChunkSize = 16;
322 :
323 72812 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
324 : int current_line_length, const uint8_t* data) {
325 : const size_t size = kInt64Size;
326 :
327 : uint64_t part1, part2;
328 : // Use memcpy for the reads since {data} is not guaranteed to be aligned.
329 : #ifdef V8_TARGET_BIG_ENDIAN
330 : memcpy(&part1, data, size);
331 : memcpy(&part2, data + size, size);
332 : #else
333 : memcpy(&part1, data + size, size);
334 : memcpy(&part2, data, size);
335 : #endif // V8_TARGET_BIG_ENDIAN
336 :
337 72812 : if (part1 != 0) {
338 : current_line_length +=
339 72812 : fprintf(w->fp(), "0x%" PRIx64 "%016" PRIx64, part1, part2);
340 : } else {
341 0 : current_line_length += fprintf(w->fp(), "0x%" PRIx64, part2);
342 : }
343 72812 : return current_line_length;
344 : }
345 : #endif // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
346 : #undef V8_COMPILER_IS_MSVC
347 :
348 103468 : static int WriteDirectiveOrSeparator(PlatformDependentEmbeddedFileWriter* w,
349 : int current_line_length,
350 : DataDirective directive) {
351 : int printed_chars;
352 103468 : if (current_line_length == 0) {
353 39640 : printed_chars = w->IndentedDataDirective(directive);
354 : DCHECK_LT(0, printed_chars);
355 : } else {
356 : printed_chars = fprintf(w->fp(), ",");
357 : DCHECK_EQ(1, printed_chars);
358 : }
359 103468 : return current_line_length + printed_chars;
360 : }
361 :
362 : static int WriteLineEndIfNeeded(PlatformDependentEmbeddedFileWriter* w,
363 : int current_line_length, int write_size) {
364 : static const int kTextWidth = 100;
365 : // Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use
366 : // the actual size of the string to be written to determine this so it's
367 : // more conservative than strictly needed.
368 103468 : if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) {
369 : fprintf(w->fp(), "\n");
370 : return 0;
371 : } else {
372 : return current_line_length;
373 : }
374 : }
375 :
376 2446 : static void WriteBinaryContentsAsInlineAssembly(
377 : PlatformDependentEmbeddedFileWriter* w, const uint8_t* data,
378 : uint32_t size) {
379 : int current_line_length = 0;
380 : uint32_t i = 0;
381 :
382 : // Begin by writing out byte chunks.
383 77704 : for (; i + kByteChunkSize < size; i += kByteChunkSize) {
384 : current_line_length = WriteDirectiveOrSeparator(w, current_line_length,
385 72812 : kByteChunkDirective);
386 72812 : current_line_length = WriteByteChunk(w, current_line_length, data + i);
387 : current_line_length =
388 : WriteLineEndIfNeeded(w, current_line_length, kByteChunkSize);
389 : }
390 2446 : if (current_line_length != 0) w->Newline();
391 : current_line_length = 0;
392 :
393 : // Write any trailing bytes one-by-one.
394 30656 : for (; i < size; i++) {
395 : current_line_length =
396 30656 : WriteDirectiveOrSeparator(w, current_line_length, kByte);
397 30656 : current_line_length += w->HexLiteral(data[i]);
398 : current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1);
399 : }
400 :
401 2446 : if (current_line_length != 0) w->Newline();
402 2446 : }
403 :
404 : static int ExternalFilenameIndexToId(int index) {
405 66 : return kFirstExternalFilenameId + index;
406 : }
407 :
408 : static int ExternalFilenameIdToIndex(int id) {
409 0 : return id - kFirstExternalFilenameId;
410 : }
411 :
412 : std::vector<byte> source_positions_[Builtins::builtin_count];
413 :
414 : // In assembly directives, filename ids need to begin with 1.
415 : static const int kFirstExternalFilenameId = 1;
416 : std::map<const char*, int> external_filenames_;
417 : std::vector<const char*> external_filenames_by_index_;
418 :
419 : const char* embedded_src_path_ = nullptr;
420 : const char* embedded_variant_ = kDefaultEmbeddedVariant;
421 : };
422 :
423 : } // namespace internal
424 : } // namespace v8
425 :
426 : #endif // V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
|