Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (191815 => 191816)
--- trunk/Source/_javascript_Core/ChangeLog 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-10-30 21:49:23 UTC (rev 191816)
@@ -1,3 +1,127 @@
+2015-10-30 Filip Pizlo <[email protected]>
+
+ B3 should be able to compile a control flow diamond
+ https://bugs.webkit.org/show_bug.cgi?id=150720
+
+ Reviewed by Benjamin Poulain.
+
+ Adds support for Branch, Jump, Upsilon, and Phi. Adds some basic strength reduction for
+ comparisons and boolean-like operations.
+
+ * assembler/MacroAssembler.cpp:
+ (WTF::printInternal):
+ * assembler/MacroAssembler.h:
+ * b3/B3BasicBlockUtils.h:
+ (JSC::B3::replacePredecessor):
+ (JSC::B3::resetReachability):
+ * b3/B3CheckValue.h:
+ * b3/B3Common.h:
+ (JSC::B3::isRepresentableAsImpl):
+ (JSC::B3::isRepresentableAs):
+ * b3/B3Const32Value.cpp:
+ (JSC::B3::Const32Value::subConstant):
+ (JSC::B3::Const32Value::equalConstant):
+ (JSC::B3::Const32Value::notEqualConstant):
+ (JSC::B3::Const32Value::dumpMeta):
+ * b3/B3Const32Value.h:
+ * b3/B3Const64Value.cpp:
+ (JSC::B3::Const64Value::subConstant):
+ (JSC::B3::Const64Value::equalConstant):
+ (JSC::B3::Const64Value::notEqualConstant):
+ (JSC::B3::Const64Value::dumpMeta):
+ * b3/B3Const64Value.h:
+ * b3/B3ConstDoubleValue.cpp:
+ (JSC::B3::ConstDoubleValue::subConstant):
+ (JSC::B3::ConstDoubleValue::equalConstant):
+ (JSC::B3::ConstDoubleValue::notEqualConstant):
+ (JSC::B3::ConstDoubleValue::dumpMeta):
+ * b3/B3ConstDoubleValue.h:
+ * b3/B3ControlValue.cpp:
+ (JSC::B3::ControlValue::~ControlValue):
+ (JSC::B3::ControlValue::convertToJump):
+ (JSC::B3::ControlValue::dumpMeta):
+ * b3/B3ControlValue.h:
+ * b3/B3LowerToAir.cpp:
+ (JSC::B3::Air::LowerToAir::imm):
+ (JSC::B3::Air::LowerToAir::tryStackSlot):
+ (JSC::B3::Air::LowerToAir::tryUpsilon):
+ (JSC::B3::Air::LowerToAir::tryPhi):
+ (JSC::B3::Air::LowerToAir::tryBranch):
+ (JSC::B3::Air::LowerToAir::tryJump):
+ (JSC::B3::Air::LowerToAir::tryIdentity):
+ * b3/B3LoweringMatcher.patterns:
+ * b3/B3Opcode.h:
+ * b3/B3Procedure.cpp:
+ (JSC::B3::Procedure::resetReachability):
+ (JSC::B3::Procedure::dump):
+ * b3/B3ReduceStrength.cpp:
+ * b3/B3UpsilonValue.cpp:
+ (JSC::B3::UpsilonValue::dumpMeta):
+ * b3/B3UpsilonValue.h:
+ (JSC::B3::UpsilonValue::accepts): Deleted.
+ (JSC::B3::UpsilonValue::phi): Deleted.
+ (JSC::B3::UpsilonValue::UpsilonValue): Deleted.
+ * b3/B3Validate.cpp:
+ * b3/B3Value.cpp:
+ (JSC::B3::Value::subConstant):
+ (JSC::B3::Value::equalConstant):
+ (JSC::B3::Value::notEqualConstant):
+ (JSC::B3::Value::returnsBool):
+ (JSC::B3::Value::asTriState):
+ (JSC::B3::Value::effects):
+ * b3/B3Value.h:
+ * b3/B3ValueInlines.h:
+ (JSC::B3::Value::asInt32):
+ (JSC::B3::Value::isInt32):
+ (JSC::B3::Value::hasInt64):
+ (JSC::B3::Value::asInt64):
+ (JSC::B3::Value::isInt64):
+ (JSC::B3::Value::hasInt):
+ (JSC::B3::Value::asIntPtr):
+ (JSC::B3::Value::isIntPtr):
+ (JSC::B3::Value::hasDouble):
+ (JSC::B3::Value::asDouble):
+ (JSC::B3::Value::isEqualToDouble):
+ (JSC::B3::Value::hasNumber):
+ (JSC::B3::Value::representableAs):
+ (JSC::B3::Value::asNumber):
+ (JSC::B3::Value::stackmap):
+ * b3/air/AirArg.cpp:
+ (JSC::B3::Air::Arg::dump):
+ * b3/air/AirArg.h:
+ (JSC::B3::Air::Arg::resCond):
+ (JSC::B3::Air::Arg::doubleCond):
+ (JSC::B3::Air::Arg::special):
+ (JSC::B3::Air::Arg::isResCond):
+ (JSC::B3::Air::Arg::isDoubleCond):
+ (JSC::B3::Air::Arg::isSpecial):
+ (JSC::B3::Air::Arg::isGP):
+ (JSC::B3::Air::Arg::isFP):
+ (JSC::B3::Air::Arg::asResultCondition):
+ (JSC::B3::Air::Arg::asDoubleCondition):
+ (JSC::B3::Air::Arg::Arg):
+ * b3/air/AirCode.cpp:
+ (JSC::B3::Air::Code::resetReachability):
+ (JSC::B3::Air::Code::dump):
+ * b3/air/AirOpcode.opcodes:
+ * b3/air/opcode_generator.rb:
+ * b3/testb3.cpp:
+ (hiddenTruthBecauseNoReturnIsStupid):
+ (usage):
+ (JSC::B3::compile):
+ (JSC::B3::invoke):
+ (JSC::B3::compileAndRun):
+ (JSC::B3::test42):
+ (JSC::B3::testStoreLoadStackSlot):
+ (JSC::B3::testBranch):
+ (JSC::B3::testDiamond):
+ (JSC::B3::testBranchNotEqual):
+ (JSC::B3::testBranchFold):
+ (JSC::B3::testDiamondFold):
+ (JSC::B3::run):
+ (run):
+ (main):
+
2015-10-30 Keith Miller <[email protected]>
[ES6] Add support for toStringTag
Modified: trunk/Source/_javascript_Core/assembler/MacroAssembler.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/assembler/MacroAssembler.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/assembler/MacroAssembler.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -112,6 +112,50 @@
RELEASE_ASSERT_NOT_REACHED();
}
+void printInternal(PrintStream& out, MacroAssembler::DoubleCondition cond)
+{
+ switch (cond) {
+ case MacroAssembler::DoubleEqual:
+ out.print("DoubleEqual");
+ return;
+ case MacroAssembler::DoubleNotEqual:
+ out.print("DoubleNotEqual");
+ return;
+ case MacroAssembler::DoubleGreaterThan:
+ out.print("DoubleGreaterThan");
+ return;
+ case MacroAssembler::DoubleGreaterThanOrEqual:
+ out.print("DoubleGreaterThanOrEqual");
+ return;
+ case MacroAssembler::DoubleLessThan:
+ out.print("DoubleLessThan");
+ return;
+ case MacroAssembler::DoubleLessThanOrEqual:
+ out.print("DoubleLessThanOrEqual");
+ return;
+ case MacroAssembler::DoubleEqualOrUnordered:
+ out.print("DoubleEqualOrUnordered");
+ return;
+ case MacroAssembler::DoubleNotEqualOrUnordered:
+ out.print("DoubleNotEqualOrUnordered");
+ return;
+ case MacroAssembler::DoubleGreaterThanOrUnordered:
+ out.print("DoubleGreaterThanOrUnordered");
+ return;
+ case MacroAssembler::DoubleGreaterThanOrEqualOrUnordered:
+ out.print("DoubleGreaterThanOrEqualOrUnordered");
+ return;
+ case MacroAssembler::DoubleLessThanOrUnordered:
+ out.print("DoubleLessThanOrUnordered");
+ return;
+ case MacroAssembler::DoubleLessThanOrEqualOrUnordered:
+ out.print("DoubleLessThanOrEqualOrUnordered");
+ return;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
} // namespace WTF
#endif // ENABLE(ASSEMBLER)
Modified: trunk/Source/_javascript_Core/assembler/MacroAssembler.h (191815 => 191816)
--- trunk/Source/_javascript_Core/assembler/MacroAssembler.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/assembler/MacroAssembler.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -1614,6 +1614,7 @@
void printInternal(PrintStream&, JSC::MacroAssembler::RelationalCondition);
void printInternal(PrintStream&, JSC::MacroAssembler::ResultCondition);
+void printInternal(PrintStream&, JSC::MacroAssembler::DoubleCondition);
} // namespace WTF
Modified: trunk/Source/_javascript_Core/b3/B3BasicBlockUtils.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3BasicBlockUtils.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3BasicBlockUtils.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -75,8 +75,9 @@
}
// This recomputes predecessors and removes blocks that aren't reachable.
-template<typename BasicBlock>
-void resetReachability(Vector<std::unique_ptr<BasicBlock>>& blocks)
+template<typename BasicBlock, typename DeleteFunctor>
+void resetReachability(
+ Vector<std::unique_ptr<BasicBlock>>& blocks, const DeleteFunctor& deleteFunctor)
{
// Clear all predecessor lists first.
for (auto& block : blocks)
@@ -86,14 +87,16 @@
worklist.push(blocks[0].get());
while (BasicBlock* block = worklist.pop()) {
for (BasicBlock* successor : block->successorBlocks()) {
- if (worklist.push(successor))
- addPredecessor(successor, block);
+ addPredecessor(successor, block);
+ worklist.push(successor);
}
}
for (unsigned i = 1; i < blocks.size(); ++i) {
- if (blocks[i]->predecessors().isEmpty())
+ if (blocks[i]->predecessors().isEmpty()) {
+ deleteFunctor(blocks[i].get());
blocks[i] = nullptr;
+ }
}
}
Modified: trunk/Source/_javascript_Core/b3/B3CheckValue.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3CheckValue.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3CheckValue.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -62,7 +62,7 @@
CheckValue(unsigned index, Opcode opcode, Origin origin, Value* left, Value* right)
: Value(index, opcode, left->type(), origin, left, right)
{
- ASSERT(isInt(type()));
+ ASSERT(B3::isInt(type()));
ASSERT(left->type() == right->type());
ASSERT(opcode == CheckAdd || opcode == CheckSub || opcode == CheckMul);
}
Modified: trunk/Source/_javascript_Core/b3/B3Common.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Common.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Common.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -39,6 +39,43 @@
bool shouldValidateIRAtEachPhase();
bool shouldSaveIRBeforePhase();
+template<typename ResultType, typename InputType, typename BitsType>
+inline bool isRepresentableAsImpl(InputType originalValue)
+{
+ // Get the raw bits of the original value.
+ BitsType originalBits = bitwise_cast<BitsType>(originalValue);
+
+ // Convert the original value to the desired result type.
+ ResultType result = static_cast<ResultType>(originalValue);
+
+ // Convert the converted value back to the original type. The original value is representable
+ // using the new type if such round-tripping doesn't lose bits.
+ InputType newValue = static_cast<InputType>(result);
+
+ // Get the raw bits of the round-tripped value.
+ BitsType newBits = bitwise_cast<BitsType>(newValue);
+
+ return originalBits == newBits;
+}
+
+template<typename ResultType>
+inline bool isRepresentableAs(int32_t value)
+{
+ return isRepresentableAsImpl<ResultType, int32_t, int32_t>(value);
+}
+
+template<typename ResultType>
+inline bool isRepresentableAs(int64_t value)
+{
+ return isRepresentableAsImpl<ResultType, int64_t, int64_t>(value);
+}
+
+template<typename ResultType>
+inline bool isRepresentableAs(double value)
+{
+ return isRepresentableAsImpl<ResultType, double, int64_t>(value);
+}
+
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)
Modified: trunk/Source/_javascript_Core/b3/B3Const32Value.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Const32Value.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Const32Value.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -61,6 +61,20 @@
return proc.add<Const32Value>(origin(), m_value - other->asInt32());
}
+Value* Const32Value::equalConstant(Procedure& proc, Value* other) const
+{
+ if (!other->hasInt32())
+ return nullptr;
+ return proc.add<Const32Value>(origin(), m_value == other->asInt32());
+}
+
+Value* Const32Value::notEqualConstant(Procedure& proc, Value* other) const
+{
+ if (!other->hasInt32())
+ return nullptr;
+ return proc.add<Const32Value>(origin(), m_value != other->asInt32());
+}
+
void Const32Value::dumpMeta(PrintStream& out) const
{
out.print(m_value);
Modified: trunk/Source/_javascript_Core/b3/B3Const32Value.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Const32Value.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Const32Value.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -44,6 +44,8 @@
Value* addConstant(Procedure&, int32_t other) const override;
Value* addConstant(Procedure&, Value* other) const override;
Value* subConstant(Procedure&, Value* other) const override;
+ Value* equalConstant(Procedure&, Value* other) const override;
+ Value* notEqualConstant(Procedure&, Value* other) const override;
protected:
JS_EXPORT_PRIVATE void dumpMeta(PrintStream&) const override;
Modified: trunk/Source/_javascript_Core/b3/B3Const64Value.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Const64Value.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Const64Value.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -61,6 +61,20 @@
return proc.add<Const64Value>(origin(), m_value - other->asInt64());
}
+Value* Const64Value::equalConstant(Procedure& proc, Value* other) const
+{
+ if (!other->hasInt64())
+ return nullptr;
+ return proc.add<Const32Value>(origin(), m_value == other->asInt64());
+}
+
+Value* Const64Value::notEqualConstant(Procedure& proc, Value* other) const
+{
+ if (!other->hasInt64())
+ return nullptr;
+ return proc.add<Const32Value>(origin(), m_value != other->asInt64());
+}
+
void Const64Value::dumpMeta(PrintStream& out) const
{
out.print(m_value);
Modified: trunk/Source/_javascript_Core/b3/B3Const64Value.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Const64Value.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Const64Value.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -44,6 +44,8 @@
Value* addConstant(Procedure&, int32_t other) const override;
Value* addConstant(Procedure&, Value* other) const override;
Value* subConstant(Procedure&, Value* other) const override;
+ Value* equalConstant(Procedure&, Value* other) const override;
+ Value* notEqualConstant(Procedure&, Value* other) const override;
protected:
void dumpMeta(PrintStream&) const override;
Modified: trunk/Source/_javascript_Core/b3/B3ConstDoubleValue.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3ConstDoubleValue.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3ConstDoubleValue.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -61,6 +61,20 @@
return proc.add<ConstDoubleValue>(origin(), m_value - other->asDouble());
}
+Value* ConstDoubleValue::equalConstant(Procedure& proc, Value* other) const
+{
+ if (!other->hasDouble())
+ return nullptr;
+ return proc.add<Const32Value>(origin(), m_value == other->asDouble());
+}
+
+Value* ConstDoubleValue::notEqualConstant(Procedure& proc, Value* other) const
+{
+ if (!other->hasDouble())
+ return nullptr;
+ return proc.add<Const32Value>(origin(), m_value != other->asDouble());
+}
+
void ConstDoubleValue::dumpMeta(PrintStream& out) const
{
out.printf("%le", m_value);
Modified: trunk/Source/_javascript_Core/b3/B3ConstDoubleValue.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3ConstDoubleValue.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3ConstDoubleValue.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -44,6 +44,8 @@
Value* addConstant(Procedure& proc, int32_t other) const override;
Value* addConstant(Procedure& proc, Value* other) const override;
Value* subConstant(Procedure& proc, Value* other) const override;
+ Value* equalConstant(Procedure& proc, Value* other) const override;
+ Value* notEqualConstant(Procedure& proc, Value* other) const override;
protected:
void dumpMeta(PrintStream&) const override;
Modified: trunk/Source/_javascript_Core/b3/B3ControlValue.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3ControlValue.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3ControlValue.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -37,6 +37,19 @@
{
}
+void ControlValue::convertToJump(const FrequentedBlock& destination)
+{
+ unsigned index = this->index();
+ Origin origin = this->origin();
+ BasicBlock* owner = this->owner;
+
+ this->ControlValue::~ControlValue();
+
+ new (this) ControlValue(index, Jump, origin, destination);
+
+ this->owner = owner;
+}
+
void ControlValue::dumpMeta(PrintStream& out) const
{
out.print(listDump(m_successors));
Modified: trunk/Source/_javascript_Core/b3/B3ControlValue.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3ControlValue.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3ControlValue.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -64,6 +64,29 @@
const SuccessorList& successors() const { return m_successors; }
SuccessorList& successors() { return m_successors; }
+ const FrequentedBlock& taken() const
+ {
+ ASSERT(opcode() == Jump || opcode() == Branch);
+ return successor(0);
+ }
+ FrequentedBlock& taken()
+ {
+ ASSERT(opcode() == Jump || opcode() == Branch);
+ return successor(0);
+ }
+ const FrequentedBlock& notTaken() const
+ {
+ ASSERT(opcode() == Branch);
+ return successor(1);
+ }
+ FrequentedBlock& notTaken()
+ {
+ ASSERT(opcode() == Branch);
+ return successor(1);
+ }
+
+ void convertToJump(const FrequentedBlock& destination);
+
protected:
JS_EXPORT_PRIVATE void dumpMeta(PrintStream&) const override;
Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -43,6 +43,7 @@
#include "B3PhaseScope.h"
#include "B3Procedure.h"
#include "B3StackSlotValue.h"
+#include "B3UpsilonValue.h"
#include "B3UseCounts.h"
#include "B3ValueInlines.h"
#include <wtf/ListDump.h>
@@ -217,12 +218,8 @@
Arg imm(Value* value)
{
- if (value->hasInt()) {
- int64_t fullValue = value->asInt();
- int32_t immediateValue = static_cast<int32_t>(fullValue);
- if (fullValue == immediateValue)
- return Arg::imm(immediateValue);
- }
+ if (value->representableAs<int32_t>())
+ return Arg::imm(value->asNumber<int32_t>());
return Arg();
}
@@ -657,6 +654,72 @@
return true;
}
+ bool tryUpsilon(Value* value)
+ {
+ append(
+ relaxedMoveForType(value->type()),
+ immOrTmp(value),
+ tmp(currentValue->as<UpsilonValue>()->phi()));
+ return true;
+ }
+
+ bool tryPhi()
+ {
+ // Our semantics are determined by Upsilons, so we have nothing to do here.
+ return true;
+ }
+
+ bool tryBranch(Value* value)
+ {
+ // FIXME: Implement branch fusion by delegating to a pattern matcher for comparisons.
+ // https://bugs.webkit.org/show_bug.cgi?id=150721
+
+ // In B3 it's possible to branch on any value. The semantics of:
+ //
+ // Branch(value)
+ //
+ // Are guaranteed to be identical to:
+ //
+ // Branch(NotEqual(value, 0))
+
+ if (!isInt(value->type())) {
+ // FIXME: Implement double branches.
+ // https://bugs.webkit.org/show_bug.cgi?id=150727
+ return false;
+ }
+
+ Air::Opcode opcode;
+ switch (value->type()) {
+ case Int32:
+ opcode = BranchTest32;
+ break;
+ case Int64:
+ opcode = BranchTest64;
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+
+ Arg resCond = Arg::resCond(MacroAssembler::NonZero);
+
+ Arg addr = loadAddr(value);
+ if (isValidForm(opcode, resCond.kind(), addr.kind(), Arg::Imm)) {
+ commitInternal(value);
+ append(opcode, resCond, addr, Arg::imm(-1));
+ return true;
+ }
+
+ append(opcode, resCond, tmp(value), Arg::imm(-1));
+ return true;
+ }
+
+ bool tryJump()
+ {
+ append(Air::Jump);
+ return true;
+ }
+
bool tryIdentity(Value* value)
{
append(relaxedMoveForType(value->type()), immOrTmp(value), tmp(currentValue));
Modified: trunk/Source/_javascript_Core/b3/B3LoweringMatcher.patterns (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3LoweringMatcher.patterns 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3LoweringMatcher.patterns 2015-10-30 21:49:23 UTC (rev 191816)
@@ -48,6 +48,12 @@
StackSlot = StackSlot()
FramePointer = FramePointer()
+Upsilon = Upsilon(value)
+Phi = Phi()
+
+Branch = Branch(value)
+Jump = Jump()
+
# It would be fantastic to not have to have this here, but the philosophy of our lowering is that
# it should be sound even if we haven't run optimizations.
Identity = Identity(value)
Modified: trunk/Source/_javascript_Core/b3/B3Opcode.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Opcode.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Opcode.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -96,7 +96,7 @@
DToI32,
// Polymorphic comparisons, usable with any value type. Returns int32 0 or 1. Note that "Not"
- // is just Equal(x, 0).
+ // is just Equal(x, 0), and "ToBoolean" is just NotEqual(x, 0).
Equal,
NotEqual,
LessThan,
Modified: trunk/Source/_javascript_Core/b3/B3Procedure.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Procedure.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -62,7 +62,13 @@
void Procedure::resetReachability()
{
- B3::resetReachability(m_blocks);
+ B3::resetReachability(
+ m_blocks,
+ [&] (BasicBlock* deleted) {
+ // Gotta delete the values in this block.
+ for (Value* value : *deleted)
+ deleteValue(value);
+ });
}
void Procedure::dump(PrintStream& out) const
Modified: trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3ReduceStrength.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -28,6 +28,7 @@
#if ENABLE(B3_JIT)
+#include "B3ControlValue.h"
#include "B3InsertionSetInlines.h"
#include "B3MemoryValue.h"
#include "B3PhaseScope.h"
@@ -52,6 +53,7 @@
bool result = false;
do {
m_changed = false;
+ m_changedCFG = false;
for (BasicBlock* block : m_proc.blocksInPreOrder()) {
m_block = block;
@@ -63,6 +65,11 @@
block->removeNops(m_proc);
}
+ if (m_changedCFG) {
+ m_proc.resetReachability();
+ m_changed = true;
+ }
+
UseCounts useCounts(m_proc);
for (Value* value : m_proc.values()) {
if (!useCounts[value] && !value->effects().mustExecute()) {
@@ -109,10 +116,9 @@
if (isInt(m_value->type())) {
if (Value* negatedConstant = m_value->child(1)->negConstant(m_proc)) {
m_insertionSet.insertValue(m_index, negatedConstant);
- Value* add = m_insertionSet.insert<Value>(
- m_index, Add, m_value->origin(), m_value->child(0), negatedConstant);
- m_value->replaceWithIdentity(add);
- m_changed = true;
+ replaceWithNew<Value>(
+ Add, m_value->origin(), m_value->child(0), negatedConstant);
+ break;
}
}
@@ -172,7 +178,102 @@
break;
}
+
+ case Equal:
+ handleCommutativity();
+
+ // Turn this: Equal(Equal(x, 0), 0)
+ // Into this: NotEqual(x, 0)
+ if (m_value->child(0)->opcode() == Equal && m_value->child(1)->isInt32(0)
+ && m_value->child(0)->child(1)->isLikeZero()) {
+ replaceWithNew<Value>(
+ NotEqual, m_value->origin(),
+ m_value->child(0)->child(0), m_value->child(0)->child(1));
+ break;
+ }
+
+ // Turn this Equal(bool, 1)
+ // Into this: bool
+ if (m_value->child(0)->returnsBool() && m_value->child(1)->isInt32(1)) {
+ m_value->replaceWithIdentity(m_value->child(0));
+ m_changed = true;
+ break;
+ }
+
+ // FIXME: Have a compare-flipping optimization, like Equal(LessThan(a, b), 0) turns into
+ // GreaterEqual(a, b).
+ // https://bugs.webkit.org/show_bug.cgi?id=150726
+
+ // Turn this: Equal(const1, const2)
+ // Into this: const1 == const2
+ replaceWithNewValue(m_value->child(0)->equalConstant(m_proc, m_value->child(1)));
+ break;
+ case NotEqual:
+ handleCommutativity();
+
+ if (m_value->child(0)->returnsBool()) {
+ // Turn this: NotEqual(bool, 0)
+ // Into this: bool
+ if (m_value->child(1)->isInt32(0)) {
+ m_value->replaceWithIdentity(m_value->child(0));
+ m_changed = true;
+ break;
+ }
+
+ // Turn this: NotEqual(bool, 1)
+ // Into this: Equal(bool, 0)
+ if (m_value->child(1)->isInt32(1)) {
+ replaceWithNew<Value>(
+ Equal, m_value->origin(), m_value->child(0), m_value->child(1));
+ break;
+ }
+ }
+
+ // Turn this: NotEqual(const1, const2)
+ // Into this: const1 != const2
+ replaceWithNewValue(m_value->child(0)->notEqualConstant(m_proc, m_value->child(1)));
+ break;
+
+ case Branch: {
+ ControlValue* branch = m_value->as<ControlValue>();
+
+ // Turn this: Branch(NotEqual(x, 0))
+ // Into this: Branch(x)
+ if (branch->child(0)->opcode() == NotEqual && branch->child(0)->child(1)->isLikeZero()) {
+ branch->child(0) = branch->child(0)->child(0);
+ m_changed = true;
+ }
+
+ // Turn this: Branch(Equal(x, 0), then, else)
+ // Into this: Branch(x, else, then)
+ if (branch->child(0)->opcode() == Equal && branch->child(0)->child(1)->isLikeZero()) {
+ branch->child(0) = branch->child(0)->child(0);
+ std::swap(branch->taken(), branch->notTaken());
+ m_changed = true;
+ }
+
+ TriState triState = branch->child(0)->asTriState();
+
+ // Turn this: Branch(0, then, else)
+ // Into this: Jump(else)
+ if (triState == FalseTriState) {
+ branch->convertToJump(branch->notTaken());
+ m_changedCFG = true;
+ break;
+ }
+
+ // Turn this: Branch(not 0, then, else)
+ // Into this: Jump(then)
+ if (triState == TrueTriState) {
+ branch->convertToJump(branch->taken());
+ m_changedCFG = true;
+ break;
+ }
+
+ break;
+ }
+
default:
break;
}
@@ -206,6 +307,12 @@
}
}
+ template<typename ValueType, typename... Arguments>
+ void replaceWithNew(Arguments... arguments)
+ {
+ replaceWithNewValue(m_proc.add<ValueType>(arguments...));
+ }
+
void replaceWithNewValue(Value* newValue)
{
if (!newValue)
@@ -221,6 +328,7 @@
unsigned m_index;
Value* m_value;
bool m_changed;
+ bool m_changedCFG;
};
} // anonymous namespace
Modified: trunk/Source/_javascript_Core/b3/B3UpsilonValue.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3UpsilonValue.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3UpsilonValue.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -36,7 +36,13 @@
void UpsilonValue::dumpMeta(PrintStream& out) const
{
- out.print("^", m_phi->index());
+ if (m_phi)
+ out.print("^", m_phi->index());
+ else {
+ // We want to have a dump for when the Phi isn't set yet, since although such IR won't pass
+ // validation, we may have such IR as an intermediate step.
+ out.print("^(null)");
+ }
}
} } // namespace JSC::B3
Modified: trunk/Source/_javascript_Core/b3/B3UpsilonValue.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3UpsilonValue.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3UpsilonValue.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -32,25 +32,37 @@
namespace JSC { namespace B3 {
-class UpsilonValue : public Value {
+class JS_EXPORT_PRIVATE UpsilonValue : public Value {
public:
static bool accepts(Opcode opcode) { return opcode == Upsilon; }
~UpsilonValue();
Value* phi() const { return m_phi; }
+ void setPhi(Value* phi)
+ {
+ ASSERT(child(0)->type() == phi->type());
+ ASSERT(phi->opcode() == Phi);
+ m_phi = phi;
+ }
protected:
void dumpMeta(PrintStream&) const override;
private:
friend class Procedure;
-
- UpsilonValue(unsigned index, Origin origin, Value* value, Value* phi)
- : Value(index, Phi, Void, origin, value)
+
+ // Note that passing the Phi during construction is optional. A valid pattern is to first create
+ // the Upsilons without the Phi, then create the Phi, then go back and tell the Upsilons about
+ // the Phi. This allows you to emit code in its natural order.
+ UpsilonValue(unsigned index, Origin origin, Value* value, Value* phi = nullptr)
+ : Value(index, Upsilon, Void, origin, value)
, m_phi(phi)
{
- ASSERT(value->type() == phi->type());
+ if (phi) {
+ ASSERT(value->type() == phi->type());
+ ASSERT(phi->opcode() == Phi);
+ }
}
Value* m_phi;
Modified: trunk/Source/_javascript_Core/b3/B3Validate.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Validate.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Validate.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -281,6 +281,7 @@
break;
case Upsilon:
VALIDATE(value->numChildren() == 1, ("At ", *value));
+ VALIDATE(value->as<UpsilonValue>()->phi(), ("At ", *value));
VALIDATE(value->child(0)->type() == value->as<UpsilonValue>()->phi()->type(), ("At ", *value));
VALIDATE(valueInProc.contains(value->as<UpsilonValue>()->phi()), ("At ", *value));
break;
Modified: trunk/Source/_javascript_Core/b3/B3Value.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Value.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Value.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -127,6 +127,60 @@
return nullptr;
}
+Value* Value::equalConstant(Procedure&, Value*) const
+{
+ return nullptr;
+}
+
+Value* Value::notEqualConstant(Procedure&, Value*) const
+{
+ return nullptr;
+}
+
+bool Value::returnsBool() const
+{
+ if (type() != Int32)
+ return false;
+ switch (opcode()) {
+ case Const32:
+ return asInt32() == 0 || asInt32() == 1;
+ case BitAnd:
+ return child(1)->isInt32(1);
+ case Equal:
+ case NotEqual:
+ case LessThan:
+ case GreaterThan:
+ case LessEqual:
+ case GreaterEqual:
+ case Above:
+ case Below:
+ case AboveEqual:
+ case BelowEqual:
+ return true;
+ case Phi:
+ // FIXME: We should have a story here.
+ // https://bugs.webkit.org/show_bug.cgi?id=150725
+ return false;
+ default:
+ return false;
+ }
+}
+
+TriState Value::asTriState() const
+{
+ switch (opcode()) {
+ case Const32:
+ return triState(!!asInt32());
+ case Const64:
+ return triState(!!asInt64());
+ case ConstDouble:
+ // Use "!= 0" to really emphasize what this mean with respect to NaN and such.
+ return triState(asDouble() != 0);
+ default:
+ return MixedTriState;
+ }
+}
+
Effects Value::effects() const
{
Effects result;
Modified: trunk/Source/_javascript_Core/b3/B3Value.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3Value.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3Value.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -98,18 +98,30 @@
template<typename T>
const T* as() const;
+ // What follows are a bunch of helpers for inspecting and modifying values. Note that we have a
+ // bunch of different idioms for implementing such helpers. You can use virtual methods, and
+ // override from the various Value subclasses. You can put the method inside Value and make it
+ // non-virtual, and the implementation can switch on opcode. The method could be inline or not.
+ // If a method is specific to some Value subclass, you could put it in the subclass, or you could
+ // put it on Value anyway. It's fine to pick whatever feels right, and we shouldn't restrict
+ // ourselves to any particular idiom.
+
bool isConstant() const;
virtual Value* negConstant(Procedure&) const;
virtual Value* addConstant(Procedure&, int32_t other) const;
virtual Value* addConstant(Procedure&, Value* other) const;
virtual Value* subConstant(Procedure&, Value* other) const;
+ virtual Value* equalConstant(Procedure&, Value* other) const;
+ virtual Value* notEqualConstant(Procedure&, Value* other) const;
bool hasInt32() const;
int32_t asInt32() const;
+ bool isInt32(int32_t) const;
bool hasInt64() const;
int64_t asInt64() const;
+ bool isInt64(int64_t) const;
bool hasInt() const;
int64_t asInt() const;
@@ -117,10 +129,24 @@
bool hasIntPtr() const;
intptr_t asIntPtr() const;
+ bool isIntPtr(intptr_t) const;
bool hasDouble() const;
double asDouble() const;
+ bool isEqualToDouble(double) const; // We say "isEqualToDouble" because "isDouble" would be a bit equality.
+ bool hasNumber() const;
+ template<typename T> bool representableAs() const;
+ template<typename T> T asNumber() const;
+
+ // Booleans in B3 are Const32(0) or Const32(1). So this is true if the type is Int32 and the only
+ // possible return values are 0 or 1. It's OK for this method to conservatively return false.
+ bool returnsBool() const;
+
+ TriState asTriState() const;
+ bool isLikeZero() const { return asTriState() == FalseTriState; }
+ bool isLikeNonZero() const { return asTriState() == TrueTriState; }
+
Effects effects() const;
Stackmap* stackmap();
Modified: trunk/Source/_javascript_Core/b3/B3ValueInlines.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/B3ValueInlines.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/B3ValueInlines.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -74,6 +74,11 @@
return as<Const32Value>()->value();
}
+inline bool Value::isInt32(int32_t value) const
+{
+ return hasInt32() && asInt32() == value;
+}
+
inline bool Value::hasInt64() const
{
return !!as<Const64Value>();
@@ -84,6 +89,11 @@
return as<Const64Value>()->value();
}
+inline bool Value::isInt64(int64_t value) const
+{
+ return hasInt64() && asInt64() == value;
+}
+
inline bool Value::hasInt() const
{
return hasInt32() || hasInt64();
@@ -113,6 +123,11 @@
return asInt32();
}
+inline bool Value::isIntPtr(intptr_t value) const
+{
+ return hasIntPtr() && asIntPtr() == value;
+}
+
inline bool Value::hasDouble() const
{
return !!as<ConstDoubleValue>();
@@ -123,6 +138,46 @@
return as<ConstDoubleValue>()->value();
}
+inline bool Value::isEqualToDouble(double value) const
+{
+ return hasDouble() && asDouble() == value;
+}
+
+inline bool Value::hasNumber() const
+{
+ return hasInt() || hasDouble();
+}
+
+template<typename T>
+inline bool Value::representableAs() const
+{
+ switch (opcode()) {
+ case Const32:
+ return isRepresentableAs<T>(asInt32());
+ case Const64:
+ return isRepresentableAs<T>(asInt64());
+ case ConstDouble:
+ return isRepresentableAs<T>(asDouble());
+ default:
+ return false;
+ }
+}
+
+template<typename T>
+inline T Value::asNumber() const
+{
+ switch (opcode()) {
+ case Const32:
+ return static_cast<T>(asInt32());
+ case Const64:
+ return static_cast<T>(asInt64());
+ case ConstDouble:
+ return static_cast<T>(asDouble());
+ default:
+ return T();
+ }
+}
+
inline Stackmap* Value::stackmap()
{
if (CheckValue* check = as<CheckValue>())
Modified: trunk/Source/_javascript_Core/b3/air/AirArg.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/air/AirArg.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -77,6 +77,9 @@
case ResCond:
out.print(asResultCondition());
return;
+ case DoubleCond:
+ out.print(asDoubleCondition());
+ return;
case Special:
out.print(pointerDump(special()));
return;
Modified: trunk/Source/_javascript_Core/b3/air/AirArg.h (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/air/AirArg.h 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.h 2015-10-30 21:49:23 UTC (rev 191816)
@@ -64,6 +64,7 @@
// secondary opcodes. They are always "Use"'d.
RelCond,
ResCond,
+ DoubleCond,
Special
};
@@ -266,6 +267,14 @@
return result;
}
+ static Arg doubleCond(MacroAssembler::DoubleCondition condition)
+ {
+ Arg result;
+ result.m_kind = DoubleCond;
+ result.m_offset = condition;
+ return result;
+ }
+
static Arg special(Air::Special* special)
{
Arg result;
@@ -340,6 +349,11 @@
return kind() == ResCond;
}
+ bool isDoubleCond() const
+ {
+ return kind() == DoubleCond;
+ }
+
bool isSpecial() const
{
return kind() == Special;
@@ -446,6 +460,7 @@
case CallArg:
case RelCond:
case ResCond:
+ case DoubleCond:
case Special:
return true;
case Tmp:
@@ -462,6 +477,7 @@
case Imm:
case RelCond:
case ResCond:
+ case DoubleCond:
case Special:
case Invalid:
return false;
@@ -662,6 +678,12 @@
return static_cast<MacroAssembler::ResultCondition>(m_offset);
}
+ MacroAssembler::DoubleCondition asDoubleCondition() const
+ {
+ ASSERT(isDoubleCond());
+ return static_cast<MacroAssembler::DoubleCondition>(m_offset);
+ }
+
void dump(PrintStream&) const;
Arg(WTF::HashTableDeletedValueType)
Modified: trunk/Source/_javascript_Core/b3/air/AirCode.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/air/AirCode.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -83,7 +83,11 @@
void Code::resetReachability()
{
- B3::resetReachability(m_blocks);
+ B3::resetReachability(
+ m_blocks,
+ [&] (BasicBlock*) {
+ // We don't have to do anything special for deleted blocks.
+ });
}
void Code::dump(PrintStream& out) const
Modified: trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/air/AirOpcode.opcodes 2015-10-30 21:49:23 UTC (rev 191816)
@@ -134,6 +134,22 @@
MoveDoubleTo64 U:F, D:G
Tmp, Tmp
+BranchTest32 U:G, U:G, U:G /branch
+ ResCond, Tmp, Tmp
+ ResCond, Tmp, Imm
+ ResCond, Addr, Imm
+ ResCond, Index, Imm
+
+BranchTest64 U:G, U:G, U:G /branch
+ ResCond, Tmp, Tmp
+ ResCond, Tmp, Imm
+ ResCond, Addr, Imm
+ ResCond, Addr, Tmp
+ ResCond, Index, Imm
+
+BranchDouble U:G, U:F, U:F /branch
+ DoubleCond, Tmp, Tmp
+
BranchAdd32 U:G, U:G, UD:G /branch
ResCond, Tmp, Tmp
ResCond, Imm, Tmp
Modified: trunk/Source/_javascript_Core/b3/air/opcode_generator.rb (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/air/opcode_generator.rb 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/air/opcode_generator.rb 2015-10-30 21:49:23 UTC (rev 191816)
@@ -180,7 +180,7 @@
end
def isKind(token)
- token =~ /\A((Tmp)|(Imm)|(Imm64)|(Addr)|(Index)|(RelCond)|(ResCond))\Z/
+ token =~ /\A((Tmp)|(Imm)|(Imm64)|(Addr)|(Index)|(RelCond)|(ResCond)|(DoubleCond))\Z/
end
def isKeyword(token)
@@ -246,7 +246,7 @@
def consumeKind
result = token.string
- parseError("Expected kind (Imm, Imm64, Tmp, Addr, Index, RelCond, or ResCond)") unless isKind(result)
+ parseError("Expected kind (Imm, Imm64, Tmp, Addr, Index, RelCond, ResCond, or DoubleCond)") unless isKind(result)
advance
result
end
@@ -830,6 +830,8 @@
outp.print "args[#{index}].asRelationalCondition()"
when "ResCond"
outp.print "args[#{index}].asResultCondition()"
+ when "DoubleCond"
+ outp.print "args[#{index}].asDoubleCondition()"
end
}
Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (191815 => 191816)
--- trunk/Source/_javascript_Core/b3/testb3.cpp 2015-10-30 21:37:25 UTC (rev 191815)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp 2015-10-30 21:49:23 UTC (rev 191816)
@@ -34,6 +34,7 @@
#include "B3MemoryValue.h"
#include "B3Procedure.h"
#include "B3StackSlotValue.h"
+#include "B3UpsilonValue.h"
#include "B3ValueInlines.h"
#include "CCallHelpers.h"
#include "InitializeThreading.h"
@@ -41,6 +42,16 @@
#include "LinkBuffer.h"
#include "VM.h"
+// We don't have a NO_RETURN_DUE_TO_EXIT, nor should we. That's ridiculous.
+static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
+
+static void usage()
+{
+ dataLog("Usage: testb3 [<filter>]\n");
+ if (hiddenTruthBecauseNoReturnIsStupid())
+ exit(1);
+}
+
#if ENABLE(B3_JIT)
using namespace JSC;
@@ -53,17 +64,27 @@
VM* vm;
-template<typename T, typename... Arguments>
-T compileAndRun(Procedure& procedure, Arguments... arguments)
+MacroAssemblerCodeRef compile(Procedure& procedure)
{
CCallHelpers jit(vm);
generate(procedure, jit);
LinkBuffer linkBuffer(*vm, jit, nullptr);
- MacroAssemblerCodeRef code = FINALIZE_CODE(linkBuffer, ("testb3"));
+ return FINALIZE_CODE(linkBuffer, ("testb3"));
+}
+
+template<typename T, typename... Arguments>
+T invoke(const MacroAssemblerCodeRef& code, Arguments... arguments)
+{
T (*function)(Arguments...) = bitwise_cast<T(*)(Arguments...)>(code.code().executableAddress());
return function(arguments...);
}
+template<typename T, typename... Arguments>
+T compileAndRun(Procedure& procedure, Arguments... arguments)
+{
+ return invoke<T>(compile(procedure), arguments...);
+}
+
void test42()
{
Procedure proc;
@@ -493,17 +514,433 @@
CHECK(compileAndRun<int>(proc, value) == value);
}
+void testBranch()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchPtr()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, static_cast<intptr_t>(42)) == 1);
+ CHECK(invoke<int>(code, static_cast<intptr_t>(0)) == 0);
+}
+
+void testDiamond()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* done = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ UpsilonValue* thenResult = thenCase->appendNew<UpsilonValue>(
+ proc, Origin(), thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(done));
+
+ UpsilonValue* elseResult = elseCase->appendNew<UpsilonValue>(
+ proc, Origin(), elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(done));
+
+ Value* phi = done->appendNew<Value>(proc, Phi, Int32, Origin());
+ thenResult->setPhi(phi);
+ elseResult->setPhi(phi);
+ done->appendNew<ControlValue>(proc, Return, Origin(), phi);
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchNotEqual()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, NotEqual, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchNotEqualCommute()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, NotEqual, Origin(),
+ root->appendNew<Const32Value>(proc, Origin(), 0),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchNotEqualNotEqual()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, NotEqual, Origin(),
+ root->appendNew<Value>(
+ proc, NotEqual, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchEqual()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchEqualEqual()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchEqualCommute()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<Const32Value>(proc, Origin(), 0),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchEqualEqual1()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<Value>(
+ proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
+ root->appendNew<Const32Value>(proc, Origin(), 0)),
+ root->appendNew<Const32Value>(proc, Origin(), 1)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ auto code = compile(proc);
+ CHECK(invoke<int>(code, 42) == 1);
+ CHECK(invoke<int>(code, 0) == 0);
+}
+
+void testBranchFold(int value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Const32Value>(proc, Origin(), value),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ CHECK(compileAndRun<int>(proc) == !!value);
+}
+
+void testDiamondFold(int value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* done = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Const32Value>(proc, Origin(), value),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ UpsilonValue* thenResult = thenCase->appendNew<UpsilonValue>(
+ proc, Origin(), thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(done));
+
+ UpsilonValue* elseResult = elseCase->appendNew<UpsilonValue>(
+ proc, Origin(), elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(done));
+
+ Value* phi = done->appendNew<Value>(proc, Phi, Int32, Origin());
+ thenResult->setPhi(phi);
+ elseResult->setPhi(phi);
+ done->appendNew<ControlValue>(proc, Return, Origin(), phi);
+
+ CHECK(compileAndRun<int>(proc) == !!value);
+}
+
+void testBranchNotEqualFoldPtr(intptr_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, NotEqual, Origin(),
+ root->appendNew<ConstPtrValue>(proc, Origin(), value),
+ root->appendNew<ConstPtrValue>(proc, Origin(), 0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ CHECK(compileAndRun<int>(proc) == !!value);
+}
+
+void testBranchEqualFoldPtr(intptr_t value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ root->appendNew<Value>(
+ proc, Equal, Origin(),
+ root->appendNew<ConstPtrValue>(proc, Origin(), value),
+ root->appendNew<ConstPtrValue>(proc, Origin(), 0)),
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ thenCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ thenCase->appendNew<Const32Value>(proc, Origin(), 1));
+
+ elseCase->appendNew<ControlValue>(
+ proc, Return, Origin(),
+ elseCase->appendNew<Const32Value>(proc, Origin(), 0));
+
+ CHECK(compileAndRun<int>(proc) == !value);
+}
+
#define RUN(test) do { \
+ if (!shouldRun(#test)) \
+ break; \
dataLog(#test ":\n"); \
test; \
dataLog(" OK!\n"); \
+ didRun++; \
} while (false);
-void run()
+void run(const char* filter)
{
JSC::initializeThreading();
vm = &VM::create(LargeHeap).leakRef();
+ auto shouldRun = [&] (const char* testName) -> bool {
+ return !!strcasestr(testName, filter);
+ };
+ unsigned didRun = 0;
+
RUN(test42());
RUN(testLoad42());
RUN(testArg(43));
@@ -532,13 +969,34 @@
RUN(testStackSlot());
RUN(testLoadFromFramePointer());
RUN(testStoreLoadStackSlot(50));
+ RUN(testBranch());
+ RUN(testBranchPtr());
+ RUN(testDiamond());
+ RUN(testBranchNotEqual());
+ RUN(testBranchNotEqualCommute());
+ RUN(testBranchNotEqualNotEqual());
+ RUN(testBranchEqual());
+ RUN(testBranchEqualEqual());
+ RUN(testBranchEqualCommute());
+ RUN(testBranchEqualEqual1());
+ RUN(testBranchFold(42));
+ RUN(testBranchFold(0));
+ RUN(testDiamondFold(42));
+ RUN(testDiamondFold(0));
+ RUN(testBranchNotEqualFoldPtr(42));
+ RUN(testBranchNotEqualFoldPtr(0));
+ RUN(testBranchEqualFoldPtr(42));
+ RUN(testBranchEqualFoldPtr(0));
+
+ if (!didRun)
+ usage();
}
} // anonymous namespace
#else // ENABLE(B3_JIT)
-static void run()
+static void run(const char*)
{
dataLog("B3 JIT is not enabled.\n");
}
@@ -547,10 +1005,19 @@
int main(int argc, char** argv)
{
- UNUSED_PARAM(argc);
- UNUSED_PARAM(argv);
+ const char* filter = "";
+ switch (argc) {
+ case 1:
+ break;
+ case 2:
+ filter = argv[1];
+ break;
+ default:
+ usage();
+ break;
+ }
- run();
+ run(filter);
return 0;
}