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