~mmach/netext73/webkit2gtk

« back to all changes in this revision

Viewing changes to Source/JavaScriptCore/wasm/WasmSlowPaths.cpp

  • Committer: mmach
  • Date: 2023-06-16 17:21:37 UTC
  • Revision ID: netbit73@gmail.com-20230616172137-2rqx6yr96ga9g3kp
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2019 Apple Inc. All rights reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
7
 * 1. Redistributions of source code must retain the above copyright
 
8
 *    notice, this list of conditions and the following disclaimer.
 
9
 * 2. Redistributions in binary form must reproduce the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer in the
 
11
 *    documentation and/or other materials provided with the distribution.
 
12
 *
 
13
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 
14
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
15
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
16
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 
17
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
18
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
19
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
20
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 
21
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
23
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "config.h"
 
27
#include "WasmSlowPaths.h"
 
28
 
 
29
#if ENABLE(WEBASSEMBLY)
 
30
 
 
31
#include "BytecodeStructs.h"
 
32
#include "ExceptionFuzz.h"
 
33
#include "FrameTracers.h"
 
34
#include "LLIntData.h"
 
35
#include "WasmBBQPlan.h"
 
36
#include "WasmCallee.h"
 
37
#include "WasmCompilationMode.h"
 
38
#include "WasmContextInlines.h"
 
39
#include "WasmFunctionCodeBlock.h"
 
40
#include "WasmInstance.h"
 
41
#include "WasmModuleInformation.h"
 
42
#include "WasmOMGForOSREntryPlan.h"
 
43
#include "WasmOMGPlan.h"
 
44
#include "WasmOperations.h"
 
45
#include "WasmSignatureInlines.h"
 
46
#include "WasmThunks.h"
 
47
#include "WasmWorklist.h"
 
48
 
 
49
namespace JSC { namespace LLInt {
 
50
 
 
51
#define WASM_RETURN_TWO(first, second) do { \
 
52
        return encodeResult(first, second); \
 
53
    } while (false)
 
54
 
 
55
#define WASM_END_IMPL() WASM_RETURN_TWO(pc, 0)
 
56
 
 
57
#define WASM_THROW(exceptionType) do { \
 
58
        callFrame->setArgumentCountIncludingThis(static_cast<int>(exceptionType)); \
 
59
        WASM_RETURN_TWO(LLInt::wasmExceptionInstructions(), 0); \
 
60
    } while (false)
 
61
 
 
62
#define WASM_END() do { \
 
63
        WASM_END_IMPL(); \
 
64
    } while (false)
 
65
 
 
66
#define WASM_RETURN(value) do { \
 
67
        callFrame->uncheckedR(instruction.m_dst) = static_cast<EncodedJSValue>(value); \
 
68
        WASM_END_IMPL(); \
 
69
    } while (false)
 
70
 
 
71
#define WASM_CALL_RETURN(targetInstance, callTarget, callTargetTag) do { \
 
72
        WASM_RETURN_TWO(retagCodePtr(callTarget, callTargetTag, SlowPathPtrTag), targetInstance); \
 
73
    } while (false)
 
74
 
 
75
#define CODE_BLOCK() \
 
76
    bitwise_cast<Wasm::FunctionCodeBlock*>(callFrame->codeBlock())
 
77
 
 
78
#define READ(virtualRegister) \
 
79
    (virtualRegister.isConstant() \
 
80
        ? JSValue::decode(CODE_BLOCK()->getConstant(virtualRegister)) \
 
81
        : callFrame->r(virtualRegister))
 
82
 
 
83
inline bool jitCompileAndSetHeuristics(Wasm::LLIntCallee* callee, Wasm::FunctionCodeBlock* codeBlock, Wasm::Instance* instance)
 
84
{
 
85
    Wasm::LLIntTierUpCounter& tierUpCounter = codeBlock->tierUpCounter();
 
86
    if (!tierUpCounter.checkIfOptimizationThresholdReached()) {
 
87
        dataLogLnIf(Options::verboseOSR(), "    JIT threshold should be lifted.");
 
88
        return false;
 
89
    }
 
90
 
 
91
    if (callee->replacement())  {
 
92
        dataLogLnIf(Options::verboseOSR(), "    Code was already compiled.");
 
93
        tierUpCounter.optimizeSoon();
 
94
        return true;
 
95
    }
 
96
 
 
97
    bool compile = false;
 
98
    {
 
99
        auto locker = holdLock(tierUpCounter.m_lock);
 
100
        switch (tierUpCounter.m_compilationStatus) {
 
101
        case Wasm::LLIntTierUpCounter::CompilationStatus::NotCompiled:
 
102
            compile = true;
 
103
            tierUpCounter.m_compilationStatus = Wasm::LLIntTierUpCounter::CompilationStatus::Compiling;
 
104
            break;
 
105
        case Wasm::LLIntTierUpCounter::CompilationStatus::Compiling:
 
106
            tierUpCounter.optimizeAfterWarmUp();
 
107
            break;
 
108
        case Wasm::LLIntTierUpCounter::CompilationStatus::Compiled:
 
109
            break;
 
110
        }
 
111
    }
 
112
 
 
113
    if (compile) {
 
114
        uint32_t functionIndex = codeBlock->functionIndex();
 
115
        RefPtr<Wasm::Plan> plan;
 
116
        if (Options::wasmLLIntTiersUpToBBQ())
 
117
            plan = adoptRef(*new Wasm::BBQPlan(instance->context(), makeRef(const_cast<Wasm::ModuleInformation&>(instance->module().moduleInformation())), functionIndex, instance->codeBlock(), Wasm::Plan::dontFinalize()));
 
118
        else
 
119
            plan = adoptRef(*new Wasm::OMGPlan(instance->context(), Ref<Wasm::Module>(instance->module()), functionIndex, instance->memory()->mode(), Wasm::Plan::dontFinalize()));
 
120
 
 
121
        Wasm::ensureWorklist().enqueue(makeRef(*plan));
 
122
        if (UNLIKELY(!Options::useConcurrentJIT()))
 
123
            plan->waitForCompletion();
 
124
        else
 
125
            tierUpCounter.optimizeAfterWarmUp();
 
126
    }
 
127
 
 
128
    return !!callee->replacement();
 
129
}
 
130
 
 
131
WASM_SLOW_PATH_DECL(prologue_osr)
 
132
{
 
133
    UNUSED_PARAM(pc);
 
134
 
 
135
    Wasm::LLIntCallee* callee = static_cast<Wasm::LLIntCallee*>(callFrame->callee().asWasmCallee());
 
136
    Wasm::FunctionCodeBlock* codeBlock = CODE_BLOCK();
 
137
 
 
138
    dataLogLnIf(Options::verboseOSR(), *callee, ": Entered epilogue_osr with tierUpCounter = ", codeBlock->tierUpCounter());
 
139
 
 
140
    if (!jitCompileAndSetHeuristics(callee, codeBlock, instance))
 
141
        WASM_RETURN_TWO(0, 0);
 
142
 
 
143
    WASM_RETURN_TWO(callee->replacement()->entrypoint().executableAddress(), 0);
 
144
}
 
145
 
 
146
WASM_SLOW_PATH_DECL(loop_osr)
 
147
{
 
148
    if (!Options::useWebAssemblyOSR()) {
 
149
        slow_path_wasm_prologue_osr(callFrame, pc, instance);
 
150
        WASM_RETURN_TWO(0, 0);
 
151
    }
 
152
 
 
153
    Wasm::LLIntCallee* callee = static_cast<Wasm::LLIntCallee*>(callFrame->callee().asWasmCallee());
 
154
    Wasm::FunctionCodeBlock* codeBlock = CODE_BLOCK();
 
155
    Wasm::LLIntTierUpCounter& tierUpCounter = codeBlock->tierUpCounter();
 
156
 
 
157
    dataLogLnIf(Options::verboseOSR(), *callee, ": Entered loop_osr with tierUpCounter = ", codeBlock->tierUpCounter());
 
158
 
 
159
    unsigned loopOSREntryBytecodeOffset = codeBlock->bytecodeOffset(pc);
 
160
    const auto& osrEntryData = tierUpCounter.osrEntryDataForLoop(loopOSREntryBytecodeOffset);
 
161
 
 
162
    if (!tierUpCounter.checkIfOptimizationThresholdReached()) {
 
163
        dataLogLnIf(Options::verboseOSR(), "    JIT threshold should be lifted.");
 
164
        WASM_RETURN_TWO(0, 0);
 
165
    }
 
166
 
 
167
    const auto doOSREntry = [&] {
 
168
        Wasm::OMGForOSREntryCallee* osrEntryCallee = callee->osrEntryCallee();
 
169
        if (osrEntryCallee->loopIndex() != osrEntryData.loopIndex)
 
170
            WASM_RETURN_TWO(0, 0);
 
171
 
 
172
        size_t osrEntryScratchBufferSize = osrEntryCallee->osrEntryScratchBufferSize();
 
173
        RELEASE_ASSERT(osrEntryScratchBufferSize == osrEntryData.values.size());
 
174
        uint64_t* buffer = instance->context()->scratchBufferForSize(osrEntryScratchBufferSize);
 
175
        if (!buffer)
 
176
            WASM_RETURN_TWO(0, 0);
 
177
 
 
178
        uint32_t index = 0;
 
179
        for (VirtualRegister reg : osrEntryData.values)
 
180
            buffer[index++] = READ(reg).encodedJSValue();
 
181
 
 
182
        WASM_RETURN_TWO(buffer, osrEntryCallee->entrypoint().executableAddress());
 
183
    };
 
184
 
 
185
    if (callee->osrEntryCallee())
 
186
        return doOSREntry();
 
187
 
 
188
    bool compile = false;
 
189
    {
 
190
        auto locker = holdLock(tierUpCounter.m_lock);
 
191
        switch (tierUpCounter.m_loopCompilationStatus) {
 
192
        case Wasm::LLIntTierUpCounter::CompilationStatus::NotCompiled:
 
193
            compile = true;
 
194
            tierUpCounter.m_loopCompilationStatus = Wasm::LLIntTierUpCounter::CompilationStatus::Compiling;
 
195
            break;
 
196
        case Wasm::LLIntTierUpCounter::CompilationStatus::Compiling:
 
197
            tierUpCounter.optimizeAfterWarmUp();
 
198
            break;
 
199
        case Wasm::LLIntTierUpCounter::CompilationStatus::Compiled:
 
200
            break;
 
201
        }
 
202
    }
 
203
 
 
204
    if (compile) {
 
205
        Ref<Wasm::Plan> plan = adoptRef(*static_cast<Wasm::Plan*>(new Wasm::OMGForOSREntryPlan(instance->context(), Ref<Wasm::Module>(instance->module()), Ref<Wasm::Callee>(*callee), codeBlock->functionIndex(), osrEntryData.loopIndex, instance->memory()->mode(), Wasm::Plan::dontFinalize())));
 
206
        Wasm::ensureWorklist().enqueue(plan.copyRef());
 
207
        if (UNLIKELY(!Options::useConcurrentJIT()))
 
208
            plan->waitForCompletion();
 
209
        else
 
210
            tierUpCounter.optimizeAfterWarmUp();
 
211
    }
 
212
 
 
213
    if (callee->osrEntryCallee())
 
214
        return doOSREntry();
 
215
 
 
216
    WASM_RETURN_TWO(0, 0);
 
217
}
 
218
 
 
219
WASM_SLOW_PATH_DECL(epilogue_osr)
 
220
{
 
221
    Wasm::LLIntCallee* callee = static_cast<Wasm::LLIntCallee*>(callFrame->callee().asWasmCallee());
 
222
    Wasm::FunctionCodeBlock* codeBlock = CODE_BLOCK();
 
223
 
 
224
    dataLogLnIf(Options::verboseOSR(), *callee, ": Entered epilogue_osr with tierUpCounter = ", codeBlock->tierUpCounter());
 
225
 
 
226
    jitCompileAndSetHeuristics(callee, codeBlock, instance);
 
227
    WASM_END_IMPL();
 
228
}
 
229
 
 
230
 
 
231
WASM_SLOW_PATH_DECL(trace)
 
232
{
 
233
    UNUSED_PARAM(instance);
 
234
 
 
235
    if (!Options::traceLLIntExecution())
 
236
        WASM_END_IMPL();
 
237
 
 
238
    WasmOpcodeID opcodeID = pc->opcodeID<WasmOpcodeTraits>();
 
239
    dataLogF("<%p> %p / %p: executing bc#%zu, %s, pc = %p\n",
 
240
        &Thread::current(),
 
241
        callFrame->codeBlock(),
 
242
        callFrame,
 
243
        static_cast<intptr_t>(CODE_BLOCK()->bytecodeOffset(pc)),
 
244
        pc->name<WasmOpcodeTraits>(),
 
245
        pc);
 
246
    if (opcodeID == wasm_enter) {
 
247
        dataLogF("Frame will eventually return to %p\n", callFrame->returnPC().value());
 
248
        *removeCodePtrTag<volatile char*>(callFrame->returnPC().value());
 
249
    }
 
250
    if (opcodeID == wasm_ret) {
 
251
        dataLogF("Will be returning to %p\n", callFrame->returnPC().value());
 
252
        dataLogF("The new cfr will be %p\n", callFrame->callerFrame());
 
253
    }
 
254
    WASM_END_IMPL();
 
255
}
 
256
 
 
257
WASM_SLOW_PATH_DECL(out_of_line_jump_target)
 
258
{
 
259
    UNUSED_PARAM(instance);
 
260
 
 
261
    pc = CODE_BLOCK()->outOfLineJumpTarget(pc);
 
262
    WASM_END_IMPL();
 
263
}
 
264
 
 
265
WASM_SLOW_PATH_DECL(ref_func)
 
266
{
 
267
    auto instruction = pc->as<WasmRefFunc, WasmOpcodeTraits>();
 
268
    WASM_RETURN(Wasm::operationWasmRefFunc(instance, instruction.m_functionIndex));
 
269
}
 
270
 
 
271
WASM_SLOW_PATH_DECL(table_get)
 
272
{
 
273
    auto instruction = pc->as<WasmTableGet, WasmOpcodeTraits>();
 
274
    int32_t index = READ(instruction.m_index).unboxedInt32();
 
275
    EncodedJSValue result = Wasm::operationGetWasmTableElement(instance, instruction.m_tableIndex, index);
 
276
    if (!result)
 
277
        WASM_THROW(Wasm::ExceptionType::OutOfBoundsTableAccess);
 
278
    WASM_RETURN(result);
 
279
}
 
280
 
 
281
WASM_SLOW_PATH_DECL(table_set)
 
282
{
 
283
    auto instruction = pc->as<WasmTableSet, WasmOpcodeTraits>();
 
284
    int32_t index = READ(instruction.m_index).unboxedInt32();
 
285
    EncodedJSValue value = READ(instruction.m_value).encodedJSValue();
 
286
    if (!Wasm::operationSetWasmTableElement(instance, instruction.m_tableIndex, index, value))
 
287
        WASM_THROW(Wasm::ExceptionType::OutOfBoundsTableAccess);
 
288
    WASM_END();
 
289
}
 
290
 
 
291
WASM_SLOW_PATH_DECL(table_size)
 
292
{
 
293
    auto instruction = pc->as<WasmTableSize, WasmOpcodeTraits>();
 
294
    WASM_RETURN(Wasm::operationGetWasmTableSize(instance, instruction.m_tableIndex));
 
295
}
 
296
 
 
297
WASM_SLOW_PATH_DECL(table_fill)
 
298
{
 
299
    auto instruction = pc->as<WasmTableFill, WasmOpcodeTraits>();
 
300
    int32_t offset = READ(instruction.m_offset).unboxedInt32();
 
301
    EncodedJSValue fill = READ(instruction.m_fill).encodedJSValue();
 
302
    int32_t size = READ(instruction.m_size).unboxedInt32();
 
303
    if (!Wasm::operationWasmTableFill(instance, instruction.m_tableIndex, offset, fill, size))
 
304
        WASM_THROW(Wasm::ExceptionType::OutOfBoundsTableAccess);
 
305
    WASM_END();
 
306
}
 
307
 
 
308
WASM_SLOW_PATH_DECL(table_grow)
 
309
{
 
310
    auto instruction = pc->as<WasmTableGrow, WasmOpcodeTraits>();
 
311
    EncodedJSValue fill = READ(instruction.m_fill).encodedJSValue();
 
312
    int32_t size = READ(instruction.m_size).unboxedInt32();
 
313
    WASM_RETURN(Wasm::operationWasmTableGrow(instance, instruction.m_tableIndex, fill, size));
 
314
}
 
315
 
 
316
WASM_SLOW_PATH_DECL(grow_memory)
 
317
{
 
318
    auto instruction = pc->as<WasmGrowMemory, WasmOpcodeTraits>();
 
319
    int32_t delta = READ(instruction.m_delta).unboxedInt32();
 
320
    WASM_RETURN(Wasm::operationGrowMemory(callFrame, instance, delta));
 
321
}
 
322
 
 
323
inline SlowPathReturnType doWasmCall(Wasm::Instance* instance, unsigned functionIndex)
 
324
{
 
325
    uint32_t importFunctionCount = instance->module().moduleInformation().importFunctionCount();
 
326
 
 
327
    MacroAssemblerCodePtr<WasmEntryPtrTag> codePtr;
 
328
 
 
329
    if (functionIndex < importFunctionCount) {
 
330
        Wasm::Instance::ImportFunctionInfo* functionInfo = instance->importFunctionInfo(functionIndex);
 
331
        if (functionInfo->targetInstance) {
 
332
            // target is a wasm function from a different instance
 
333
            codePtr = instance->codeBlock()->wasmToWasmExitStub(functionIndex);
 
334
        } else {
 
335
            // target is JS
 
336
            codePtr = functionInfo->wasmToEmbedderStub;
 
337
        }
 
338
    } else {
 
339
        // Target is a wasm function within the same instance
 
340
        codePtr = *instance->codeBlock()->entrypointLoadLocationFromFunctionIndexSpace(functionIndex);
 
341
    }
 
342
 
 
343
    WASM_CALL_RETURN(instance, codePtr.executableAddress(), WasmEntryPtrTag);
 
344
}
 
345
 
 
346
WASM_SLOW_PATH_DECL(call)
 
347
{
 
348
    UNUSED_PARAM(callFrame);
 
349
 
 
350
    auto instruction = pc->as<WasmCall, WasmOpcodeTraits>();
 
351
    return doWasmCall(instance, instruction.m_functionIndex);
 
352
}
 
353
 
 
354
WASM_SLOW_PATH_DECL(call_no_tls)
 
355
{
 
356
    UNUSED_PARAM(callFrame);
 
357
 
 
358
    auto instruction = pc->as<WasmCallNoTls, WasmOpcodeTraits>();
 
359
    return doWasmCall(instance, instruction.m_functionIndex);
 
360
}
 
361
 
 
362
inline SlowPathReturnType doWasmCallIndirect(CallFrame* callFrame, Wasm::Instance* instance, unsigned functionIndex, unsigned tableIndex, unsigned signatureIndex)
 
363
{
 
364
    Wasm::FuncRefTable* table = instance->table(tableIndex)->asFuncrefTable();
 
365
 
 
366
    if (functionIndex >= table->length())
 
367
        WASM_THROW(Wasm::ExceptionType::OutOfBoundsCallIndirect);
 
368
 
 
369
    Wasm::Instance* targetInstance = table->instance(functionIndex);
 
370
    const Wasm::WasmToWasmImportableFunction& function = table->function(functionIndex);
 
371
 
 
372
    if (function.signatureIndex == Wasm::Signature::invalidIndex)
 
373
        WASM_THROW(Wasm::ExceptionType::NullTableEntry);
 
374
 
 
375
    const Wasm::Signature& callSignature = CODE_BLOCK()->signature(signatureIndex);
 
376
    if (function.signatureIndex != Wasm::SignatureInformation::get(callSignature))
 
377
        WASM_THROW(Wasm::ExceptionType::BadSignature);
 
378
 
 
379
    if (targetInstance != instance)
 
380
        targetInstance->setCachedStackLimit(instance->cachedStackLimit());
 
381
 
 
382
    WASM_CALL_RETURN(targetInstance, function.entrypointLoadLocation->executableAddress(), WasmEntryPtrTag);
 
383
}
 
384
 
 
385
WASM_SLOW_PATH_DECL(call_indirect)
 
386
{
 
387
    auto instruction = pc->as<WasmCallIndirect, WasmOpcodeTraits>();
 
388
    unsigned functionIndex = READ(instruction.m_functionIndex).unboxedInt32();
 
389
    return doWasmCallIndirect(callFrame, instance, functionIndex, instruction.m_tableIndex, instruction.m_signatureIndex);
 
390
}
 
391
 
 
392
WASM_SLOW_PATH_DECL(call_indirect_no_tls)
 
393
{
 
394
    auto instruction = pc->as<WasmCallIndirectNoTls, WasmOpcodeTraits>();
 
395
    unsigned functionIndex = READ(instruction.m_functionIndex).unboxedInt32();
 
396
    return doWasmCallIndirect(callFrame, instance, functionIndex, instruction.m_tableIndex, instruction.m_signatureIndex);
 
397
}
 
398
 
 
399
WASM_SLOW_PATH_DECL(set_global_ref)
 
400
{
 
401
    auto instruction = pc->as<WasmSetGlobalRef, WasmOpcodeTraits>();
 
402
    instance->setGlobal(instruction.m_globalIndex, READ(instruction.m_value).jsValue());
 
403
    WASM_END_IMPL();
 
404
}
 
405
 
 
406
WASM_SLOW_PATH_DECL(set_global_ref_portable_binding)
 
407
{
 
408
    auto instruction = pc->as<WasmSetGlobalRefPortableBinding, WasmOpcodeTraits>();
 
409
    instance->setGlobal(instruction.m_globalIndex, READ(instruction.m_value).jsValue());
 
410
    WASM_END_IMPL();
 
411
}
 
412
 
 
413
extern "C" SlowPathReturnType slow_path_wasm_throw_exception(CallFrame* callFrame, const Instruction* pc, Wasm::Instance* instance, Wasm::ExceptionType exceptionType)
 
414
{
 
415
    UNUSED_PARAM(pc);
 
416
    WASM_RETURN_TWO(operationWasmToJSException(callFrame, exceptionType, instance), 0);
 
417
}
 
418
 
 
419
extern "C" SlowPathReturnType slow_path_wasm_popcount(const Instruction* pc, uint32_t x)
 
420
{
 
421
    void* result = bitwise_cast<void*>(static_cast<uint64_t>(__builtin_popcount(x)));
 
422
    WASM_RETURN_TWO(pc, result);
 
423
}
 
424
 
 
425
extern "C" SlowPathReturnType slow_path_wasm_popcountll(const Instruction* pc, uint64_t x)
 
426
{
 
427
    void* result = bitwise_cast<void*>(static_cast<uint64_t>(__builtin_popcountll(x)));
 
428
    WASM_RETURN_TWO(pc, result);
 
429
}
 
430
 
 
431
} } // namespace JSC::LLInt
 
432
 
 
433
#endif // ENABLE(WEBASSEMBLY)