Commit 81c68cce authored by ggaren@apple.com's avatar ggaren@apple.com

Source/JavaScriptCore: Re-landing <http://trac.webkit.org/changeset/148999>

    Filled out more cases of branch folding in bytecode when emitting
    expressions into a branching context
    https://bugs.webkit.org/show_bug.cgi?id=115057

    Reviewed by Phil Pizlo.

We can't fold the number == 1 case to boolean because all non-zero numbers
down-cast to true, but only 1 is == to true.

Source/WTF: Filled out more cases of branch folding in bytecode when emitting expressions into a branching context
https://bugs.webkit.org/show_bug.cgi?id=115057

Reviewed by Filip Pizlo.

Added a helper constructor for TriState so clients can make one without
branching or making assumptions about the integer values of TriStates.

* wtf/TriState.h:
(WTF::triState):
(WTF):

LayoutTests: Re-landing <http://trac.webkit.org/changeset/148999>

    Filled out more cases of branch folding in bytecode when emitting
    expressions into a branching context
    https://bugs.webkit.org/show_bug.cgi?id=115057

    Reviewed by Phil Pizlo.

Added a more exhaustive correctness test.

* fast/js/branch-fold-correctness-expected.txt: Added.
* fast/js/branch-fold-correctness.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@149236 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent cc6c60df
2013-04-26 Geoffrey Garen <ggaren@apple.com>
Re-landing <http://trac.webkit.org/changeset/148999>
Filled out more cases of branch folding in bytecode when emitting
expressions into a branching context
https://bugs.webkit.org/show_bug.cgi?id=115057
Reviewed by Phil Pizlo.
Added a more exhaustive correctness test.
* fast/js/branch-fold-correctness-expected.txt: Added.
* fast/js/branch-fold-correctness.html: Added.
2013-04-26 Jer Noble <jer.noble@apple.com>
Video playback has corruption on the edges of the video
......
This page tests branches that might cause interesting forms of expression folding in bytecode.
tests completed: 32
<p>This page tests branches that might cause interesting forms of expression folding in bytecode.</p>
<pre id="console"></pre>
<script>
if (window.testRunner)
testRunner.dumpAsText();
function log(s)
{
document.getElementById("console").appendChild(document.createTextNode(s + "\n"));
}
(function () {
var count = 0;
function fail()
{
log("FAIL on test " + count + ".");
}
var zero = 0;
var one = 1;
var two = 2;
// bool vs bool
++count;
if (true == (one == one)) {
} else
fail();
++count;
if (false == (one != one)) {
} else
fail();
++count;
if (true != (one != one)) {
} else
fail();
++count;
if (false != (one == one)) {
} else
fail();
++count;
if (true === (one == one)) {
} else
fail();
++count;
if (false === (one != one)) {
} else
fail();
++count;
if (true !== (one != one)) {
} else
fail();
++count;
if (false !== (one == one)) {
} else
fail();
// int vs bool
++count;
if (1 == (one == one)) {
} else
fail();
++count;
if (0 == (one != one)) {
} else
fail();
++count;
if (2 != (one != one)) {
} else
fail();
++count;
if (1 != (one != one)) {
} else
fail();
++count;
if (0 != (one == one)) {
} else
fail();
++count;
if (2 != (one == one)) {
} else
fail();
++count;
if (1 !== (one == one)) {
} else
fail();
++count;
if (0 !== (one != one)) {
} else
fail();
++count;
if (2 !== (one != one)) {
} else
fail();
++count;
if (1 !== (one != one)) {
} else
fail();
++count;
if (0 !== (one == one)) {
} else
fail();
++count;
if (2 !== (one == one)) {
} else
fail();
// int vs int
++count;
if (1 == (one | 0)) {
} else
fail();
++count;
if (0 == (zero | 0)) {
} else
fail();
++count;
if (2 != (one | 0)) {
} else
fail();
++count;
if (1 != (zero | 0)) {
} else
fail();
++count;
if (0 != (one | 0)) {
} else
fail();
++count;
if (2 != (one | 0)) {
} else
fail();
++count;
if (1 === (one | 0)) {
} else
fail();
++count;
if (0 === (zero | 0)) {
} else
fail();
++count;
if (2 !== (one | 0)) {
} else
fail();
++count;
if (1 !== (two | 0)) {
} else
fail();
++count;
if (0 !== (one | 0)) {
} else
fail();
++count;
if (2 !== (one | 0)) {
} else
fail();
log("tests completed: " + count);
})();
</script>
2013-04-26 Geoffrey Garen <ggaren@apple.com>
Re-landing <http://trac.webkit.org/changeset/148999>
Filled out more cases of branch folding in bytecode when emitting
expressions into a branching context
https://bugs.webkit.org/show_bug.cgi?id=115057
Reviewed by Phil Pizlo.
We can't fold the number == 1 case to boolean because all non-zero numbers
down-cast to true, but only 1 is == to true.
2013-04-26 Filip Pizlo <fpizlo@apple.com>
Correct indentation of SymbolTable.h
......
......@@ -717,7 +717,6 @@ __ZNK3JSC14ExpressionNode13isResolveNodeEv
__ZNK3JSC14ExpressionNode21isBracketAccessorNodeEv
__ZN3JSC9CodeBlock25createRareDataIfNecessaryEv
__ZN3WTF6VectorIN3JSC8LineInfoELm0EE14expandCapacityEm
__ZN3JSC6IfNode12emitBytecodeERNS_17BytecodeGeneratorEPNS_10RegisterIDE
__ZN3JSC17BytecodeGenerator8newLabelEv
__ZNK3JSC14ExpressionNode26hasConditionContextCodegenEv
__ZN3WTF6VectorIN3JSC19ExpressionRangeInfoELm0EE14expandCapacityEm
......
......@@ -1628,7 +1628,7 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen
bool hadVariableExpression = false;
if (length) {
for (ElementNode* n = elements; n; n = n->next()) {
if (!n->value()->isNumber() && !n->value()->isString()) {
if (!n->value()->isConstant()) {
hadVariableExpression = true;
break;
}
......@@ -1644,12 +1644,8 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen
JSValue* constantBuffer = m_codeBlock->constantBuffer(constantBufferIndex).data();
unsigned index = 0;
for (ElementNode* n = elements; index < length; n = n->next()) {
if (n->value()->isNumber())
constantBuffer[index++] = jsNumber(static_cast<NumberNode*>(n->value())->value());
else {
ASSERT(n->value()->isString());
constantBuffer[index++] = addStringConstant(static_cast<StringNode*>(n->value())->value());
}
ASSERT(n->value()->isConstant());
constantBuffer[index++] = static_cast<ConstantNode*>(n->value())->jsValue(*this);
}
emitOpcode(op_new_array_buffer);
instructions().append(dst->index());
......@@ -2365,7 +2361,7 @@ RegisterID* BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID*
void BytecodeGenerator::emitThrowReferenceError(const String& message)
{
emitOpcode(op_throw_static_error);
instructions().append(addConstantValue(jsString(vm(), message))->index());
instructions().append(addConstantValue(addStringConstant(Identifier(m_vm, message)))->index());
instructions().append(true);
}
......@@ -2523,7 +2519,7 @@ void BytecodeGenerator::emitReadOnlyExceptionIfNeeded()
if (!isStrictMode())
return;
emitOpcode(op_throw_static_error);
instructions().append(addConstantValue(jsString(vm(), StrictModeReadonlyPropertyWriteError))->index());
instructions().append(addConstantValue(addStringConstant(Identifier(m_vm, StrictModeReadonlyPropertyWriteError)))->index());
instructions().append(false);
}
......
......@@ -553,6 +553,7 @@ namespace JSC {
CodeType codeType() const { return m_codeType; }
bool shouldEmitProfileHooks() { return m_shouldEmitProfileHooks; }
bool shouldEmitDebugHooks() { return m_shouldEmitDebugHooks; }
bool isStrictMode() const { return m_codeBlock->isStrictMode(); }
......@@ -646,8 +647,6 @@ namespace JSC {
return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), body);
}
JSString* addStringConstant(const Identifier&);
void addLineInfo(unsigned lineNo)
{
m_codeBlock->addLineInfo(instructions().size(), lineNo - m_scopeNode->firstLine());
......@@ -656,6 +655,8 @@ namespace JSC {
RegisterID* emitInitLazyRegister(RegisterID*);
public:
JSString* addStringConstant(const Identifier&);
Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; }
SharedSymbolTable& symbolTable() { return *m_symbolTable; }
......
......@@ -92,40 +92,31 @@ RegisterID* ThrowableExpressionData::emitThrowReferenceError(BytecodeGenerator&
return generator.newTemporary();
}
// ------------------------------ NullNode -------------------------------------
// ------------------------------ ConstantNode ----------------------------------
RegisterID* NullNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
void ConstantNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, FallThroughMode fallThroughMode)
{
if (dst == generator.ignoredResult())
return 0;
return generator.emitLoad(dst, jsNull());
}
TriState value = jsValue(generator).pureToBoolean();
if (value == MixedTriState)
ExpressionNode::emitBytecodeInConditionContext(generator, trueTarget, falseTarget, fallThroughMode);
else if (value == TrueTriState && fallThroughMode == FallThroughMeansFalse)
generator.emitJump(trueTarget);
else if (value == FalseTriState && fallThroughMode == FallThroughMeansTrue)
generator.emitJump(falseTarget);
// ------------------------------ BooleanNode ----------------------------------
RegisterID* BooleanNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
if (dst == generator.ignoredResult())
return 0;
return generator.emitLoad(dst, m_value);
// All other cases are unconditional fall-throughs, like "if (true)".
}
// ------------------------------ NumberNode -----------------------------------
RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
RegisterID* ConstantNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
if (dst == generator.ignoredResult())
return 0;
return generator.emitLoad(dst, m_value);
return generator.emitLoad(dst, jsValue(generator));
}
// ------------------------------ StringNode -----------------------------------
RegisterID* StringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
JSValue StringNode::jsValue(BytecodeGenerator& generator) const
{
if (dst == generator.ignoredResult())
return 0;
return generator.emitLoad(dst, m_value);
return generator.addStringConstant(m_value);
}
// ------------------------------ RegExpNode -----------------------------------
......@@ -1042,6 +1033,64 @@ RegisterID* BinaryOpNode::emitStrcat(BytecodeGenerator& generator, RegisterID* d
return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size());
}
void BinaryOpNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, FallThroughMode fallThroughMode)
{
TriState branchCondition;
ExpressionNode* branchExpression;
tryFoldToBranch(generator, branchCondition, branchExpression);
if (branchCondition == MixedTriState)
ExpressionNode::emitBytecodeInConditionContext(generator, trueTarget, falseTarget, fallThroughMode);
else if (branchCondition == TrueTriState)
generator.emitNodeInConditionContext(branchExpression, trueTarget, falseTarget, fallThroughMode);
else
generator.emitNodeInConditionContext(branchExpression, falseTarget, trueTarget, invert(fallThroughMode));
}
static inline bool canFoldToBranch(OpcodeID opcodeID, ExpressionNode* branchExpression, JSValue constant)
{
ResultType expressionType = branchExpression->resultDescriptor();
if (expressionType.definitelyIsBoolean() && constant.isBoolean())
return true;
else if (expressionType.definitelyIsBoolean() && constant.isInt32() && (constant.asInt32() == 0 || constant.asInt32() == 1))
return opcodeID == op_eq || opcodeID == op_neq; // Strict equality is false in the case of type mismatch.
else if (expressionType.isInt32() && constant.isInt32() && constant.asInt32() == 0)
return true;
return false;
}
void BinaryOpNode::tryFoldToBranch(BytecodeGenerator& generator, TriState& branchCondition, ExpressionNode*& branchExpression)
{
branchCondition = MixedTriState;
branchExpression = 0;
ConstantNode* constant = 0;
if (m_expr1->isConstant()) {
constant = static_cast<ConstantNode*>(m_expr1);
branchExpression = m_expr2;
} else if (m_expr2->isConstant()) {
constant = static_cast<ConstantNode*>(m_expr2);
branchExpression = m_expr1;
}
if (!constant)
return;
ASSERT(branchExpression);
OpcodeID opcodeID = this->opcodeID();
JSValue value = constant->jsValue(generator);
bool canFoldToBranch = JSC::canFoldToBranch(opcodeID, branchExpression, value);
if (!canFoldToBranch)
return;
if (opcodeID == op_eq || opcodeID == op_stricteq)
branchCondition = triState(value.pureToBoolean());
else if (opcodeID == op_neq || opcodeID == op_nstricteq)
branchCondition = triState(!value.pureToBoolean());
}
RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
OpcodeID opcodeID = this->opcodeID();
......@@ -1504,41 +1553,71 @@ void VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
generator.emitNode(m_expr);
}
// ------------------------------ IfNode ---------------------------------------
// ------------------------------ IfElseNode ---------------------------------------
void IfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
static inline StatementNode* singleStatement(StatementNode* statementNode)
{
generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine(), charPosition());
RefPtr<Label> afterThen = generator.newLabel();
if (statementNode->isBlock())
return static_cast<BlockNode*>(statementNode)->singleStatement();
return statementNode;
}
RefPtr<Label> beforeThen = generator.newLabel();
generator.emitNodeInConditionContext(m_condition, beforeThen.get(), afterThen.get(), FallThroughMeansTrue);
generator.emitLabel(beforeThen.get());
bool IfElseNode::tryFoldBreakAndContinue(BytecodeGenerator& generator, StatementNode* ifBlock,
Label*& trueTarget, FallThroughMode& fallThroughMode)
{
StatementNode* singleStatement = JSC::singleStatement(ifBlock);
if (!singleStatement)
return false;
generator.emitNode(dst, m_ifBlock);
generator.emitLabel(afterThen.get());
}
if (singleStatement->isBreak()) {
BreakNode* breakNode = static_cast<BreakNode*>(singleStatement);
Label* target = breakNode->trivialTarget(generator);
if (!target)
return false;
trueTarget = target;
fallThroughMode = FallThroughMeansFalse;
return true;
}
// ------------------------------ IfElseNode ---------------------------------------
if (singleStatement->isContinue()) {
ContinueNode* continueNode = static_cast<ContinueNode*>(singleStatement);
Label* target = continueNode->trivialTarget(generator);
if (!target)
return false;
trueTarget = target;
fallThroughMode = FallThroughMeansFalse;
return true;
}
return false;
}
void IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine(), charPosition());
RefPtr<Label> beforeThen = generator.newLabel();
RefPtr<Label> beforeElse = generator.newLabel();
RefPtr<Label> afterElse = generator.newLabel();
RefPtr<Label> beforeThen = generator.newLabel();
generator.emitNodeInConditionContext(m_condition, beforeThen.get(), beforeElse.get(), FallThroughMeansTrue);
Label* trueTarget = beforeThen.get();
Label* falseTarget = beforeElse.get();
FallThroughMode fallThroughMode = FallThroughMeansTrue;
bool didFoldIfBlock = tryFoldBreakAndContinue(generator, m_ifBlock, trueTarget, fallThroughMode);
generator.emitNodeInConditionContext(m_condition, trueTarget, falseTarget, fallThroughMode);
generator.emitLabel(beforeThen.get());
generator.emitNode(dst, m_ifBlock);
generator.emitJump(afterElse.get());
if (!didFoldIfBlock) {
generator.emitNode(dst, m_ifBlock);
if (m_elseBlock)
generator.emitJump(afterElse.get());
}
generator.emitLabel(beforeElse.get());
generator.emitNode(dst, m_elseBlock);
if (m_elseBlock)
generator.emitNode(dst, m_elseBlock);
generator.emitLabel(afterElse.get());
}
......@@ -1700,6 +1779,20 @@ void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
// ------------------------------ ContinueNode ---------------------------------
Label* ContinueNode::trivialTarget(BytecodeGenerator& generator)
{
if (generator.shouldEmitDebugHooks())
return 0;
LabelScope* scope = generator.continueTarget(m_ident);
ASSERT(scope);
if (generator.scopeDepth() != scope->scopeDepth())
return 0;
return scope->continueTarget();
}
void ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
{
generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine(), charPosition());
......@@ -1713,6 +1806,20 @@ void ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
// ------------------------------ BreakNode ------------------------------------
Label* BreakNode::trivialTarget(BytecodeGenerator& generator)
{
if (generator.shouldEmitDebugHooks())
return 0;
LabelScope* scope = generator.breakTarget(m_ident);
ASSERT(scope);
if (generator.scopeDepth() != scope->scopeDepth())
return 0;
return scope->breakTarget();
}
void BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
{
generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine(), charPosition());
......
......@@ -344,16 +344,9 @@ public:
return result;
}
StatementNode* createIfStatement(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* trueBlock, int start, int end)
{
IfNode* result = new (m_vm) IfNode(location, condition, trueBlock);
result->setLoc(start, end, location.charPosition);
return result;
}
StatementNode* createIfStatement(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* trueBlock, StatementNode* falseBlock, int start, int end)
{
IfNode* result = new (m_vm) IfElseNode(location, condition, trueBlock, falseBlock);
IfElseNode* result = new (m_vm) IfElseNode(location, condition, trueBlock, falseBlock);
result->setLoc(start, end, location.charPosition);
return result;
}
......
......@@ -60,25 +60,30 @@ namespace JSC {
{
}
inline ConstantNode::ConstantNode(const JSTokenLocation& location, ResultType resultType)
: ExpressionNode(location, resultType)
{
}
inline NullNode::NullNode(const JSTokenLocation& location)
: ExpressionNode(location, ResultType::nullType())
: ConstantNode(location, ResultType::nullType())
{
}
inline BooleanNode::BooleanNode(const JSTokenLocation& location, bool value)
: ExpressionNode(location, ResultType::booleanType())
: ConstantNode(location, ResultType::booleanType())
, m_value(value)
{
}
inline NumberNode::NumberNode(const JSTokenLocation& location, double value)
: ExpressionNode(location, ResultType::numberType())
: ConstantNode(location, JSValue(value).isInt32() ? ResultType::numberTypeIsInt32() : ResultType::numberType())
, m_value(value)
{
}
inline StringNode::StringNode(const JSTokenLocation& location, const Identifier& value)
: ExpressionNode(location, ResultType::stringType())
: ConstantNode(location, ResultType::stringType())
, m_value(value)
{
}
......@@ -631,15 +636,10 @@ namespace JSC {
{
}
inline IfNode::IfNode(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* ifBlock)
inline IfElseNode::IfElseNode(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock)
: StatementNode(location)
, m_condition(condition)
, m_ifBlock(ifBlock)
{
}
inline IfElseNode::IfElseNode(const JSTokenLocation& location, ExpressionNode* condition, StatementNode* ifBlock, StatementNode* elseBlock)
: IfNode(location, condition, ifBlock)
, m_elseBlock(elseBlock)
{
}
......
......@@ -149,6 +149,7 @@ namespace JSC {
virtual bool isString() const { return false; }
virtual bool isNull() const { return false; }
virtual bool isPure(BytecodeGenerator&) const { return false; }
virtual bool isConstant() const { return false; }
virtual bool isLocation() const { return false; }
virtual bool isResolveNode() const { return false; }
virtual bool isBracketAccessorNode() const { return false; }
......@@ -158,6 +159,7 @@ namespace JSC {
virtual bool isSimpleArray() const { return false; }
virtual bool isAdd() const { return false; }
virtual bool isSubtract() const { return false; }
virtual bool isBoolean() const { return false; }
virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, FallThroughMode);
......@@ -183,63 +185,67 @@ namespace JSC {
virtual bool isEmptyStatement() const { return false; }