39
39
#include " WasmModuleParser.h"
40
40
#include " WasmValidate.h"
41
41
#include < wtf/DataLog.h>
42
+ #include < wtf/Locker.h>
43
+ #include < wtf/MonotonicTime.h>
44
+ #include < wtf/NumberOfCores.h>
42
45
#include < wtf/StdLibExtras.h>
43
46
#include < wtf/text/StringBuilder.h>
44
47
@@ -60,6 +63,10 @@ Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength)
60
63
61
64
bool Plan::parseAndValidateModule ()
62
65
{
66
+ MonotonicTime startTime;
67
+ if (verbose || Options::reportCompileTimes ())
68
+ startTime = MonotonicTime::now ();
69
+
63
70
{
64
71
ModuleParser moduleParser (m_vm, m_source, m_sourceLength);
65
72
auto parseResult = moduleParser.parse ();
@@ -78,7 +85,7 @@ bool Plan::parseAndValidateModule()
78
85
dataLogLn (" Processing function starting at: " , m_functionLocationInBinary[functionIndex].start , " and ending at: " , m_functionLocationInBinary[functionIndex].end );
79
86
const uint8_t * functionStart = m_source + m_functionLocationInBinary[functionIndex].start ;
80
87
size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start ;
81
- ASSERT (Checked< uintptr_t >(bitwise_cast< uintptr_t >(functionStart)) + functionLength <= Checked< uintptr_t >(bitwise_cast< uintptr_t >(m_source)) + m_sourceLength);
88
+ ASSERT (functionLength <= m_sourceLength);
82
89
SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices [functionIndex];
83
90
const Signature* signature = SignatureInformation::get (m_vm, signatureIndex);
84
91
@@ -94,9 +101,15 @@ bool Plan::parseAndValidateModule()
94
101
}
95
102
}
96
103
104
+ if (verbose || Options::reportCompileTimes ())
105
+ dataLogLn (" Took " , (MonotonicTime::now () - startTime).microseconds (), " us to validate module" );
97
106
return true ;
98
107
}
99
108
109
+ // We are creating a bunch of threads that touch the main thread's stack. This will make ASAN unhappy.
110
+ // The reason this is OK is that we guarantee that the main thread doesn't continue until all threads
111
+ // that could touch its stack are done executing.
112
+ SUPPRESS_ASAN
100
113
void Plan::run ()
101
114
{
102
115
if (!parseAndValidateModule ())
@@ -114,12 +127,16 @@ void Plan::run()
114
127
return true ;
115
128
};
116
129
117
- Vector<Vector<UnlinkedWasmToWasmCall>> unlinkedWasmToWasmCalls;
118
130
if (!tryReserveCapacity (m_wasmToJSStubs, m_moduleInformation->importFunctionSignatureIndices .size (), " WebAssembly to JavaScript stubs" )
119
- || !tryReserveCapacity (unlinkedWasmToWasmCalls, m_functionLocationInBinary.size (), " unlinked WebAssembly to WebAssembly calls" )
120
- || !tryReserveCapacity (m_wasmInternalFunctions, m_functionLocationInBinary.size (), " WebAssembly functions" ))
131
+ || !tryReserveCapacity (m_unlinkedWasmToWasmCalls, m_functionLocationInBinary.size (), " unlinked WebAssembly to WebAssembly calls" )
132
+ || !tryReserveCapacity (m_wasmInternalFunctions, m_functionLocationInBinary.size (), " WebAssembly functions" )
133
+ || !tryReserveCapacity (m_compilationContexts, m_functionLocationInBinary.size (), " compilation contexts" ))
121
134
return ;
122
135
136
+ m_unlinkedWasmToWasmCalls.resize (m_functionLocationInBinary.size ());
137
+ m_wasmInternalFunctions.resize (m_functionLocationInBinary.size ());
138
+ m_compilationContexts.resize (m_functionLocationInBinary.size ());
139
+
123
140
for (unsigned importIndex = 0 ; importIndex < m_moduleInformation->imports .size (); ++importIndex) {
124
141
Import* import = &m_moduleInformation->imports [importIndex];
125
142
if (import->kind != ExternalKind::Function)
@@ -132,31 +149,92 @@ void Plan::run()
132
149
m_functionIndexSpace.buffer .get ()[importFunctionIndex].code = m_wasmToJSStubs[importFunctionIndex].code ().executableAddress ();
133
150
}
134
151
135
- for (unsigned functionIndex = 0 ; functionIndex < m_functionLocationInBinary.size (); ++functionIndex) {
136
- if (verbose)
137
- dataLogLn (" Processing function starting at: " , m_functionLocationInBinary[functionIndex].start , " and ending at: " , m_functionLocationInBinary[functionIndex].end );
138
- const uint8_t * functionStart = m_source + m_functionLocationInBinary[functionIndex].start ;
139
- size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start ;
140
- ASSERT (functionLength <= m_sourceLength);
141
- SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices [functionIndex];
142
- const Signature* signature = SignatureInformation::get (m_vm, signatureIndex);
143
- unsigned functionIndexSpace = m_wasmToJSStubs.size () + functionIndex;
144
- ASSERT (m_functionIndexSpace.buffer .get ()[functionIndexSpace].signatureIndex == signatureIndex);
152
+ m_currentIndex = 0 ;
153
+
154
+ auto doWork = [this ] {
155
+ while (true ) {
156
+ uint32_t functionIndex;
157
+ {
158
+ auto locker = holdLock (m_lock);
159
+ if (m_currentIndex >= m_functionLocationInBinary.size ())
160
+ return ;
161
+ functionIndex = m_currentIndex;
162
+ ++m_currentIndex;
163
+ }
164
+
165
+ const uint8_t * functionStart = m_source + m_functionLocationInBinary[functionIndex].start ;
166
+ size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start ;
167
+ ASSERT (functionLength <= m_sourceLength);
168
+ SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices [functionIndex];
169
+ const Signature* signature = SignatureInformation::get (m_vm, signatureIndex);
170
+ unsigned functionIndexSpace = m_wasmToJSStubs.size () + functionIndex;
171
+ ASSERT_UNUSED (functionIndexSpace, m_functionIndexSpace.buffer .get ()[functionIndexSpace].signatureIndex == signatureIndex);
172
+ ASSERT (validateFunction (m_vm, functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation));
145
173
146
- ASSERT (validateFunction (m_vm, functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation));
174
+ m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
175
+ auto parseAndCompileResult = parseAndCompile (*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], m_functionIndexSpace, *m_moduleInformation);
147
176
148
- unlinkedWasmToWasmCalls.uncheckedAppend (Vector<UnlinkedWasmToWasmCall>());
149
- auto parseAndCompileResult = parseAndCompile (*m_vm, functionStart, functionLength, signature, unlinkedWasmToWasmCalls.at (functionIndex), m_functionIndexSpace, *m_moduleInformation);
150
- if (UNLIKELY (!parseAndCompileResult)) {
151
- m_errorMessage = parseAndCompileResult.error ();
152
- return ; // FIXME make this an Expected.
177
+ if (UNLIKELY (!parseAndCompileResult)) {
178
+ auto locker = holdLock (m_lock);
179
+ if (!m_errorMessage) {
180
+ // Multiple compiles could fail simultaneously. We arbitrarily choose the first.
181
+ m_errorMessage = parseAndCompileResult.error (); // FIXME make this an Expected.
182
+ }
183
+ m_currentIndex = m_functionLocationInBinary.size ();
184
+
185
+ // We will terminate on the next execution.
186
+ continue ;
187
+ }
188
+
189
+ m_wasmInternalFunctions[functionIndex] = WTFMove (*parseAndCompileResult);
190
+ }
191
+ };
192
+
193
+ MonotonicTime startTime;
194
+ if (verbose || Options::reportCompileTimes ())
195
+ startTime = MonotonicTime::now ();
196
+
197
+ uint32_t threadCount = WTF::numberOfProcessorCores ();
198
+ uint32_t numWorkerThreads = threadCount - 1 ;
199
+ Vector<ThreadIdentifier> threads;
200
+ threads.reserveCapacity (numWorkerThreads);
201
+ for (uint32_t i = 0 ; i < numWorkerThreads; i++)
202
+ threads.uncheckedAppend (createThread (" jsc.wasm-b3-compilation.thread" , doWork));
203
+
204
+ doWork (); // Let the main thread do some work too.
205
+
206
+ for (uint32_t i = 0 ; i < numWorkerThreads; i++)
207
+ waitForThreadCompletion (threads[i]);
208
+
209
+ for (uint32_t functionIndex = 0 ; functionIndex < m_functionLocationInBinary.size (); functionIndex++) {
210
+ {
211
+ CompilationContext& context = m_compilationContexts[functionIndex];
212
+ {
213
+ LinkBuffer linkBuffer (*m_vm, *context.wasmEntrypointJIT , nullptr );
214
+ m_wasmInternalFunctions[functionIndex]->wasmEntrypoint .compilation =
215
+ std::make_unique<B3::Compilation>(FINALIZE_CODE (linkBuffer, (" Wasm function" )), WTFMove (context.wasmEntrypointByproducts ));
216
+ }
217
+
218
+ {
219
+ LinkBuffer linkBuffer (*m_vm, *context.jsEntrypointJIT , nullptr );
220
+ linkBuffer.link (context.jsEntrypointToWasmEntrypointCall , FunctionPtr (m_wasmInternalFunctions[functionIndex]->wasmEntrypoint .compilation ->code ().executableAddress ()));
221
+
222
+ m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint .compilation =
223
+ std::make_unique<B3::Compilation>(FINALIZE_CODE (linkBuffer, (" Wasm JS entrypoint" )), WTFMove (context.jsEntrypointByproducts ));
224
+ }
153
225
}
154
- m_wasmInternalFunctions.uncheckedAppend (WTFMove (*parseAndCompileResult));
226
+
227
+ unsigned functionIndexSpace = m_wasmToJSStubs.size () + functionIndex;
155
228
m_functionIndexSpace.buffer .get ()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->wasmEntrypoint .compilation ->code ().executableAddress ();
156
229
}
157
230
231
+ if (verbose || Options::reportCompileTimes ()) {
232
+ dataLogLn (" Took " , (MonotonicTime::now () - startTime).microseconds (),
233
+ " us to compile and link the module" );
234
+ }
235
+
158
236
// Patch the call sites for each WebAssembly function.
159
- for (auto & unlinked : unlinkedWasmToWasmCalls ) {
237
+ for (auto & unlinked : m_unlinkedWasmToWasmCalls ) {
160
238
for (auto & call : unlinked)
161
239
MacroAssembler::repatchCall (call.callLocation , CodeLocationLabel (m_functionIndexSpace.buffer .get ()[call.functionIndex ].code ));
162
240
}
0 commit comments