• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 "DFGByteCodeParser.h"
28 
29 #if ENABLE(DFG_JIT)
30 
31 #include "DFGAliasTracker.h"
32 #include "DFGScoreBoard.h"
33 #include "CodeBlock.h"
34 
35 namespace JSC { namespace DFG {
36 
37 #if ENABLE(DFG_JIT_RESTRICTIONS)
38 // FIXME: Temporarily disable arithmetic, until we fix associated performance regressions.
39 #define ARITHMETIC_OP() m_parseFailed = true
40 #else
41 #define ARITHMETIC_OP() ((void)0)
42 #endif
43 
44 // === ByteCodeParser ===
45 //
46 // This class is used to compile the dataflow graph from a CodeBlock.
47 class ByteCodeParser {
48 public:
ByteCodeParser(JSGlobalData * globalData,CodeBlock * codeBlock,Graph & graph)49     ByteCodeParser(JSGlobalData* globalData, CodeBlock* codeBlock, Graph& graph)
50         : m_globalData(globalData)
51         , m_codeBlock(codeBlock)
52         , m_graph(graph)
53         , m_currentIndex(0)
54         , m_parseFailed(false)
55         , m_constantUndefined(UINT_MAX)
56         , m_constantNull(UINT_MAX)
57         , m_constant1(UINT_MAX)
58         , m_constants(codeBlock->numberOfConstantRegisters())
59         , m_arguments(codeBlock->m_numParameters)
60         , m_variables(codeBlock->m_numVars)
61         , m_temporaries(codeBlock->m_numCalleeRegisters - codeBlock->m_numVars)
62     {
63         for (unsigned i = 0; i < m_temporaries.size(); ++i)
64             m_temporaries[i] = NoNode;
65     }
66 
67     // Parse a full CodeBlock of bytecode.
68     bool parse();
69 
70 private:
71     // Parse a single basic block of bytecode instructions.
72     bool parseBlock(unsigned limit);
73 
74     // Get/Set the operands/result of a bytecode instruction.
get(int operand)75     NodeIndex get(int operand)
76     {
77         // Is this a constant?
78         if (operand >= FirstConstantRegisterIndex) {
79             unsigned constant = operand - FirstConstantRegisterIndex;
80             ASSERT(constant < m_constants.size());
81             return getJSConstant(constant);
82         }
83 
84         // Is this an argument?
85         if (operand < 0)
86             return getArgument(operand);
87 
88         // Is this a variable?
89         unsigned numVariables = m_variables.size();
90         if ((unsigned)operand < numVariables)
91             return getVariable((unsigned)operand);
92 
93         // Must be a temporary.
94         unsigned temporary = (unsigned)operand - numVariables;
95         ASSERT(temporary < m_temporaries.size());
96         return getTemporary(temporary);
97     }
set(int operand,NodeIndex value)98     void set(int operand, NodeIndex value)
99     {
100         // Is this an argument?
101         if (operand < 0) {
102             setArgument(operand, value);
103             return;
104         }
105 
106         // Is this a variable?
107         unsigned numVariables = m_variables.size();
108         if ((unsigned)operand < numVariables) {
109             setVariable((unsigned)operand, value);
110             return;
111         }
112 
113         // Must be a temporary.
114         unsigned temporary = (unsigned)operand - numVariables;
115         ASSERT(temporary < m_temporaries.size());
116         setTemporary(temporary, value);
117     }
118 
119     // Used in implementing get/set, above, where the operand is a local variable.
getVariable(unsigned operand)120     NodeIndex getVariable(unsigned operand)
121     {
122         NodeIndex setNode = m_variables[operand].set;
123         if (setNode != NoNode)
124             return m_graph[setNode].child1;
125 
126         NodeIndex getNode = m_variables[operand].get;
127         if (getNode != NoNode)
128             return getNode;
129 
130         getNode = addToGraph(GetLocal, OpInfo(operand));
131         m_variables[operand].get = getNode;
132         return getNode;
133     }
setVariable(unsigned operand,NodeIndex value)134     void setVariable(unsigned operand, NodeIndex value)
135     {
136         NodeIndex priorSet = m_variables[operand].set;
137         m_variables[operand].set = addToGraph(SetLocal, OpInfo(operand), value);
138         if (priorSet != NoNode)
139             m_graph.deref(priorSet);
140     }
141 
142     // Used in implementing get/set, above, where the operand is a temporary.
getTemporary(unsigned operand)143     NodeIndex getTemporary(unsigned operand)
144     {
145         NodeIndex index = m_temporaries[operand];
146         if (index != NoNode)
147             return index;
148 
149         // Detect a read of an temporary that is not a yet defined within this block (e.g. use of ?:).
150         m_parseFailed = true;
151         return constantUndefined();
152     }
setTemporary(unsigned operand,NodeIndex value)153     void setTemporary(unsigned operand, NodeIndex value)
154     {
155         m_temporaries[operand] = value;
156     }
157 
158     // Used in implementing get/set, above, where the operand is an argument.
getArgument(unsigned operand)159     NodeIndex getArgument(unsigned operand)
160     {
161         unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
162         ASSERT(argument < m_arguments.size());
163 
164         NodeIndex setNode = m_arguments[argument].set;
165         if (setNode != NoNode)
166             return m_graph[setNode].child1;
167 
168         NodeIndex getNode = m_arguments[argument].get;
169         if (getNode != NoNode)
170             return getNode;
171 
172         getNode = addToGraph(GetLocal, OpInfo(operand));
173         m_arguments[argument].get = getNode;
174         return getNode;
175     }
setArgument(int operand,NodeIndex value)176     void setArgument(int operand, NodeIndex value)
177     {
178         unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
179         ASSERT(argument < m_arguments.size());
180 
181         NodeIndex priorSet = m_arguments[argument].set;
182         m_arguments[argument].set = addToGraph(SetLocal, OpInfo(operand), value);
183         if (priorSet != NoNode)
184             m_graph.deref(priorSet);
185     }
186 
187     // Get an operand, and perform a ToInt32/ToNumber conversion on it.
getToInt32(int operand)188     NodeIndex getToInt32(int operand)
189     {
190         // Avoid wastefully adding a JSConstant node to the graph, only to
191         // replace it with a Int32Constant (which is what would happen if
192         // we called 'toInt32(get(operand))' in this case).
193         if (operand >= FirstConstantRegisterIndex) {
194             JSValue v = m_codeBlock->getConstant(operand);
195             if (v.isInt32())
196                 return getInt32Constant(v.asInt32(), operand - FirstConstantRegisterIndex);
197         }
198         return toInt32(get(operand));
199     }
getToNumber(int operand)200     NodeIndex getToNumber(int operand)
201     {
202         // Avoid wastefully adding a JSConstant node to the graph, only to
203         // replace it with a DoubleConstant (which is what would happen if
204         // we called 'toNumber(get(operand))' in this case).
205         if (operand >= FirstConstantRegisterIndex) {
206             JSValue v = m_codeBlock->getConstant(operand);
207             if (v.isNumber())
208                 return getDoubleConstant(v.uncheckedGetNumber(), operand - FirstConstantRegisterIndex);
209         }
210         return toNumber(get(operand));
211     }
212 
213     // Perform an ES5 ToInt32 operation - returns a node of type NodeResultInt32.
toInt32(NodeIndex index)214     NodeIndex toInt32(NodeIndex index)
215     {
216         Node& node = m_graph[index];
217 
218         if (node.hasInt32Result())
219             return index;
220 
221         if (node.hasDoubleResult()) {
222             if (node.op == DoubleConstant)
223                 return getInt32Constant(JSC::toInt32(valueOfDoubleConstant(index)), node.constantNumber());
224             // 'NumberToInt32(Int32ToNumber(X))' == X, and 'NumberToInt32(UInt32ToNumber(X)) == X'
225             if (node.op == Int32ToNumber || node.op == UInt32ToNumber)
226                 return node.child1;
227 
228             // We unique NumberToInt32 nodes in a map to prevent duplicate conversions.
229             pair<UnaryOpMap::iterator, bool> result = m_numberToInt32Nodes.add(index, NoNode);
230             // Either we added a new value, or the existing value in the map is non-zero.
231             ASSERT(result.second == (result.first->second == NoNode));
232             if (result.second)
233                 result.first->second = addToGraph(NumberToInt32, index);
234             return result.first->second;
235         }
236 
237         // Check for numeric constants boxed as JSValues.
238         if (node.op == JSConstant) {
239             JSValue v = valueOfJSConstant(index);
240             if (v.isInt32())
241                 return getInt32Constant(v.asInt32(), node.constantNumber());
242             if (v.isNumber())
243                 return getInt32Constant(JSC::toInt32(v.uncheckedGetNumber()), node.constantNumber());
244         }
245 
246         return addToGraph(ValueToInt32, index);
247     }
248 
249     // Perform an ES5 ToNumber operation - returns a node of type NodeResultDouble.
toNumber(NodeIndex index)250     NodeIndex toNumber(NodeIndex index)
251     {
252         Node& node = m_graph[index];
253 
254         if (node.hasDoubleResult())
255             return index;
256 
257         if (node.hasInt32Result()) {
258             if (node.op == Int32Constant)
259                 return getDoubleConstant(valueOfInt32Constant(index), node.constantNumber());
260 
261             // We unique Int32ToNumber nodes in a map to prevent duplicate conversions.
262             pair<UnaryOpMap::iterator, bool> result = m_int32ToNumberNodes.add(index, NoNode);
263             // Either we added a new value, or the existing value in the map is non-zero.
264             ASSERT(result.second == (result.first->second == NoNode));
265             if (result.second)
266                 result.first->second = addToGraph(Int32ToNumber, index);
267             return result.first->second;
268         }
269 
270         if (node.op == JSConstant) {
271             JSValue v = valueOfJSConstant(index);
272             if (v.isNumber())
273                 return getDoubleConstant(v.uncheckedGetNumber(), node.constantNumber());
274         }
275 
276         return addToGraph(ValueToNumber, index);
277     }
278 
279 
280     // Used in implementing get, above, where the operand is a constant.
getInt32Constant(int32_t value,unsigned constant)281     NodeIndex getInt32Constant(int32_t value, unsigned constant)
282     {
283         NodeIndex index = m_constants[constant].asInt32;
284         if (index != NoNode)
285             return index;
286         NodeIndex resultIndex = addToGraph(Int32Constant, OpInfo(constant));
287         m_graph[resultIndex].setInt32Constant(value);
288         m_constants[constant].asInt32 = resultIndex;
289         return resultIndex;
290     }
getDoubleConstant(double value,unsigned constant)291     NodeIndex getDoubleConstant(double value, unsigned constant)
292     {
293         NodeIndex index = m_constants[constant].asNumeric;
294         if (index != NoNode)
295             return index;
296         NodeIndex resultIndex = addToGraph(DoubleConstant, OpInfo(constant));
297         m_graph[resultIndex].setDoubleConstant(value);
298         m_constants[constant].asNumeric = resultIndex;
299         return resultIndex;
300     }
getJSConstant(unsigned constant)301     NodeIndex getJSConstant(unsigned constant)
302     {
303         NodeIndex index = m_constants[constant].asJSValue;
304         if (index != NoNode)
305             return index;
306 
307         NodeIndex resultIndex = addToGraph(JSConstant, OpInfo(constant));
308         m_constants[constant].asJSValue = resultIndex;
309         return resultIndex;
310     }
311 
312     // Helper functions to get/set the this value.
getThis()313     NodeIndex getThis()
314     {
315         return getArgument(m_codeBlock->thisRegister());
316     }
setThis(NodeIndex value)317     void setThis(NodeIndex value)
318     {
319         setArgument(m_codeBlock->thisRegister(), value);
320     }
321 
322     // Convenience methods for checking nodes for constants.
isInt32Constant(NodeIndex index)323     bool isInt32Constant(NodeIndex index)
324     {
325         return m_graph[index].op == Int32Constant;
326     }
isDoubleConstant(NodeIndex index)327     bool isDoubleConstant(NodeIndex index)
328     {
329         return m_graph[index].op == DoubleConstant;
330     }
isJSConstant(NodeIndex index)331     bool isJSConstant(NodeIndex index)
332     {
333         return m_graph[index].op == JSConstant;
334     }
335 
336     // Convenience methods for getting constant values.
valueOfInt32Constant(NodeIndex index)337     int32_t valueOfInt32Constant(NodeIndex index)
338     {
339         ASSERT(isInt32Constant(index));
340         return m_graph[index].int32Constant();
341     }
valueOfDoubleConstant(NodeIndex index)342     double valueOfDoubleConstant(NodeIndex index)
343     {
344         ASSERT(isDoubleConstant(index));
345         return m_graph[index].numericConstant();
346     }
valueOfJSConstant(NodeIndex index)347     JSValue valueOfJSConstant(NodeIndex index)
348     {
349         ASSERT(isJSConstant(index));
350         return m_codeBlock->getConstant(FirstConstantRegisterIndex + m_graph[index].constantNumber());
351     }
352 
353     // This method returns a JSConstant with the value 'undefined'.
constantUndefined()354     NodeIndex constantUndefined()
355     {
356         // Has m_constantUndefined been set up yet?
357         if (m_constantUndefined == UINT_MAX) {
358             // Search the constant pool for undefined, if we find it, we can just reuse this!
359             unsigned numberOfConstants = m_codeBlock->numberOfConstantRegisters();
360             for (m_constantUndefined = 0; m_constantUndefined < numberOfConstants; ++m_constantUndefined) {
361                 JSValue testMe = m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantUndefined);
362                 if (testMe.isUndefined())
363                     return getJSConstant(m_constantUndefined);
364             }
365 
366             // Add undefined to the CodeBlock's constants, and add a corresponding slot in m_constants.
367             ASSERT(m_constants.size() == numberOfConstants);
368             m_codeBlock->addConstant(jsUndefined());
369             m_constants.append(ConstantRecord());
370             ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
371         }
372 
373         // m_constantUndefined must refer to an entry in the CodeBlock's constant pool that has the value 'undefined'.
374         ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantUndefined).isUndefined());
375         return getJSConstant(m_constantUndefined);
376     }
377 
378     // This method returns a JSConstant with the value 'null'.
constantNull()379     NodeIndex constantNull()
380     {
381         // Has m_constantNull been set up yet?
382         if (m_constantNull == UINT_MAX) {
383             // Search the constant pool for null, if we find it, we can just reuse this!
384             unsigned numberOfConstants = m_codeBlock->numberOfConstantRegisters();
385             for (m_constantNull = 0; m_constantNull < numberOfConstants; ++m_constantNull) {
386                 JSValue testMe = m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantNull);
387                 if (testMe.isNull())
388                     return getJSConstant(m_constantNull);
389             }
390 
391             // Add null to the CodeBlock's constants, and add a corresponding slot in m_constants.
392             ASSERT(m_constants.size() == numberOfConstants);
393             m_codeBlock->addConstant(jsNull());
394             m_constants.append(ConstantRecord());
395             ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
396         }
397 
398         // m_constantNull must refer to an entry in the CodeBlock's constant pool that has the value 'null'.
399         ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantNull).isNull());
400         return getJSConstant(m_constantNull);
401     }
402 
403     // This method returns a DoubleConstant with the value 1.
one()404     NodeIndex one()
405     {
406         // Has m_constant1 been set up yet?
407         if (m_constant1 == UINT_MAX) {
408             // Search the constant pool for the value 1, if we find it, we can just reuse this!
409             unsigned numberOfConstants = m_codeBlock->numberOfConstantRegisters();
410             for (m_constant1 = 0; m_constant1 < numberOfConstants; ++m_constant1) {
411                 JSValue testMe = m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constant1);
412                 if (testMe.isInt32() && testMe.asInt32() == 1)
413                     return getDoubleConstant(1, m_constant1);
414             }
415 
416             // Add the value 1 to the CodeBlock's constants, and add a corresponding slot in m_constants.
417             ASSERT(m_constants.size() == numberOfConstants);
418             m_codeBlock->addConstant(jsNumber(1));
419             m_constants.append(ConstantRecord());
420             ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
421         }
422 
423         // m_constant1 must refer to an entry in the CodeBlock's constant pool that has the integer value 1.
424         ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constant1).isInt32());
425         ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constant1).asInt32() == 1);
426         return getDoubleConstant(1, m_constant1);
427     }
428 
429 
430     // These methods create a node and add it to the graph. If nodes of this type are
431     // 'mustGenerate' then the node  will implicitly be ref'ed to ensure generation.
addToGraph(NodeType op,NodeIndex child1=NoNode,NodeIndex child2=NoNode,NodeIndex child3=NoNode)432     NodeIndex addToGraph(NodeType op, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
433     {
434         NodeIndex resultIndex = (NodeIndex)m_graph.size();
435         m_graph.append(Node(op, m_currentIndex, child1, child2, child3));
436 
437         if (op & NodeMustGenerate)
438             m_graph.ref(resultIndex);
439         return resultIndex;
440     }
addToGraph(NodeType op,OpInfo info,NodeIndex child1=NoNode,NodeIndex child2=NoNode,NodeIndex child3=NoNode)441     NodeIndex addToGraph(NodeType op, OpInfo info, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
442     {
443         NodeIndex resultIndex = (NodeIndex)m_graph.size();
444         m_graph.append(Node(op, m_currentIndex, info, child1, child2, child3));
445 
446         if (op & NodeMustGenerate)
447             m_graph.ref(resultIndex);
448         return resultIndex;
449     }
addToGraph(NodeType op,OpInfo info1,OpInfo info2,NodeIndex child1=NoNode,NodeIndex child2=NoNode,NodeIndex child3=NoNode)450     NodeIndex addToGraph(NodeType op, OpInfo info1, OpInfo info2, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
451     {
452         NodeIndex resultIndex = (NodeIndex)m_graph.size();
453         m_graph.append(Node(op, m_currentIndex, info1, info2, child1, child2, child3));
454 
455         if (op & NodeMustGenerate)
456             m_graph.ref(resultIndex);
457         return resultIndex;
458     }
459 
460     JSGlobalData* m_globalData;
461     CodeBlock* m_codeBlock;
462     Graph& m_graph;
463 
464     // The bytecode index of the current instruction being generated.
465     unsigned m_currentIndex;
466 
467     // Record failures due to unimplemented functionality or regressions.
468     bool m_parseFailed;
469 
470     // We use these values during code generation, and to avoid the need for
471     // special handling we make sure they are available as constants in the
472     // CodeBlock's constant pool. These variables are initialized to
473     // UINT_MAX, and lazily updated to hold an index into the CodeBlock's
474     // constant pool, as necessary.
475     unsigned m_constantUndefined;
476     unsigned m_constantNull;
477     unsigned m_constant1;
478 
479     // A constant in the constant pool may be represented by more than one
480     // node in the graph, depending on the context in which it is being used.
481     struct ConstantRecord {
ConstantRecordJSC::DFG::ByteCodeParser::ConstantRecord482         ConstantRecord()
483             : asInt32(NoNode)
484             , asNumeric(NoNode)
485             , asJSValue(NoNode)
486         {
487         }
488 
489         NodeIndex asInt32;
490         NodeIndex asNumeric;
491         NodeIndex asJSValue;
492     };
493 
494     // For every local variable we track any existing get or set of the value.
495     // We track the get so that these may be shared, and we track the set to
496     // retrieve the current value, and to reference the final definition.
497     struct VariableRecord {
VariableRecordJSC::DFG::ByteCodeParser::VariableRecord498         VariableRecord()
499             : get(NoNode)
500             , set(NoNode)
501         {
502         }
503 
504         NodeIndex get;
505         NodeIndex set;
506     };
507 
508     // Track the index of the node whose result is the current value for every
509     // register value in the bytecode - argument, local, and temporary.
510     Vector <ConstantRecord, 32> m_constants;
511     Vector <VariableRecord, 32> m_arguments;
512     Vector <VariableRecord, 32> m_variables;
513     Vector <NodeIndex, 32> m_temporaries;
514 
515     // These maps are used to unique ToNumber and ToInt32 operations.
516     typedef HashMap<NodeIndex, NodeIndex> UnaryOpMap;
517     UnaryOpMap m_int32ToNumberNodes;
518     UnaryOpMap m_numberToInt32Nodes;
519 };
520 
521 #define NEXT_OPCODE(name) \
522     m_currentIndex += OPCODE_LENGTH(name); \
523     continue
524 
525 #define LAST_OPCODE(name) \
526     m_currentIndex += OPCODE_LENGTH(name); \
527     return !m_parseFailed
528 
parseBlock(unsigned limit)529 bool ByteCodeParser::parseBlock(unsigned limit)
530 {
531     // No need to reset state initially, since it has been set by the constructor.
532     if (m_currentIndex) {
533         for (unsigned i = 0; i < m_constants.size(); ++i)
534             m_constants[i] = ConstantRecord();
535         for (unsigned i = 0; i < m_variables.size(); ++i)
536             m_variables[i] = VariableRecord();
537         for (unsigned i = 0; i < m_arguments.size(); ++i)
538             m_arguments[i] = VariableRecord();
539         for (unsigned i = 0; i < m_temporaries.size(); ++i)
540             m_temporaries[i] = NoNode;
541     }
542 
543     AliasTracker aliases(m_graph);
544 
545     Interpreter* interpreter = m_globalData->interpreter;
546     Instruction* instructionsBegin = m_codeBlock->instructions().begin();
547     while (true) {
548         // Don't extend over jump destinations.
549         if (m_currentIndex == limit) {
550             addToGraph(Jump, OpInfo(m_currentIndex));
551             return !m_parseFailed;
552         }
553 
554         // Switch on the current bytecode opcode.
555         Instruction* currentInstruction = instructionsBegin + m_currentIndex;
556         switch (interpreter->getOpcodeID(currentInstruction->u.opcode)) {
557 
558         // === Function entry opcodes ===
559 
560         case op_enter:
561             // Initialize all locals to undefined.
562             for (int i = 0; i < m_codeBlock->m_numVars; ++i)
563                 set(i, constantUndefined());
564             NEXT_OPCODE(op_enter);
565 
566         case op_convert_this: {
567             NodeIndex op1 = getThis();
568             setThis(addToGraph(ConvertThis, op1));
569             NEXT_OPCODE(op_convert_this);
570         }
571 
572         // === Bitwise operations ===
573 
574         case op_bitand: {
575             NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
576             NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
577             set(currentInstruction[1].u.operand, addToGraph(BitAnd, op1, op2));
578             NEXT_OPCODE(op_bitand);
579         }
580 
581         case op_bitor: {
582             NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
583             NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
584             set(currentInstruction[1].u.operand, addToGraph(BitOr, op1, op2));
585             NEXT_OPCODE(op_bitor);
586         }
587 
588         case op_bitxor: {
589             NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
590             NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
591             set(currentInstruction[1].u.operand, addToGraph(BitXor, op1, op2));
592             NEXT_OPCODE(op_bitxor);
593         }
594 
595         case op_rshift: {
596             NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
597             NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
598             NodeIndex result;
599             // Optimize out shifts by zero.
600             if (isInt32Constant(op2) && !(valueOfInt32Constant(op2) & 0x1f))
601                 result = op1;
602             else
603                 result = addToGraph(BitRShift, op1, op2);
604             set(currentInstruction[1].u.operand, result);
605             NEXT_OPCODE(op_rshift);
606         }
607 
608         case op_lshift: {
609             NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
610             NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
611             NodeIndex result;
612             // Optimize out shifts by zero.
613             if (isInt32Constant(op2) && !(valueOfInt32Constant(op2) & 0x1f))
614                 result = op1;
615             else
616                 result = addToGraph(BitLShift, op1, op2);
617             set(currentInstruction[1].u.operand, result);
618             NEXT_OPCODE(op_lshift);
619         }
620 
621         case op_urshift: {
622             NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
623             NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
624             NodeIndex result;
625             // The result of a zero-extending right shift is treated as an unsigned value.
626             // This means that if the top bit is set, the result is not in the int32 range,
627             // and as such must be stored as a double. If the shift amount is a constant,
628             // we may be able to optimize.
629             if (isInt32Constant(op2)) {
630                 // If we know we are shifting by a non-zero amount, then since the operation
631                 // zero fills we know the top bit of the result must be zero, and as such the
632                 // result must be within the int32 range. Conversely, if this is a shift by
633                 // zero, then the result may be changed by the conversion to unsigned, but it
634                 // is not necessary to perform the shift!
635                 if (valueOfInt32Constant(op2) & 0x1f)
636                     result = addToGraph(BitURShift, op1, op2);
637                 else
638                     result = addToGraph(UInt32ToNumber, op1);
639             }  else {
640                 // Cannot optimize at this stage; shift & potentially rebox as a double.
641                 result = addToGraph(BitURShift, op1, op2);
642                 result = addToGraph(UInt32ToNumber, result);
643             }
644             set(currentInstruction[1].u.operand, result);
645             NEXT_OPCODE(op_urshift);
646         }
647 
648         // === Increment/Decrement opcodes ===
649 
650         case op_pre_inc: {
651             unsigned srcDst = currentInstruction[1].u.operand;
652             NodeIndex op = getToNumber(srcDst);
653             set(srcDst, addToGraph(ArithAdd, op, one()));
654             NEXT_OPCODE(op_pre_inc);
655         }
656 
657         case op_post_inc: {
658             unsigned result = currentInstruction[1].u.operand;
659             unsigned srcDst = currentInstruction[2].u.operand;
660             NodeIndex op = getToNumber(srcDst);
661             set(result, op);
662             set(srcDst, addToGraph(ArithAdd, op, one()));
663             NEXT_OPCODE(op_post_inc);
664         }
665 
666         case op_pre_dec: {
667             unsigned srcDst = currentInstruction[1].u.operand;
668             NodeIndex op = getToNumber(srcDst);
669             set(srcDst, addToGraph(ArithSub, op, one()));
670             NEXT_OPCODE(op_pre_dec);
671         }
672 
673         case op_post_dec: {
674             unsigned result = currentInstruction[1].u.operand;
675             unsigned srcDst = currentInstruction[2].u.operand;
676             NodeIndex op = getToNumber(srcDst);
677             set(result, op);
678             set(srcDst, addToGraph(ArithSub, op, one()));
679             NEXT_OPCODE(op_post_dec);
680         }
681 
682         // === Arithmetic operations ===
683 
684         case op_add: {
685             ARITHMETIC_OP();
686             NodeIndex op1 = get(currentInstruction[2].u.operand);
687             NodeIndex op2 = get(currentInstruction[3].u.operand);
688             // If both operands can statically be determined to the numbers, then this is an arithmetic add.
689             // Otherwise, we must assume this may be performing a concatenation to a string.
690             if (m_graph[op1].hasNumericResult() && m_graph[op2].hasNumericResult())
691                 set(currentInstruction[1].u.operand, addToGraph(ArithAdd, toNumber(op1), toNumber(op2)));
692             else
693                 set(currentInstruction[1].u.operand, addToGraph(ValueAdd, op1, op2));
694             NEXT_OPCODE(op_add);
695         }
696 
697         case op_sub: {
698             ARITHMETIC_OP();
699             NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
700             NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
701             set(currentInstruction[1].u.operand, addToGraph(ArithSub, op1, op2));
702             NEXT_OPCODE(op_sub);
703         }
704 
705         case op_mul: {
706             ARITHMETIC_OP();
707             NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
708             NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
709             set(currentInstruction[1].u.operand, addToGraph(ArithMul, op1, op2));
710             NEXT_OPCODE(op_mul);
711         }
712 
713         case op_mod: {
714             ARITHMETIC_OP();
715             NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
716             NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
717             set(currentInstruction[1].u.operand, addToGraph(ArithMod, op1, op2));
718             NEXT_OPCODE(op_mod);
719         }
720 
721         case op_div: {
722             ARITHMETIC_OP();
723             NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
724             NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
725             set(currentInstruction[1].u.operand, addToGraph(ArithDiv, op1, op2));
726             NEXT_OPCODE(op_div);
727         }
728 
729         // === Misc operations ===
730 
731         case op_mov: {
732             NodeIndex op = get(currentInstruction[2].u.operand);
733             set(currentInstruction[1].u.operand, op);
734             NEXT_OPCODE(op_mov);
735         }
736 
737         case op_not: {
738             ARITHMETIC_OP();
739             NodeIndex value = get(currentInstruction[2].u.operand);
740             set(currentInstruction[1].u.operand, addToGraph(LogicalNot, value));
741             NEXT_OPCODE(op_not);
742         }
743 
744         case op_less: {
745             ARITHMETIC_OP();
746             NodeIndex op1 = get(currentInstruction[2].u.operand);
747             NodeIndex op2 = get(currentInstruction[3].u.operand);
748             set(currentInstruction[1].u.operand, addToGraph(CompareLess, op1, op2));
749             NEXT_OPCODE(op_less);
750         }
751 
752         case op_lesseq: {
753             ARITHMETIC_OP();
754             NodeIndex op1 = get(currentInstruction[2].u.operand);
755             NodeIndex op2 = get(currentInstruction[3].u.operand);
756             set(currentInstruction[1].u.operand, addToGraph(CompareLessEq, op1, op2));
757             NEXT_OPCODE(op_lesseq);
758         }
759 
760         case op_eq: {
761             ARITHMETIC_OP();
762             NodeIndex op1 = get(currentInstruction[2].u.operand);
763             NodeIndex op2 = get(currentInstruction[3].u.operand);
764             set(currentInstruction[1].u.operand, addToGraph(CompareEq, op1, op2));
765             NEXT_OPCODE(op_eq);
766         }
767 
768         case op_eq_null: {
769             ARITHMETIC_OP();
770             NodeIndex value = get(currentInstruction[2].u.operand);
771             set(currentInstruction[1].u.operand, addToGraph(CompareEq, value, constantNull()));
772             NEXT_OPCODE(op_eq_null);
773         }
774 
775         case op_stricteq: {
776             ARITHMETIC_OP();
777             NodeIndex op1 = get(currentInstruction[2].u.operand);
778             NodeIndex op2 = get(currentInstruction[3].u.operand);
779             set(currentInstruction[1].u.operand, addToGraph(CompareStrictEq, op1, op2));
780             NEXT_OPCODE(op_stricteq);
781         }
782 
783         case op_neq: {
784             ARITHMETIC_OP();
785             NodeIndex op1 = get(currentInstruction[2].u.operand);
786             NodeIndex op2 = get(currentInstruction[3].u.operand);
787             set(currentInstruction[1].u.operand, addToGraph(LogicalNot, addToGraph(CompareEq, op1, op2)));
788             NEXT_OPCODE(op_neq);
789         }
790 
791         case op_neq_null: {
792             ARITHMETIC_OP();
793             NodeIndex value = get(currentInstruction[2].u.operand);
794             set(currentInstruction[1].u.operand, addToGraph(LogicalNot, addToGraph(CompareEq, value, constantNull())));
795             NEXT_OPCODE(op_neq_null);
796         }
797 
798         case op_nstricteq: {
799             ARITHMETIC_OP();
800             NodeIndex op1 = get(currentInstruction[2].u.operand);
801             NodeIndex op2 = get(currentInstruction[3].u.operand);
802             set(currentInstruction[1].u.operand, addToGraph(LogicalNot, addToGraph(CompareStrictEq, op1, op2)));
803             NEXT_OPCODE(op_nstricteq);
804         }
805 
806         // === Property access operations ===
807 
808         case op_get_by_val: {
809             NodeIndex base = get(currentInstruction[2].u.operand);
810             NodeIndex property = get(currentInstruction[3].u.operand);
811 
812             NodeIndex getByVal = addToGraph(GetByVal, base, property, aliases.lookupGetByVal(base, property));
813             set(currentInstruction[1].u.operand, getByVal);
814             aliases.recordGetByVal(getByVal);
815 
816             NEXT_OPCODE(op_get_by_val);
817         }
818 
819         case op_put_by_val: {
820             NodeIndex base = get(currentInstruction[1].u.operand);
821             NodeIndex property = get(currentInstruction[2].u.operand);
822             NodeIndex value = get(currentInstruction[3].u.operand);
823 
824             NodeIndex aliasedGet = aliases.lookupGetByVal(base, property);
825             NodeIndex putByVal = addToGraph(aliasedGet != NoNode ? PutByValAlias : PutByVal, base, property, value);
826             aliases.recordPutByVal(putByVal);
827 
828             NEXT_OPCODE(op_put_by_val);
829         }
830 
831         case op_get_by_id: {
832             NodeIndex base = get(currentInstruction[2].u.operand);
833             unsigned identifier = currentInstruction[3].u.operand;
834 
835             NodeIndex getById = addToGraph(GetById, OpInfo(identifier), base);
836             set(currentInstruction[1].u.operand, getById);
837             aliases.recordGetById(getById);
838 
839             NEXT_OPCODE(op_get_by_id);
840         }
841 
842         case op_put_by_id: {
843             NodeIndex value = get(currentInstruction[3].u.operand);
844             NodeIndex base = get(currentInstruction[1].u.operand);
845             unsigned identifier = currentInstruction[2].u.operand;
846             bool direct = currentInstruction[8].u.operand;
847 
848             if (direct) {
849                 NodeIndex putByIdDirect = addToGraph(PutByIdDirect, OpInfo(identifier), base, value);
850                 aliases.recordPutByIdDirect(putByIdDirect);
851             } else {
852                 NodeIndex putById = addToGraph(PutById, OpInfo(identifier), base, value);
853                 aliases.recordPutById(putById);
854             }
855 
856             NEXT_OPCODE(op_put_by_id);
857         }
858 
859         case op_get_global_var: {
860             NodeIndex getGlobalVar = addToGraph(GetGlobalVar, OpInfo(currentInstruction[2].u.operand));
861             set(currentInstruction[1].u.operand, getGlobalVar);
862             NEXT_OPCODE(op_get_global_var);
863         }
864 
865         case op_put_global_var: {
866             NodeIndex value = get(currentInstruction[2].u.operand);
867             addToGraph(PutGlobalVar, OpInfo(currentInstruction[1].u.operand), value);
868             NEXT_OPCODE(op_put_global_var);
869         }
870 
871         // === Block terminators. ===
872 
873         case op_jmp: {
874             unsigned relativeOffset = currentInstruction[1].u.operand;
875             addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
876             LAST_OPCODE(op_jmp);
877         }
878 
879         case op_loop: {
880             unsigned relativeOffset = currentInstruction[1].u.operand;
881             addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
882             LAST_OPCODE(op_loop);
883         }
884 
885         case op_jtrue: {
886             unsigned relativeOffset = currentInstruction[2].u.operand;
887             NodeIndex condition = get(currentInstruction[1].u.operand);
888             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jtrue)), condition);
889             LAST_OPCODE(op_jtrue);
890         }
891 
892         case op_jfalse: {
893             unsigned relativeOffset = currentInstruction[2].u.operand;
894             NodeIndex condition = get(currentInstruction[1].u.operand);
895             addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jfalse)), OpInfo(m_currentIndex + relativeOffset), condition);
896             LAST_OPCODE(op_jfalse);
897         }
898 
899         case op_loop_if_true: {
900             unsigned relativeOffset = currentInstruction[2].u.operand;
901             NodeIndex condition = get(currentInstruction[1].u.operand);
902             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_true)), condition);
903             LAST_OPCODE(op_loop_if_true);
904         }
905 
906         case op_loop_if_false: {
907             unsigned relativeOffset = currentInstruction[2].u.operand;
908             NodeIndex condition = get(currentInstruction[1].u.operand);
909             addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_false)), OpInfo(m_currentIndex + relativeOffset), condition);
910             LAST_OPCODE(op_loop_if_false);
911         }
912 
913         case op_jeq_null: {
914             unsigned relativeOffset = currentInstruction[2].u.operand;
915             NodeIndex value = get(currentInstruction[1].u.operand);
916             NodeIndex condition = addToGraph(CompareEq, value, constantNull());
917             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jeq_null)), condition);
918             LAST_OPCODE(op_jeq_null);
919         }
920 
921         case op_jneq_null: {
922             unsigned relativeOffset = currentInstruction[2].u.operand;
923             NodeIndex value = get(currentInstruction[1].u.operand);
924             NodeIndex condition = addToGraph(CompareEq, value, constantNull());
925             addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jneq_null)), OpInfo(m_currentIndex + relativeOffset), condition);
926             LAST_OPCODE(op_jneq_null);
927         }
928 
929         case op_jnless: {
930             unsigned relativeOffset = currentInstruction[3].u.operand;
931             NodeIndex op1 = get(currentInstruction[1].u.operand);
932             NodeIndex op2 = get(currentInstruction[2].u.operand);
933             NodeIndex condition = addToGraph(CompareLess, op1, op2);
934             addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jnless)), OpInfo(m_currentIndex + relativeOffset), condition);
935             LAST_OPCODE(op_jnless);
936         }
937 
938         case op_jnlesseq: {
939             unsigned relativeOffset = currentInstruction[3].u.operand;
940             NodeIndex op1 = get(currentInstruction[1].u.operand);
941             NodeIndex op2 = get(currentInstruction[2].u.operand);
942             NodeIndex condition = addToGraph(CompareLessEq, op1, op2);
943             addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jnlesseq)), OpInfo(m_currentIndex + relativeOffset), condition);
944             LAST_OPCODE(op_jnlesseq);
945         }
946 
947         case op_jless: {
948             unsigned relativeOffset = currentInstruction[3].u.operand;
949             NodeIndex op1 = get(currentInstruction[1].u.operand);
950             NodeIndex op2 = get(currentInstruction[2].u.operand);
951             NodeIndex condition = addToGraph(CompareLess, op1, op2);
952             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jless)), condition);
953             LAST_OPCODE(op_jless);
954         }
955 
956         case op_jlesseq: {
957             unsigned relativeOffset = currentInstruction[3].u.operand;
958             NodeIndex op1 = get(currentInstruction[1].u.operand);
959             NodeIndex op2 = get(currentInstruction[2].u.operand);
960             NodeIndex condition = addToGraph(CompareLessEq, op1, op2);
961             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jlesseq)), condition);
962             LAST_OPCODE(op_jlesseq);
963         }
964 
965         case op_loop_if_less: {
966             unsigned relativeOffset = currentInstruction[3].u.operand;
967             NodeIndex op1 = get(currentInstruction[1].u.operand);
968             NodeIndex op2 = get(currentInstruction[2].u.operand);
969             NodeIndex condition = addToGraph(CompareLess, op1, op2);
970             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_less)), condition);
971             LAST_OPCODE(op_loop_if_less);
972         }
973 
974         case op_loop_if_lesseq: {
975             unsigned relativeOffset = currentInstruction[3].u.operand;
976             NodeIndex op1 = get(currentInstruction[1].u.operand);
977             NodeIndex op2 = get(currentInstruction[2].u.operand);
978             NodeIndex condition = addToGraph(CompareLessEq, op1, op2);
979             addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_lesseq)), condition);
980             LAST_OPCODE(op_loop_if_lesseq);
981         }
982 
983         case op_ret: {
984             addToGraph(Return, get(currentInstruction[1].u.operand));
985 
986             // FIXME: throw away terminal definitions of variables;
987             // should not be necessary once we have proper DCE!
988             for (unsigned i = 0; i < m_variables.size(); ++i) {
989                 NodeIndex priorSet = m_variables[i].set;
990                 if (priorSet != NoNode)
991                     m_graph.deref(priorSet);
992             }
993 
994             LAST_OPCODE(op_ret);
995         }
996 
997         default:
998             // Parse failed!
999             return false;
1000         }
1001     }
1002 }
1003 
parse()1004 bool ByteCodeParser::parse()
1005 {
1006     // Set during construction.
1007     ASSERT(!m_currentIndex);
1008 
1009     for (unsigned jumpTargetIndex = 0; jumpTargetIndex <= m_codeBlock->numberOfJumpTargets(); ++jumpTargetIndex) {
1010         // The maximum bytecode offset to go into the current basicblock is either the next jump target, or the end of the instructions.
1011         unsigned limit = jumpTargetIndex < m_codeBlock->numberOfJumpTargets() ? m_codeBlock->jumpTarget(jumpTargetIndex) : m_codeBlock->instructions().size();
1012         ASSERT(m_currentIndex < limit);
1013 
1014         // Loop until we reach the current limit (i.e. next jump target).
1015         do {
1016             unsigned bytecodeBegin = m_currentIndex;
1017             NodeIndex begin = m_graph.size();
1018 
1019             if (!parseBlock(limit))
1020                 return false;
1021             // We should not have gone beyond the limit.
1022             ASSERT(m_currentIndex <= limit);
1023 
1024             NodeIndex end = m_graph.size();
1025             m_graph.m_blocks.append(BasicBlock(bytecodeBegin, begin, end));
1026         } while (m_currentIndex < limit);
1027     }
1028 
1029     // Should have reached the end of the instructions.
1030     ASSERT(m_currentIndex == m_codeBlock->instructions().size());
1031 
1032     // Assign VirtualRegisters.
1033     ScoreBoard scoreBoard(m_graph, m_variables.size());
1034     Node* nodes = m_graph.begin();
1035     size_t size = m_graph.size();
1036     for (size_t i = 0; i < size; ++i) {
1037         Node& node = nodes[i];
1038         if (node.refCount) {
1039             // First, call use on all of the current node's children, then
1040             // allocate a VirtualRegister for this node. We do so in this
1041             // order so that if a child is on its last use, and a
1042             // VirtualRegister is freed, then it may be reused for node.
1043             scoreBoard.use(node.child1);
1044             scoreBoard.use(node.child2);
1045             scoreBoard.use(node.child3);
1046             node.virtualRegister = scoreBoard.allocate();
1047             // 'mustGenerate' nodes have their useCount artificially elevated,
1048             // call use now to account for this.
1049             if (node.mustGenerate())
1050                 scoreBoard.use(i);
1051         }
1052     }
1053 
1054     // 'm_numCalleeRegisters' is the number of locals and temporaries allocated
1055     // for the function (and checked for on entry). Since we perform a new and
1056     // different allocation of temporaries, more registers may now be required.
1057     unsigned calleeRegisters = scoreBoard.allocatedCount() + m_variables.size();
1058     if ((unsigned)m_codeBlock->m_numCalleeRegisters < calleeRegisters)
1059         m_codeBlock->m_numCalleeRegisters = calleeRegisters;
1060 
1061 #if DFG_DEBUG_VERBOSE
1062     m_graph.dump(m_codeBlock);
1063 #endif
1064 
1065     return true;
1066 }
1067 
parse(Graph & graph,JSGlobalData * globalData,CodeBlock * codeBlock)1068 bool parse(Graph& graph, JSGlobalData* globalData, CodeBlock* codeBlock)
1069 {
1070 #if DFG_DEBUG_LOCAL_DISBALE
1071     UNUSED_PARAM(graph);
1072     UNUSED_PARAM(globalData);
1073     UNUSED_PARAM(codeBlock);
1074     return false;
1075 #else
1076     return ByteCodeParser(globalData, codeBlock, graph).parse();
1077 #endif
1078 }
1079 
1080 } } // namespace JSC::DFG
1081 
1082 #endif
1083