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 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 : enum DataDirective {
18 : kByte,
19 : kLong,
20 : kQuad,
21 : kOcta,
22 : };
23 :
24 : static constexpr char kDefaultEmbeddedVariant[] = "Default";
25 :
26 : // The platform-dependent logic for emitting assembly code for the generated
27 : // embedded.S file.
28 : class PlatformDependentEmbeddedFileWriter final {
29 : public:
30 1 : void SetFile(FILE* fp) { fp_ = fp; }
31 :
32 : void SectionText();
33 : void SectionData();
34 : void SectionRoData();
35 :
36 : void AlignToCodeAlignment();
37 : void AlignToDataAlignment();
38 :
39 : void DeclareUint32(const char* name, uint32_t value);
40 : void DeclarePointerToSymbol(const char* name, const char* target);
41 :
42 : void DeclareLabel(const char* name);
43 :
44 : void DeclareFunctionBegin(const char* name);
45 : void DeclareFunctionEnd(const char* name);
46 :
47 : // Returns the number of printed characters.
48 : int HexLiteral(uint64_t value);
49 :
50 : void Comment(const char* string);
51 117 : void Newline() { fprintf(fp_, "\n"); }
52 :
53 : void FilePrologue();
54 : void FileEpilogue();
55 :
56 : int IndentedDataDirective(DataDirective directive);
57 :
58 146608 : FILE* fp() const { return fp_; }
59 :
60 : private:
61 : void DeclareSymbolGlobal(const char* name);
62 :
63 : private:
64 : FILE* fp_ = nullptr;
65 : };
66 :
67 : // Generates the embedded.S file which is later compiled into the final v8
68 : // binary. Its contents are exported through two symbols:
69 : //
70 : // v8_<variant>_embedded_blob_ (intptr_t):
71 : // a pointer to the start of the embedded blob.
72 : // v8_<variant>_embedded_blob_size_ (uint32_t):
73 : // size of the embedded blob in bytes.
74 : //
75 : // The variant is usually "Default" but can be modified in multisnapshot builds.
76 : class EmbeddedFileWriter {
77 : public:
78 : void SetEmbeddedFile(const char* embedded_src_path) {
79 1 : embedded_src_path_ = embedded_src_path;
80 : }
81 :
82 : void SetEmbeddedVariant(const char* embedded_variant) {
83 1 : embedded_variant_ = embedded_variant;
84 : }
85 :
86 : void WriteEmbedded(const i::EmbeddedData* blob) const {
87 1 : MaybeWriteEmbeddedFile(blob);
88 : }
89 :
90 : private:
91 2 : void MaybeWriteEmbeddedFile(const i::EmbeddedData* blob) const {
92 1 : if (embedded_src_path_ == nullptr) return;
93 :
94 1 : FILE* fp = GetFileDescriptorOrDie(embedded_src_path_);
95 :
96 1 : PlatformDependentEmbeddedFileWriter writer;
97 : writer.SetFile(fp);
98 :
99 1 : WriteFilePrologue(&writer);
100 1 : WriteMetadataSection(&writer, blob);
101 1 : WriteInstructionStreams(&writer, blob);
102 1 : WriteFileEpilogue(&writer, blob);
103 :
104 1 : fclose(fp);
105 : }
106 :
107 1 : static FILE* GetFileDescriptorOrDie(const char* filename) {
108 1 : FILE* fp = v8::base::OS::FOpen(filename, "wb");
109 1 : if (fp == nullptr) {
110 0 : i::PrintF("Unable to open file \"%s\" for writing.\n", filename);
111 0 : exit(1);
112 : }
113 1 : return fp;
114 : }
115 :
116 1 : static void WriteFilePrologue(PlatformDependentEmbeddedFileWriter* w) {
117 1 : w->Comment("Autogenerated file. Do not edit.");
118 : w->Newline();
119 1 : w->FilePrologue();
120 1 : }
121 :
122 : // Fairly arbitrary but should fit all symbol names.
123 : static constexpr int kTemporaryStringLength = 256;
124 :
125 1 : void WriteMetadataSection(PlatformDependentEmbeddedFileWriter* w,
126 1 : const i::EmbeddedData* blob) const {
127 : char embedded_blob_data_symbol[kTemporaryStringLength];
128 : i::SNPrintF(i::Vector<char>(embedded_blob_data_symbol),
129 2 : "v8_%s_embedded_blob_data_", embedded_variant_);
130 :
131 1 : w->Comment("The embedded blob starts here. Metadata comes first, followed");
132 1 : w->Comment("by builtin instruction streams.");
133 1 : w->SectionText();
134 1 : w->AlignToCodeAlignment();
135 1 : w->DeclareLabel(embedded_blob_data_symbol);
136 :
137 : WriteBinaryContentsAsInlineAssembly(w, blob->data(),
138 1 : i::EmbeddedData::RawDataOffset());
139 1 : }
140 :
141 1 : void WriteInstructionStreams(PlatformDependentEmbeddedFileWriter* w,
142 : const i::EmbeddedData* blob) const {
143 : const bool is_default_variant =
144 1 : std::strcmp(embedded_variant_, kDefaultEmbeddedVariant) == 0;
145 :
146 1512 : for (int i = 0; i < i::Builtins::builtin_count; i++) {
147 1511 : if (!blob->ContainsBuiltin(i)) continue;
148 :
149 : char builtin_symbol[kTemporaryStringLength];
150 1511 : if (is_default_variant) {
151 : // Create nicer symbol names for the default mode.
152 : i::SNPrintF(i::Vector<char>(builtin_symbol), "Builtins_%s",
153 3022 : i::Builtins::name(i));
154 : } else {
155 : i::SNPrintF(i::Vector<char>(builtin_symbol), "%s_Builtins_%s",
156 0 : embedded_variant_, i::Builtins::name(i));
157 : }
158 :
159 : // Labels created here will show up in backtraces. We check in
160 : // Isolate::SetEmbeddedBlob that the blob layout remains unchanged, i.e.
161 : // that labels do not insert bytes into the middle of the blob byte
162 : // stream.
163 1511 : w->DeclareFunctionBegin(builtin_symbol);
164 : WriteBinaryContentsAsInlineAssembly(
165 : w,
166 1511 : reinterpret_cast<const uint8_t*>(blob->InstructionStartOfBuiltin(i)),
167 1511 : blob->PaddedInstructionSizeOfBuiltin(i));
168 1511 : w->DeclareFunctionEnd(builtin_symbol);
169 : }
170 : w->Newline();
171 1 : }
172 :
173 1 : void WriteFileEpilogue(PlatformDependentEmbeddedFileWriter* w,
174 1 : const i::EmbeddedData* blob) const {
175 : {
176 : char embedded_blob_data_symbol[kTemporaryStringLength];
177 : i::SNPrintF(i::Vector<char>(embedded_blob_data_symbol),
178 2 : "v8_%s_embedded_blob_data_", embedded_variant_);
179 :
180 : char embedded_blob_symbol[kTemporaryStringLength];
181 : i::SNPrintF(i::Vector<char>(embedded_blob_symbol), "v8_%s_embedded_blob_",
182 2 : embedded_variant_);
183 :
184 1 : w->Comment("Pointer to the beginning of the embedded blob.");
185 1 : w->SectionData();
186 1 : w->AlignToDataAlignment();
187 : w->DeclarePointerToSymbol(embedded_blob_symbol,
188 1 : embedded_blob_data_symbol);
189 : w->Newline();
190 : }
191 :
192 : {
193 : char embedded_blob_size_symbol[kTemporaryStringLength];
194 : i::SNPrintF(i::Vector<char>(embedded_blob_size_symbol),
195 2 : "v8_%s_embedded_blob_size_", embedded_variant_);
196 :
197 1 : w->Comment("The size of the embedded blob in bytes.");
198 1 : w->SectionRoData();
199 1 : w->DeclareUint32(embedded_blob_size_symbol, blob->size());
200 : w->Newline();
201 : }
202 :
203 1 : w->FileEpilogue();
204 1 : }
205 :
206 : #if defined(_MSC_VER) && !defined(__clang__)
207 : #define V8_COMPILER_IS_MSVC
208 : #endif
209 :
210 : #if defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
211 : // Windows MASM doesn't have an .octa directive, use QWORDs instead.
212 : // Note: MASM *really* does not like large data streams. It takes over 5
213 : // minutes to assemble the ~350K lines of embedded.S produced when using
214 : // BYTE directives in a debug build. QWORD produces roughly 120KLOC and
215 : // reduces assembly time to ~40 seconds. Still terrible, but much better
216 : // than before. See also: https://crbug.com/v8/8475.
217 :
218 : // GCC MASM on Aix doesn't have an .octa directive, use .llong instead.
219 :
220 : static constexpr DataDirective kByteChunkDirective = kQuad;
221 : static constexpr int kByteChunkSize = 8;
222 :
223 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
224 : int current_line_length, const uint8_t* data) {
225 : const uint64_t* quad_ptr = reinterpret_cast<const uint64_t*>(data);
226 : return current_line_length + w->HexLiteral(*quad_ptr);
227 : }
228 : #else // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
229 : static constexpr DataDirective kByteChunkDirective = kOcta;
230 : static constexpr int kByteChunkSize = 16;
231 :
232 73360 : static int WriteByteChunk(PlatformDependentEmbeddedFileWriter* w,
233 : int current_line_length, const uint8_t* data) {
234 : const uint64_t* quad_ptr1 = reinterpret_cast<const uint64_t*>(data);
235 : const uint64_t* quad_ptr2 = reinterpret_cast<const uint64_t*>(data + 8);
236 :
237 : #ifdef V8_TARGET_BIG_ENDIAN
238 : uint64_t part1 = *quad_ptr1;
239 : uint64_t part2 = *quad_ptr2;
240 : #else
241 73360 : uint64_t part1 = *quad_ptr2;
242 73360 : uint64_t part2 = *quad_ptr1;
243 : #endif // V8_TARGET_BIG_ENDIAN
244 :
245 73360 : if (part1 != 0) {
246 : current_line_length +=
247 73360 : fprintf(w->fp(), "0x%" PRIx64 "%016" PRIx64, part1, part2);
248 : } else {
249 0 : current_line_length += fprintf(w->fp(), "0x%" PRIx64, part2);
250 : }
251 73360 : return current_line_length;
252 : }
253 : #endif // defined(V8_COMPILER_IS_MSVC) || defined(V8_OS_AIX)
254 : #undef V8_COMPILER_IS_MSVC
255 :
256 73360 : static int WriteDirectiveOrSeparator(PlatformDependentEmbeddedFileWriter* w,
257 : int current_line_length,
258 : DataDirective directive) {
259 : int printed_chars;
260 73360 : if (current_line_length == 0) {
261 36615 : printed_chars = w->IndentedDataDirective(directive);
262 : DCHECK_LT(0, printed_chars);
263 : } else {
264 : printed_chars = fprintf(w->fp(), ",");
265 : DCHECK_EQ(1, printed_chars);
266 : }
267 73360 : return current_line_length + printed_chars;
268 : }
269 :
270 : static int WriteLineEndIfNeeded(PlatformDependentEmbeddedFileWriter* w,
271 : int current_line_length, int write_size) {
272 : static const int kTextWidth = 100;
273 : // Check if adding ',0xFF...FF\n"' would force a line wrap. This doesn't use
274 : // the actual size of the string to be written to determine this so it's
275 : // more conservative than strictly needed.
276 73360 : if (current_line_length + strlen(",0x") + write_size * 2 > kTextWidth) {
277 : fprintf(w->fp(), "\n");
278 : return 0;
279 : } else {
280 : return current_line_length;
281 : }
282 : }
283 :
284 1512 : static void WriteBinaryContentsAsInlineAssembly(
285 : PlatformDependentEmbeddedFileWriter* w, const uint8_t* data,
286 : uint32_t size) {
287 : int current_line_length = 0;
288 :
289 : uint32_t i = 0;
290 :
291 : // Begin by writing out byte chunks.
292 74872 : for (; i <= size - kByteChunkSize; i += kByteChunkSize) {
293 : current_line_length = WriteDirectiveOrSeparator(w, current_line_length,
294 73360 : kByteChunkDirective);
295 73360 : current_line_length = WriteByteChunk(w, current_line_length, data + i);
296 : current_line_length =
297 : WriteLineEndIfNeeded(w, current_line_length, kByteChunkSize);
298 : }
299 1512 : if (current_line_length != 0) w->Newline();
300 : current_line_length = 0;
301 :
302 : // Write any trailing bytes one-by-one.
303 0 : for (; i < size; i++) {
304 : current_line_length =
305 0 : WriteDirectiveOrSeparator(w, current_line_length, kByte);
306 0 : current_line_length += w->HexLiteral(data[i]);
307 : current_line_length = WriteLineEndIfNeeded(w, current_line_length, 1);
308 : }
309 1512 : if (current_line_length != 0) w->Newline();
310 1512 : }
311 :
312 : const char* embedded_src_path_ = nullptr;
313 : const char* embedded_variant_ = kDefaultEmbeddedVariant;
314 : };
315 :
316 : } // namespace internal
317 : } // namespace v8
318 :
319 : #endif // V8_SNAPSHOT_EMBEDDED_FILE_WRITER_H_
|