2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "DFGByteCodeParser.h"
31 #include "DFGAliasTracker.h"
32 #include "DFGScoreBoard.h"
33 #include "CodeBlock.h"
35 namespace JSC
{ namespace DFG
{
37 #if ENABLE(DFG_JIT_RESTRICTIONS)
38 // FIXME: Temporarily disable arithmetic, until we fix associated performance regressions.
39 #define ARITHMETIC_OP() m_parseFailed = true
41 #define ARITHMETIC_OP() ((void)0)
44 // === ByteCodeParser ===
46 // This class is used to compile the dataflow graph from a CodeBlock.
47 class ByteCodeParser
{
49 ByteCodeParser(JSGlobalData
* globalData
, CodeBlock
* codeBlock
, Graph
& graph
)
50 : m_globalData(globalData
)
51 , m_codeBlock(codeBlock
)
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_numArguments(codeBlock
->m_numParameters
)
60 , m_numLocals(codeBlock
->m_numCalleeRegisters
)
61 , m_preservedVars(codeBlock
->m_numVars
)
65 // Parse a full CodeBlock of bytecode.
69 // Parse a single basic block of bytecode instructions.
70 bool parseBlock(unsigned limit
);
71 // Setup predecessor links in the graph's BasicBlocks.
72 void setupPredecessors();
73 // Link GetLocal & SetLocal nodes, to ensure live values are generated.
78 template<PhiStackType stackType
>
79 void processPhiStack();
80 // Add spill locations to nodes.
81 void allocateVirtualRegisters();
83 // Get/Set the operands/result of a bytecode instruction.
84 NodeIndex
get(int operand
)
86 // Is this a constant?
87 if (operand
>= FirstConstantRegisterIndex
) {
88 unsigned constant
= operand
- FirstConstantRegisterIndex
;
89 ASSERT(constant
< m_constants
.size());
90 return getJSConstant(constant
);
93 // Is this an argument?
94 if (operandIsArgument(operand
))
95 return getArgument(operand
);
98 return getLocal((unsigned)operand
);
100 void set(int operand
, NodeIndex value
, PredictedType prediction
= PredictNone
)
102 m_graph
.predict(operand
, prediction
);
104 // Is this an argument?
105 if (operandIsArgument(operand
)) {
106 setArgument(operand
, value
);
111 setLocal((unsigned)operand
, value
);
114 // Used in implementing get/set, above, where the operand is a local variable.
115 NodeIndex
getLocal(unsigned operand
)
117 NodeIndex nodeIndex
= m_currentBlock
->m_locals
[operand
].value
;
119 if (nodeIndex
!= NoNode
) {
120 Node
& node
= m_graph
[nodeIndex
];
121 if (node
.op
== GetLocal
)
123 ASSERT(node
.op
== SetLocal
);
127 // Check for reads of temporaries from prior blocks,
128 // expand m_preservedVars to cover these.
129 m_preservedVars
= std::max(m_preservedVars
, operand
+ 1);
131 NodeIndex phi
= addToGraph(Phi
);
132 m_localPhiStack
.append(PhiStackEntry(m_currentBlock
, phi
, operand
));
133 nodeIndex
= addToGraph(GetLocal
, OpInfo(operand
), phi
);
134 m_currentBlock
->m_locals
[operand
].value
= nodeIndex
;
137 void setLocal(unsigned operand
, NodeIndex value
)
139 m_currentBlock
->m_locals
[operand
].value
= addToGraph(SetLocal
, OpInfo(operand
), value
);
142 // Used in implementing get/set, above, where the operand is an argument.
143 NodeIndex
getArgument(unsigned operand
)
145 unsigned argument
= operand
+ m_codeBlock
->m_numParameters
+ RegisterFile::CallFrameHeaderSize
;
146 ASSERT(argument
< m_numArguments
);
148 NodeIndex nodeIndex
= m_currentBlock
->m_arguments
[argument
].value
;
150 if (nodeIndex
!= NoNode
) {
151 Node
& node
= m_graph
[nodeIndex
];
152 if (node
.op
== GetLocal
)
154 ASSERT(node
.op
== SetLocal
);
158 NodeIndex phi
= addToGraph(Phi
);
159 m_argumentPhiStack
.append(PhiStackEntry(m_currentBlock
, phi
, argument
));
160 nodeIndex
= addToGraph(GetLocal
, OpInfo(operand
), phi
);
161 m_currentBlock
->m_arguments
[argument
].value
= nodeIndex
;
164 void setArgument(int operand
, NodeIndex value
)
166 unsigned argument
= operand
+ m_codeBlock
->m_numParameters
+ RegisterFile::CallFrameHeaderSize
;
167 ASSERT(argument
< m_numArguments
);
169 m_currentBlock
->m_arguments
[argument
].value
= addToGraph(SetLocal
, OpInfo(operand
), value
);
172 // Get an operand, and perform a ToInt32/ToNumber conversion on it.
173 NodeIndex
getToInt32(int operand
)
175 // Avoid wastefully adding a JSConstant node to the graph, only to
176 // replace it with a Int32Constant (which is what would happen if
177 // we called 'toInt32(get(operand))' in this case).
178 if (operand
>= FirstConstantRegisterIndex
) {
179 JSValue v
= m_codeBlock
->getConstant(operand
);
181 return getInt32Constant(v
.asInt32(), operand
- FirstConstantRegisterIndex
);
183 return toInt32(get(operand
));
185 NodeIndex
getToNumber(int operand
)
187 // Avoid wastefully adding a JSConstant node to the graph, only to
188 // replace it with a DoubleConstant (which is what would happen if
189 // we called 'toNumber(get(operand))' in this case).
190 if (operand
>= FirstConstantRegisterIndex
) {
191 JSValue v
= m_codeBlock
->getConstant(operand
);
193 return getDoubleConstant(v
.uncheckedGetNumber(), operand
- FirstConstantRegisterIndex
);
195 return toNumber(get(operand
));
198 // Perform an ES5 ToInt32 operation - returns a node of type NodeResultInt32.
199 NodeIndex
toInt32(NodeIndex index
)
201 Node
& node
= m_graph
[index
];
203 if (node
.hasInt32Result())
206 if (node
.hasDoubleResult()) {
207 if (node
.op
== DoubleConstant
)
208 return getInt32Constant(JSC::toInt32(valueOfDoubleConstant(index
)), node
.constantNumber());
209 // 'NumberToInt32(Int32ToNumber(X))' == X, and 'NumberToInt32(UInt32ToNumber(X)) == X'
210 if (node
.op
== Int32ToNumber
|| node
.op
== UInt32ToNumber
)
213 // We unique NumberToInt32 nodes in a map to prevent duplicate conversions.
214 pair
<UnaryOpMap::iterator
, bool> result
= m_numberToInt32Nodes
.add(index
, NoNode
);
215 // Either we added a new value, or the existing value in the map is non-zero.
216 ASSERT(result
.second
== (result
.first
->second
== NoNode
));
218 result
.first
->second
= addToGraph(NumberToInt32
, index
);
219 return result
.first
->second
;
222 // Check for numeric constants boxed as JSValues.
223 if (node
.op
== JSConstant
) {
224 JSValue v
= valueOfJSConstant(index
);
226 return getInt32Constant(v
.asInt32(), node
.constantNumber());
228 return getInt32Constant(JSC::toInt32(v
.uncheckedGetNumber()), node
.constantNumber());
231 return addToGraph(ValueToInt32
, index
);
234 // Perform an ES5 ToNumber operation - returns a node of type NodeResultDouble.
235 NodeIndex
toNumber(NodeIndex index
)
237 Node
& node
= m_graph
[index
];
239 if (node
.hasDoubleResult())
242 if (node
.hasInt32Result()) {
243 if (node
.op
== Int32Constant
)
244 return getDoubleConstant(valueOfInt32Constant(index
), node
.constantNumber());
246 // We unique Int32ToNumber nodes in a map to prevent duplicate conversions.
247 pair
<UnaryOpMap::iterator
, bool> result
= m_int32ToNumberNodes
.add(index
, NoNode
);
248 // Either we added a new value, or the existing value in the map is non-zero.
249 ASSERT(result
.second
== (result
.first
->second
== NoNode
));
251 result
.first
->second
= addToGraph(Int32ToNumber
, index
);
252 return result
.first
->second
;
255 if (node
.op
== JSConstant
) {
256 JSValue v
= valueOfJSConstant(index
);
258 return getDoubleConstant(v
.uncheckedGetNumber(), node
.constantNumber());
261 return addToGraph(ValueToNumber
, index
);
265 // Used in implementing get, above, where the operand is a constant.
266 NodeIndex
getInt32Constant(int32_t value
, unsigned constant
)
268 NodeIndex index
= m_constants
[constant
].asInt32
;
271 NodeIndex resultIndex
= addToGraph(Int32Constant
, OpInfo(constant
));
272 m_graph
[resultIndex
].setInt32Constant(value
);
273 m_constants
[constant
].asInt32
= resultIndex
;
276 NodeIndex
getDoubleConstant(double value
, unsigned constant
)
278 NodeIndex index
= m_constants
[constant
].asNumeric
;
281 NodeIndex resultIndex
= addToGraph(DoubleConstant
, OpInfo(constant
));
282 m_graph
[resultIndex
].setDoubleConstant(value
);
283 m_constants
[constant
].asNumeric
= resultIndex
;
286 NodeIndex
getJSConstant(unsigned constant
)
288 NodeIndex index
= m_constants
[constant
].asJSValue
;
292 NodeIndex resultIndex
= addToGraph(JSConstant
, OpInfo(constant
));
293 m_constants
[constant
].asJSValue
= resultIndex
;
297 // Helper functions to get/set the this value.
300 return getArgument(m_codeBlock
->thisRegister());
302 void setThis(NodeIndex value
)
304 setArgument(m_codeBlock
->thisRegister(), value
);
307 // Convenience methods for checking nodes for constants.
308 bool isInt32Constant(NodeIndex index
)
310 return m_graph
[index
].op
== Int32Constant
;
312 bool isDoubleConstant(NodeIndex index
)
314 return m_graph
[index
].op
== DoubleConstant
;
316 bool isJSConstant(NodeIndex index
)
318 return m_graph
[index
].op
== JSConstant
;
321 // Convenience methods for getting constant values.
322 int32_t valueOfInt32Constant(NodeIndex index
)
324 ASSERT(isInt32Constant(index
));
325 return m_graph
[index
].int32Constant();
327 double valueOfDoubleConstant(NodeIndex index
)
329 ASSERT(isDoubleConstant(index
));
330 return m_graph
[index
].numericConstant();
332 JSValue
valueOfJSConstant(NodeIndex index
)
334 ASSERT(isJSConstant(index
));
335 return m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_graph
[index
].constantNumber());
338 // This method returns a JSConstant with the value 'undefined'.
339 NodeIndex
constantUndefined()
341 // Has m_constantUndefined been set up yet?
342 if (m_constantUndefined
== UINT_MAX
) {
343 // Search the constant pool for undefined, if we find it, we can just reuse this!
344 unsigned numberOfConstants
= m_codeBlock
->numberOfConstantRegisters();
345 for (m_constantUndefined
= 0; m_constantUndefined
< numberOfConstants
; ++m_constantUndefined
) {
346 JSValue testMe
= m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constantUndefined
);
347 if (testMe
.isUndefined())
348 return getJSConstant(m_constantUndefined
);
351 // Add undefined to the CodeBlock's constants, and add a corresponding slot in m_constants.
352 ASSERT(m_constants
.size() == numberOfConstants
);
353 m_codeBlock
->addConstant(jsUndefined());
354 m_constants
.append(ConstantRecord());
355 ASSERT(m_constants
.size() == m_codeBlock
->numberOfConstantRegisters());
358 // m_constantUndefined must refer to an entry in the CodeBlock's constant pool that has the value 'undefined'.
359 ASSERT(m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constantUndefined
).isUndefined());
360 return getJSConstant(m_constantUndefined
);
363 // This method returns a JSConstant with the value 'null'.
364 NodeIndex
constantNull()
366 // Has m_constantNull been set up yet?
367 if (m_constantNull
== UINT_MAX
) {
368 // Search the constant pool for null, if we find it, we can just reuse this!
369 unsigned numberOfConstants
= m_codeBlock
->numberOfConstantRegisters();
370 for (m_constantNull
= 0; m_constantNull
< numberOfConstants
; ++m_constantNull
) {
371 JSValue testMe
= m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constantNull
);
373 return getJSConstant(m_constantNull
);
376 // Add null to the CodeBlock's constants, and add a corresponding slot in m_constants.
377 ASSERT(m_constants
.size() == numberOfConstants
);
378 m_codeBlock
->addConstant(jsNull());
379 m_constants
.append(ConstantRecord());
380 ASSERT(m_constants
.size() == m_codeBlock
->numberOfConstantRegisters());
383 // m_constantNull must refer to an entry in the CodeBlock's constant pool that has the value 'null'.
384 ASSERT(m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constantNull
).isNull());
385 return getJSConstant(m_constantNull
);
388 // This method returns a DoubleConstant with the value 1.
391 // Has m_constant1 been set up yet?
392 if (m_constant1
== UINT_MAX
) {
393 // Search the constant pool for the value 1, if we find it, we can just reuse this!
394 unsigned numberOfConstants
= m_codeBlock
->numberOfConstantRegisters();
395 for (m_constant1
= 0; m_constant1
< numberOfConstants
; ++m_constant1
) {
396 JSValue testMe
= m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constant1
);
397 if (testMe
.isInt32() && testMe
.asInt32() == 1)
398 return getDoubleConstant(1, m_constant1
);
401 // Add the value 1 to the CodeBlock's constants, and add a corresponding slot in m_constants.
402 ASSERT(m_constants
.size() == numberOfConstants
);
403 m_codeBlock
->addConstant(jsNumber(1));
404 m_constants
.append(ConstantRecord());
405 ASSERT(m_constants
.size() == m_codeBlock
->numberOfConstantRegisters());
408 // m_constant1 must refer to an entry in the CodeBlock's constant pool that has the integer value 1.
409 ASSERT(m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constant1
).isInt32());
410 ASSERT(m_codeBlock
->getConstant(FirstConstantRegisterIndex
+ m_constant1
).asInt32() == 1);
411 return getDoubleConstant(1, m_constant1
);
415 // These methods create a node and add it to the graph. If nodes of this type are
416 // 'mustGenerate' then the node will implicitly be ref'ed to ensure generation.
417 NodeIndex
addToGraph(NodeType op
, NodeIndex child1
= NoNode
, NodeIndex child2
= NoNode
, NodeIndex child3
= NoNode
)
419 NodeIndex resultIndex
= (NodeIndex
)m_graph
.size();
420 m_graph
.append(Node(op
, m_currentIndex
, child1
, child2
, child3
));
422 if (op
& NodeMustGenerate
)
423 m_graph
.ref(resultIndex
);
426 NodeIndex
addToGraph(NodeType op
, OpInfo info
, NodeIndex child1
= NoNode
, NodeIndex child2
= NoNode
, NodeIndex child3
= NoNode
)
428 NodeIndex resultIndex
= (NodeIndex
)m_graph
.size();
429 m_graph
.append(Node(op
, m_currentIndex
, info
, child1
, child2
, child3
));
431 if (op
& NodeMustGenerate
)
432 m_graph
.ref(resultIndex
);
435 NodeIndex
addToGraph(NodeType op
, OpInfo info1
, OpInfo info2
, NodeIndex child1
= NoNode
, NodeIndex child2
= NoNode
, NodeIndex child3
= NoNode
)
437 NodeIndex resultIndex
= (NodeIndex
)m_graph
.size();
438 m_graph
.append(Node(op
, m_currentIndex
, info1
, info2
, child1
, child2
, child3
));
440 if (op
& NodeMustGenerate
)
441 m_graph
.ref(resultIndex
);
445 void predictArray(NodeIndex nodeIndex
)
447 Node
* nodePtr
= &m_graph
[nodeIndex
];
449 if (nodePtr
->op
== GetLocal
)
450 m_graph
.predict(nodePtr
->local(), PredictArray
);
453 void predictInt32(NodeIndex nodeIndex
)
455 Node
* nodePtr
= &m_graph
[nodeIndex
];
457 if (nodePtr
->op
== ValueToNumber
)
458 nodePtr
= &m_graph
[nodePtr
->child1
];
460 if (nodePtr
->op
== ValueToInt32
)
461 nodePtr
= &m_graph
[nodePtr
->child1
];
463 if (nodePtr
->op
== NumberToInt32
)
464 nodePtr
= &m_graph
[nodePtr
->child1
];
466 if (nodePtr
->op
== GetLocal
)
467 m_graph
.predict(nodePtr
->local(), PredictInt32
);
470 JSGlobalData
* m_globalData
;
471 CodeBlock
* m_codeBlock
;
474 // The current block being generated.
475 BasicBlock
* m_currentBlock
;
476 // The bytecode index of the current instruction being generated.
477 unsigned m_currentIndex
;
479 // Record failures due to unimplemented functionality or regressions.
482 // We use these values during code generation, and to avoid the need for
483 // special handling we make sure they are available as constants in the
484 // CodeBlock's constant pool. These variables are initialized to
485 // UINT_MAX, and lazily updated to hold an index into the CodeBlock's
486 // constant pool, as necessary.
487 unsigned m_constantUndefined
;
488 unsigned m_constantNull
;
489 unsigned m_constant1
;
491 // A constant in the constant pool may be represented by more than one
492 // node in the graph, depending on the context in which it is being used.
493 struct ConstantRecord
{
506 // Track the index of the node whose result is the current value for every
507 // register value in the bytecode - argument, local, and temporary.
508 Vector
<ConstantRecord
, 16> m_constants
;
510 // The number of arguments passed to the function.
511 unsigned m_numArguments
;
512 // The number of locals (vars + temporaries) used in the function.
513 unsigned m_numLocals
;
514 // The number of registers we need to preserve across BasicBlock boundaries;
515 // typically equal to the number vars, but we expand this to cover all
516 // temporaries that persist across blocks (dues to ?:, &&, ||, etc).
517 unsigned m_preservedVars
;
519 struct PhiStackEntry
{
520 PhiStackEntry(BasicBlock
* block
, NodeIndex phi
, unsigned varNo
)
531 Vector
<PhiStackEntry
, 16> m_argumentPhiStack
;
532 Vector
<PhiStackEntry
, 16> m_localPhiStack
;
534 // These maps are used to unique ToNumber and ToInt32 operations.
535 typedef HashMap
<NodeIndex
, NodeIndex
> UnaryOpMap
;
536 UnaryOpMap m_int32ToNumberNodes
;
537 UnaryOpMap m_numberToInt32Nodes
;
540 #define NEXT_OPCODE(name) \
541 m_currentIndex += OPCODE_LENGTH(name); \
544 #define LAST_OPCODE(name) \
545 m_currentIndex += OPCODE_LENGTH(name); \
546 return !m_parseFailed
548 bool ByteCodeParser::parseBlock(unsigned limit
)
550 // No need to reset state initially, since it has been set by the constructor.
551 if (m_currentIndex
) {
552 for (unsigned i
= 0; i
< m_constants
.size(); ++i
)
553 m_constants
[i
] = ConstantRecord();
556 AliasTracker
aliases(m_graph
);
558 Interpreter
* interpreter
= m_globalData
->interpreter
;
559 Instruction
* instructionsBegin
= m_codeBlock
->instructions().begin();
561 // Don't extend over jump destinations.
562 if (m_currentIndex
== limit
) {
563 addToGraph(Jump
, OpInfo(m_currentIndex
));
564 return !m_parseFailed
;
567 // Switch on the current bytecode opcode.
568 Instruction
* currentInstruction
= instructionsBegin
+ m_currentIndex
;
569 switch (interpreter
->getOpcodeID(currentInstruction
->u
.opcode
)) {
571 // === Function entry opcodes ===
574 // Initialize all locals to undefined.
575 for (int i
= 0; i
< m_codeBlock
->m_numVars
; ++i
)
576 set(i
, constantUndefined());
577 NEXT_OPCODE(op_enter
);
579 case op_convert_this
: {
580 NodeIndex op1
= getThis();
581 setThis(addToGraph(ConvertThis
, op1
));
582 NEXT_OPCODE(op_convert_this
);
585 // === Bitwise operations ===
588 NodeIndex op1
= getToInt32(currentInstruction
[2].u
.operand
);
589 NodeIndex op2
= getToInt32(currentInstruction
[3].u
.operand
);
592 set(currentInstruction
[1].u
.operand
, addToGraph(BitAnd
, op1
, op2
), PredictInt32
);
593 NEXT_OPCODE(op_bitand
);
597 NodeIndex op1
= getToInt32(currentInstruction
[2].u
.operand
);
598 NodeIndex op2
= getToInt32(currentInstruction
[3].u
.operand
);
601 set(currentInstruction
[1].u
.operand
, addToGraph(BitOr
, op1
, op2
), PredictInt32
);
602 NEXT_OPCODE(op_bitor
);
606 NodeIndex op1
= getToInt32(currentInstruction
[2].u
.operand
);
607 NodeIndex op2
= getToInt32(currentInstruction
[3].u
.operand
);
610 set(currentInstruction
[1].u
.operand
, addToGraph(BitXor
, op1
, op2
), PredictInt32
);
611 NEXT_OPCODE(op_bitxor
);
615 NodeIndex op1
= getToInt32(currentInstruction
[2].u
.operand
);
616 NodeIndex op2
= getToInt32(currentInstruction
[3].u
.operand
);
620 // Optimize out shifts by zero.
621 if (isInt32Constant(op2
) && !(valueOfInt32Constant(op2
) & 0x1f))
624 result
= addToGraph(BitRShift
, op1
, op2
);
625 set(currentInstruction
[1].u
.operand
, result
, PredictInt32
);
626 NEXT_OPCODE(op_rshift
);
630 NodeIndex op1
= getToInt32(currentInstruction
[2].u
.operand
);
631 NodeIndex op2
= getToInt32(currentInstruction
[3].u
.operand
);
635 // Optimize out shifts by zero.
636 if (isInt32Constant(op2
) && !(valueOfInt32Constant(op2
) & 0x1f))
639 result
= addToGraph(BitLShift
, op1
, op2
);
640 set(currentInstruction
[1].u
.operand
, result
, PredictInt32
);
641 NEXT_OPCODE(op_lshift
);
645 NodeIndex op1
= getToInt32(currentInstruction
[2].u
.operand
);
646 NodeIndex op2
= getToInt32(currentInstruction
[3].u
.operand
);
650 // The result of a zero-extending right shift is treated as an unsigned value.
651 // This means that if the top bit is set, the result is not in the int32 range,
652 // and as such must be stored as a double. If the shift amount is a constant,
653 // we may be able to optimize.
654 if (isInt32Constant(op2
)) {
655 // If we know we are shifting by a non-zero amount, then since the operation
656 // zero fills we know the top bit of the result must be zero, and as such the
657 // result must be within the int32 range. Conversely, if this is a shift by
658 // zero, then the result may be changed by the conversion to unsigned, but it
659 // is not necessary to perform the shift!
660 if (valueOfInt32Constant(op2
) & 0x1f)
661 result
= addToGraph(BitURShift
, op1
, op2
);
663 result
= addToGraph(UInt32ToNumber
, op1
);
665 // Cannot optimize at this stage; shift & potentially rebox as a double.
666 result
= addToGraph(BitURShift
, op1
, op2
);
667 result
= addToGraph(UInt32ToNumber
, result
);
669 set(currentInstruction
[1].u
.operand
, result
, PredictInt32
);
670 NEXT_OPCODE(op_urshift
);
673 // === Increment/Decrement opcodes ===
676 unsigned srcDst
= currentInstruction
[1].u
.operand
;
677 NodeIndex op
= getToNumber(srcDst
);
679 set(srcDst
, addToGraph(ArithAdd
, op
, one()));
680 NEXT_OPCODE(op_pre_inc
);
684 unsigned result
= currentInstruction
[1].u
.operand
;
685 unsigned srcDst
= currentInstruction
[2].u
.operand
;
686 NodeIndex op
= getToNumber(srcDst
);
689 set(srcDst
, addToGraph(ArithAdd
, op
, one()));
690 NEXT_OPCODE(op_post_inc
);
694 unsigned srcDst
= currentInstruction
[1].u
.operand
;
695 NodeIndex op
= getToNumber(srcDst
);
697 set(srcDst
, addToGraph(ArithSub
, op
, one()));
698 NEXT_OPCODE(op_pre_dec
);
702 unsigned result
= currentInstruction
[1].u
.operand
;
703 unsigned srcDst
= currentInstruction
[2].u
.operand
;
704 NodeIndex op
= getToNumber(srcDst
);
707 set(srcDst
, addToGraph(ArithSub
, op
, one()));
708 NEXT_OPCODE(op_post_dec
);
711 // === Arithmetic operations ===
715 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
716 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
717 // If both operands can statically be determined to the numbers, then this is an arithmetic add.
718 // Otherwise, we must assume this may be performing a concatenation to a string.
719 if (m_graph
[op1
].hasNumericResult() && m_graph
[op2
].hasNumericResult())
720 set(currentInstruction
[1].u
.operand
, addToGraph(ArithAdd
, toNumber(op1
), toNumber(op2
)));
722 set(currentInstruction
[1].u
.operand
, addToGraph(ValueAdd
, op1
, op2
));
728 NodeIndex op1
= getToNumber(currentInstruction
[2].u
.operand
);
729 NodeIndex op2
= getToNumber(currentInstruction
[3].u
.operand
);
730 set(currentInstruction
[1].u
.operand
, addToGraph(ArithSub
, op1
, op2
));
736 NodeIndex op1
= getToNumber(currentInstruction
[2].u
.operand
);
737 NodeIndex op2
= getToNumber(currentInstruction
[3].u
.operand
);
738 set(currentInstruction
[1].u
.operand
, addToGraph(ArithMul
, op1
, op2
));
744 NodeIndex op1
= getToNumber(currentInstruction
[2].u
.operand
);
745 NodeIndex op2
= getToNumber(currentInstruction
[3].u
.operand
);
746 set(currentInstruction
[1].u
.operand
, addToGraph(ArithMod
, op1
, op2
));
752 NodeIndex op1
= getToNumber(currentInstruction
[2].u
.operand
);
753 NodeIndex op2
= getToNumber(currentInstruction
[3].u
.operand
);
754 set(currentInstruction
[1].u
.operand
, addToGraph(ArithDiv
, op1
, op2
));
758 // === Misc operations ===
761 NodeIndex op
= get(currentInstruction
[2].u
.operand
);
762 set(currentInstruction
[1].u
.operand
, op
);
768 NodeIndex value
= get(currentInstruction
[2].u
.operand
);
769 set(currentInstruction
[1].u
.operand
, addToGraph(LogicalNot
, value
));
775 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
776 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
777 set(currentInstruction
[1].u
.operand
, addToGraph(CompareLess
, op1
, op2
));
778 NEXT_OPCODE(op_less
);
783 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
784 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
785 set(currentInstruction
[1].u
.operand
, addToGraph(CompareLessEq
, op1
, op2
));
786 NEXT_OPCODE(op_lesseq
);
791 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
792 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
793 set(currentInstruction
[1].u
.operand
, addToGraph(CompareEq
, op1
, op2
));
799 NodeIndex value
= get(currentInstruction
[2].u
.operand
);
800 set(currentInstruction
[1].u
.operand
, addToGraph(CompareEq
, value
, constantNull()));
801 NEXT_OPCODE(op_eq_null
);
806 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
807 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
808 set(currentInstruction
[1].u
.operand
, addToGraph(CompareStrictEq
, op1
, op2
));
809 NEXT_OPCODE(op_stricteq
);
814 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
815 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
816 set(currentInstruction
[1].u
.operand
, addToGraph(LogicalNot
, addToGraph(CompareEq
, op1
, op2
)));
822 NodeIndex value
= get(currentInstruction
[2].u
.operand
);
823 set(currentInstruction
[1].u
.operand
, addToGraph(LogicalNot
, addToGraph(CompareEq
, value
, constantNull())));
824 NEXT_OPCODE(op_neq_null
);
829 NodeIndex op1
= get(currentInstruction
[2].u
.operand
);
830 NodeIndex op2
= get(currentInstruction
[3].u
.operand
);
831 set(currentInstruction
[1].u
.operand
, addToGraph(LogicalNot
, addToGraph(CompareStrictEq
, op1
, op2
)));
832 NEXT_OPCODE(op_nstricteq
);
835 // === Property access operations ===
837 case op_get_by_val
: {
838 NodeIndex base
= get(currentInstruction
[2].u
.operand
);
839 NodeIndex property
= get(currentInstruction
[3].u
.operand
);
841 predictInt32(property
);
843 NodeIndex getByVal
= addToGraph(GetByVal
, base
, property
, aliases
.lookupGetByVal(base
, property
));
844 set(currentInstruction
[1].u
.operand
, getByVal
);
845 aliases
.recordGetByVal(getByVal
);
847 NEXT_OPCODE(op_get_by_val
);
850 case op_put_by_val
: {
851 NodeIndex base
= get(currentInstruction
[1].u
.operand
);
852 NodeIndex property
= get(currentInstruction
[2].u
.operand
);
853 NodeIndex value
= get(currentInstruction
[3].u
.operand
);
855 predictInt32(property
);
857 NodeIndex aliasedGet
= aliases
.lookupGetByVal(base
, property
);
858 NodeIndex putByVal
= addToGraph(aliasedGet
!= NoNode
? PutByValAlias
: PutByVal
, base
, property
, value
);
859 aliases
.recordPutByVal(putByVal
);
861 NEXT_OPCODE(op_put_by_val
);
865 NodeIndex base
= get(currentInstruction
[2].u
.operand
);
866 unsigned identifier
= currentInstruction
[3].u
.operand
;
868 NodeIndex getById
= addToGraph(GetById
, OpInfo(identifier
), base
);
869 set(currentInstruction
[1].u
.operand
, getById
);
870 aliases
.recordGetById(getById
);
872 NEXT_OPCODE(op_get_by_id
);
876 NodeIndex value
= get(currentInstruction
[3].u
.operand
);
877 NodeIndex base
= get(currentInstruction
[1].u
.operand
);
878 unsigned identifier
= currentInstruction
[2].u
.operand
;
879 bool direct
= currentInstruction
[8].u
.operand
;
882 NodeIndex putByIdDirect
= addToGraph(PutByIdDirect
, OpInfo(identifier
), base
, value
);
883 aliases
.recordPutByIdDirect(putByIdDirect
);
885 NodeIndex putById
= addToGraph(PutById
, OpInfo(identifier
), base
, value
);
886 aliases
.recordPutById(putById
);
889 NEXT_OPCODE(op_put_by_id
);
892 case op_get_global_var
: {
893 NodeIndex getGlobalVar
= addToGraph(GetGlobalVar
, OpInfo(currentInstruction
[2].u
.operand
));
894 set(currentInstruction
[1].u
.operand
, getGlobalVar
);
895 NEXT_OPCODE(op_get_global_var
);
898 case op_put_global_var
: {
899 NodeIndex value
= get(currentInstruction
[2].u
.operand
);
900 addToGraph(PutGlobalVar
, OpInfo(currentInstruction
[1].u
.operand
), value
);
901 NEXT_OPCODE(op_put_global_var
);
904 // === Block terminators. ===
907 unsigned relativeOffset
= currentInstruction
[1].u
.operand
;
908 addToGraph(Jump
, OpInfo(m_currentIndex
+ relativeOffset
));
913 unsigned relativeOffset
= currentInstruction
[1].u
.operand
;
914 addToGraph(Jump
, OpInfo(m_currentIndex
+ relativeOffset
));
915 LAST_OPCODE(op_loop
);
919 unsigned relativeOffset
= currentInstruction
[2].u
.operand
;
920 NodeIndex condition
= get(currentInstruction
[1].u
.operand
);
921 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jtrue
)), condition
);
922 LAST_OPCODE(op_jtrue
);
926 unsigned relativeOffset
= currentInstruction
[2].u
.operand
;
927 NodeIndex condition
= get(currentInstruction
[1].u
.operand
);
928 addToGraph(Branch
, OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jfalse
)), OpInfo(m_currentIndex
+ relativeOffset
), condition
);
929 LAST_OPCODE(op_jfalse
);
932 case op_loop_if_true
: {
933 unsigned relativeOffset
= currentInstruction
[2].u
.operand
;
934 NodeIndex condition
= get(currentInstruction
[1].u
.operand
);
935 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_loop_if_true
)), condition
);
936 LAST_OPCODE(op_loop_if_true
);
939 case op_loop_if_false
: {
940 unsigned relativeOffset
= currentInstruction
[2].u
.operand
;
941 NodeIndex condition
= get(currentInstruction
[1].u
.operand
);
942 addToGraph(Branch
, OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_loop_if_false
)), OpInfo(m_currentIndex
+ relativeOffset
), condition
);
943 LAST_OPCODE(op_loop_if_false
);
947 unsigned relativeOffset
= currentInstruction
[2].u
.operand
;
948 NodeIndex value
= get(currentInstruction
[1].u
.operand
);
949 NodeIndex condition
= addToGraph(CompareEq
, value
, constantNull());
950 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jeq_null
)), condition
);
951 LAST_OPCODE(op_jeq_null
);
955 unsigned relativeOffset
= currentInstruction
[2].u
.operand
;
956 NodeIndex value
= get(currentInstruction
[1].u
.operand
);
957 NodeIndex condition
= addToGraph(CompareEq
, value
, constantNull());
958 addToGraph(Branch
, OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jneq_null
)), OpInfo(m_currentIndex
+ relativeOffset
), condition
);
959 LAST_OPCODE(op_jneq_null
);
963 unsigned relativeOffset
= currentInstruction
[3].u
.operand
;
964 NodeIndex op1
= get(currentInstruction
[1].u
.operand
);
965 NodeIndex op2
= get(currentInstruction
[2].u
.operand
);
966 NodeIndex condition
= addToGraph(CompareLess
, op1
, op2
);
967 addToGraph(Branch
, OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jnless
)), OpInfo(m_currentIndex
+ relativeOffset
), condition
);
968 LAST_OPCODE(op_jnless
);
972 unsigned relativeOffset
= currentInstruction
[3].u
.operand
;
973 NodeIndex op1
= get(currentInstruction
[1].u
.operand
);
974 NodeIndex op2
= get(currentInstruction
[2].u
.operand
);
975 NodeIndex condition
= addToGraph(CompareLessEq
, op1
, op2
);
976 addToGraph(Branch
, OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jnlesseq
)), OpInfo(m_currentIndex
+ relativeOffset
), condition
);
977 LAST_OPCODE(op_jnlesseq
);
981 unsigned relativeOffset
= currentInstruction
[3].u
.operand
;
982 NodeIndex op1
= get(currentInstruction
[1].u
.operand
);
983 NodeIndex op2
= get(currentInstruction
[2].u
.operand
);
984 NodeIndex condition
= addToGraph(CompareLess
, op1
, op2
);
985 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jless
)), condition
);
986 LAST_OPCODE(op_jless
);
990 unsigned relativeOffset
= currentInstruction
[3].u
.operand
;
991 NodeIndex op1
= get(currentInstruction
[1].u
.operand
);
992 NodeIndex op2
= get(currentInstruction
[2].u
.operand
);
993 NodeIndex condition
= addToGraph(CompareLessEq
, op1
, op2
);
994 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_jlesseq
)), condition
);
995 LAST_OPCODE(op_jlesseq
);
998 case op_loop_if_less
: {
999 unsigned relativeOffset
= currentInstruction
[3].u
.operand
;
1000 NodeIndex op1
= get(currentInstruction
[1].u
.operand
);
1001 NodeIndex op2
= get(currentInstruction
[2].u
.operand
);
1002 NodeIndex condition
= addToGraph(CompareLess
, op1
, op2
);
1003 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_loop_if_less
)), condition
);
1004 LAST_OPCODE(op_loop_if_less
);
1007 case op_loop_if_lesseq
: {
1008 unsigned relativeOffset
= currentInstruction
[3].u
.operand
;
1009 NodeIndex op1
= get(currentInstruction
[1].u
.operand
);
1010 NodeIndex op2
= get(currentInstruction
[2].u
.operand
);
1011 NodeIndex condition
= addToGraph(CompareLessEq
, op1
, op2
);
1012 addToGraph(Branch
, OpInfo(m_currentIndex
+ relativeOffset
), OpInfo(m_currentIndex
+ OPCODE_LENGTH(op_loop_if_lesseq
)), condition
);
1013 LAST_OPCODE(op_loop_if_lesseq
);
1017 addToGraph(Return
, get(currentInstruction
[1].u
.operand
));
1018 LAST_OPCODE(op_ret
);
1028 template<ByteCodeParser::PhiStackType stackType
>
1029 void ByteCodeParser::processPhiStack()
1031 Vector
<PhiStackEntry
, 16>& phiStack
= (stackType
== ArgumentPhiStack
) ? m_argumentPhiStack
: m_localPhiStack
;
1033 while (!phiStack
.isEmpty()) {
1034 PhiStackEntry entry
= phiStack
.last();
1035 phiStack
.removeLast();
1037 Node
& phiNode
= m_graph
[entry
.m_phi
];
1038 PredecessorList
& predecessors
= entry
.m_block
->m_predecessors
;
1039 unsigned varNo
= entry
.m_varNo
;
1041 for (size_t i
= 0; i
< predecessors
.size(); ++i
) {
1042 BasicBlock
* predecessorBlock
= m_graph
.m_blocks
[predecessors
[i
]].get();
1044 VariableRecord
& var
= (stackType
== ArgumentPhiStack
) ? predecessorBlock
->m_arguments
[varNo
] : predecessorBlock
->m_locals
[varNo
];
1046 NodeIndex valueInPredecessor
= var
.value
;
1047 if (valueInPredecessor
== NoNode
) {
1048 valueInPredecessor
= addToGraph(Phi
);
1049 var
.value
= valueInPredecessor
;
1050 phiStack
.append(PhiStackEntry(predecessorBlock
, valueInPredecessor
, varNo
));
1051 } else if (m_graph
[valueInPredecessor
].op
== GetLocal
)
1052 valueInPredecessor
= m_graph
[valueInPredecessor
].child1
;
1053 ASSERT(m_graph
[valueInPredecessor
].op
== SetLocal
|| m_graph
[valueInPredecessor
].op
== Phi
);
1055 if (phiNode
.refCount())
1056 m_graph
.ref(valueInPredecessor
);
1058 if (phiNode
.child1
== NoNode
) {
1059 phiNode
.child1
= valueInPredecessor
;
1062 if (phiNode
.child2
== NoNode
) {
1063 phiNode
.child2
= valueInPredecessor
;
1066 if (phiNode
.child3
== NoNode
) {
1067 phiNode
.child3
= valueInPredecessor
;
1071 NodeIndex newPhi
= addToGraph(Phi
);
1072 Node
& newPhiNode
= m_graph
[newPhi
];
1073 if (phiNode
.refCount())
1074 m_graph
.ref(newPhi
);
1076 newPhiNode
.child1
= phiNode
.child1
;
1077 newPhiNode
.child2
= phiNode
.child2
;
1078 newPhiNode
.child3
= phiNode
.child3
;
1080 phiNode
.child1
= newPhi
;
1081 phiNode
.child1
= valueInPredecessor
;
1082 phiNode
.child3
= NoNode
;
1087 void ByteCodeParser::setupPredecessors()
1089 for (BlockIndex index
= 0; index
< m_graph
.m_blocks
.size(); ++index
) {
1090 BasicBlock
* block
= m_graph
.m_blocks
[index
].get();
1091 ASSERT(block
->end
!= NoNode
);
1092 Node
& node
= m_graph
[block
->end
- 1];
1093 ASSERT(node
.isTerminal());
1096 m_graph
.blockForBytecodeOffset(node
.takenBytecodeOffset()).m_predecessors
.append(index
);
1097 else if (node
.isBranch()) {
1098 m_graph
.blockForBytecodeOffset(node
.takenBytecodeOffset()).m_predecessors
.append(index
);
1099 m_graph
.blockForBytecodeOffset(node
.notTakenBytecodeOffset()).m_predecessors
.append(index
);
1104 void ByteCodeParser::allocateVirtualRegisters()
1106 ScoreBoard
scoreBoard(m_graph
, m_preservedVars
);
1107 unsigned sizeExcludingPhiNodes
= m_graph
.m_blocks
.last()->end
;
1108 for (size_t i
= 0; i
< sizeExcludingPhiNodes
; ++i
) {
1109 Node
& node
= m_graph
[i
];
1110 if (!node
.shouldGenerate())
1113 // GetLocal nodes are effectively phi nodes in the graph, referencing
1114 // results from prior blocks.
1115 if (node
.op
!= GetLocal
) {
1116 // First, call use on all of the current node's children, then
1117 // allocate a VirtualRegister for this node. We do so in this
1118 // order so that if a child is on its last use, and a
1119 // VirtualRegister is freed, then it may be reused for node.
1120 scoreBoard
.use(node
.child1
);
1121 scoreBoard
.use(node
.child2
);
1122 scoreBoard
.use(node
.child3
);
1125 if (!node
.hasResult())
1128 node
.setVirtualRegister(scoreBoard
.allocate());
1129 // 'mustGenerate' nodes have their useCount artificially elevated,
1130 // call use now to account for this.
1131 if (node
.mustGenerate())
1135 // 'm_numCalleeRegisters' is the number of locals and temporaries allocated
1136 // for the function (and checked for on entry). Since we perform a new and
1137 // different allocation of temporaries, more registers may now be required.
1138 unsigned calleeRegisters
= scoreBoard
.allocatedCount() + m_preservedVars
;
1139 if ((unsigned)m_codeBlock
->m_numCalleeRegisters
< calleeRegisters
)
1140 m_codeBlock
->m_numCalleeRegisters
= calleeRegisters
;
1143 bool ByteCodeParser::parse()
1145 // Set during construction.
1146 ASSERT(!m_currentIndex
);
1148 for (unsigned jumpTargetIndex
= 0; jumpTargetIndex
<= m_codeBlock
->numberOfJumpTargets(); ++jumpTargetIndex
) {
1149 // The maximum bytecode offset to go into the current basicblock is either the next jump target, or the end of the instructions.
1150 unsigned limit
= jumpTargetIndex
< m_codeBlock
->numberOfJumpTargets() ? m_codeBlock
->jumpTarget(jumpTargetIndex
) : m_codeBlock
->instructions().size();
1151 ASSERT(m_currentIndex
< limit
);
1153 // Loop until we reach the current limit (i.e. next jump target).
1155 OwnPtr
<BasicBlock
> block
= adoptPtr(new BasicBlock(m_currentIndex
, m_graph
.size(), m_numArguments
, m_numLocals
));
1156 m_currentBlock
= block
.get();
1157 m_graph
.m_blocks
.append(block
.release());
1159 if (!parseBlock(limit
))
1161 // We should not have gone beyond the limit.
1162 ASSERT(m_currentIndex
<= limit
);
1164 m_currentBlock
->end
= m_graph
.size();
1165 } while (m_currentIndex
< limit
);
1168 // Should have reached the end of the instructions.
1169 ASSERT(m_currentIndex
== m_codeBlock
->instructions().size());
1171 setupPredecessors();
1172 processPhiStack
<LocalPhiStack
>();
1173 processPhiStack
<ArgumentPhiStack
>();
1175 allocateVirtualRegisters();
1177 #if DFG_DEBUG_VERBOSE
1178 m_graph
.dump(m_codeBlock
);
1184 bool parse(Graph
& graph
, JSGlobalData
* globalData
, CodeBlock
* codeBlock
)
1186 #if DFG_DEBUG_LOCAL_DISBALE
1187 UNUSED_PARAM(graph
);
1188 UNUSED_PARAM(globalData
);
1189 UNUSED_PARAM(codeBlock
);
1192 return ByteCodeParser(globalData
, codeBlock
, graph
).parse();
1196 } } // namespace JSC::DFG