1 | /* |
2 | * Copyright (C) 2013-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 "DFGTierUpCheckInjectionPhase.h" |
28 | |
29 | #if ENABLE(DFG_JIT) |
30 | |
31 | #include "DFGGraph.h" |
32 | #include "DFGInsertionSet.h" |
33 | #include "DFGNaturalLoops.h" |
34 | #include "DFGPhase.h" |
35 | #include "FTLCapabilities.h" |
36 | #include "FunctionWhitelist.h" |
37 | #include "JSCInlines.h" |
38 | #include <wtf/NeverDestroyed.h> |
39 | |
40 | namespace JSC { namespace DFG { |
41 | |
42 | static FunctionWhitelist& ensureGlobalFTLWhitelist() |
43 | { |
44 | static LazyNeverDestroyed<FunctionWhitelist> ftlWhitelist; |
45 | static std::once_flag initializeWhitelistFlag; |
46 | std::call_once(initializeWhitelistFlag, [] { |
47 | const char* functionWhitelistFile = Options::ftlWhitelist(); |
48 | ftlWhitelist.construct(functionWhitelistFile); |
49 | }); |
50 | return ftlWhitelist; |
51 | } |
52 | |
53 | using NaturalLoop = CPSNaturalLoop; |
54 | |
55 | class TierUpCheckInjectionPhase : public Phase { |
56 | public: |
57 | TierUpCheckInjectionPhase(Graph& graph) |
58 | : Phase(graph, "tier-up check injection" ) |
59 | { |
60 | } |
61 | |
62 | bool run() |
63 | { |
64 | RELEASE_ASSERT(m_graph.m_plan.mode() == DFGMode); |
65 | |
66 | if (!Options::useFTLJIT()) |
67 | return false; |
68 | |
69 | if (m_graph.m_profiledBlock->m_didFailFTLCompilation) |
70 | return false; |
71 | |
72 | if (!Options::bytecodeRangeToFTLCompile().isInRange(m_graph.m_profiledBlock->instructionsSize())) |
73 | return false; |
74 | |
75 | if (!ensureGlobalFTLWhitelist().contains(m_graph.m_profiledBlock)) |
76 | return false; |
77 | |
78 | #if ENABLE(FTL_JIT) |
79 | FTL::CapabilityLevel level = FTL::canCompile(m_graph); |
80 | if (level == FTL::CannotCompile) |
81 | return false; |
82 | |
83 | if (!Options::useOSREntryToFTL()) |
84 | level = FTL::CanCompile; |
85 | |
86 | m_graph.ensureCPSNaturalLoops(); |
87 | CPSNaturalLoops& naturalLoops = *m_graph.m_cpsNaturalLoops; |
88 | HashMap<const NaturalLoop*, BytecodeIndex> naturalLoopToLoopHint = buildNaturalLoopToLoopHintMap(naturalLoops); |
89 | |
90 | HashMap<BytecodeIndex, LoopHintDescriptor> tierUpHierarchy; |
91 | |
92 | InsertionSet insertionSet(m_graph); |
93 | for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { |
94 | BasicBlock* block = m_graph.block(blockIndex); |
95 | if (!block) |
96 | continue; |
97 | |
98 | for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { |
99 | Node* node = block->at(nodeIndex); |
100 | if (node->op() != LoopHint) |
101 | continue; |
102 | |
103 | NodeOrigin origin = node->origin; |
104 | bool canOSREnter = canOSREnterAtLoopHint(level, block, nodeIndex); |
105 | |
106 | NodeType tierUpType = CheckTierUpAndOSREnter; |
107 | if (!canOSREnter) |
108 | tierUpType = CheckTierUpInLoop; |
109 | insertionSet.insertNode(nodeIndex + 1, SpecNone, tierUpType, origin); |
110 | |
111 | auto bytecodeIndex = origin.semantic.bytecodeIndex(); |
112 | if (canOSREnter) |
113 | m_graph.m_plan.tierUpAndOSREnterBytecodes().append(bytecodeIndex); |
114 | |
115 | if (const NaturalLoop* loop = naturalLoops.innerMostLoopOf(block)) { |
116 | LoopHintDescriptor descriptor; |
117 | descriptor.canOSREnter = canOSREnter; |
118 | |
119 | const NaturalLoop* outerLoop = loop; |
120 | while ((outerLoop = naturalLoops.innerMostOuterLoop(*outerLoop))) { |
121 | auto it = naturalLoopToLoopHint.find(outerLoop); |
122 | if (it != naturalLoopToLoopHint.end()) |
123 | descriptor.osrEntryCandidates.append(it->value); |
124 | } |
125 | tierUpHierarchy.add(bytecodeIndex, WTFMove(descriptor)); |
126 | } |
127 | break; |
128 | } |
129 | |
130 | NodeAndIndex terminal = block->findTerminal(); |
131 | if (terminal.node->isFunctionTerminal()) { |
132 | insertionSet.insertNode( |
133 | terminal.index, SpecNone, CheckTierUpAtReturn, terminal.node->origin); |
134 | } |
135 | |
136 | insertionSet.execute(block); |
137 | } |
138 | |
139 | // Add all the candidates that can be OSR Entered. |
140 | for (auto entry : tierUpHierarchy) { |
141 | Vector<BytecodeIndex> tierUpCandidates; |
142 | for (BytecodeIndex bytecodeIndex : entry.value.osrEntryCandidates) { |
143 | auto descriptorIt = tierUpHierarchy.find(bytecodeIndex); |
144 | if (descriptorIt != tierUpHierarchy.end() |
145 | && descriptorIt->value.canOSREnter) |
146 | tierUpCandidates.append(bytecodeIndex); |
147 | } |
148 | |
149 | if (!tierUpCandidates.isEmpty()) |
150 | m_graph.m_plan.tierUpInLoopHierarchy().add(entry.key, WTFMove(tierUpCandidates)); |
151 | } |
152 | m_graph.m_plan.setWillTryToTierUp(true); |
153 | return true; |
154 | #else // ENABLE(FTL_JIT) |
155 | RELEASE_ASSERT_NOT_REACHED(); |
156 | return false; |
157 | #endif // ENABLE(FTL_JIT) |
158 | } |
159 | |
160 | private: |
161 | #if ENABLE(FTL_JIT) |
162 | struct LoopHintDescriptor { |
163 | Vector<BytecodeIndex> osrEntryCandidates; |
164 | bool canOSREnter; |
165 | }; |
166 | |
167 | bool canOSREnterAtLoopHint(FTL::CapabilityLevel level, const BasicBlock* block, unsigned nodeIndex) |
168 | { |
169 | Node* node = block->at(nodeIndex); |
170 | ASSERT(node->op() == LoopHint); |
171 | |
172 | NodeOrigin origin = node->origin; |
173 | if (level != FTL::CanCompileAndOSREnter || origin.semantic.inlineCallFrame()) |
174 | return false; |
175 | |
176 | // We only put OSR checks for the first LoopHint in the block. Note that |
177 | // more than one LoopHint could happen in cases where we did a lot of CFG |
178 | // simplification in the bytecode parser, but it should be very rare. |
179 | for (unsigned subNodeIndex = nodeIndex; subNodeIndex--;) { |
180 | if (!block->at(subNodeIndex)->isSemanticallySkippable()) |
181 | return false; |
182 | } |
183 | return true; |
184 | } |
185 | |
186 | HashMap<const NaturalLoop*, BytecodeIndex> buildNaturalLoopToLoopHintMap(const CPSNaturalLoops& naturalLoops) |
187 | { |
188 | HashMap<const NaturalLoop*, BytecodeIndex> naturalLoopsToLoopHint; |
189 | |
190 | for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { |
191 | for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { |
192 | Node* node = block->at(nodeIndex); |
193 | if (node->op() != LoopHint) |
194 | continue; |
195 | |
196 | if (const NaturalLoop* loop = naturalLoops.innerMostLoopOf(block)) { |
197 | BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex(); |
198 | naturalLoopsToLoopHint.add(loop, bytecodeIndex); |
199 | } |
200 | break; |
201 | } |
202 | } |
203 | return naturalLoopsToLoopHint; |
204 | } |
205 | #endif |
206 | }; |
207 | |
208 | bool performTierUpCheckInjection(Graph& graph) |
209 | { |
210 | return runPhase<TierUpCheckInjectionPhase>(graph); |
211 | } |
212 | |
213 | } } // namespace JSC::DFG |
214 | |
215 | #endif // ENABLE(DFG_JIT) |
216 | |
217 | |
218 | |