1/*
2 * Copyright (C) 2015-2018 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 "FTLCompile.h"
28
29#if ENABLE(FTL_JIT)
30
31#include "AirCode.h"
32#include "AirDisassembler.h"
33#include "B3Generate.h"
34#include "B3ProcedureInlines.h"
35#include "B3StackSlot.h"
36#include "B3Value.h"
37#include "CodeBlockWithJITType.h"
38#include "CCallHelpers.h"
39#include "DFGCommon.h"
40#include "DFGGraphSafepoint.h"
41#include "DFGOperations.h"
42#include "DataView.h"
43#include "Disassembler.h"
44#include "FTLJITCode.h"
45#include "FTLThunks.h"
46#include "JITSubGenerator.h"
47#include "JSCInlines.h"
48#include "LinkBuffer.h"
49#include "PCToCodeOriginMap.h"
50#include "ScratchRegisterAllocator.h"
51#include <wtf/RecursableLambda.h>
52
53namespace JSC { namespace FTL {
54
55using namespace DFG;
56
57void compile(State& state, Safepoint::Result& safepointResult)
58{
59 Graph& graph = state.graph;
60 CodeBlock* codeBlock = graph.m_codeBlock;
61 VM& vm = graph.m_vm;
62
63 if (shouldDumpDisassembly())
64 state.proc->code().setDisassembler(std::make_unique<B3::Air::Disassembler>());
65
66 {
67 GraphSafepoint safepoint(state.graph, safepointResult);
68
69 B3::prepareForGeneration(*state.proc);
70 }
71
72 if (safepointResult.didGetCancelled())
73 return;
74 RELEASE_ASSERT(!state.graph.m_vm.heap.worldIsStopped());
75
76 if (state.allocationFailed)
77 return;
78
79 std::unique_ptr<RegisterAtOffsetList> registerOffsets =
80 std::make_unique<RegisterAtOffsetList>(state.proc->calleeSaveRegisterAtOffsetList());
81 if (shouldDumpDisassembly())
82 dataLog("Unwind info for ", CodeBlockWithJITType(codeBlock, JITType::FTLJIT), ": ", *registerOffsets, "\n");
83 codeBlock->setCalleeSaveRegisters(WTFMove(registerOffsets));
84 ASSERT(!(state.proc->frameSize() % sizeof(EncodedJSValue)));
85 state.jitCode->common.frameRegisterCount = state.proc->frameSize() / sizeof(EncodedJSValue);
86
87 int localsOffset =
88 state.capturedValue->offsetFromFP() / sizeof(EncodedJSValue) + graph.m_nextMachineLocal;
89 if (shouldDumpDisassembly()) {
90 dataLog(
91 "localsOffset = ", localsOffset, " for stack slot: ",
92 pointerDump(state.capturedValue), " at ", RawPointer(state.capturedValue), "\n");
93 }
94
95 for (unsigned i = graph.m_inlineVariableData.size(); i--;) {
96 InlineCallFrame* inlineCallFrame = graph.m_inlineVariableData[i].inlineCallFrame;
97
98 if (inlineCallFrame->argumentCountRegister.isValid())
99 inlineCallFrame->argumentCountRegister += localsOffset;
100
101 for (unsigned argument = inlineCallFrame->argumentsWithFixup.size(); argument-- > 1;) {
102 inlineCallFrame->argumentsWithFixup[argument] =
103 inlineCallFrame->argumentsWithFixup[argument].withLocalsOffset(localsOffset);
104 }
105
106 if (inlineCallFrame->isClosureCall) {
107 inlineCallFrame->calleeRecovery =
108 inlineCallFrame->calleeRecovery.withLocalsOffset(localsOffset);
109 }
110
111 }
112
113 // Note that the scope register could be invalid here if the original code had CallEval but it
114 // got killed. That's because it takes the CallEval to cause the scope register to be kept alive
115 // unless the debugger is also enabled.
116 if (graph.needsScopeRegister() && codeBlock->scopeRegister().isValid())
117 codeBlock->setScopeRegister(codeBlock->scopeRegister() + localsOffset);
118
119 for (OSRExitDescriptor& descriptor : state.jitCode->osrExitDescriptors) {
120 for (unsigned i = descriptor.m_values.size(); i--;)
121 descriptor.m_values[i] = descriptor.m_values[i].withLocalsOffset(localsOffset);
122 for (ExitTimeObjectMaterialization* materialization : descriptor.m_materializations)
123 materialization->accountForLocalsOffset(localsOffset);
124 }
125
126 // We will add exception handlers while generating.
127 codeBlock->clearExceptionHandlers();
128
129 CCallHelpers jit(codeBlock);
130 B3::generate(*state.proc, jit);
131
132 // Emit the exception handler.
133 *state.exceptionHandler = jit.label();
134 jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame);
135 jit.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0);
136 jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
137 CCallHelpers::Call call = jit.call(OperationPtrTag);
138 jit.jumpToExceptionHandler(vm);
139 jit.addLinkTask(
140 [=] (LinkBuffer& linkBuffer) {
141 linkBuffer.link(call, FunctionPtr<OperationPtrTag>(lookupExceptionHandler));
142 });
143
144 state.finalizer->b3CodeLinkBuffer = std::make_unique<LinkBuffer>(jit, codeBlock, JITCompilationCanFail);
145
146 if (state.finalizer->b3CodeLinkBuffer->didFailToAllocate()) {
147 state.allocationFailed = true;
148 return;
149 }
150
151 B3::PCToOriginMap originMap = state.proc->releasePCToOriginMap();
152 if (vm.shouldBuilderPCToCodeOriginMapping())
153 codeBlock->setPCToCodeOriginMap(std::make_unique<PCToCodeOriginMap>(PCToCodeOriginMapBuilder(vm, WTFMove(originMap)), *state.finalizer->b3CodeLinkBuffer));
154
155 CodeLocationLabel<JSEntryPtrTag> label = state.finalizer->b3CodeLinkBuffer->locationOf<JSEntryPtrTag>(state.proc->entrypointLabel(0));
156 state.generatedFunction = label;
157 state.jitCode->initializeB3Byproducts(state.proc->releaseByproducts());
158
159 for (auto pair : state.graph.m_entrypointIndexToCatchBytecodeOffset) {
160 unsigned catchBytecodeOffset = pair.value;
161 unsigned entrypointIndex = pair.key;
162 Vector<FlushFormat> argumentFormats = state.graph.m_argumentFormats[entrypointIndex];
163 state.jitCode->common.appendCatchEntrypoint(
164 catchBytecodeOffset, state.finalizer->b3CodeLinkBuffer->locationOf<ExceptionHandlerPtrTag>(state.proc->entrypointLabel(entrypointIndex)), WTFMove(argumentFormats));
165 }
166 state.jitCode->common.finalizeCatchEntrypoints();
167
168 if (B3::Air::Disassembler* disassembler = state.proc->code().disassembler()) {
169 PrintStream& out = WTF::dataFile();
170
171 out.print("Generated ", state.graph.m_plan.mode(), " code for ", CodeBlockWithJITType(state.graph.m_codeBlock, JITType::FTLJIT), ", instructions size = ", state.graph.m_codeBlock->instructionsSize(), ":\n");
172
173 LinkBuffer& linkBuffer = *state.finalizer->b3CodeLinkBuffer;
174 B3::Value* currentB3Value = nullptr;
175 Node* currentDFGNode = nullptr;
176
177 HashSet<B3::Value*> printedValues;
178 HashSet<Node*> printedNodes;
179 const char* dfgPrefix = " ";
180 const char* b3Prefix = " ";
181 const char* airPrefix = " ";
182 const char* asmPrefix = " ";
183
184 auto printDFGNode = [&] (Node* node) {
185 if (currentDFGNode == node)
186 return;
187
188 currentDFGNode = node;
189 if (!currentDFGNode)
190 return;
191
192 HashSet<Node*> localPrintedNodes;
193 WTF::Function<void(Node*)> printNodeRecursive = [&] (Node* node) {
194 if (printedNodes.contains(node) || localPrintedNodes.contains(node))
195 return;
196
197 localPrintedNodes.add(node);
198 graph.doToChildren(node, [&] (Edge child) {
199 printNodeRecursive(child.node());
200 });
201 graph.dump(out, dfgPrefix, node);
202 };
203 printNodeRecursive(node);
204 printedNodes.add(node);
205 };
206
207 auto printB3Value = [&] (B3::Value* value) {
208 if (currentB3Value == value)
209 return;
210
211 currentB3Value = value;
212 if (!currentB3Value)
213 return;
214
215 printDFGNode(bitwise_cast<Node*>(value->origin().data()));
216
217 HashSet<B3::Value*> localPrintedValues;
218 auto printValueRecursive = recursableLambda([&] (auto self, B3::Value* value) -> void {
219 if (printedValues.contains(value) || localPrintedValues.contains(value))
220 return;
221
222 localPrintedValues.add(value);
223 for (unsigned i = 0; i < value->numChildren(); i++)
224 self(value->child(i));
225 out.print(b3Prefix);
226 value->deepDump(state.proc.get(), out);
227 out.print("\n");
228 });
229
230 printValueRecursive(currentB3Value);
231 printedValues.add(value);
232 };
233
234 auto forEachInst = scopedLambda<void(B3::Air::Inst&)>([&] (B3::Air::Inst& inst) {
235 printB3Value(inst.origin);
236 });
237
238 disassembler->dump(state.proc->code(), out, linkBuffer, airPrefix, asmPrefix, forEachInst);
239 linkBuffer.didAlreadyDisassemble();
240 }
241}
242
243} } // namespace JSC::FTL
244
245#endif // ENABLE(FTL_JIT)
246
247