source: webkit/trunk/Source/JavaScriptCore/dfg/DFGThunks.cpp

Last change on this file was 294180, checked in by [email protected], 3 years ago

Enhance the ARM64Disassembler to print pc indices and better branch target labels.
https://bugs.webkit.org/show_bug.cgi?id=240370

Reviewed by Saam Barati.

Disassemblies used to look like this:

0x10e480ff8: ldurb w17, [x0, #7]
0x10e480ffc: cmp w17, #0
0x10e481000: b.hi 0x10e48103c
0x10e481004: stur x0, [fp, #-72]
...
0x10e481040: movk x3, #0xfffe, lsl #48
0x10e481044: b 0x10e4814f4
0x10e481048: nop

With this patch, it will now look like this:

<748> 0x10e120aec: ldurb w17, [x0, #7]
<752> 0x10e120af0: cmp w17, #0
<756> 0x10e120af4: b.hi 0x10e120b30 -> <816>
<760> 0x10e120af8: stur x0, [fp, #-80]
...
<820> 0x10e120b34: movk x3, #0xfffe, lsl #48
<824> 0x10e120b38: b 0x10e120fc8 -> <1992>
<828> 0x10e120b3c: nop

  1. Each instruction pc is now prefixed with a pc index i.e. the offset of the pc address from the start of the compilation unit e.g. <756>.
  1. Relative branches now show the branch target as a pc index (effectively, an internal label in this compilation unit) in addition to the pc address e.g. the "-> <816>" in:

<756> 0x10e120af4: b.hi 0x10e120b30 -> <816>

Also fixed a formatting bug where the space between relative branch instructions
and their target pc was short 2 spaces.

  1. If the relative branch target is a known thunk, the disassembler will now print the thunk label e.g.

<828> 0x10e12033c: bl 0x10e0f0a00 -> <thunk: get_from_scope thunk>

<1476> 0x10e120dc4: cbnz x16, 0x10e104100 -> <thunk: handleExceptionWithCallFrameRollback>
<2368> 0x10e121140: b 0x10e10c000 -> <thunk: DFG OSR exit generation thunk>

Introduced a FINALIZE_THUNK macro that will be used instead of FINALIZE_CODE in
thunk generators. By doing so, thunk labels will automatically be registered
with the disassembler, and will be used for the above look up.

Thunk label registration is only done if disassembly is enabled.

  1. If the branch target is neither an internal label nor a thunk, then the disassembler will print some useful info about it to the best of its knowledge e.g.

<168> 0x10e1002e8: b 0x10e120b60 -> <JIT PC>
<168> 0x10e1002e8: b 0x10e120b60 -> <LLInt PC>
<168> 0x10e1002e8: b 0x10e120b60 -> <unknown>

  1. The disassemble() function now takes 2 additional arguments: codeStart, and codeEnd. These are needed so that the disassembler can compute the pc index for each instruction, as well as determine if a branch target is internal to this compilation unit, or pointing out of it.

This feature is currently only supported for the ARM64 disassembler.

Printing of JIT operation labels (via movz + movk + indirect branch) is not yet
supported.

  • assembler/LinkBuffer.cpp:

(JSC::LinkBuffer::finalizeCodeWithDisassemblyImpl):

  • assembler/LinkBuffer.h:

(JSC::LinkBuffer::setIsThunk):

  • b3/air/AirDisassembler.cpp:

(JSC::B3::Air::Disassembler::dump):

  • dfg/DFGDisassembler.cpp:

(JSC::DFG::Disassembler::dumpDisassembly):

  • dfg/DFGThunks.cpp:

(JSC::DFG::osrExitGenerationThunkGenerator):
(JSC::DFG::osrEntryThunkGenerator):

  • disassembler/ARM64/A64DOpcode.cpp:

(JSC::ARM64Disassembler::A64DOpcode::appendPCRelativeOffset):
(JSC::ARM64Disassembler::A64DOpcodeConditionalBranchImmediate::format):

  • disassembler/ARM64/A64DOpcode.h:

(JSC::ARM64Disassembler::A64DOpcode::A64DOpcode):
(JSC::ARM64Disassembler::A64DOpcode::appendPCRelativeOffset): Deleted.

  • disassembler/ARM64Disassembler.cpp:

(JSC::tryToDisassemble):

  • disassembler/CapstoneDisassembler.cpp:

(JSC::tryToDisassemble):

  • disassembler/Disassembler.cpp:

(JSC::disassemble):
(JSC::disassembleAsynchronously):
(JSC::ensureThunkLabelMap):
(JSC::registerThunkLabel):
(JSC::labelForThunk):

  • disassembler/Disassembler.h:

(JSC::tryToDisassemble):

  • disassembler/RISCV64Disassembler.cpp:

(JSC::tryToDisassemble):

  • disassembler/X86Disassembler.cpp:

(JSC::tryToDisassemble):

  • ftl/FTLThunks.cpp:

(JSC::FTL::genericGenerationThunkGenerator):
(JSC::FTL::slowPathCallThunkGenerator):

  • jit/JIT.cpp:

(JSC::JIT::consistencyCheckGenerator):

  • jit/JITCall.cpp:

(JSC::JIT::returnFromBaselineGenerator):

  • jit/JITDisassembler.cpp:

(JSC::JITDisassembler::dump):
(JSC::JITDisassembler::dumpDisassembly):

  • jit/JITDisassembler.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::valueIsFalseyGenerator):
(JSC::JIT::valueIsTruthyGenerator):
(JSC::JIT::op_throw_handlerGenerator):
(JSC::JIT::op_enter_handlerGenerator):
(JSC::JIT::op_check_traps_handlerGenerator):

  • jit/JITPropertyAccess.cpp:

(JSC::JIT::slow_op_get_by_val_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_get_private_name_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_put_by_val_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_put_private_name_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_del_by_id_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_del_by_val_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_get_by_id_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_get_by_id_with_this_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::slow_op_put_by_id_callSlowOperationThenCheckExceptionGenerator):
(JSC::JIT::generateOpResolveScopeThunk):
(JSC::JIT::slow_op_resolve_scopeGenerator):
(JSC::JIT::generateOpGetFromScopeThunk):
(JSC::JIT::slow_op_get_from_scopeGenerator):
(JSC::JIT::slow_op_put_to_scopeGenerator):

  • jit/SlowPathCall.cpp:

(JSC::JITSlowPathCall::generateThunk):

  • jit/SpecializedThunkJIT.h:

(JSC::SpecializedThunkJIT::finalize):

  • jit/ThunkGenerator.h:
  • jit/ThunkGenerators.cpp:

(JSC::handleExceptionGenerator):
(JSC::handleExceptionWithCallFrameRollbackGenerator):
(JSC::popThunkStackPreservesAndHandleExceptionGenerator):
(JSC::checkExceptionGenerator):
(JSC::throwExceptionFromCallSlowPathGenerator):
(JSC::linkCallThunkGenerator):
(JSC::linkPolymorphicCallThunkGenerator):
(JSC::virtualThunkFor):
(JSC::nativeForGenerator):
(JSC::arityFixupGenerator):
(JSC::unreachableGenerator):
(JSC::stringGetByValGenerator):
(JSC::boundFunctionCallGenerator):
(JSC::remoteFunctionCallGenerator):

  • llint/LLIntThunks.cpp:

(JSC::LLInt::generateThunkWithJumpTo):
(JSC::LLInt::generateThunkWithJumpToPrologue):
(JSC::LLInt::generateThunkWithJumpToLLIntReturnPoint):
(JSC::LLInt::createJSGateThunk):
(JSC::LLInt::createWasmGateThunk):
(JSC::LLInt::createTailCallGate):
(JSC::LLInt::tagGateThunk):
(JSC::LLInt::untagGateThunk):

  • yarr/YarrDisassembler.cpp:

(JSC::Yarr::YarrDisassembler::dump):
(JSC::Yarr::YarrDisassembler::dumpDisassembly):

  • yarr/YarrDisassembler.h:
File size: 7.9 KB
Line 
1/*
2 * Copyright (C) 2011-2022 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 "DFGThunks.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "AssemblyHelpersSpoolers.h"
32#include "CCallHelpers.h"
33#include "DFGJITCode.h"
34#include "DFGOSRExit.h"
35#include "DFGOSRExitCompilerCommon.h"
36#include "FPRInfo.h"
37#include "GPRInfo.h"
38#include "LinkBuffer.h"
39#include "MacroAssembler.h"
40#include "ProbeContext.h"
41
42namespace JSC { namespace DFG {
43
44MacroAssemblerCodeRef<JITThunkPtrTag> osrExitGenerationThunkGenerator(VM& vm)
45{
46 CCallHelpers jit(nullptr);
47
48 // This needs to happen before we use the scratch buffer because this function also uses the scratch buffer.
49 adjustFrameAndStackInOSRExitCompilerThunk<DFG::JITCode>(jit, vm, JITType::DFGJIT);
50
51 size_t scratchSize = sizeof(EncodedJSValue) * (GPRInfo::numberOfRegisters + FPRInfo::numberOfRegisters);
52 ScratchBuffer* scratchBuffer = vm.scratchBufferForSize(scratchSize);
53 EncodedJSValue* buffer = static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer());
54
55#if CPU(ARM64)
56 constexpr GPRReg bufferGPR = CCallHelpers::memoryTempRegister;
57 constexpr unsigned firstGPR = 0;
58#elif CPU(X86_64)
59 GPRReg bufferGPR = jit.scratchRegister();
60 constexpr unsigned firstGPR = 0;
61#else
62 GPRReg bufferGPR = GPRInfo::toRegister(0);
63 constexpr unsigned firstGPR = 1;
64#endif
65
66 if constexpr (firstGPR) {
67 // We're using the firstGPR as the bufferGPR, and need to save it manually.
68 RELEASE_ASSERT(GPRInfo::numberOfRegisters >= 1);
69 RELEASE_ASSERT(bufferGPR == GPRInfo::toRegister(0));
70#if USE(JSVALUE64)
71 jit.store64(bufferGPR, buffer);
72#else
73 jit.store32(bufferGPR, buffer);
74#endif
75 }
76
77 jit.move(CCallHelpers::TrustedImmPtr(buffer), bufferGPR);
78
79 CCallHelpers::StoreRegSpooler storeSpooler(jit, bufferGPR);
80
81 for (unsigned i = firstGPR; i < GPRInfo::numberOfRegisters; ++i) {
82 ptrdiff_t offset = i * sizeof(CPURegister);
83 storeSpooler.storeGPR({ GPRInfo::toRegister(i), offset });
84 }
85 storeSpooler.finalizeGPR();
86
87 for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) {
88 ptrdiff_t offset = (GPRInfo::numberOfRegisters + i) * sizeof(double);
89 storeSpooler.storeFPR({ FPRInfo::toRegister(i), offset });
90 }
91 storeSpooler.finalizeFPR();
92
93 // This will implicitly pass GPRInfo::callFrameRegister as the first argument based on the operation type.
94 jit.setupArguments<decltype(operationCompileOSRExit)>(bufferGPR);
95 jit.prepareCallOperation(vm);
96
97 MacroAssembler::Call functionCall = jit.call(OperationPtrTag);
98
99 jit.move(CCallHelpers::TrustedImmPtr(buffer), bufferGPR);
100 CCallHelpers::LoadRegSpooler loadSpooler(jit, bufferGPR);
101
102 for (unsigned i = firstGPR; i < GPRInfo::numberOfRegisters; ++i) {
103 ptrdiff_t offset = i * sizeof(CPURegister);
104 loadSpooler.loadGPR({ GPRInfo::toRegister(i), offset });
105 }
106 loadSpooler.finalizeGPR();
107
108 for (unsigned i = 0; i < FPRInfo::numberOfRegisters; ++i) {
109 ptrdiff_t offset = (GPRInfo::numberOfRegisters + i) * sizeof(double);
110 loadSpooler.loadFPR({ FPRInfo::toRegister(i), offset });
111 }
112 loadSpooler.finalizeFPR();
113
114 if constexpr (firstGPR) {
115 // We're using the firstGPR as the bufferGPR, and need to restore it manually.
116 ASSERT(bufferGPR == GPRInfo::toRegister(0));
117#if USE(JSVALUE64)
118 jit.load64(buffer, bufferGPR);
119#else
120 jit.load32(buffer, bufferGPR);
121#endif
122 }
123
124 jit.farJump(MacroAssembler::AbsoluteAddress(&vm.osrExitJumpDestination), OSRExitPtrTag);
125
126 LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::DFGThunk);
127
128 patchBuffer.link(functionCall, FunctionPtr<OperationPtrTag>(operationCompileOSRExit));
129
130 return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "DFG OSR exit generation thunk");
131}
132
133MacroAssemblerCodeRef<JITThunkPtrTag> osrEntryThunkGenerator(VM& vm)
134{
135 AssemblyHelpers jit(nullptr);
136
137 // We get passed the address of a scratch buffer in GPRInfo::returnValueGPR2.
138 // The first 8-byte slot of the buffer is the frame size. The second 8-byte slot
139 // is the pointer to where we are supposed to jump. The remaining bytes are
140 // the new call frame header followed by the locals.
141
142 ptrdiff_t offsetOfFrameSize = 0; // This is the DFG frame count.
143 ptrdiff_t offsetOfTargetPC = offsetOfFrameSize + sizeof(EncodedJSValue);
144 ptrdiff_t offsetOfPayload = offsetOfTargetPC + sizeof(EncodedJSValue);
145 ptrdiff_t offsetOfLocals = offsetOfPayload + sizeof(Register) * CallFrame::headerSizeInRegisters;
146
147 jit.move(GPRInfo::returnValueGPR2, GPRInfo::regT0);
148 jit.loadPtr(MacroAssembler::Address(GPRInfo::regT0, offsetOfFrameSize), GPRInfo::regT1); // Load the frame size.
149 jit.negPtr(GPRInfo::regT1, GPRInfo::regT2);
150 jit.getEffectiveAddress(MacroAssembler::BaseIndex(GPRInfo::callFrameRegister, GPRInfo::regT2, MacroAssembler::TimesEight), MacroAssembler::stackPointerRegister);
151
152 // Copying locals and header from scratch buffer to the new CallFrame. This also replaces
153 MacroAssembler::Label loop = jit.label();
154 jit.subPtr(MacroAssembler::TrustedImm32(1), GPRInfo::regT1);
155 jit.negPtr(GPRInfo::regT1, GPRInfo::regT4);
156 jit.loadValue(MacroAssembler::BaseIndex(GPRInfo::regT0, GPRInfo::regT1, MacroAssembler::TimesEight, offsetOfLocals), JSRInfo::jsRegT32);
157 jit.storeValue(JSRInfo::jsRegT32, MacroAssembler::BaseIndex(GPRInfo::callFrameRegister, GPRInfo::regT4, MacroAssembler::TimesEight, -static_cast<intptr_t>(sizeof(Register))));
158 jit.branchPtr(MacroAssembler::NotEqual, GPRInfo::regT1, MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(-static_cast<intptr_t>(CallFrame::headerSizeInRegisters)))).linkTo(loop, &jit);
159
160 jit.loadPtr(MacroAssembler::Address(GPRInfo::regT0, offsetOfTargetPC), GPRInfo::regT1);
161 MacroAssembler::Jump ok = jit.branchPtr(MacroAssembler::Above, GPRInfo::regT1, MacroAssembler::TrustedImmPtr(bitwise_cast<void*>(static_cast<intptr_t>(1000))));
162 jit.abortWithReason(DFGUnreasonableOSREntryJumpDestination);
163
164 ok.link(&jit);
165
166 jit.jitAssertCodeBlockOnCallFrameIsOptimizingJIT(GPRInfo::regT2);
167
168 jit.restoreCalleeSavesFromEntryFrameCalleeSavesBuffer(vm.topEntryFrame);
169 jit.emitMaterializeTagCheckRegisters();
170#if USE(JSVALUE64)
171 jit.emitGetFromCallFrameHeaderPtr(CallFrameSlot::codeBlock, GPRInfo::constantsRegister);
172 jit.loadPtr(CCallHelpers::Address(GPRInfo::constantsRegister, CodeBlock::offsetOfJITData()), GPRInfo::constantsRegister);
173#endif
174
175 jit.farJump(GPRInfo::regT1, GPRInfo::callFrameRegister);
176
177 LinkBuffer patchBuffer(jit, GLOBAL_THUNK_ID, LinkBuffer::Profile::DFGOSREntry);
178 return FINALIZE_THUNK(patchBuffer, JITThunkPtrTag, "DFG OSR entry thunk");
179}
180
181} } // namespace JSC::DFG
182
183#endif // ENABLE(DFG_JIT)
Note: See TracBrowser for help on using the repository browser.