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

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

Inline RegExp.test JIT code in DFG and FTL
https://bugs.webkit.org/show_bug.cgi?id=230469

Reviewed by Saam Barati.

JSTests:

New microbenchmark.

  • microbenchmarks/regexp-test-inlined.js: Added.

Source/JavaScriptCore:

Restructured YarrJIT from inheriting from MacroAssembler to having a MacroAssembler
member. Added a new path to Yarr JIT code to compile inline code by changing how the
code is entered and exited. Added statistic to the normal compilation path to record
the size of the matching code generated, the amount of stack space needed, and if
the code can be inlined. This patch only inlines 8bit code, as this seems to cover
the most common performance sensitive cases. Adding 16 bit, non-Unicode inlining
would be straightforward. The code is structured to take the inlined path for the
case of non-rope string arguments. For other cases, we fall back to calling out
to C++ paths.

Here are the perf results running the newly added regexp-test-inlined micro
benchmark (time in msec):

Baseline With this patch Result

ARM64 137.3849+-3.0740 64.9282+-0.7348 2.12x faster
X86-64 220.2616+-19.2814 105.2034+-6.8722 2.09x faster

As part of this change, found that the strength reduction didn't work properly for the
existing cases for RegExpExec, RegExpTest and related since we added that checks for
overriding the RegExp object. Clobberize for tryGetById was clobber top, but added
an exception for RegExp.lastIndex. This fix allowed many of the strength reductions
cases to start working again, namely the costant folding cases.

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGCommonData.h:
  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNode.cpp:

(JSC::DFG::Node::convertToRegExpTestInline):

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasCellOperand):
(JSC::DFG::Node::hasCellOperand2):
(JSC::DFG::Node::cellOperand2):

  • dfg/DFGNodeType.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compileRegExpTestInline):
(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGStrengthReductionPhase.cpp:

(JSC::DFG::StrengthReductionPhase::handleNode):

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):

  • runtime/OptionsList.h:
  • runtime/RegExp.h:
  • runtime/StackAlignment.h:

(JSC::argumentCountForStackSize):

  • yarr/YarrJIT.cpp:

(JSC::Yarr::jitCompile):
(JSC::Yarr::jitCompileInlinedTest):

  • yarr/YarrJIT.h:

(JSC::Yarr::YarrBoyerMoyerData::saveMaps):
(JSC::Yarr::YarrBoyerMoyerData::clearMaps):
(JSC::Yarr::YarrBoyerMoyerData::tryReuseBoyerMooreBitmap const):
(JSC::Yarr::YarrCodeBlock::InlineStats::InlineStats):
(JSC::Yarr::YarrCodeBlock::InlineStats::set):
(JSC::Yarr::YarrCodeBlock::InlineStats::clear):
(JSC::Yarr::YarrCodeBlock::InlineStats::codeSize const):
(JSC::Yarr::YarrCodeBlock::InlineStats::stackSize const):
(JSC::Yarr::YarrCodeBlock::InlineStats::canInline const):
(JSC::Yarr::YarrCodeBlock::InlineStats::needsTemp2 const):
(JSC::Yarr::YarrCodeBlock::set8BitCode):
(JSC::Yarr::YarrCodeBlock::set16BitCode):
(JSC::Yarr::YarrCodeBlock::set8BitCodeMatchOnly):
(JSC::Yarr::YarrCodeBlock::set16BitCodeMatchOnly):
(JSC::Yarr::YarrCodeBlock::set8BitInlineStats):
(JSC::Yarr::YarrCodeBlock::set16BitInlineStats):
(JSC::Yarr::YarrCodeBlock::get8BitInlineStats):
(JSC::Yarr::YarrCodeBlock::get16BitInlineStats):
(JSC::Yarr::YarrCodeBlock::clear):
(JSC::Yarr::YarrCodeBlock::tryReuseBoyerMooreBitmap const): Deleted.

  • yarr/YarrJITRegisters.h: Added.

(JSC::Yarr::YarrJITRegisters::YarrJITRegisters):

Source/WTF:

Added a new enablement: ENABLE_YARR_JIT_ALL_PARENS_EXPRESSIONS.

  • wtf/PlatformEnable.h:
File size: 10.5 KB
Line 
1/*
2 * Copyright (C) 2013-2021 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 "DFGNode.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "DFGGraph.h"
32#include "DFGPromotedHeapLocation.h"
33#include "DOMJITSignature.h"
34#include "JSImmutableButterfly.h"
35
36namespace JSC { namespace DFG {
37
38const char Node::HashSetTemplateInstantiationString[] = "::JSC::DFG::Node*";
39
40DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(DFGNode);
41
42bool MultiPutByOffsetData::writesStructures() const
43{
44 for (unsigned i = variants.size(); i--;) {
45 if (variants[i].writesStructures())
46 return true;
47 }
48 return false;
49}
50
51bool MultiPutByOffsetData::reallocatesStorage() const
52{
53 for (unsigned i = variants.size(); i--;) {
54 if (variants[i].reallocatesStorage())
55 return true;
56 }
57 return false;
58}
59
60bool MultiDeleteByOffsetData::writesStructures() const
61{
62 for (unsigned i = variants.size(); i--;) {
63 if (variants[i].writesStructures())
64 return true;
65 }
66 return false;
67}
68
69bool MultiDeleteByOffsetData::allVariantsStoreEmpty() const
70{
71 for (unsigned i = variants.size(); i--;) {
72 if (!variants[i].newStructure())
73 return false;
74 }
75 return true;
76}
77
78void BranchTarget::dump(PrintStream& out) const
79{
80 if (!block)
81 return;
82
83 out.print(*block);
84
85 if (count == count) // If the count is not NaN, then print it.
86 out.print("/w:", count);
87}
88
89bool Node::hasVariableAccessData(Graph& graph)
90{
91 switch (op()) {
92 case Phi:
93 return graph.m_form != SSA;
94 case GetLocal:
95 case SetLocal:
96 case SetArgumentDefinitely:
97 case SetArgumentMaybe:
98 case Flush:
99 case PhantomLocal:
100 return true;
101 default:
102 return false;
103 }
104}
105
106void Node::remove(Graph& graph)
107{
108 switch (op()) {
109 case MultiGetByOffset: {
110 MultiGetByOffsetData& data = multiGetByOffsetData();
111 StructureSet set;
112 for (MultiGetByOffsetCase& getCase : data.cases) {
113 getCase.set().forEach(
114 [&] (RegisteredStructure structure) {
115 set.add(structure.get());
116 });
117 }
118 convertToCheckStructure(graph.addStructureSet(set));
119 return;
120 }
121
122 case MatchStructure: {
123 MatchStructureData& data = matchStructureData();
124 RegisteredStructureSet set;
125 for (MatchStructureVariant& variant : data.variants)
126 set.add(variant.structure);
127 convertToCheckStructure(graph.addStructureSet(set));
128 return;
129 }
130
131 default:
132 if (flags() & NodeHasVarArgs) {
133 unsigned targetIndex = 0;
134 for (unsigned i = 0; i < numChildren(); ++i) {
135 Edge& edge = graph.varArgChild(this, i);
136 if (!edge)
137 continue;
138 if (edge.willHaveCheck()) {
139 Edge& dst = graph.varArgChild(this, targetIndex++);
140 std::swap(dst, edge);
141 continue;
142 }
143 edge = Edge();
144 }
145 setOpAndDefaultFlags(CheckVarargs);
146 children.setNumChildren(targetIndex);
147 } else {
148 children = children.justChecks();
149 setOpAndDefaultFlags(Check);
150 }
151 return;
152 }
153}
154
155void Node::removeWithoutChecks()
156{
157 children = AdjacencyList();
158 setOpAndDefaultFlags(Check);
159}
160
161void Node::replaceWith(Graph& graph, Node* other)
162{
163 remove(graph);
164 setReplacement(other);
165}
166
167void Node::replaceWithWithoutChecks(Node* other)
168{
169 removeWithoutChecks();
170 setReplacement(other);
171}
172
173void Node::convertToIdentity()
174{
175 RELEASE_ASSERT(child1());
176 RELEASE_ASSERT(!child2());
177 NodeFlags result = canonicalResultRepresentation(this->result());
178 setOpAndDefaultFlags(Identity);
179 setResult(result);
180}
181
182void Node::convertToIdentityOn(Node* child)
183{
184 children.reset();
185 clearFlags(NodeHasVarArgs);
186 child1() = child->defaultEdge();
187 NodeFlags output = canonicalResultRepresentation(this->result());
188 NodeFlags input = canonicalResultRepresentation(child->result());
189 if (output == input) {
190 setOpAndDefaultFlags(Identity);
191 setResult(output);
192 return;
193 }
194 switch (output) {
195 case NodeResultDouble:
196 setOpAndDefaultFlags(DoubleRep);
197 switch (input) {
198 case NodeResultInt52:
199 child1().setUseKind(Int52RepUse);
200 return;
201 case NodeResultJS:
202 child1().setUseKind(NumberUse);
203 return;
204 default:
205 RELEASE_ASSERT_NOT_REACHED();
206 return;
207 }
208 case NodeResultInt52:
209 setOpAndDefaultFlags(Int52Rep);
210 switch (input) {
211 case NodeResultDouble:
212 child1().setUseKind(DoubleRepAnyIntUse);
213 return;
214 case NodeResultJS:
215 child1().setUseKind(AnyIntUse);
216 return;
217 default:
218 RELEASE_ASSERT_NOT_REACHED();
219 return;
220 }
221 case NodeResultJS:
222 setOpAndDefaultFlags(ValueRep);
223 switch (input) {
224 case NodeResultDouble:
225 child1().setUseKind(DoubleRepUse);
226 return;
227 case NodeResultInt52:
228 child1().setUseKind(Int52RepUse);
229 return;
230 default:
231 RELEASE_ASSERT_NOT_REACHED();
232 return;
233 }
234 default:
235 RELEASE_ASSERT_NOT_REACHED();
236 return;
237 }
238}
239
240void Node::convertToLazyJSConstant(Graph& graph, LazyJSValue value)
241{
242 m_op = LazyJSConstant;
243 m_flags &= ~NodeMustGenerate;
244 m_opInfo = graph.m_lazyJSValues.add(value);
245 children.reset();
246}
247
248void Node::convertToNewArrayBuffer(FrozenValue* immutableButterfly)
249{
250 setOpAndDefaultFlags(NewArrayBuffer);
251 NewArrayBufferData data { };
252 data.indexingMode = immutableButterfly->cast<JSImmutableButterfly*>()->indexingMode();
253 data.vectorLengthHint = immutableButterfly->cast<JSImmutableButterfly*>()->toButterfly()->vectorLength();
254 children.reset();
255 m_opInfo = immutableButterfly;
256 m_opInfo2 = data.asQuadWord;
257}
258
259void Node::convertToDirectCall(FrozenValue* executable)
260{
261 NodeType newOp = LastNodeType;
262 switch (op()) {
263 case Call:
264 newOp = DirectCall;
265 break;
266 case Construct:
267 newOp = DirectConstruct;
268 break;
269 case TailCallInlinedCaller:
270 newOp = DirectTailCallInlinedCaller;
271 break;
272 case TailCall:
273 newOp = DirectTailCall;
274 break;
275 default:
276 RELEASE_ASSERT_NOT_REACHED();
277 break;
278 }
279
280 m_op = newOp;
281 m_opInfo = executable;
282}
283
284void Node::convertToCallDOM(Graph& graph)
285{
286 ASSERT(op() == Call);
287 ASSERT(signature());
288
289 Edge edges[3];
290 // Skip the first one. This is callee.
291 RELEASE_ASSERT(numChildren() <= 4);
292 for (unsigned i = 1; i < numChildren(); ++i)
293 edges[i - 1] = graph.varArgChild(this, i);
294
295 setOpAndDefaultFlags(CallDOM);
296 children.setChild1(edges[0]);
297 children.setChild2(edges[1]);
298 children.setChild3(edges[2]);
299
300 if (!signature()->effect.mustGenerate())
301 clearFlags(NodeMustGenerate);
302}
303
304void Node::convertToRegExpExecNonGlobalOrStickyWithoutChecks(FrozenValue* regExp)
305{
306 ASSERT(op() == RegExpExec);
307 setOpAndDefaultFlags(RegExpExecNonGlobalOrSticky);
308 children.child1() = Edge(children.child1().node(), KnownCellUse);
309 children.child2() = Edge(children.child3().node(), KnownStringUse);
310 children.child3() = Edge();
311 m_opInfo = regExp;
312}
313
314void Node::convertToRegExpMatchFastGlobalWithoutChecks(FrozenValue* regExp)
315{
316 ASSERT(op() == RegExpMatchFast);
317 setOpAndDefaultFlags(RegExpMatchFastGlobal);
318 children.child1() = Edge(children.child1().node(), KnownCellUse);
319 children.child2() = Edge(children.child3().node(), KnownStringUse);
320 children.child3() = Edge();
321 m_opInfo = regExp;
322}
323
324void Node::convertToRegExpTestInline(FrozenValue* globalObject, FrozenValue* regExp)
325{
326 ASSERT(op() == RegExpTest);
327 setOpAndDefaultFlags(RegExpTestInline);
328 children.child1() = Edge(children.child1().node(), KnownCellUse);
329 children.child2() = Edge(children.child2().node(), RegExpObjectUse);
330 // We keep the existing child3.
331 m_opInfo = globalObject;
332 m_opInfo2 = regExp;
333}
334
335String Node::tryGetString(Graph& graph)
336{
337 if (hasConstant())
338 return constant()->tryGetString(graph);
339 if (hasLazyJSValue())
340 return lazyJSValue().tryGetString(graph);
341 return String();
342}
343
344PromotedLocationDescriptor Node::promotedLocationDescriptor()
345{
346 return PromotedLocationDescriptor(static_cast<PromotedLocationKind>(m_opInfo.as<uint32_t>()), m_opInfo2.as<uint32_t>());
347}
348
349} } // namespace JSC::DFG
350
351namespace WTF {
352
353using namespace JSC;
354using namespace JSC::DFG;
355
356void printInternal(PrintStream& out, SwitchKind kind)
357{
358 switch (kind) {
359 case SwitchImm:
360 out.print("SwitchImm");
361 return;
362 case SwitchChar:
363 out.print("SwitchChar");
364 return;
365 case SwitchString:
366 out.print("SwitchString");
367 return;
368 case SwitchCell:
369 out.print("SwitchCell");
370 return;
371 }
372 RELEASE_ASSERT_NOT_REACHED();
373}
374
375void printInternal(PrintStream& out, Node* node)
376{
377 if (!node) {
378 out.print("-");
379 return;
380 }
381 out.print("D@", node->index());
382 if (node->hasDoubleResult())
383 out.print("<Double>");
384 else if (node->hasInt52Result())
385 out.print("<Int52>");
386}
387
388} // namespace WTF
389
390#endif // ENABLE(DFG_JIT)
391
Note: See TracBrowser for help on using the repository browser.