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, const char* filename, 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 4457 : 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 193831 : 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 1520 : class EmbeddedFileWriter : public EmbeddedFileWriterInterface {
96 : public:
97 15419 : int LookupOrAddExternallyCompiledFilename(const char* filename) override {
98 : auto result = external_filenames_.find(filename);
99 15419 : if (result != external_filenames_.end()) {
100 15354 : return result->second;
101 : }
102 : int new_id =
103 65 : ExternalFilenameIndexToId(static_cast<int>(external_filenames_.size()));
104 130 : external_filenames_.insert(std::make_pair(filename, new_id));
105 65 : external_filenames_by_index_.push_back(filename);
106 : DCHECK_EQ(external_filenames_by_index_.size(), external_filenames_.size());
107 65 : return new_id;
108 : }
109 :
110 1077 : const char* GetExternallyCompiledFilename(int fileid) const override {
111 1077 : 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 1077 : 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 1 : 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 1 : int size = static_cast<int>(external_filenames_by_index_.size());
177 131 : for (int i = 0; i < size; i++) {
178 65 : w->DeclareExternalFilename(ExternalFilenameIndexToId(i),
179 130 : 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 : const i::EmbeddedData* blob) const {
188 : char embedded_blob_data_symbol[kTemporaryStringLength];
189 1 : 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 1515 : void WriteBuiltin(PlatformDependentEmbeddedFileWriter* w,
203 : const i::EmbeddedData* blob, const int builtin_id) const {
204 : const bool is_default_variant =
205 1515 : std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0;
206 :
207 : char builtin_symbol[kTemporaryStringLength];
208 1515 : if (is_default_variant) {
209 : // Create nicer symbol names for the default mode.
210 3030 : i::SNPrintF(i::Vector<char>(builtin_symbol), "Builtins_%s",
211 1515 : i::Builtins::name(builtin_id));
212 : } else {
213 0 : 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 1515 : w->DeclareFunctionBegin(builtin_symbol);
222 1515 : 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 1515 : vpos, SourcePositionTableIterator::kExternalOnly);
229 :
230 : const uint8_t* data = reinterpret_cast<const uint8_t*>(
231 1515 : 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 1515 : positions.done() ? size : positions.code_offset());
236 6699 : while (i < size) {
237 2592 : if (i == next_offset) {
238 : // Write source directive.
239 1077 : w->SourceInfo(positions.source_position().ExternalFileId(),
240 : GetExternallyCompiledFilename(
241 1077 : positions.source_position().ExternalFileId()),
242 1077 : positions.source_position().ExternalLine());
243 1077 : positions.Advance();
244 : next_offset = static_cast<uint32_t>(
245 1077 : positions.done() ? size : positions.code_offset());
246 : }
247 2592 : CHECK_GE(next_offset, i);
248 2592 : WriteBinaryContentsAsInlineAssembly(w, data + i, next_offset - i);
249 : i = next_offset;
250 : }
251 :
252 1515 : w->DeclareFunctionEnd(builtin_symbol);
253 1515 : }
254 :
255 1 : void WriteInstructionStreams(PlatformDependentEmbeddedFileWriter* w,
256 : const i::EmbeddedData* blob) const {
257 3031 : for (int i = 0; i < i::Builtins::builtin_count; i++) {
258 1515 : if (!blob->ContainsBuiltin(i)) continue;
259 :
260 1515 : WriteBuiltin(w, blob, i);
261 : }
262 : w->Newline();
263 1 : }
264 :
265 1 : void WriteFileEpilogue(PlatformDependentEmbeddedFileWriter* w,
266 : const i::EmbeddedData* blob) const {
267 : {
268 : char embedded_blob_data_symbol[kTemporaryStringLength];
269 1 : i::SNPrintF(i::Vector<char>(embedded_blob_data_symbol),
270 2 : "v8_%s_embedded_blob_data_", embedded_variant_);
271 :
272 : char embedded_blob_symbol[kTemporaryStringLength];
273 1 : i::SNPrintF(i::Vector<char>(embedded_blob_symbol), "v8_%s_embedded_blob_",
274 2 : embedded_variant_);
275 :
276 1 : w->Comment("Pointer to the beginning of the embedded blob.");
277 1 : w->SectionData();
278 1 : w->AlignToDataAlignment();
279 : w->DeclarePointerToSymbol(embedded_blob_symbol,
280 1 : embedded_blob_data_symbol);
281 : w->Newline();
282 : }
283 :
284 : {
285 : char embedded_blob_size_symbol[kTemporaryStringLength];
286 1 : i::SNPrintF(i::Vector<char>(embedded_blob_size_symbol),
287 2 : "v8_%s_embedded_blob_size_", embedded_variant_);
288 :
289 1 : w->Comment("The size of the embedded blob in bytes.");
290 1 : w->SectionRoData();
291 1 : w->AlignToDataAlignment();
292 1 : w->DeclareUint32(embedded_blob_size_symbol, blob->size());
293 : w->Newline();
294 : }
295 :
296 1 : w->FileEpilogue();
297 1 : }
298 :
299 : #if defined(_MSC_VER) && !defined(__clang__)
300 : #define V8_COMPILER_IS_MSVC
301 : #endif
302 :
303 : #if defined(V8_COMPILER_IS_MSVC)
304 : // Windows MASM doesn't have an .octa directive, use QWORDs instead.
305 : // Note: MASM *really* does not like large data streams. It takes over 5
306 : // minutes to assemble the ~350K lines of embedded.S produced when using
307 : // BYTE directives in a debug build. QWORD produces roughly 120KLOC and
308 : // reduces assembly time to ~40 seconds. Still terrible, but much better
309 : // than before. See also: https://crbug.com/v8/8475.
310 : static constexpr DataDirective kByteChunkDirective = kQuad;
311 : static constexpr int kByteChunkSize = 8;
312 :
313 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
314 : int current_line_length, const uint8_t* data) {
315 : const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
316 : return current_line_length + w->HexLiteral(*quad_ptr);
317 : }
318 :
319 : #elif defined(V8_OS_AIX)
320 : // PPC uses a fixed 4 byte instruction set, using .long
321 : // to prevent any unnecessary padding.
322 : static constexpr DataDirective kByteChunkDirective = kLong;
323 : static constexpr int kByteChunkSize = 4;
324 :
325 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
326 : int current_line_length, const uint8_t* data) {
327 : const uint32_t* long_ptr = reinterpret_cast<const uint32_t*>(data);
328 : return current_line_length + w->HexLiteral(*long_ptr);
329 : }
330 :
331 : #else // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
332 : static constexpr DataDirective kByteChunkDirective = kOcta;
333 : static constexpr int kByteChunkSize = 16;
334 :
335 83341 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
336 : int current_line_length, const uint8_t* data) {
337 : const size_t size = kInt64Size;
338 :
339 : uint64_t part1, part2;
340 : // Use memcpy for the reads since {data} is not guaranteed to be aligned.
341 : #ifdef V8_TARGET_BIG_ENDIAN
342 : memcpy(&part1, data, size);
343 : memcpy(&part2, data + size, size);
344 : #else
345 : memcpy(&part1, data + size, size);
346 : memcpy(&part2, data, size);
347 : #endif // V8_TARGET_BIG_ENDIAN
348 :
349 83341 : if (part1 != 0) {
350 : current_line_length +=
351 83341 : fprintf(w->fp(), "0x%" PRIx64 "%016" PRIx64, part1, part2);
352 : } else {
353 0 : current_line_length += fprintf(w->fp(), "0x%" PRIx64, part2);
354 : }
355 83341 : return current_line_length;
356 : }
357 : #endif // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
358 : #undef V8_COMPILER_IS_MSVC
359 :
360 114941 : static int WriteDirectiveOrSeparator(PlatformDependentEmbeddedFileWriter* w,
361 : int current_line_length,
362 : DataDirective directive) {
363 : int printed_chars;
364 114941 : if (current_line_length == 0) {
365 45072 : printed_chars = w->IndentedDataDirective(directive);
366 : DCHECK_LT(0, printed_chars);
367 : } else {
368 : printed_chars = fprintf(w->fp(), ",");
369 : DCHECK_EQ(1, printed_chars);
370 : }
371 114941 : return current_line_length + printed_chars;
372 : }
373 :
374 : static int WriteLineEndIfNeeded(PlatformDependentEmbeddedFileWriter* w,
375 : int current_line_length, int write_size) {
376 : static const int kTextWidth = 100;
377 : // Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use
378 : // the actual size of the string to be written to determine this so it's
379 : // more conservative than strictly needed.
380 114941 : if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) {
381 : fprintf(w->fp(), "\n");
382 : return 0;
383 : } else {
384 : return current_line_length;
385 : }
386 : }
387 :
388 2593 : static void WriteBinaryContentsAsInlineAssembly(
389 : PlatformDependentEmbeddedFileWriter* w, const uint8_t* data,
390 : uint32_t size) {
391 : int current_line_length = 0;
392 : uint32_t i = 0;
393 :
394 : // Begin by writing out byte chunks.
395 169275 : for (; i + kByteChunkSize < size; i += kByteChunkSize) {
396 : current_line_length = WriteDirectiveOrSeparator(w, current_line_length,
397 83341 : kByteChunkDirective);
398 83341 : current_line_length = WriteByteChunk(w, current_line_length, data + i);
399 : current_line_length =
400 : WriteLineEndIfNeeded(w, current_line_length, kByteChunkSize);
401 : }
402 2593 : if (current_line_length != 0) w->Newline();
403 : current_line_length = 0;
404 :
405 : // Write any trailing bytes one-by-one.
406 65793 : for (; i < size; i++) {
407 : current_line_length =
408 31600 : WriteDirectiveOrSeparator(w, current_line_length, kByte);
409 31600 : current_line_length += w->HexLiteral(data[i]);
410 : current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1);
411 : }
412 :
413 2593 : if (current_line_length != 0) w->Newline();
414 2593 : }
415 :
416 : static int ExternalFilenameIndexToId(int index) {
417 130 : return kFirstExternalFilenameId + index;
418 : }
419 :
420 : static int ExternalFilenameIdToIndex(int id) {
421 1077 : return id - kFirstExternalFilenameId;
422 : }
423 :
424 : std::vector<byte> source_positions_[Builtins::builtin_count];
425 :
426 : // In assembly directives, filename ids need to begin with 1.
427 : static const int kFirstExternalFilenameId = 1;
428 : std::map<const char*, int> external_filenames_;
429 : std::vector<const char*> external_filenames_by_index_;
430 :
431 : const char* embedded_src_path_ = nullptr;
432 : const char* embedded_variant_ = kDefaultEmbeddedVariant;
433 : };
434 :
435 : } // namespace internal
436 : } // namespace v8
437 :
438 : #endif // V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
|