Revision: 24637
Author: [email protected]
Date: Wed Oct 15 13:35:30 2014 UTC
Log: Version 3.30.11 (based on bleeding_edge revision r24621)
Array.prototype.{every, filter, find, findIndex, forEach, map, some}: Use
fresh primitive wrapper for calls (issue 3536).
Correctly expand literal buffer for surrogate pairs (Chromium issue 423212).
Performance and stability improvements on all platforms.
https://code.google.com/p/v8/source/detail?r=24637
Added:
/trunk/test/cctest/compiler/test-typer.cc
/trunk/test/mjsunit/asm/int32div.js
/trunk/test/mjsunit/asm/int32mod.js
/trunk/test/mjsunit/regress/regress-417709a.js
Modified:
/trunk/ChangeLog
/trunk/include/v8.h
/trunk/src/accessors.cc
/trunk/src/accessors.h
/trunk/src/api.cc
/trunk/src/array.js
/trunk/src/ast.cc
/trunk/src/ast.h
/trunk/src/bootstrapper.cc
/trunk/src/code-stubs-hydrogen.cc
/trunk/src/collection.js
/trunk/src/compiler/arm/code-generator-arm.cc
/trunk/src/compiler/arm/instruction-codes-arm.h
/trunk/src/compiler/arm/instruction-selector-arm.cc
/trunk/src/compiler/arm64/code-generator-arm64.cc
/trunk/src/compiler/arm64/instruction-codes-arm64.h
/trunk/src/compiler/arm64/instruction-selector-arm64.cc
/trunk/src/compiler/code-generator.cc
/trunk/src/compiler/ia32/code-generator-ia32.cc
/trunk/src/compiler/ia32/instruction-codes-ia32.h
/trunk/src/compiler/ia32/instruction-selector-ia32.cc
/trunk/src/compiler/instruction-selector.cc
/trunk/src/compiler/instruction.h
/trunk/src/compiler/js-typed-lowering.cc
/trunk/src/compiler/machine-operator-reducer.cc
/trunk/src/compiler/machine-operator-reducer.h
/trunk/src/compiler/machine-operator.cc
/trunk/src/compiler/machine-operator.h
/trunk/src/compiler/mips/code-generator-mips.cc
/trunk/src/compiler/mips/instruction-codes-mips.h
/trunk/src/compiler/mips/instruction-selector-mips.cc
/trunk/src/compiler/opcodes.h
/trunk/src/compiler/raw-machine-assembler.h
/trunk/src/compiler/scheduler.cc
/trunk/src/compiler/typer.cc
/trunk/src/compiler/typer.h
/trunk/src/compiler/verifier.cc
/trunk/src/compiler/x64/code-generator-x64.cc
/trunk/src/compiler/x64/instruction-codes-x64.h
/trunk/src/compiler/x64/instruction-selector-x64.cc
/trunk/src/counters.cc
/trunk/src/factory.cc
/trunk/src/factory.h
/trunk/src/harmony-array.js
/trunk/src/heap/heap.cc
/trunk/src/heap/heap.h
/trunk/src/heap/mark-compact.cc
/trunk/src/heap/mark-compact.h
/trunk/src/heap/objects-visiting-inl.h
/trunk/src/heap/objects-visiting.cc
/trunk/src/heap/objects-visiting.h
/trunk/src/ic/ic.h
/trunk/src/isolate.cc
/trunk/src/log-inl.h
/trunk/src/log.cc
/trunk/src/log.h
/trunk/src/macros.py
/trunk/src/mksnapshot.cc
/trunk/src/objects-debug.cc
/trunk/src/objects-inl.h
/trunk/src/objects-printer.cc
/trunk/src/objects.cc
/trunk/src/objects.h
/trunk/src/runtime/runtime-function.cc
/trunk/src/runtime/runtime-maths.cc
/trunk/src/runtime/runtime-object.cc
/trunk/src/scanner.h
/trunk/src/types.cc
/trunk/src/types.h
/trunk/src/version.cc
/trunk/test/cctest/cctest.gyp
/trunk/test/cctest/compiler/test-js-constant-cache.cc
/trunk/test/cctest/compiler/test-js-typed-lowering.cc
/trunk/test/cctest/compiler/test-run-machops.cc
/trunk/test/cctest/compiler/test-scheduler.cc
/trunk/test/cctest/test-api.cc
/trunk/test/cctest/test-heap.cc
/trunk/test/cctest/test-serialize.cc
/trunk/test/cctest/test-types.cc
/trunk/test/mjsunit/array-iteration.js
/trunk/test/mjsunit/es6/collections.js
/trunk/test/mjsunit/harmony/array-find.js
/trunk/test/mjsunit/harmony/array-findindex.js
/trunk/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc
/trunk/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc
/trunk/test/unittests/compiler/graph-unittest.cc
/trunk/test/unittests/compiler/graph-unittest.h
/trunk/test/unittests/compiler/ia32/instruction-selector-ia32-unittest.cc
/trunk/test/unittests/compiler/machine-operator-reducer-unittest.cc
/trunk/test/unittests/compiler/machine-operator-unittest.cc
/trunk/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc
/trunk/tools/push-to-trunk/common_includes.py
/trunk/tools/whitespace.txt
=======================================
--- /dev/null
+++ /trunk/test/cctest/compiler/test-typer.cc Wed Oct 15 13:35:30 2014 UTC
@@ -0,0 +1,277 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// This tests the correctness of the typer.
+//
+// For simplicity, it currently only tests it on expression operators that
have
+// a direct equivalent in C++. Also, testing is currently limited to
ranges as
+// input types.
+
+
+#include <functional>
+
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/typer.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+
+
+class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
+ public:
+ TyperTester()
+ : GraphAndBuilders(main_zone()),
+ typer_(main_zone()),
+ javascript_(main_zone()) {
+ Node* s = graph()->NewNode(common()->Start(3));
+ graph()->SetStart(s);
+ context_node_ = graph()->NewNode(common()->Parameter(2),
graph()->start());
+ rng_ = isolate()->random_number_generator();
+
+ integers.push_back(0);
+ integers.push_back(0);
+ integers.push_back(-1);
+ integers.push_back(+1);
+ integers.push_back(-V8_INFINITY);
+ integers.push_back(+V8_INFINITY);
+ for (int i = 0; i < 5; ++i) {
+ double x = rng_->NextInt();
+ integers.push_back(x);
+ x *= rng_->NextInt();
+ if (!IsMinusZero(x)) integers.push_back(x);
+ }
+
+ int32s.push_back(0);
+ int32s.push_back(0);
+ int32s.push_back(-1);
+ int32s.push_back(+1);
+ int32s.push_back(kMinInt);
+ int32s.push_back(kMaxInt);
+ for (int i = 0; i < 10; ++i) {
+ int32s.push_back(rng_->NextInt());
+ }
+ }
+
+ Typer typer_;
+ JSOperatorBuilder javascript_;
+ Node* context_node_;
+ v8::base::RandomNumberGenerator* rng_;
+ std::vector<double> integers;
+ std::vector<double> int32s;
+
+ Isolate* isolate() { return main_isolate(); }
+ Graph* graph() { return main_graph_; }
+ CommonOperatorBuilder* common() { return &main_common_; }
+
+ Node* Parameter(int index = 0) {
+ return graph()->NewNode(common()->Parameter(index), graph()->start());
+ }
+
+ Type* TypeBinaryOp(const Operator* op, Type* lhs, Type* rhs) {
+ Node* p0 = Parameter(0);
+ Node* p1 = Parameter(1);
+ NodeProperties::SetBounds(p0, Bounds(lhs));
+ NodeProperties::SetBounds(p1, Bounds(rhs));
+ Node* n = graph()->NewNode(
+ op, p0, p1, context_node_, graph()->start(), graph()->start());
+ typer_.Init(n);
+ return NodeProperties::GetBounds(n).upper;
+ }
+
+ Type* RandomRange(bool int32 = false) {
+ std::vector<double>& numbers = int32 ? int32s : integers;
+ Factory* f = isolate()->factory();
+ int i = rng_->NextInt(static_cast<int>(numbers.size()));
+ int j = rng_->NextInt(static_cast<int>(numbers.size()));
+ i::Handle<i::Object> min = f->NewNumber(numbers[i]);
+ i::Handle<i::Object> max = f->NewNumber(numbers[j]);
+ if (min->Number() > max->Number()) std::swap(min, max);
+ return Type::Range(min, max, main_zone());
+ }
+
+ double RandomInt(double min, double max) {
+ switch (rng_->NextInt(4)) {
+ case 0: return min;
+ case 1: return max;
+ default: break;
+ }
+ if (min == +V8_INFINITY) return +V8_INFINITY;
+ if (max == -V8_INFINITY) return -V8_INFINITY;
+ if (min == -V8_INFINITY && max == +V8_INFINITY) {
+ return rng_->NextInt() * static_cast<double>(rng_->NextInt());
+ }
+ double result = nearbyint(min + (max - min) * rng_->NextDouble());
+ if (IsMinusZero(result)) return 0;
+ if (std::isnan(result)) return rng_->NextInt(2) ? min : max;
+ DCHECK(min <= result && result <= max);
+ return result;
+ }
+
+ double RandomInt(Type::RangeType* range) {
+ return RandomInt(range->Min()->Number(), range->Max()->Number());
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryArithOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange()->AsRange();
+ Type::RangeType* r2 = RandomRange()->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ double x1 = RandomInt(r1);
+ double x2 = RandomInt(r2);
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryCompareOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange()->AsRange();
+ Type::RangeType* r2 = RandomRange()->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ double x1 = RandomInt(r1);
+ double x2 = RandomInt(r2);
+ bool result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(result_value ?
+ isolate()->factory()->true_value() :
+ isolate()->factory()->false_value(), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryBitOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange(true)->AsRange();
+ Type::RangeType* r2 = RandomRange(true)->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ int32_t x1 = static_cast<int32_t>(RandomInt(r1));
+ int32_t x2 = static_cast<int32_t>(RandomInt(r2));
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+};
+
+
+static int32_t shift_left(int32_t x, int32_t y) { return x << y; }
+static int32_t shift_right(int32_t x, int32_t y) { return x >> y; }
+static int32_t bit_or(int32_t x, int32_t y) { return x | y; }
+static int32_t bit_and(int32_t x, int32_t y) { return x & y; }
+static int32_t bit_xor(int32_t x, int32_t y) { return x ^ y; }
+
+
+TEST(TypeJSAdd) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Subtract(), std::plus<double>());
+}
+
+
+TEST(TypeJSSubtract) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Subtract(), std::minus<double>());
+}
+
+
+TEST(TypeJSMultiply) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Multiply(), std::multiplies<double>());
+}
+
+
+TEST(TypeJSDivide) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Divide(), std::divides<double>());
+}
+
+
+TEST(TypeJSBitwiseOr) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseOr(), bit_or);
+}
+
+
+TEST(TypeJSBitwiseAnd) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseAnd(), bit_and);
+}
+
+
+TEST(TypeJSBitwiseXor) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseXor(), bit_xor);
+}
+
+
+TEST(TypeJSShiftLeft) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.ShiftLeft(), shift_left);
+}
+
+
+TEST(TypeJSShiftRight) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.ShiftRight(), shift_right);
+}
+
+
+TEST(TypeJSLessThan) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.LessThan(), std::less<double>());
+}
+
+
+TEST(TypeJSLessThanOrEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.LessThanOrEqual(), std::less_equal<double>());
+}
+
+
+TEST(TypeJSGreaterThan) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.GreaterThan(),
std::greater<double>());
+}
+
+
+TEST(TypeJSGreaterThanOrEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.GreaterThanOrEqual(), std::greater_equal<double>());
+}
+
+
+TEST(TypeJSEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.Equal(), std::equal_to<double>());
+}
+
+
+TEST(TypeJSNotEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.NotEqual(),
std::not_equal_to<double>());
+}
+
+
+// For numbers there's no difference between strict and non-strict
equality.
+TEST(TypeJSStrictEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.StrictEqual(),
std::equal_to<double>());
+}
+
+
+TEST(TypeJSStrictNotEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.StrictNotEqual(), std::not_equal_to<double>());
+}
=======================================
--- /dev/null
+++ /trunk/test/mjsunit/asm/int32div.js Wed Oct 15 13:35:30 2014 UTC
@@ -0,0 +1,33 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var stdlib = {};
+var foreign = {};
+var heap = new ArrayBuffer(64 * 1024);
+
+function Int32Div(divisor) {
+ var name = "div_";
+ if (divisor < 0) {
+ name += "minus_";
+ }
+ name += Math.abs(divisor);
+ var m = eval("function Module(stdlib, foreign, heap) {\n"
+ + " \"use asm\";\n"
+ + " function " + name + "(dividend) {\n"
+ + " return ((dividend | 0) / " + divisor + ") | 0;\n"
+ + " }\n"
+ + " return { f: " + name + "}\n"
+ + "}; Module");
+ return m(stdlib, foreign, heap).f;
+}
+
+var divisors = [-2147483648, -32 * 1024, -1000, -16, -7, -2, -1, 0,
+ 1, 3, 4, 10, 64, 100, 1024, 2147483647];
+for (var i in divisors) {
+ var divisor = divisors[i];
+ var div = Int32Div(divisor);
+ for (var dividend = -2147483648; dividend < 2147483648; dividend +=
3999773) {
+ assertEquals((dividend / divisor) | 0, div(dividend));
+ }
+}
=======================================
--- /dev/null
+++ /trunk/test/mjsunit/asm/int32mod.js Wed Oct 15 13:35:30 2014 UTC
@@ -0,0 +1,33 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var stdlib = {};
+var foreign = {};
+var heap = new ArrayBuffer(64 * 1024);
+
+function Int32Mod(divisor) {
+ var name = "mod_";
+ if (divisor < 0) {
+ name += "minus_";
+ }
+ name += Math.abs(divisor);
+ var m = eval("function Module(stdlib, foreign, heap) {\n"
+ + " \"use asm\";\n"
+ + " function " + name + "(dividend) {\n"
+ + " return ((dividend | 0) % " + divisor + ") | 0;\n"
+ + " }\n"
+ + " return { f: " + name + "}\n"
+ + "}; Module");
+ return m(stdlib, foreign, heap).f;
+}
+
+var divisors = [-2147483648, -32 * 1024, -1000, -16, -7, -2, -1,
+ 1, 3, 4, 10, 64, 100, 1024, 2147483647];
+for (var i in divisors) {
+ var divisor = divisors[i];
+ var mod = Int32Mod(divisor);
+ for (var dividend = -2147483648; dividend < 2147483648; dividend +=
3999773) {
+ assertEquals((dividend % divisor) | 0, mod(dividend));
+ }
+}
=======================================
--- /dev/null
+++ /trunk/test/mjsunit/regress/regress-417709a.js Wed Oct 15 13:35:30 2014
UTC
@@ -0,0 +1,16 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --stack-size=100
+
+var a = [];
+
+Object.observe(a, function() {});
+
+function f(a, x) {
+ a.length = x;
+ f(a, x + 1);
+}
+
+assertThrows(function() { f(a, 1); }, RangeError);
=======================================
--- /trunk/ChangeLog Wed Oct 15 00:05:09 2014 UTC
+++ /trunk/ChangeLog Wed Oct 15 13:35:30 2014 UTC
@@ -1,3 +1,14 @@
+2014-10-15: Version 3.30.11
+
+ Array.prototype.{every, filter, find, findIndex, forEach, map,
some}:
+ Use fresh primitive wrapper for calls (issue 3536).
+
+ Correctly expand literal buffer for surrogate pairs (Chromium issue
+ 423212).
+
+ Performance and stability improvements on all platforms.
+
+
2014-10-15: Version 3.30.10
Squeeze the layout of various AST node types (Chromium issue
417697).
=======================================
--- /trunk/include/v8.h Thu Oct 9 00:05:16 2014 UTC
+++ /trunk/include/v8.h Wed Oct 15 13:35:30 2014 UTC
@@ -5927,7 +5927,7 @@
static const int kNullValueRootIndex = 7;
static const int kTrueValueRootIndex = 8;
static const int kFalseValueRootIndex = 9;
- static const int kEmptyStringRootIndex = 153;
+ static const int kEmptyStringRootIndex = 154;
// The external allocation limit should be below 256 MB on all
architectures
// to avoid that resource-constrained embedders run low on memory.
@@ -5942,7 +5942,7 @@
static const int kNodeIsIndependentShift = 4;
static const int kNodeIsPartiallyDependentShift = 5;
- static const int kJSObjectType = 0xbc;
+ static const int kJSObjectType = 0xbd;
static const int kFirstNonstringType = 0x80;
static const int kOddballType = 0x83;
static const int kForeignType = 0x88;
=======================================
--- /trunk/src/accessors.cc Fri Oct 3 00:04:58 2014 UTC
+++ /trunk/src/accessors.cc Wed Oct 15 13:35:30 2014 UTC
@@ -172,7 +172,10 @@
LookupIterator it(object, Utils::OpenHandle(*name));
CHECK_EQ(LookupIterator::ACCESSOR, it.state());
DCHECK(it.HolderIsReceiverOrHiddenPrototype());
- Object::SetDataProperty(&it, value);
+
+ if (Object::SetDataProperty(&it, value).is_null()) {
+ isolate->OptionalRescheduleException(false);
+ }
}
@@ -247,7 +250,7 @@
if (uint32_v->Number() == number_v->Number()) {
maybe = JSArray::SetElementsLength(array_handle, uint32_v);
- maybe.Check();
+ if (maybe.is_null()) isolate->OptionalRescheduleException(false);
return;
}
@@ -881,9 +884,8 @@
}
-static Handle<Object> SetFunctionPrototype(Isolate* isolate,
- Handle<JSFunction> function,
- Handle<Object> value) {
+MUST_USE_RESULT static MaybeHandle<Object> SetFunctionPrototype(
+ Isolate* isolate, Handle<JSFunction> function, Handle<Object> value) {
Handle<Object> old_value;
bool is_observed = function->map()->is_observed();
if (is_observed) {
@@ -897,16 +899,17 @@
DCHECK(function->prototype() == *value);
if (is_observed && !old_value->SameValue(*value)) {
- JSObject::EnqueueChangeRecord(
+ MaybeHandle<Object> result = JSObject::EnqueueChangeRecord(
function, "update", isolate->factory()->prototype_string(),
old_value);
+ if (result.is_null()) return MaybeHandle<Object>();
}
return function;
}
-Handle<Object> Accessors::FunctionSetPrototype(Handle<JSFunction> function,
- Handle<Object> prototype) {
+MaybeHandle<Object> Accessors::FunctionSetPrototype(Handle<JSFunction>
function,
+ Handle<Object>
prototype) {
DCHECK(function->should_have_prototype());
Isolate* isolate = function->GetIsolate();
return SetFunctionPrototype(isolate, function, prototype);
@@ -937,7 +940,9 @@
}
Handle<JSFunction> object =
Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
- SetFunctionPrototype(isolate, object, value);
+ if (SetFunctionPrototype(isolate, object, value).is_null()) {
+ isolate->OptionalRescheduleException(false);
+ }
}
=======================================
--- /trunk/src/accessors.h Fri Oct 3 00:04:58 2014 UTC
+++ /trunk/src/accessors.h Wed Oct 15 13:35:30 2014 UTC
@@ -66,8 +66,8 @@
};
// Accessor functions called directly from the runtime system.
- static Handle<Object> FunctionSetPrototype(Handle<JSFunction> object,
- Handle<Object> value);
+ MUST_USE_RESULT static MaybeHandle<Object> FunctionSetPrototype(
+ Handle<JSFunction> object, Handle<Object> value);
static Handle<Object> FunctionGetArguments(Handle<JSFunction> object);
// Accessor infos.
=======================================
--- /trunk/src/api.cc Thu Oct 9 00:05:16 2014 UTC
+++ /trunk/src/api.cc Wed Oct 15 13:35:30 2014 UTC
@@ -6711,7 +6711,7 @@
void Isolate::SetEventLogger(LogEventCallback that) {
// Do not overwrite the event logger if we want to log explicitly.
- if (i::FLAG_log_timer_events) return;
+ if (i::FLAG_log_internal_timer_events) return;
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->set_event_logger(that);
}
=======================================
--- /trunk/src/array.js Wed Oct 1 00:05:35 2014 UTC
+++ /trunk/src/array.js Wed Oct 15 13:35:30 2014 UTC
@@ -1133,10 +1133,11 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(receiver)) {
receiver = %GetDefaultReceiver(f) || receiver;
- } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
- receiver = ToObject(receiver);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var result = new $Array();
@@ -1148,7 +1149,8 @@
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
- if (%_CallFunction(receiver, element, i, array, f)) {
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ if (%_CallFunction(new_receiver, element, i, array, f)) {
accumulator[accumulator_length++] = element;
}
}
@@ -1169,10 +1171,11 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(receiver)) {
receiver = %GetDefaultReceiver(f) || receiver;
- } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
- receiver = ToObject(receiver);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
@@ -1181,7 +1184,8 @@
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
- %_CallFunction(receiver, element, i, array, f);
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ %_CallFunction(new_receiver, element, i, array, f);
}
}
}
@@ -1200,10 +1204,11 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(receiver)) {
receiver = %GetDefaultReceiver(f) || receiver;
- } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
- receiver = ToObject(receiver);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
@@ -1212,7 +1217,8 @@
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
- if (%_CallFunction(receiver, element, i, array, f)) return true;
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ if (%_CallFunction(new_receiver, element, i, array, f)) return true;
}
}
return false;
@@ -1230,10 +1236,11 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(receiver)) {
receiver = %GetDefaultReceiver(f) || receiver;
- } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
- receiver = ToObject(receiver);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
@@ -1242,7 +1249,8 @@
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
- if (!%_CallFunction(receiver, element, i, array, f)) return false;
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ if (!%_CallFunction(new_receiver, element, i, array, f)) return
false;
}
}
return true;
@@ -1259,10 +1267,11 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(receiver)) {
receiver = %GetDefaultReceiver(f) || receiver;
- } else if (!IS_SPEC_OBJECT(receiver) && %IsSloppyModeFunction(f)) {
- receiver = ToObject(receiver);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
}
var result = new $Array();
@@ -1273,7 +1282,8 @@
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
- accumulator[i] = %_CallFunction(receiver, element, i, array, f);
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ accumulator[i] = %_CallFunction(new_receiver, element, i, array, f);
}
}
%MoveArrayContents(accumulator, result);
=======================================
--- /trunk/src/ast.cc Wed Oct 15 00:05:09 2014 UTC
+++ /trunk/src/ast.cc Wed Oct 15 13:35:30 2014 UTC
@@ -101,6 +101,7 @@
Expression* value, int pos, IdGen* id_gen)
: Expression(zone, pos, num_ids(), id_gen),
is_uninitialized_(false),
+ key_type_(ELEMENT),
store_mode_(STANDARD_STORE),
op_(op),
target_(target),
=======================================
--- /trunk/src/ast.h Wed Oct 15 00:05:09 2014 UTC
+++ /trunk/src/ast.h Wed Oct 15 13:35:30 2014 UTC
@@ -1742,6 +1742,10 @@
virtual KeyedAccessStoreMode GetStoreMode() OVERRIDE {
return STANDARD_STORE;
}
+ virtual IcCheckType GetKeyType() {
+ // PROPERTY key types currently aren't implemented for KeyedLoadICs.
+ return ELEMENT;
+ }
bool IsUninitialized() { return !is_for_call_ && is_uninitialized_; }
bool HasNoTypeInformation() {
return is_uninitialized_;
@@ -2123,6 +2127,7 @@
: Expression(zone, pos, num_ids(), id_gen),
op_(op),
is_prefix_(is_prefix),
+ key_type_(ELEMENT),
store_mode_(STANDARD_STORE),
expression_(expr) {}
=======================================
--- /trunk/src/bootstrapper.cc Thu Oct 9 00:05:16 2014 UTC
+++ /trunk/src/bootstrapper.cc Wed Oct 15 13:35:30 2014 UTC
@@ -509,7 +509,7 @@
// prototype, otherwise the missing initial_array_prototype will cause
// assertions during startup.
native_context()->set_initial_array_prototype(*prototype);
- Accessors::FunctionSetPrototype(object_fun, prototype);
+ Accessors::FunctionSetPrototype(object_fun, prototype).Assert();
}
// Allocate the empty function as the prototype for function ECMAScript
@@ -1673,7 +1673,7 @@
isolate()->initial_object_prototype(), Builtins::kIllegal);
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
- Accessors::FunctionSetPrototype(script_fun, prototype);
+ Accessors::FunctionSetPrototype(script_fun, prototype).Assert();
native_context()->set_script_function(*script_fun);
Handle<Map> script_map = Handle<Map>(script_fun->initial_map());
@@ -1815,7 +1815,7 @@
isolate()->initial_object_prototype(), Builtins::kIllegal);
Handle<JSObject> prototype =
factory()->NewJSObject(isolate()->object_function(), TENURED);
- Accessors::FunctionSetPrototype(opaque_reference_fun, prototype);
+ Accessors::FunctionSetPrototype(opaque_reference_fun,
prototype).Assert();
native_context()->set_opaque_reference_function(*opaque_reference_fun);
}
=======================================
--- /trunk/src/code-stubs-hydrogen.cc Tue Oct 14 07:51:07 2014 UTC
+++ /trunk/src/code-stubs-hydrogen.cc Wed Oct 15 13:35:30 2014 UTC
@@ -416,7 +416,12 @@
HInstruction* boilerplate = Add<HLoadNamedField>(
allocation_site, static_cast<HValue*>(NULL), access);
- int size = JSObject::kHeaderSize + casted_stub()->length() *
kPointerSize;
+ int length = casted_stub()->length();
+ if (length == 0) {
+ // Empty objects have some slack added to them.
+ length = JSObject::kInitialGlobalObjectUnusedPropertiesCount;
+ }
+ int size = JSObject::kHeaderSize + length * kPointerSize;
int object_size = size;
if (FLAG_allocation_site_pretenuring) {
size += AllocationMemento::kSize;
=======================================
--- /trunk/src/collection.js Thu Aug 21 00:04:56 2014 UTC
+++ /trunk/src/collection.js Wed Oct 15 13:35:30 2014 UTC
@@ -105,6 +105,12 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [f]);
}
+ var needs_wrapper = false;
+ if (IS_NULL_OR_UNDEFINED(receiver)) {
+ receiver = %GetDefaultReceiver(f) || receiver;
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
+ }
var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
var key;
@@ -113,7 +119,8 @@
while (%SetIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f);
key = value_array[0];
- %_CallFunction(receiver, key, key, this, f);
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ %_CallFunction(new_receiver, key, key, this, f);
}
}
@@ -249,13 +256,20 @@
if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [f]);
}
+ var needs_wrapper = false;
+ if (IS_NULL_OR_UNDEFINED(receiver)) {
+ receiver = %GetDefaultReceiver(f) || receiver;
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver);
+ }
var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f);
var value_array = [UNDEFINED, UNDEFINED];
while (%MapIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f);
- %_CallFunction(receiver, value_array[1], value_array[0], this, f);
+ var new_receiver = needs_wrapper ? ToObject(receiver) : receiver;
+ %_CallFunction(new_receiver, value_array[1], value_array[0], this, f);
}
}
=======================================
--- /trunk/src/compiler/arm/code-generator-arm.cc Wed Oct 15 00:05:09 2014
UTC
+++ /trunk/src/compiler/arm/code-generator-arm.cc Wed Oct 15 13:35:30 2014
UTC
@@ -239,6 +239,15 @@
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
+ case kArmSmmul:
+ __ smmul(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
+ DCHECK_EQ(LeaveCC, i.OutputSBit());
+ break;
+ case kArmSmmla:
+ __ smmla(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
+ i.InputRegister(2));
+ DCHECK_EQ(LeaveCC, i.OutputSBit());
+ break;
case kArmSdiv: {
CpuFeatureScope scope(masm(), SUDIV);
__ sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
=======================================
--- /trunk/src/compiler/arm/instruction-codes-arm.h Fri Sep 26 00:05:23
2014 UTC
+++ /trunk/src/compiler/arm/instruction-codes-arm.h Wed Oct 15 13:35:30
2014 UTC
@@ -26,6 +26,8 @@
V(ArmMul) \
V(ArmMla) \
V(ArmMls) \
+ V(ArmSmmul) \
+ V(ArmSmmla) \
V(ArmSdiv) \
V(ArmUdiv) \
V(ArmMov) \
=======================================
--- /trunk/src/compiler/arm/instruction-selector-arm.cc Tue Oct 14 07:51:07
2014 UTC
+++ /trunk/src/compiler/arm/instruction-selector-arm.cc Wed Oct 15 13:35:30
2014 UTC
@@ -78,6 +78,8 @@
case kArmMul:
case kArmMla:
case kArmMls:
+ case kArmSmmul:
+ case kArmSmmla:
case kArmSdiv:
case kArmUdiv:
case kArmBfc:
@@ -569,6 +571,20 @@
g.UseRegister(mright.right().node()),
g.UseRegister(m.left().node()));
return;
}
+ if (m.left().IsInt32MulHigh() && CanCover(node, m.left().node())) {
+ Int32BinopMatcher mleft(m.left().node());
+ Emit(kArmSmmla, g.DefineAsRegister(node),
+ g.UseRegister(mleft.left().node()),
+ g.UseRegister(mleft.right().node()),
g.UseRegister(m.right().node()));
+ return;
+ }
+ if (m.right().IsInt32MulHigh() && CanCover(node, m.right().node())) {
+ Int32BinopMatcher mright(m.right().node());
+ Emit(kArmSmmla, g.DefineAsRegister(node),
+ g.UseRegister(mright.left().node()),
+ g.UseRegister(mright.right().node()),
g.UseRegister(m.left().node()));
+ return;
+ }
VisitBinop(this, node, kArmAdd, kArmAdd);
}
@@ -610,6 +626,13 @@
Emit(kArmMul, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
g.UseRegister(m.right().node()));
}
+
+
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ ArmOperandGenerator g(this);
+ Emit(kArmSmmul, g.DefineAsRegister(node),
g.UseRegister(node->InputAt(0)),
+ g.UseRegister(node->InputAt(1)));
+}
static void EmitDiv(InstructionSelector* selector, ArchOpcode div_opcode,
=======================================
--- /trunk/src/compiler/arm64/code-generator-arm64.cc Wed Oct 15 00:05:09
2014 UTC
+++ /trunk/src/compiler/arm64/code-generator-arm64.cc Wed Oct 15 13:35:30
2014 UTC
@@ -252,6 +252,9 @@
case kArm64Mul32:
__ Mul(i.OutputRegister32(), i.InputRegister32(0),
i.InputRegister32(1));
break;
+ case kArm64Smull:
+ __ Smull(i.OutputRegister(), i.InputRegister32(0),
i.InputRegister32(1));
+ break;
case kArm64Madd:
__ Madd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.InputRegister(2));
=======================================
--- /trunk/src/compiler/arm64/instruction-codes-arm64.h Wed Oct 15 00:05:09
2014 UTC
+++ /trunk/src/compiler/arm64/instruction-codes-arm64.h Wed Oct 15 13:35:30
2014 UTC
@@ -36,6 +36,7 @@
V(Arm64Sub32) \
V(Arm64Mul) \
V(Arm64Mul32) \
+ V(Arm64Smull) \
V(Arm64Madd) \
V(Arm64Madd32) \
V(Arm64Msub) \
=======================================
--- /trunk/src/compiler/arm64/instruction-selector-arm64.cc Wed Oct 15
00:05:09 2014 UTC
+++ /trunk/src/compiler/arm64/instruction-selector-arm64.cc Wed Oct 15
13:35:30 2014 UTC
@@ -744,6 +744,16 @@
VisitRRR(this, kArm64Mul, node);
}
+
+
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ // TODO(arm64): Can we do better here?
+ Arm64OperandGenerator g(this);
+ InstructionOperand* const smull_operand = g.TempRegister();
+ Emit(kArm64Smull, smull_operand, g.UseRegister(node->InputAt(0)),
+ g.UseRegister(node->InputAt(1)));
+ Emit(kArm64Asr, g.DefineAsRegister(node), smull_operand,
g.TempImmediate(32));
+}
void InstructionSelector::VisitInt32Div(Node* node) {
=======================================
--- /trunk/src/compiler/code-generator.cc Wed Oct 15 00:05:09 2014 UTC
+++ /trunk/src/compiler/code-generator.cc Wed Oct 15 13:35:30 2014 UTC
@@ -107,8 +107,7 @@
if (FLAG_code_comments) {
// TODO(titzer): these code comments are a giant memory leak.
Vector<char> buffer = Vector<char>::New(32);
- // TODO(dcarney): should not be rpo number there
- SNPrintF(buffer, "-- B%d (rpo) start --", current_block_.ToInt());
+ SNPrintF(buffer, "-- B%d start --", block_start->id().ToInt());
masm()->RecordComment(buffer.start());
}
masm()->bind(block_start->label());
=======================================
--- /trunk/src/compiler/ia32/code-generator-ia32.cc Wed Oct 15 00:05:09
2014 UTC
+++ /trunk/src/compiler/ia32/code-generator-ia32.cc Wed Oct 15 13:35:30
2014 UTC
@@ -243,6 +243,9 @@
} else {
__ imul(i.OutputRegister(), i.InputOperand(1));
}
+ break;
+ case kIA32ImulHigh:
+ __ imul(i.InputRegister(1));
break;
case kIA32Idiv:
__ cdq();
=======================================
--- /trunk/src/compiler/ia32/instruction-codes-ia32.h Wed Oct 1 00:05:35
2014 UTC
+++ /trunk/src/compiler/ia32/instruction-codes-ia32.h Wed Oct 15 13:35:30
2014 UTC
@@ -20,6 +20,7 @@
V(IA32Xor) \
V(IA32Sub) \
V(IA32Imul) \
+ V(IA32ImulHigh) \
V(IA32Idiv) \
V(IA32Udiv) \
V(IA32Not) \
=======================================
--- /trunk/src/compiler/ia32/instruction-selector-ia32.cc Wed Oct 15
00:05:09 2014 UTC
+++ /trunk/src/compiler/ia32/instruction-selector-ia32.cc Wed Oct 15
13:35:30 2014 UTC
@@ -259,7 +259,18 @@
size_t output_count = 0;
// TODO(turbofan): match complex addressing modes.
- if (g.CanBeImmediate(right)) {
+ if (left == right) {
+ // If both inputs refer to the same operand, enforce allocating a
register
+ // for both of them to ensure that we don't end up generating code like
+ // this:
+ //
+ // mov eax, [ebp-0x10]
+ // add eax, [ebp-0x10]
+ // jo label
+ InstructionOperand* const input = g.UseRegister(left);
+ inputs[input_count++] = input;
+ inputs[input_count++] = input;
+ } else if (g.CanBeImmediate(right)) {
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.UseImmediate(right);
} else {
@@ -453,6 +464,16 @@
g.Use(right));
}
}
+
+
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand* temps[] = {g.TempRegister(eax)};
+ size_t temp_count = arraysize(temps);
+ Emit(kIA32ImulHigh, g.DefineAsFixed(node, edx),
+ g.UseFixed(node->InputAt(0), eax), g.UseRegister(node->InputAt(1)),
+ temp_count, temps);
+}
static inline void VisitDiv(InstructionSelector* selector, Node* node,
@@ -662,13 +683,6 @@
VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right),
cont);
}
}
-
-
-static void VisitWordTest(InstructionSelector* selector, Node* node,
- FlagsContinuation* cont) {
- IA32OperandGenerator g(selector);
- VisitCompare(selector, kIA32Test, g.Use(node), g.TempImmediate(-1),
cont);
-}
// Shared routine for multiple float compare operations.
@@ -684,7 +698,7 @@
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
- OperandGenerator g(this);
+ IA32OperandGenerator g(this);
Node* user = branch;
Node* value = branch->InputAt(0);
@@ -770,7 +784,7 @@
}
// Branch could not be combined with a compare, emit compare against 0.
- VisitWordTest(this, value, &cont);
+ VisitCompare(this, kIA32Cmp, g.Use(value), g.TempImmediate(0), &cont);
}
@@ -789,7 +803,6 @@
default:
break;
}
- return VisitWordTest(this, value, &cont);
}
}
return VisitWordCompare(this, node, kIA32Cmp, &cont, false);
=======================================
--- /trunk/src/compiler/instruction-selector.cc Wed Oct 15 00:05:09 2014 UTC
+++ /trunk/src/compiler/instruction-selector.cc Wed Oct 15 13:35:30 2014 UTC
@@ -658,6 +658,8 @@
return VisitInt32SubWithOverflow(node);
case IrOpcode::kInt32Mul:
return VisitInt32Mul(node);
+ case IrOpcode::kInt32MulHigh:
+ return VisitInt32MulHigh(node);
case IrOpcode::kInt32Div:
return VisitInt32Div(node);
case IrOpcode::kInt32Mod:
=======================================
--- /trunk/src/compiler/instruction.h Wed Oct 15 00:05:09 2014 UTC
+++ /trunk/src/compiler/instruction.h Wed Oct 15 13:35:30 2014 UTC
@@ -598,6 +598,7 @@
public:
Label* label() { return &label_; }
BasicBlock::RpoNumber rpo_number() const { return rpo_number_; }
+ BasicBlock::Id id() const { return id_; }
static BlockStartInstruction* New(Zone* zone, BasicBlock* block) {
void* buffer = zone->New(sizeof(BlockStartInstruction));
@@ -612,8 +613,10 @@
private:
explicit BlockStartInstruction(BasicBlock* block)
: GapInstruction(kBlockStartInstruction),
+ id_(block->id()),
rpo_number_(block->GetRpoNumber()) {}
+ BasicBlock::Id id_;
BasicBlock::RpoNumber rpo_number_;
Label label_;
};
=======================================
--- /trunk/src/compiler/js-typed-lowering.cc Tue Oct 14 07:51:07 2014 UTC
+++ /trunk/src/compiler/js-typed-lowering.cc Wed Oct 15 13:35:30 2014 UTC
@@ -383,16 +383,6 @@
: jsgraph()->TrueConstant());
}
}
- /* TODO(neis): This is currently unsound.
- if (!r.left_type()->Maybe(r.right_type())) {
- // Type intersection is empty; === is always false unless both
- // inputs could be strings (one internalized and one not).
- if (r.OneInputCannotBe(Type::String())) {
- return ReplaceEagerly(node, invert ? jsgraph()->TrueConstant()
- : jsgraph()->FalseConstant());
- }
- }
- */
if (r.OneInputIs(Type::Undefined())) {
return r.ChangeToPureOperator(
simplified()->ReferenceEqual(Type::Undefined()), invert);
=======================================
--- /trunk/src/compiler/machine-operator-reducer.cc Tue Oct 14 07:51:07
2014 UTC
+++ /trunk/src/compiler/machine-operator-reducer.cc Wed Oct 15 13:35:30
2014 UTC
@@ -5,6 +5,7 @@
#include "src/compiler/machine-operator-reducer.h"
#include "src/base/bits.h"
+#include "src/base/division-by-constant.h"
#include "src/codegen.h"
#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
@@ -40,6 +41,54 @@
Node* MachineOperatorReducer::Int64Constant(int64_t value) {
return graph()->NewNode(common()->Int64Constant(value));
}
+
+
+Node* MachineOperatorReducer::Word32And(Node* lhs, uint32_t rhs) {
+ return graph()->NewNode(machine()->Word32And(), lhs,
Uint32Constant(rhs));
+}
+
+
+Node* MachineOperatorReducer::Word32Sar(Node* lhs, uint32_t rhs) {
+ return graph()->NewNode(machine()->Word32Sar(), lhs,
Uint32Constant(rhs));
+}
+
+
+Node* MachineOperatorReducer::Word32Shr(Node* lhs, uint32_t rhs) {
+ return graph()->NewNode(machine()->Word32Shr(), lhs,
Uint32Constant(rhs));
+}
+
+
+Node* MachineOperatorReducer::Int32Add(Node* lhs, Node* rhs) {
+ return graph()->NewNode(machine()->Int32Add(), lhs, rhs);
+}
+
+
+Node* MachineOperatorReducer::Int32Sub(Node* lhs, Node* rhs) {
+ return graph()->NewNode(machine()->Int32Sub(), lhs, rhs);
+}
+
+
+Node* MachineOperatorReducer::Int32Mul(Node* lhs, Node* rhs) {
+ return graph()->NewNode(machine()->Int32Mul(), lhs, rhs);
+}
+
+
+Node* MachineOperatorReducer::TruncatingDiv(Node* dividend, int32_t
divisor) {
+ DCHECK_NE(std::numeric_limits<int32_t>::min(), divisor);
+ base::MagicNumbersForDivision<uint32_t> const mag =
+ base::SignedDivisionByConstant(bit_cast<uint32_t>(divisor));
+ Node* quotient = graph()->NewNode(machine()->Int32MulHigh(), dividend,
+ Uint32Constant(mag.multiplier));
+ if (divisor > 0 && bit_cast<int32_t>(mag.multiplier) < 0) {
+ quotient = Int32Add(quotient, dividend);
+ } else if (divisor < 0 && bit_cast<int32_t>(mag.multiplier) > 0) {
+ quotient = Int32Sub(quotient, dividend);
+ }
+ if (mag.shift) {
+ quotient = Word32Sar(quotient, mag.shift);
+ }
+ return Int32Add(quotient, Word32Shr(dividend, 31));
+}
// Perform constant folding and strength reduction on machine operators.
@@ -227,25 +276,8 @@
}
break;
}
- case IrOpcode::kInt32Div: {
- Int32BinopMatcher m(node);
- if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
- // TODO(turbofan): if (m.left().Is(0))
- // TODO(turbofan): if (m.right().IsPowerOf2())
- // TODO(turbofan): if (m.right().Is(0))
- // TODO(turbofan): if (m.LeftEqualsRight())
- if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K
- if (m.right().Is(-1)) return ReplaceInt32(-m.left().Value());
- return ReplaceInt32(m.left().Value() / m.right().Value());
- }
- if (m.right().Is(-1)) { // x / -1 => 0 - x
- node->set_op(machine()->Int32Sub());
- node->ReplaceInput(0, Int32Constant(0));
- node->ReplaceInput(1, m.left().node());
- return Changed(node);
- }
- break;
- }
+ case IrOpcode::kInt32Div:
+ return ReduceInt32Div(node);
case IrOpcode::kUint32Div: {
Uint32BinopMatcher m(node);
if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
@@ -499,7 +531,50 @@
}
-Reduction MachineOperatorReducer::ReduceInt32Mod(Node* const node) {
+Reduction MachineOperatorReducer::ReduceInt32Div(Node* node) {
+ Int32BinopMatcher m(node);
+ if (m.right().Is(0)) return Replace(m.right().node()); // x / 0 => 0
+ if (m.right().Is(1)) return Replace(m.left().node()); // x / 1 => x
+ // TODO(turbofan): if (m.left().Is(0))
+ // TODO(turbofan): if (m.LeftEqualsRight())
+ if (m.IsFoldable() && !m.right().Is(0)) { // K / K => K
+ if (m.right().Is(-1)) return ReplaceInt32(-m.left().Value());
+ return ReplaceInt32(m.left().Value() / m.right().Value());
+ }
+ if (m.right().Is(-1)) { // x / -1 => 0 - x
+ node->set_op(machine()->Int32Sub());
+ node->ReplaceInput(0, Int32Constant(0));
+ node->ReplaceInput(1, m.left().node());
+ return Changed(node);
+ }
+ if (m.right().HasValue()) {
+ int32_t const divisor = m.right().Value();
+ Node* const dividend = m.left().node();
+ Node* quotient = dividend;
+ if (base::bits::IsPowerOfTwo32(Abs(divisor))) {
+ uint32_t const shift = WhichPowerOf2Abs(divisor);
+ DCHECK_NE(0, shift);
+ if (shift > 1) {
+ quotient = Word32Sar(quotient, 31);
+ }
+ quotient = Int32Add(Word32Shr(quotient, 32u - shift), dividend);
+ quotient = Word32Sar(quotient, shift);
+ } else {
+ quotient = TruncatingDiv(quotient, Abs(divisor));
+ }
+ if (divisor < 0) {
+ node->set_op(machine()->Int32Sub());
+ node->ReplaceInput(0, Int32Constant(0));
+ node->ReplaceInput(1, quotient);
+ return Changed(node);
+ }
+ return Replace(quotient);
+ }
+ return NoChange();
+}
+
+
+Reduction MachineOperatorReducer::ReduceInt32Mod(Node* node) {
Int32BinopMatcher m(node);
if (m.right().Is(1)) return ReplaceInt32(0); // x % 1 => 0
if (m.right().Is(-1)) return ReplaceInt32(0); // x % -1 => 0
@@ -509,29 +584,35 @@
if (m.IsFoldable() && !m.right().Is(0)) { // K % K => K
return ReplaceInt32(m.left().Value() % m.right().Value());
}
- if (m.right().IsPowerOf2()) {
- int32_t const divisor = m.right().Value();
- Node* zero = Int32Constant(0);
- Node* mask = Int32Constant(divisor - 1);
- Node* dividend = m.left().node();
+ if (m.right().HasValue()) {
+ Node* const dividend = m.left().node();
+ int32_t const divisor = Abs(m.right().Value());
+ if (base::bits::IsPowerOfTwo32(divisor)) {
+ uint32_t const mask = divisor - 1;
+ Node* const zero = Int32Constant(0);
- Node* check = graph()->NewNode(machine()->Int32LessThan(), dividend,
zero);
- Node* branch =
- graph()->NewNode(common()->Branch(), check, graph()->start());
+ Node* check =
+ graph()->NewNode(machine()->Int32LessThan(), dividend, zero);
+ Node* branch =
+ graph()->NewNode(common()->Branch(), check, graph()->start());
- Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
- Node* neg = graph()->NewNode(
- machine()->Int32Sub(), zero,
- graph()->NewNode(
- machine()->Word32And(),
- graph()->NewNode(machine()->Int32Sub(), zero, dividend),
mask));
+ Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+ Node* neg = Int32Sub(zero, Word32And(Int32Sub(zero, dividend),
mask));
- Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
- Node* pos = graph()->NewNode(machine()->Word32And(), dividend, mask);
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+ Node* pos = Word32And(dividend, mask);
- Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
- Node* phi = graph()->NewNode(common()->Phi(kMachInt32, 2), neg, pos,
merge);
- return Replace(phi);
+ Node* merge = graph()->NewNode(common()->Merge(2), if_true,
if_false);
+ Node* phi =
+ graph()->NewNode(common()->Phi(kMachInt32, 2), neg, pos, merge);
+ return Replace(phi);
+ } else {
+ Node* quotient = TruncatingDiv(dividend, divisor);
+ node->set_op(machine()->Int32Sub());
+ DCHECK_EQ(dividend, node->InputAt(0));
+ node->ReplaceInput(1, Int32Mul(quotient, Int32Constant(divisor)));
+ return Changed(node);
+ }
}
return NoChange();
}
=======================================
--- /trunk/src/compiler/machine-operator-reducer.h Tue Oct 14 07:51:07 2014
UTC
+++ /trunk/src/compiler/machine-operator-reducer.h Wed Oct 15 13:35:30 2014
UTC
@@ -34,6 +34,14 @@
Node* Uint32Constant(uint32_t value) {
return Int32Constant(bit_cast<uint32_t>(value));
}
+ Node* Word32And(Node* lhs, uint32_t rhs);
+ Node* Word32Sar(Node* lhs, uint32_t rhs);
+ Node* Word32Shr(Node* lhs, uint32_t rhs);
+ Node* Int32Add(Node* lhs, Node* rhs);
+ Node* Int32Sub(Node* lhs, Node* rhs);
+ Node* Int32Mul(Node* lhs, Node* rhs);
+
+ Node* TruncatingDiv(Node* dividend, int32_t divisor);
Reduction ReplaceBool(bool value) { return ReplaceInt32(value ? 1 : 0); }
Reduction ReplaceFloat32(volatile float value) {
@@ -49,6 +57,7 @@
return Replace(Int64Constant(value));
}
+ Reduction ReduceInt32Div(Node* node);
Reduction ReduceInt32Mod(Node* node);
Reduction ReduceProjection(size_t index, Node* node);
=======================================
--- /trunk/src/compiler/machine-operator.cc Fri Oct 10 00:05:16 2014 UTC
+++ /trunk/src/compiler/machine-operator.cc Wed Oct 15 13:35:30 2014 UTC
@@ -75,6 +75,7 @@
V(Int32Sub, Operator::kNoProperties, 2,
1) \
V(Int32SubWithOverflow, Operator::kNoProperties, 2,
2) \
V(Int32Mul, Operator::kAssociative | Operator::kCommutative, 2,
1) \
+ V(Int32MulHigh, Operator::kAssociative | Operator::kCommutative, 2,
1) \
V(Int32Div, Operator::kNoProperties, 2,
1) \
V(Int32Mod, Operator::kNoProperties, 2,
1) \
V(Int32LessThan, Operator::kNoProperties, 2,
1) \
=======================================
--- /trunk/src/compiler/machine-operator.h Fri Oct 10 00:05:16 2014 UTC
+++ /trunk/src/compiler/machine-operator.h Wed Oct 15 13:35:30 2014 UTC
@@ -82,6 +82,7 @@
const Operator* Int32Sub();
const Operator* Int32SubWithOverflow();
const Operator* Int32Mul();
+ const Operator* Int32MulHigh();
const Operator* Int32Div();
const Operator* Int32Mod();
const Operator* Int32LessThan();
=======================================
--- /trunk/src/compiler/mips/code-generator-mips.cc Wed Oct 15 00:05:09
2014 UTC
+++ /trunk/src/compiler/mips/code-generator-mips.cc Wed Oct 15 13:35:30
2014 UTC
@@ -185,6 +185,9 @@
case kMipsMul:
__ Mul(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
+ case kMipsMulHigh:
+ __ Mulh(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
+ break;
case kMipsDiv:
__ Div(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;
=======================================
--- /trunk/src/compiler/mips/instruction-codes-mips.h Fri Oct 3 00:04:58
2014 UTC
+++ /trunk/src/compiler/mips/instruction-codes-mips.h Wed Oct 15 13:35:30
2014 UTC
@@ -17,6 +17,7 @@
V(MipsSub) \
V(MipsSubOvf) \
V(MipsMul) \
+ V(MipsMulHigh) \
V(MipsDiv) \
V(MipsDivU) \
V(MipsMod) \
=======================================
--- /trunk/src/compiler/mips/instruction-selector-mips.cc Tue Oct 14
07:51:07 2014 UTC
+++ /trunk/src/compiler/mips/instruction-selector-mips.cc Wed Oct 15
13:35:30 2014 UTC
@@ -298,6 +298,13 @@
Emit(kMipsMul, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
g.UseRegister(m.right().node()));
}
+
+
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ MipsOperandGenerator g(this);
+ Emit(kMipsMulHigh, g.DefineAsRegister(node),
g.UseRegister(node->InputAt(0)),
+ g.UseRegister(node->InputAt(1)));
+}
void InstructionSelector::VisitInt32Div(Node* node) {
=======================================
--- /trunk/src/compiler/opcodes.h Thu Oct 2 00:05:29 2014 UTC
+++ /trunk/src/compiler/opcodes.h Wed Oct 15 13:35:30 2014 UTC
@@ -185,6 +185,7 @@
V(Int32Sub) \
V(Int32SubWithOverflow) \
V(Int32Mul) \
+ V(Int32MulHigh) \
V(Int32Div) \
V(Int32Mod) \
V(Int32LessThan) \
=======================================
--- /trunk/src/compiler/raw-machine-assembler.h Wed Oct 8 00:05:11 2014 UTC
+++ /trunk/src/compiler/raw-machine-assembler.h Wed Oct 15 13:35:30 2014 UTC
@@ -225,6 +225,9 @@
Node* Int32Mul(Node* a, Node* b) {
return NewNode(machine()->Int32Mul(), a, b);
}
+ Node* Int32MulHigh(Node* a, Node* b) {
+ return NewNode(machine()->Int32MulHigh(), a, b);
+ }
Node* Int32Div(Node* a, Node* b) {
return NewNode(machine()->Int32Div(), a, b);
}
=======================================
--- /trunk/src/compiler/scheduler.cc Tue Oct 14 07:51:07 2014 UTC
+++ /trunk/src/compiler/scheduler.cc Wed Oct 15 13:35:30 2014 UTC
@@ -181,6 +181,7 @@
data->is_connected_control_ = true;
}
}
+
void BuildBlocks(Node* node) {
switch (node->opcode()) {
@@ -395,6 +396,24 @@
Trace(" Use count of #%d:%s (used by #%d:%s)++ = %d\n", to->id(),
to->op()->mnemonic(), from->id(), from->op()->mnemonic(),
scheduler_->GetData(to)->unscheduled_count_);
+ if (OperatorProperties::IsBasicBlockBegin(to->op()) &&
+ (from->opcode() == IrOpcode::kEffectPhi ||
+ from->opcode() == IrOpcode::kPhi) &&
+ scheduler_->GetData(to)->is_floating_control_ &&
+ !scheduler_->GetData(to)->is_connected_control_) {
+ for (InputIter i = from->inputs().begin(); i !=
from->inputs().end();
+ ++i) {
+ if (!NodeProperties::IsControlEdge(i.edge())) {
+ ++(scheduler_->GetData(*i)->unscheduled_count_);
+ Trace(
+ " Use count of #%d:%s (additional dependency of #%d:%s)++
= "
+ "%d\n",
+ (*i)->id(), (*i)->op()->mnemonic(), to->id(),
+ to->op()->mnemonic(),
+ scheduler_->GetData(*i)->unscheduled_count_);
+ }
+ }
+ }
}
}
@@ -505,6 +524,7 @@
if (schedule_->IsScheduled(node)) {
return GenericGraphVisit::CONTINUE;
}
+
Scheduler::SchedulerData* data = scheduler_->GetData(node);
DCHECK_EQ(Scheduler::kSchedulable, data->placement_);
@@ -607,6 +627,29 @@
}
}
}
+
+ for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) {
+ Node* use = *i;
+ if (use->opcode() == IrOpcode::kPhi ||
+ use->opcode() == IrOpcode::kEffectPhi) {
+ Node* control = NodeProperties::GetControlInput(use);
+ Scheduler::SchedulerData* data = scheduler_->GetData(control);
+ if (data->is_floating_control_ && !data->is_connected_control_) {
+ --data->unscheduled_count_;
+ if (FLAG_trace_turbo_scheduler) {
+ Trace(
+ " Use count for #%d:%s (additional dependency of
#%d:%s)-- = "
+ "%d\n",
+ (*i)->id(), (*i)->op()->mnemonic(), node->id(),
+ node->op()->mnemonic(), data->unscheduled_count_);
+ if (data->unscheduled_count_ == 0) {
+ Trace(" newly eligible #%d:%s\n", (*i)->id(),
+ (*i)->op()->mnemonic());
+ }
+ }
+ }
+ }
+ }
}
Scheduler* scheduler_;
@@ -665,16 +708,14 @@
// TODO(titzer): we place at most one floating control structure per
// basic block because scheduling currently can interleave phis from
// one subgraph with the merges from another subgraph.
- bool one_placed = false;
for (size_t j = 0; j < block->NodeCount(); j++) {
Node* node = block->NodeAt(block->NodeCount() - 1 - j);
SchedulerData* data = GetData(node);
- if (data->is_floating_control_ && !data->is_connected_control_ &&
- !one_placed) {
+ if (data->is_floating_control_ && !data->is_connected_control_) {
Trace(" Floating control #%d:%s was scheduled in B%d\n",
node->id(),
node->op()->mnemonic(), block->id().ToInt());
ConnectFloatingControlSubgraph(block, node);
- one_placed = true;
+ break;
}
}
}
=======================================
--- /trunk/src/compiler/typer.cc Thu Oct 9 00:05:16 2014 UTC
+++ /trunk/src/compiler/typer.cc Wed Oct 15 13:35:30 2014 UTC
@@ -17,6 +17,27 @@
Typer::Typer(Zone* zone) : zone_(zone) {
Factory* f = zone->isolate()->factory();
+ Handle<Object> zero = f->NewNumber(0);
+ Handle<Object> one = f->NewNumber(1);
+ Handle<Object> positive_infinity = f->NewNumber(+V8_INFINITY);
+ Handle<Object> negative_infinity = f->NewNumber(-V8_INFINITY);
+
+ negative_signed32 = Type::Union(
+ Type::SignedSmall(), Type::OtherSigned32(), zone);
+ non_negative_signed32 = Type::Union(
+ Type::UnsignedSmall(), Type::OtherUnsigned31(), zone);
+ undefined_or_null = Type::Union(Type::Undefined(), Type::Null(), zone);
+ singleton_false = Type::Constant(f->false_value(), zone);
+ singleton_true = Type::Constant(f->true_value(), zone);
+ singleton_zero = Type::Range(zero, zero, zone);
+ singleton_one = Type::Range(one, one, zone);
+ zero_or_one = Type::Union(singleton_zero, singleton_one, zone);
+ zeroish = Type::Union(
+ singleton_zero, Type::Union(Type::NaN(), Type::MinusZero(), zone),
zone);
+ falsish = Type::Union(Type::Undetectable(),
+ Type::Union(zeroish, undefined_or_null, zone), zone);
+ integer = Type::Range(negative_infinity, positive_infinity, zone);
+
Type* number = Type::Number();
Type* signed32 = Type::Signed32();
Type* unsigned32 = Type::Unsigned32();
@@ -24,8 +45,7 @@
Type* object = Type::Object();
Type* undefined = Type::Undefined();
Type* weakint = Type::Union(
- Type::Range(f->NewNumber(-V8_INFINITY), f->NewNumber(+V8_INFINITY),
zone),
- Type::Union(Type::NaN(), Type::MinusZero(), zone), zone);
+ integer, Type::Union(Type::NaN(), Type::MinusZero(), zone), zone);
number_fun0_ = Type::Function(number, zone);
number_fun1_ = Type::Function(number, number, zone);
@@ -35,19 +55,27 @@
random_fun_ = Type::Function(Type::Union(
Type::UnsignedSmall(), Type::OtherNumber(), zone), zone);
+ Type* int8 = Type::Intersect(
+ Type::Range(f->NewNumber(-0x7F), f->NewNumber(0x7F-1), zone),
+ Type::UntaggedInt8(), zone);
+ Type* int16 = Type::Intersect(
+ Type::Range(f->NewNumber(-0x7FFF), f->NewNumber(0x7FFF-1), zone),
+ Type::UntaggedInt16(), zone);
+ Type* uint8 = Type::Intersect(
+ Type::Range(zero, f->NewNumber(0xFF-1), zone),
+ Type::UntaggedInt8(), zone);
+ Type* uint16 = Type::Intersect(
+ Type::Range(zero, f->NewNumber(0xFFFF-1), zone),
+ Type::UntaggedInt16(), zone);
#define NATIVE_TYPE(sem, rep) \
- Type::Intersect(Type::sem(zone), Type::rep(zone), zone)
- // TODO(rossberg): Use range types for more precision, once we have them.
- Type* int8 = NATIVE_TYPE(SignedSmall, UntaggedInt8);
- Type* int16 = NATIVE_TYPE(SignedSmall, UntaggedInt16);
+ Type::Intersect(Type::sem(), Type::rep(), zone)
Type* int32 = NATIVE_TYPE(Signed32, UntaggedInt32);
- Type* uint8 = NATIVE_TYPE(UnsignedSmall, UntaggedInt8);
- Type* uint16 = NATIVE_TYPE(UnsignedSmall, UntaggedInt16);
Type* uint32 = NATIVE_TYPE(Unsigned32, UntaggedInt32);
Type* float32 = NATIVE_TYPE(Number, UntaggedFloat32);
Type* float64 = NATIVE_TYPE(Number, UntaggedFloat64);
#undef NATIVE_TYPE
+
Type* buffer = Type::Buffer(zone);
Type* int8_array = Type::Array(int8, zone);
Type* int16_array = Type::Array(int16, zone);
@@ -79,9 +107,21 @@
Bounds TypeNode(Node* node) {
switch (node->opcode()) {
+#define DECLARE_CASE(x) \
+ case IrOpcode::k##x: return TypeBinaryOp(node, x##Typer);
+ JS_SIMPLE_BINOP_LIST(DECLARE_CASE)
+#undef DECLARE_CASE
+
#define DECLARE_CASE(x) case IrOpcode::k##x: return Type##x(node);
DECLARE_CASE(Start)
- VALUE_OP_LIST(DECLARE_CASE)
+ // VALUE_OP_LIST without JS_SIMPLE_BINOP_LIST:
+ COMMON_OP_LIST(DECLARE_CASE)
+ SIMPLIFIED_OP_LIST(DECLARE_CASE)
+ MACHINE_OP_LIST(DECLARE_CASE)
+ JS_SIMPLE_UNOP_LIST(DECLARE_CASE)
+ JS_OBJECT_OP_LIST(DECLARE_CASE)
+ JS_CONTEXT_OP_LIST(DECLARE_CASE)
+ JS_OTHER_OP_LIST(DECLARE_CASE)
#undef DECLARE_CASE
#define DECLARE_CASE(x) case IrOpcode::k##x:
@@ -102,11 +142,11 @@
VALUE_OP_LIST(DECLARE_METHOD)
#undef DECLARE_METHOD
- Bounds OperandType(Node* node, int i) {
+ static Bounds OperandType(Node* node, int i) {
return NodeProperties::GetBounds(NodeProperties::GetValueInput(node,
i));
}
- Type* ContextType(Node* node) {
+ static Type* ContextType(Node* node) {
Bounds result =
NodeProperties::GetBounds(NodeProperties::GetContextInput(node));
DCHECK(result.upper->Maybe(Type::Internal()));
@@ -122,6 +162,37 @@
private:
Typer* typer_;
MaybeHandle<Context> context_;
+
+ typedef Type* (*UnaryTyperFun)(Type*, Typer* t);
+ typedef Type* (*BinaryTyperFun)(Type*, Type*, Typer* t);
+
+ Bounds TypeUnaryOp(Node* node, UnaryTyperFun);
+ Bounds TypeBinaryOp(Node* node, BinaryTyperFun);
+
+ static Type* Invert(Type*, Typer*);
+ static Type* FalsifyUndefined(Type*, Typer*);
+
+ static Type* ToPrimitive(Type*, Typer*);
+ static Type* ToBoolean(Type*, Typer*);
+ static Type* ToNumber(Type*, Typer*);
+ static Type* ToString(Type*, Typer*);
+ static Type* NumberToInt32(Type*, Typer*);
+ static Type* NumberToUint32(Type*, Typer*);
+
+ static Type* JSAddRanger(Type::RangeType*, Type::RangeType*, Typer*);
+ static Type* JSSubtractRanger(Type::RangeType*, Type::RangeType*,
Typer*);
+ static Type* JSMultiplyRanger(Type::RangeType*, Type::RangeType*,
Typer*);
+ static Type* JSDivideRanger(Type::RangeType*, Type::RangeType*, Typer*);
+
+ static Type* JSCompareTyper(Type*, Type*, Typer*);
+
+#define DECLARE_METHOD(x) static Type* x##Typer(Type*, Type*, Typer*);
+ JS_SIMPLE_BINOP_LIST(DECLARE_METHOD)
+#undef DECLARE_METHOD
+
+ static Type* JSUnaryNotTyper(Type*, Typer*);
+ static Type* JSLoadPropertyTyper(Type*, Type*, Typer*);
+ static Type* JSCallFunctionTyper(Type*, Typer*);
};
@@ -237,49 +308,160 @@
//
-----------------------------------------------------------------------------
+// Helper functions that lift a function f on types to a function on
bounds,
+// and uses that to type the given node. Note that f is never called with
None
+// as an argument.
+
+
+Bounds Typer::Visitor::TypeUnaryOp(Node* node, UnaryTyperFun f) {
+ Bounds input = OperandType(node, 0);
+ Type* upper = input.upper->Is(Type::None())
+ ? Type::None()
+ : f(input.upper, typer_);
+ Type* lower = input.lower->Is(Type::None())
+ ? Type::None()
+ : (input.lower == input.upper || upper->IsConstant())
+ ? upper // TODO(neis): Extend this to Range(x,x), NaN,
MinusZero, ...?
+ : f(input.lower, typer_);
+ // TODO(neis): Figure out what to do with lower bound.
+ return Bounds(lower, upper);
+}
+
+
+Bounds Typer::Visitor::TypeBinaryOp(Node* node, BinaryTyperFun f) {
+ Bounds left = OperandType(node, 0);
+ Bounds right = OperandType(node, 1);
+ Type* upper = left.upper->Is(Type::None()) ||
right.upper->Is(Type::None())
+ ? Type::None()
+ : f(left.upper, right.upper, typer_);
+ Type* lower = left.lower->Is(Type::None()) ||
right.lower->Is(Type::None())
+ ? Type::None()
+ : ((left.lower == left.upper && right.lower == right.upper) ||
+ upper->IsConstant())
+ ? upper
+ : f(left.lower, right.lower, typer_);
+ // TODO(neis): Figure out what to do with lower bound.
+ return Bounds(lower, upper);
+}
+
+
+Type* Typer::Visitor::Invert(Type* type, Typer* t) {
+ if (type->Is(t->singleton_false)) return t->singleton_true;
+ if (type->Is(t->singleton_true)) return t->singleton_false;
+ return type;
+}
+
+
+Type* Typer::Visitor::FalsifyUndefined(Type* type, Typer* t) {
+ if (type->Is(Type::Undefined())) return t->singleton_false;
+ return type;
+}
+
+
+// Type conversion.
+
+
+Type* Typer::Visitor::ToPrimitive(Type* type, Typer* t) {
+ if (type->Is(Type::Primitive()) && !type->Maybe(Type::Receiver())) {
+ return type;
+ }
+ return Type::Primitive();
+}
+
+
+Type* Typer::Visitor::ToBoolean(Type* type, Typer* t) {
+ if (type->Is(Type::Boolean())) return type;
+ if (type->Is(t->falsish)) return t->singleton_false;
+ if (type->Is(Type::DetectableReceiver())) return t->singleton_true;
+ if (type->Is(Type::OrderedNumber()) && (type->Max() < 0 || 0 <
type->Min())) {
+ return t->singleton_true; // Ruled out nan, -0 and +0.
+ }
+ return Type::Boolean();
+}
+
+
+Type* Typer::Visitor::ToNumber(Type* type, Typer* t) {
+ if (type->Is(Type::Number())) return type;
+ if (type->Is(Type::Undefined())) return Type::NaN();
+ if (type->Is(t->singleton_false)) return t->singleton_zero;
+ if (type->Is(t->singleton_true)) return t->singleton_one;
+ if (type->Is(Type::Boolean())) return t->zero_or_one;
+ return Type::Number();
+}
+
+
+Type* Typer::Visitor::ToString(Type* type, Typer* t) {
+ if (type->Is(Type::String())) return type;
+ return Type::String();
+}
+
+
+Type* Typer::Visitor::NumberToInt32(Type* type, Typer* t) {
+ // TODO(neis): DCHECK(type->Is(Type::Number()));
+ if (type->Is(Type::Signed32())) return type;
+ if (type->Is(t->zeroish)) return t->singleton_zero;
+ return Type::Signed32();
+}
+
+
+Type* Typer::Visitor::NumberToUint32(Type* type, Typer* t) {
+ // TODO(neis): DCHECK(type->Is(Type::Number()));
+ if (type->Is(Type::Unsigned32())) return type;
+ if (type->Is(t->zeroish)) return t->singleton_zero;
+ return Type::Unsigned32();
+}
+
+
+//
-----------------------------------------------------------------------------
+
// Control operators.
+
Bounds Typer::Visitor::TypeStart(Node* node) {
- return Bounds(Type::Internal(zone()));
+ return Bounds(Type::Internal());
}
// Common operators.
+
Bounds Typer::Visitor::TypeParameter(Node* node) {
return Bounds::Unbounded(zone());
}
Bounds Typer::Visitor::TypeInt32Constant(Node* node) {
- // TODO(titzer): only call Type::Of() if the type is not already known.
- return Bounds(Type::Of(OpParameter<int32_t>(node), zone()));
+ Factory* f = zone()->isolate()->factory();
+ Handle<Object> number = f->NewNumber(OpParameter<int32_t>(node));
+ return Bounds(Type::Intersect(
+ Type::Range(number, number, zone()), Type::UntaggedInt32(), zone()));
}
Bounds Typer::Visitor::TypeInt64Constant(Node* node) {
- // TODO(titzer): only call Type::Of() if the type is not already known.
- return Bounds(
- Type::Of(static_cast<double>(OpParameter<int64_t>(node)), zone()));
+ return Bounds(Type::Internal()); // TODO(rossberg): Add int64 bitset
type?
}
Bounds Typer::Visitor::TypeFloat32Constant(Node* node) {
- // TODO(titzer): only call Type::Of() if the type is not already known.
- return Bounds(Type::Of(OpParameter<float>(node), zone()));
+ return Bounds(Type::Intersect(
+ Type::Of(OpParameter<float>(node), zone()),
+ Type::UntaggedFloat32(), zone()));
}
Bounds Typer::Visitor::TypeFloat64Constant(Node* node) {
- // TODO(titzer): only call Type::Of() if the type is not already known.
- return Bounds(Type::Of(OpParameter<double>(node), zone()));
+ return Bounds(Type::Intersect(
+ Type::Of(OpParameter<double>(node), zone()),
+ Type::UntaggedFloat64(), zone()));
}
Bounds Typer::Visitor::TypeNumberConstant(Node* node) {
- // TODO(titzer): only call Type::Of() if the type is not already known.
- return Bounds(Type::Of(OpParameter<double>(node), zone()));
+ Factory* f = zone()->isolate()->factory();
+ return Bounds(Type::Constant(
+ f->NewNumber(OpParameter<double>(node)), zone()));
}
@@ -289,7 +471,7 @@
Bounds Typer::Visitor::TypeExternalConstant(Node* node) {
- return Bounds(Type::Internal(zone()));
+ return Bounds(Type::Internal());
}
@@ -322,12 +504,12 @@
Bounds Typer::Visitor::TypeFrameState(Node* node) {
// TODO(rossberg): Ideally FrameState wouldn't have a value output.
- return Bounds(Type::Internal(zone()));
+ return Bounds(Type::Internal());
}
Bounds Typer::Visitor::TypeStateValues(Node* node) {
- return Bounds(Type::Internal(zone()));
+ return Bounds(Type::Internal());
}
@@ -344,159 +526,339 @@
// JS comparison operators.
-#define DEFINE_METHOD(x) \
- Bounds Typer::Visitor::Type##x(Node* node) { \
- return Bounds(Type::Boolean(zone())); \
+
+Type* Typer::Visitor::JSEqualTyper(Type* lhs, Type* rhs, Typer* t) {
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return
t->singleton_false;
+ if (lhs->Is(t->undefined_or_null) && rhs->Is(t->undefined_or_null)) {
+ return t->singleton_true;
+ }
+ if (lhs->Is(Type::Number()) && rhs->Is(Type::Number()) &&
+ (lhs->Max() < rhs->Min() || lhs->Min() > rhs->Max())) {
+ return t->singleton_false;
+ }
+ if (lhs->IsConstant() && rhs->Is(lhs)) {
+ // Types are equal and are inhabited only by a single semantic value,
+ // which is not nan due to the earlier check.
+ // TODO(neis): Extend this to Range(x,x), MinusZero, ...?
+ return t->singleton_true;
+ }
+ return Type::Boolean();
+}
+
+
+Type* Typer::Visitor::JSNotEqualTyper(Type* lhs, Type* rhs, Typer* t) {
+ return Invert(JSEqualTyper(lhs, rhs, t), t);
+}
+
+
+static Type* JSType(Type* type) {
+ if (type->Is(Type::Boolean())) return Type::Boolean();
+ if (type->Is(Type::String())) return Type::String();
+ if (type->Is(Type::Number())) return Type::Number();
+ if (type->Is(Type::Undefined())) return Type::Undefined();
+ if (type->Is(Type::Null())) return Type::Null();
+ if (type->Is(Type::Symbol())) return Type::Symbol();
+ if (type->Is(Type::Receiver())) return Type::Receiver(); // JS "Object"
+ return Type::Any();
+}
+
+
+Type* Typer::Visitor::JSStrictEqualTyper(Type* lhs, Type* rhs, Typer* t) {
+ if (!JSType(lhs)->Maybe(JSType(rhs))) return t->singleton_false;
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return
t->singleton_false;
+ if (lhs->Is(Type::Number()) && rhs->Is(Type::Number()) &&
+ (lhs->Max() < rhs->Min() || lhs->Min() > rhs->Max())) {
+ return t->singleton_false;
}
-JS_COMPARE_BINOP_LIST(DEFINE_METHOD)
-#undef DEFINE_METHOD
+ if (lhs->IsConstant() && rhs->Is(lhs)) {
+ // Types are equal and are inhabited only by a single semantic value,
+ // which is not nan due to the earlier check.
+ return t->singleton_true;
+ }
+ return Type::Boolean();
+}
+
+
+Type* Typer::Visitor::JSStrictNotEqualTyper(Type* lhs, Type* rhs, Typer*
t) {
+ return Invert(JSStrictEqualTyper(lhs, rhs, t), t);
+}
+
+
+// The EcmaScript specification defines the four relational comparison
operators
+// (<, <=, >=, >) with the help of a single abstract one. It behaves like
<
+// but returns undefined when the inputs cannot be compared.
+// We implement the typing analogously.
+Type* Typer::Visitor::JSCompareTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = ToPrimitive(lhs, t);
+ rhs = ToPrimitive(rhs, t);
+ if (lhs->Maybe(Type::String()) && rhs->Maybe(Type::String())) {
+ return Type::Boolean();
+ }
+ lhs = ToNumber(lhs, t);
+ rhs = ToNumber(rhs, t);
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return
Type::Undefined();
+ if (lhs->IsConstant() && rhs->Is(lhs)) {
+ // Types are equal and are inhabited only by a single semantic value,
+ // which is not NaN due to the previous check.
+ return t->singleton_false;
+ }
+ if (lhs->Min() >= rhs->Max()) return t->singleton_false;
+ if (lhs->Max() < rhs->Min() &&
+ !lhs->Maybe(Type::NaN()) && !rhs->Maybe(Type::NaN())) {
+ return t->singleton_true;
+ }
+ return Type::Boolean();
+}
+
+
+Type* Typer::Visitor::JSLessThanTyper(Type* lhs, Type* rhs, Typer* t) {
+ return FalsifyUndefined(JSCompareTyper(lhs, rhs, t), t);
+}
+
+
+Type* Typer::Visitor::JSGreaterThanTyper(Type* lhs, Type* rhs, Typer* t) {
+ return FalsifyUndefined(JSCompareTyper(rhs, lhs, t), t);
+}
+
+
+Type* Typer::Visitor::JSLessThanOrEqualTyper(Type* lhs, Type* rhs, Typer*
t) {
+ return FalsifyUndefined(Invert(JSCompareTyper(rhs, lhs, t), t), t);
+}
+
+
+Type* Typer::Visitor::JSGreaterThanOrEqualTyper(
+ Type* lhs, Type* rhs, Typer* t) {
+ return FalsifyUndefined(Invert(JSCompareTyper(lhs, rhs, t), t), t);
+}
// JS bitwise operators.
-Bounds Typer::Visitor::TypeJSBitwiseOr(Node* node) {
- Bounds left = OperandType(node, 0);
- Bounds right = OperandType(node, 1);
- Type* upper = Type::Union(left.upper, right.upper, zone());
- if (!upper->Is(Type::Signed32())) upper = Type::Signed32(zone());
- Type* lower = Type::Intersect(Type::SignedSmall(zone()), upper, zone());
- return Bounds(lower, upper);
+
+Type* Typer::Visitor::JSBitwiseOrTyper(Type* lhs, Type* rhs, Typer* t) {
+ Factory* f = t->zone()->isolate()->factory();
+ lhs = NumberToInt32(ToNumber(lhs, t), t);
+ rhs = NumberToInt32(ToNumber(rhs, t), t);
+ double lmin = lhs->Min();
+ double rmin = rhs->Min();
+ double lmax = lhs->Max();
+ double rmax = rhs->Max();
+ // Or-ing any two values results in a value no smaller than their
minimum.
+ // Even no smaller than their maximum if both values are non-negative.
+ Handle<Object> min = f->NewNumber(
+ lmin >= 0 && rmin >= 0 ? std::max(lmin, rmin) : std::min(lmin,
rmin));
+ if (lmax < 0 || rmax < 0) {
+ // Or-ing two values of which at least one is negative results in a
negative
+ // value.
+ Handle<Object> max = f->NewNumber(-1);
+ return Type::Range(min, max, t->zone());
+ }
+ Handle<Object> max = f->NewNumber(Type::Signed32()->Max());
+ return Type::Range(min, max, t->zone());
+ // TODO(neis): Be precise for singleton inputs, here and elsewhere.
}
-Bounds Typer::Visitor::TypeJSBitwiseAnd(Node* node) {
- Bounds left = OperandType(node, 0);
- Bounds right = OperandType(node, 1);
- Type* upper = Type::Union(left.upper, right.upper, zone());
- if (!upper->Is(Type::Signed32())) upper = Type::Signed32(zone());
- Type* lower = Type::Intersect(Type::SignedSmall(zone()), upper, zone());
- return Bounds(lower, upper);
+Type* Typer::Visitor::JSBitwiseAndTyper(Type* lhs, Type* rhs, Typer* t) {
+ Factory* f = t->zone()->isolate()->factory();
+ lhs = NumberToInt32(ToNumber(lhs, t), t);
+ rhs = NumberToInt32(ToNumber(rhs, t), t);
+ double lmin = lhs->Min();
+ double rmin = rhs->Min();
+ double lmax = lhs->Max();
+ double rmax = rhs->Max();
+ // And-ing any two values results in a value no larger than their
maximum.
+ // Even no larger than their minimum if both values are non-negative.
+ Handle<Object> max = f->NewNumber(
+ lmin >= 0 && rmin >= 0 ? std::min(lmax, rmax) : std::max(lmax,
rmax));
+ if (lmin >= 0 || rmin >= 0) {
+ // And-ing two values of which at least one is non-negative results in
a
+ // non-negative value.
+ Handle<Object> min = f->NewNumber(0);
+ return Type::Range(min, max, t->zone());
+ }
+ Handle<Object> min = f->NewNumber(Type::Signed32()->Min());
+ return Type::Range(min, max, t->zone());
}
-Bounds Typer::Visitor::TypeJSBitwiseXor(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Signed32(zone()));
+Type* Typer::Visitor::JSBitwiseXorTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = NumberToInt32(ToNumber(lhs, t), t);
+ rhs = NumberToInt32(ToNumber(rhs, t), t);
+ double lmin = lhs->Min();
+ double rmin = rhs->Min();
+ double lmax = lhs->Max();
+ double rmax = rhs->Max();
+ if ((lmin >= 0 && rmin >= 0) || (lmax < 0 && rmax < 0)) {
+ // Xor-ing negative or non-negative values results in a non-negative
value.
+ return t->non_negative_signed32;
+ }
+ if ((lmax < 0 && rmin >= 0) || (lmin >= 0 && rmax < 0)) {
+ // Xor-ing a negative and a non-negative value results in a negative
value.
+ return t->negative_signed32;
+ }
+ return Type::Signed32();
}
-Bounds Typer::Visitor::TypeJSShiftLeft(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Signed32(zone()));
+Type* Typer::Visitor::JSShiftLeftTyper(Type* lhs, Type* rhs, Typer* t) {
+ return Type::Signed32();
}
-Bounds Typer::Visitor::TypeJSShiftRight(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Signed32(zone()));
+Type* Typer::Visitor::JSShiftRightTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = NumberToInt32(ToNumber(lhs, t), t);
+ Factory* f = t->zone()->isolate()->factory();
+ if (lhs->Min() >= 0) {
+ // Right-shifting a non-negative value cannot make it negative, nor
larger.
+ Handle<Object> min = f->NewNumber(0);
+ Handle<Object> max = f->NewNumber(lhs->Max());
+ return Type::Range(min, max, t->zone());
+ }
+ if (lhs->Max() < 0) {
+ // Right-shifting a negative value cannot make it non-negative, nor
smaller.
+ Handle<Object> min = f->NewNumber(lhs->Min());
+ Handle<Object> max = f->NewNumber(-1);
+ return Type::Range(min, max, t->zone());
+ }
+ return Type::Signed32();
}
-Bounds Typer::Visitor::TypeJSShiftRightLogical(Node* node) {
- return Bounds(Type::UnsignedSmall(zone()), Type::Unsigned32(zone()));
+Type* Typer::Visitor::JSShiftRightLogicalTyper(Type* lhs, Type* rhs,
Typer* t) {
+ lhs = NumberToUint32(ToNumber(lhs, t), t);
+ Factory* f = t->zone()->isolate()->factory();
+ // Logical right-shifting any value cannot make it larger.
+ Handle<Object> min = f->NewNumber(0);
+ Handle<Object> max = f->NewNumber(lhs->Max());
+ return Type::Range(min, max, t->zone());
}
// JS arithmetic operators.
-Bounds Typer::Visitor::TypeJSAdd(Node* node) {
- Bounds left = OperandType(node, 0);
- Bounds right = OperandType(node, 1);
- Type* lower =
- left.lower->Is(Type::None()) || right.lower->Is(Type::None()) ?
- Type::None(zone()) :
- left.lower->Is(Type::Number()) && right.lower->Is(Type::Number()) ?
- Type::SignedSmall(zone()) :
- left.lower->Is(Type::String()) || right.lower->Is(Type::String()) ?
- Type::String(zone()) : Type::None(zone());
- Type* upper =
- left.upper->Is(Type::None()) && right.upper->Is(Type::None()) ?
- Type::None(zone()) :
- left.upper->Is(Type::Number()) && right.upper->Is(Type::Number()) ?
- Type::Number(zone()) :
- left.upper->Is(Type::String()) || right.upper->Is(Type::String()) ?
- Type::String(zone()) : Type::NumberOrString(zone());
- return Bounds(lower, upper);
+
+Type* Typer::Visitor::JSAddTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = ToPrimitive(lhs, t);
+ rhs = ToPrimitive(rhs, t);
+ if (lhs->Maybe(Type::String()) || rhs->Maybe(Type::String())) {
+ if (lhs->Is(Type::String()) || rhs->Is(Type::String())) {
+ return Type::String();
+ } else {
+ return Type::NumberOrString();
+ }
+ }
+ lhs = ToNumber(lhs, t);
+ rhs = ToNumber(rhs, t);
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
+ // TODO(neis): Do some analysis.
+ // TODO(neis): Deal with numeric bitsets here and elsewhere.
+ return Type::Number();
}
-Bounds Typer::Visitor::TypeJSSubtract(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Number(zone()));
+Type* Typer::Visitor::JSSubtractTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = ToNumber(lhs, t);
+ rhs = ToNumber(rhs, t);
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
+ // TODO(neis): Do some analysis.
+ return Type::Number();
}
-Bounds Typer::Visitor::TypeJSMultiply(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Number(zone()));
+Type* Typer::Visitor::JSMultiplyTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = ToNumber(lhs, t);
+ rhs = ToNumber(rhs, t);
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
+ // TODO(neis): Do some analysis.
+ return Type::Number();
}
-Bounds Typer::Visitor::TypeJSDivide(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Number(zone()));
+Type* Typer::Visitor::JSDivideTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = ToNumber(lhs, t);
+ rhs = ToNumber(rhs, t);
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
+ // TODO(neis): Do some analysis.
+ return Type::Number();
}
-Bounds Typer::Visitor::TypeJSModulus(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Number(zone()));
+Type* Typer::Visitor::JSModulusTyper(Type* lhs, Type* rhs, Typer* t) {
+ lhs = ToNumber(lhs, t);
+ rhs = ToNumber(rhs, t);
+ if (lhs->Is(Type::NaN()) || rhs->Is(Type::NaN())) return Type::NaN();
+ // TODO(neis): Do some analysis.
+ return Type::Number();
}
// JS unary operators.
+
+Type* Typer::Visitor::JSUnaryNotTyper(Type* type, Typer* t) {
+ return Invert(ToBoolean(type, t), t);
+}
+
+
Bounds Typer::Visitor::TypeJSUnaryNot(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return TypeUnaryOp(node, JSUnaryNotTyper);
}
Bounds Typer::Visitor::TypeJSTypeOf(Node* node) {
- return Bounds(Type::InternalizedString(zone()));
+ return Bounds(Type::InternalizedString());
}
// JS conversion operators.
+
Bounds Typer::Visitor::TypeJSToBoolean(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return TypeUnaryOp(node, ToBoolean);
}
Bounds Typer::Visitor::TypeJSToNumber(Node* node) {
- return Bounds(Type::SignedSmall(zone()), Type::Number(zone()));
+ return TypeUnaryOp(node, ToNumber);
}
Bounds Typer::Visitor::TypeJSToString(Node* node) {
- return Bounds(Type::None(zone()), Type::String(zone()));
+ return TypeUnaryOp(node, ToString);
}
Bounds Typer::Visitor::TypeJSToName(Node* node) {
- return Bounds(Type::None(zone()), Type::Name(zone()));
+ return Bounds(Type::None(), Type::Name());
}
Bounds Typer::Visitor::TypeJSToObject(Node* node) {
- return Bounds(Type::None(zone()), Type::Receiver(zone()));
+ return Bounds(Type::None(), Type::Receiver());
}
// JS object operators.
+
Bounds Typer::Visitor::TypeJSCreate(Node* node) {
- return Bounds(Type::None(zone()), Type::Object(zone()));
+ return Bounds(Type::None(), Type::Object());
}
-Bounds Typer::Visitor::TypeJSLoadProperty(Node* node) {
- Bounds object = OperandType(node, 0);
- Bounds name = OperandType(node, 1);
- Bounds result = Bounds::Unbounded(zone());
+Type* Typer::Visitor::JSLoadPropertyTyper(Type* object, Type* name, Typer*
t) {
// TODO(rossberg): Use range types and sized array types to filter
undefined.
- if (object.lower->IsArray() && name.lower->Is(Type::Integral32())) {
- result.lower = Type::Union(
- object.lower->AsArray()->Element(), Type::Undefined(zone()),
zone());
+ if (object->IsArray() && name->Is(Type::Integral32())) {
+ return Type::Union(
+ object->AsArray()->Element(), Type::Undefined(), t->zone());
}
- if (object.upper->IsArray() && name.upper->Is(Type::Integral32())) {
- result.upper = Type::Union(
- object.upper->AsArray()->Element(), Type::Undefined(zone()),
zone());
- }
- return result;
+ return Type::Any();
+}
+
+
+Bounds Typer::Visitor::TypeJSLoadProperty(Node* node) {
+ return TypeBinaryOp(node, JSLoadPropertyTyper);
}
@@ -518,22 +880,23 @@
Bounds Typer::Visitor::TypeJSDeleteProperty(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeJSHasProperty(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeJSInstanceOf(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
// JS context operators.
+
Bounds Typer::Visitor::TypeJSLoadContext(Node* node) {
Bounds outer = OperandType(node, 0);
DCHECK(outer.upper->Maybe(Type::Internal()));
@@ -568,7 +931,7 @@
handle(context.ToHandleChecked()->get(static_cast<int>(access.index())),
isolate());
Type* lower = TypeConstant(value);
- return Bounds(lower, Type::Any(zone()));
+ return Bounds(lower, Type::Any());
}
}
@@ -618,23 +981,24 @@
// JS other operators.
+
Bounds Typer::Visitor::TypeJSYield(Node* node) {
return Bounds::Unbounded(zone());
}
Bounds Typer::Visitor::TypeJSCallConstruct(Node* node) {
- return Bounds(Type::None(zone()), Type::Receiver(zone()));
+ return Bounds(Type::None(), Type::Receiver());
+}
+
+
+Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
+ return fun->IsFunction() ? fun->AsFunction()->Result() : Type::Any();
}
Bounds Typer::Visitor::TypeJSCallFunction(Node* node) {
- Bounds fun = OperandType(node, 0);
- Type* lower = fun.lower->IsFunction()
- ? fun.lower->AsFunction()->Result() : Type::None(zone());
- Type* upper = fun.upper->IsFunction()
- ? fun.upper->AsFunction()->Result() : Type::Any(zone());
- return Bounds(lower, upper);
+ return TypeUnaryOp(node, JSCallFunctionTyper); // We ignore argument
types.
}
@@ -650,143 +1014,172 @@
// Simplified operators.
+
Bounds Typer::Visitor::TypeBooleanNot(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeBooleanToNumber(Node* node) {
- return Bounds(Type::Number(zone()));
+ return Bounds(Type::Number());
}
Bounds Typer::Visitor::TypeNumberEqual(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeNumberLessThan(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeNumberLessThanOrEqual(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeNumberAdd(Node* node) {
- return Bounds(Type::Number(zone()));
+ return Bounds(Type::Number());
}
Bounds Typer::Visitor::TypeNumberSubtract(Node* node) {
- return Bounds(Type::Number(zone()));
+ return Bounds(Type::Number());
}
Bounds Typer::Visitor::TypeNumberMultiply(Node* node) {
- return Bounds(Type::Number(zone()));
+ return Bounds(Type::Number());
}
Bounds Typer::Visitor::TypeNumberDivide(Node* node) {
- return Bounds(Type::Number(zone()));
+ return Bounds(Type::Number());
}
Bounds Typer::Visitor::TypeNumberModulus(Node* node) {
- return Bounds(Type::Number(zone()));
+ return Bounds(Type::Number());
}
Bounds Typer::Visitor::TypeNumberToInt32(Node* node) {
- Bounds arg = OperandType(node, 0);
- Type* s32 = Type::Signed32(zone());
- Type* lower = arg.lower->Is(s32) ? arg.lower : s32;
- Type* upper = arg.upper->Is(s32) ? arg.upper : s32;
- return Bounds(lower, upper);
+ return TypeUnaryOp(node, NumberToInt32);
}
Bounds Typer::Visitor::TypeNumberToUint32(Node* node) {
- Bounds arg = OperandType(node, 0);
- Type* u32 = Type::Unsigned32(zone());
- Type* lower = arg.lower->Is(u32) ? arg.lower : u32;
- Type* upper = arg.upper->Is(u32) ? arg.upper : u32;
- return Bounds(lower, upper);
+ return TypeUnaryOp(node, NumberToUint32);
}
Bounds Typer::Visitor::TypeReferenceEqual(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeStringEqual(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeStringLessThan(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeStringLessThanOrEqual(Node* node) {
- return Bounds(Type::Boolean(zone()));
+ return Bounds(Type::Boolean());
}
Bounds Typer::Visitor::TypeStringAdd(Node* node) {
- return Bounds(Type::String(zone()));
+ return Bounds(Type::String());
+}
+
+
+static Type* ChangeRepresentation(Type* type, Type* rep, Zone* zone) {
+ // TODO(neis): Enable when expressible.
+ /*
+ return Type::Union(
+ Type::Intersect(type, Type::Semantic(), zone),
+ Type::Intersect(rep, Type::Representation(), zone), zone);
+ */
+ return type;
}
Bounds Typer::Visitor::TypeChangeTaggedToInt32(Node* node) {
- // TODO(titzer): type is type of input, representation is Word32.
- return Bounds(Type::Integral32());
+ Bounds arg = OperandType(node, 0);
+ // TODO(neis): DCHECK(arg.upper->Is(Type::Signed32()));
+ return Bounds(
+ ChangeRepresentation(arg.lower, Type::UntaggedInt32(), zone()),
+ ChangeRepresentation(arg.upper, Type::UntaggedInt32(), zone()));
}
Bounds Typer::Visitor::TypeChangeTaggedToUint32(Node* node) {
- return Bounds(Type::Integral32()); // TODO(titzer): add appropriate rep
+ Bounds arg = OperandType(node, 0);
+ // TODO(neis): DCHECK(arg.upper->Is(Type::Unsigned32()));
+ return Bounds(
+ ChangeRepresentation(arg.lower, Type::UntaggedInt32(), zone()),
+ ChangeRepresentation(arg.upper, Type::UntaggedInt32(), zone()));
}
Bounds Typer::Visitor::TypeChangeTaggedToFloat64(Node* node) {
- // TODO(titzer): type is type of input, representation is Float64.
- return Bounds(Type::Number());
+ Bounds arg = OperandType(node, 0);
+ // TODO(neis): DCHECK(arg.upper->Is(Type::Number()));
+ return Bounds(
+ ChangeRepresentation(arg.lower, Type::UntaggedFloat64(), zone()),
+ ChangeRepresentation(arg.upper, Type::UntaggedFloat64(), zone()));
}
Bounds Typer::Visitor::TypeChangeInt32ToTagged(Node* node) {
- // TODO(titzer): type is type of input, representation is Tagged.
- return Bounds(Type::Integral32());
+ Bounds arg = OperandType(node, 0);
+ // TODO(neis): DCHECK(arg.upper->Is(Type::Signed32()));
+ return Bounds(
+ ChangeRepresentation(arg.lower, Type::Tagged(), zone()),
+ ChangeRepresentation(arg.upper, Type::Tagged(), zone()));
}
Bounds Typer::Visitor::TypeChangeUint32ToTagged(Node* node) {
- // TODO(titzer): type is type of input, representation is Tagged.
- return Bounds(Type::Unsigned32());
+ Bounds arg = OperandType(node, 0);
+ // TODO(neis): DCHECK(arg.upper->Is(Type::Unsigned32()));
***The diff for this file has been truncated for email.***
=======================================
--- /trunk/src/compiler/typer.h Fri Sep 26 00:05:23 2014 UTC
+++ /trunk/src/compiler/typer.h Wed Oct 15 13:35:30 2014 UTC
@@ -36,6 +36,17 @@
class WidenVisitor;
Zone* zone_;
+ Type* negative_signed32;
+ Type* non_negative_signed32;
+ Type* undefined_or_null;
+ Type* singleton_false;
+ Type* singleton_true;
+ Type* singleton_zero;
+ Type* singleton_one;
+ Type* zero_or_one;
+ Type* zeroish;
+ Type* falsish;
+ Type* integer;
Type* number_fun0_;
Type* number_fun1_;
Type* number_fun2_;
=======================================
--- /trunk/src/compiler/verifier.cc Wed Oct 1 00:05:35 2014 UTC
+++ /trunk/src/compiler/verifier.cc Wed Oct 15 13:35:30 2014 UTC
@@ -269,6 +269,19 @@
}
return false;
}
+
+
+static bool Dominates(Schedule* schedule, Node* dominator, Node*
dominatee) {
+ BasicBlock* dom = schedule->block(dominator);
+ BasicBlock* sub = schedule->block(dominatee);
+ while (sub != NULL) {
+ if (sub == dom) {
+ return true;
+ }
+ sub = sub->dominator();
+ }
+ return false;
+}
static void CheckInputsDominate(Schedule* schedule, BasicBlock* block,
@@ -289,6 +302,19 @@
input->id(), input->op()->mnemonic());
}
}
+ // Ensure that nodes are dominated by their control inputs;
+ // kEnd is an exception, as unreachable blocks resulting from kMerge
+ // are not in the RPO.
+ if (OperatorProperties::GetControlInputCount(node->op()) == 1 &&
+ node->opcode() != IrOpcode::kEnd) {
+ Node* ctl = NodeProperties::GetControlInput(node);
+ if (!Dominates(schedule, ctl, node)) {
+ V8_Fatal(__FILE__, __LINE__,
+ "Node #%d:%s in B%d is not dominated by control input
#%d:%s",
+ node->id(), node->op()->mnemonic(), block->id(), ctl->id(),
+ ctl->op()->mnemonic());
+ }
+ }
}
=======================================
--- /trunk/src/compiler/x64/code-generator-x64.cc Wed Oct 15 00:05:09 2014
UTC
+++ /trunk/src/compiler/x64/code-generator-x64.cc Wed Oct 15 13:35:30 2014
UTC
@@ -283,6 +283,9 @@
case kX64Imul:
ASSEMBLE_MULT(imulq);
break;
+ case kX64ImulHigh32:
+ __ imull(i.InputRegister(1));
+ break;
case kX64Idiv32:
__ cdq();
__ idivl(i.InputRegister(1));
@@ -867,7 +870,7 @@
switch (src.type()) {
case Constant::kInt32:
// TODO(dcarney): don't need scratch in this case.
- __ movq(dst, Immediate(src.ToInt32()));
+ __ Set(dst, src.ToInt32());
break;
case Constant::kInt64:
__ Set(dst, src.ToInt64());
=======================================
--- /trunk/src/compiler/x64/instruction-codes-x64.h Wed Oct 1 00:05:35
2014 UTC
+++ /trunk/src/compiler/x64/instruction-codes-x64.h Wed Oct 15 13:35:30
2014 UTC
@@ -28,6 +28,7 @@
V(X64Sub32) \
V(X64Imul) \
V(X64Imul32) \
+ V(X64ImulHigh32) \
V(X64Idiv) \
V(X64Idiv32) \
V(X64Udiv) \
=======================================
--- /trunk/src/compiler/x64/instruction-selector-x64.cc Wed Oct 15 00:05:09
2014 UTC
+++ /trunk/src/compiler/x64/instruction-selector-x64.cc Wed Oct 15 13:35:30
2014 UTC
@@ -240,7 +240,18 @@
size_t output_count = 0;
// TODO(turbofan): match complex addressing modes.
- if (g.CanBeImmediate(right)) {
+ if (left == right) {
+ // If both inputs refer to the same operand, enforce allocating a
register
+ // for both of them to ensure that we don't end up generating code like
+ // this:
+ //
+ // mov rax, [rbp-0x10]
+ // add rax, [rbp-0x10]
+ // jo label
+ InstructionOperand* const input = g.UseRegister(left);
+ inputs[input_count++] = input;
+ inputs[input_count++] = input;
+ } else if (g.CanBeImmediate(right)) {
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.UseImmediate(right);
} else {
@@ -544,6 +555,16 @@
if (TryEmitLeaMult(this, node, kX64Lea)) return;
VisitMul(this, node, kX64Imul);
}
+
+
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ X64OperandGenerator g(this);
+ InstructionOperand* temps[] = {g.TempRegister(rax)};
+ size_t temp_count = arraysize(temps);
+ Emit(kX64ImulHigh32, g.DefineAsFixed(node, rdx),
+ g.UseFixed(node->InputAt(0), rax), g.UseRegister(node->InputAt(1)),
+ temp_count, temps);
+}
static void VisitDiv(InstructionSelector* selector, Node* node,
@@ -784,13 +805,6 @@
VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right),
cont);
}
}
-
-
-static void VisitWordTest(InstructionSelector* selector, Node* node,
- InstructionCode opcode, FlagsContinuation* cont)
{
- X64OperandGenerator g(selector);
- VisitCompare(selector, opcode, g.Use(node), g.TempImmediate(-1), cont);
-}
static void VisitFloat64Compare(InstructionSelector* selector, Node* node,
@@ -805,7 +819,7 @@
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
- OperandGenerator g(this);
+ X64OperandGenerator g(this);
Node* user = branch;
Node* value = branch->InputAt(0);
@@ -908,15 +922,19 @@
break;
case IrOpcode::kInt32Sub:
return VisitWordCompare(this, value, kX64Cmp32, &cont, false);
+ case IrOpcode::kInt64Sub:
+ return VisitWordCompare(this, value, kX64Cmp, &cont, false);
case IrOpcode::kWord32And:
return VisitWordCompare(this, value, kX64Test32, &cont, true);
+ case IrOpcode::kWord64And:
+ return VisitWordCompare(this, value, kX64Test, &cont, true);
default:
break;
}
}
// Branch could not be combined with a compare, emit compare against 0.
- VisitWordTest(this, value, kX64Test32, &cont);
+ VisitCompare(this, kX64Cmp32, g.Use(value), g.TempImmediate(0), &cont);
}
@@ -935,7 +953,6 @@
default:
break;
}
- return VisitWordTest(this, value, kX64Test32, &cont);
}
}
VisitWordCompare(this, node, kX64Cmp32, &cont, false);
@@ -981,7 +998,6 @@
default:
break;
}
- return VisitWordTest(this, value, kX64Test, &cont);
}
}
VisitWordCompare(this, node, kX64Cmp, &cont, false);
=======================================
--- /trunk/src/counters.cc Tue Jul 29 08:45:47 2014 UTC
+++ /trunk/src/counters.cc Wed Oct 15 13:35:30 2014 UTC
@@ -7,6 +7,7 @@
#include "src/base/platform/platform.h"
#include "src/counters.h"
#include "src/isolate.h"
+#include "src/log-inl.h"
namespace v8 {
namespace internal {
@@ -39,7 +40,7 @@
if (Enabled()) {
timer_.Start();
}
- isolate()->event_logger()(name(), Logger::START);
+ Logger::CallEventLogger(isolate(), name(), Logger::START, true);
}
@@ -50,7 +51,7 @@
AddSample(static_cast<int>(timer_.Elapsed().InMilliseconds()));
timer_.Stop();
}
- isolate()->event_logger()(name(), Logger::END);
+ Logger::CallEventLogger(isolate(), name(), Logger::END, true);
}
=======================================
--- /trunk/src/factory.cc Mon Oct 13 00:05:20 2014 UTC
+++ /trunk/src/factory.cc Wed Oct 15 13:35:30 2014 UTC
@@ -929,6 +929,13 @@
PropertyCell::SetValueInferType(cell, value);
return cell;
}
+
+
+Handle<WeakCell> Factory::NewWeakCell(Handle<HeapObject> value) {
+ AllowDeferredHandleDereference convert_to_cell;
+ CALL_HEAP_FUNCTION(isolate(),
isolate()->heap()->AllocateWeakCell(*value),
+ WeakCell);
+}
Handle<AllocationSite> Factory::NewAllocationSite() {
=======================================
--- /trunk/src/factory.h Fri Oct 3 00:04:58 2014 UTC
+++ /trunk/src/factory.h Wed Oct 15 13:35:30 2014 UTC
@@ -296,6 +296,8 @@
Handle<PropertyCell> NewPropertyCell(Handle<Object> value);
+ Handle<WeakCell> NewWeakCell(Handle<HeapObject> value);
+
// Allocate a tenured AllocationSite. It's payload is null.
Handle<AllocationSite> NewAllocationSite();
=======================================
--- /trunk/src/harmony-array.js Thu Aug 21 00:04:56 2014 UTC
+++ /trunk/src/harmony-array.js Wed Oct 15 13:35:30 2014 UTC
@@ -26,16 +26,18 @@
thisArg = %_Arguments(1);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(thisArg)) {
thisArg = %GetDefaultReceiver(predicate) || thisArg;
- } else if (!IS_SPEC_OBJECT(thisArg) && %IsSloppyModeFunction(predicate))
{
- thisArg = ToObject(thisArg);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(predicate, thisArg);
}
for (var i = 0; i < length; i++) {
if (i in array) {
var element = array[i];
- if (%_CallFunction(thisArg, element, i, array, predicate)) {
+ var newThisArg = needs_wrapper ? ToObject(thisArg) : thisArg;
+ if (%_CallFunction(newThisArg, element, i, array, predicate)) {
return element;
}
}
@@ -61,16 +63,18 @@
thisArg = %_Arguments(1);
}
+ var needs_wrapper = false;
if (IS_NULL_OR_UNDEFINED(thisArg)) {
thisArg = %GetDefaultReceiver(predicate) || thisArg;
- } else if (!IS_SPEC_OBJECT(thisArg) && %IsSloppyModeFunction(predicate))
{
- thisArg = ToObject(thisArg);
+ } else {
+ needs_wrapper = SHOULD_CREATE_WRAPPER(predicate, thisArg);
}
for (var i = 0; i < length; i++) {
if (i in array) {
var element = array[i];
- if (%_CallFunction(thisArg, element, i, array, predicate)) {
+ var newThisArg = needs_wrapper ? ToObject(thisArg) : thisArg;
+ if (%_CallFunction(newThisArg, element, i, array, predicate)) {
return i;
}
}
=======================================
--- /trunk/src/heap/heap.cc Tue Oct 14 07:51:07 2014 UTC
+++ /trunk/src/heap/heap.cc Wed Oct 15 13:35:30 2014 UTC
@@ -150,6 +150,7 @@
set_array_buffers_list(Smi::FromInt(0));
set_allocation_sites_list(Smi::FromInt(0));
set_encountered_weak_collections(Smi::FromInt(0));
+ set_encountered_weak_cells(Smi::FromInt(0));
// Put a dummy entry in the remembered pages so we can find the list the
// minidump even if there are no real unmapped pages.
RememberUnmappedPage(NULL, false);
@@ -1509,6 +1510,8 @@
// Copy objects reachable from the encountered weak collections list.
scavenge_visitor.VisitPointer(&encountered_weak_collections_);
+ // Copy objects reachable from the encountered weak cells.
+ scavenge_visitor.VisitPointer(&encountered_weak_cells_);
// Copy objects reachable from the code flushing candidates list.
MarkCompactCollector* collector = mark_compact_collector();
@@ -2559,6 +2562,7 @@
ALLOCATE_MAP(CELL_TYPE, Cell::kSize, cell)
ALLOCATE_MAP(PROPERTY_CELL_TYPE, PropertyCell::kSize,
global_property_cell)
+ ALLOCATE_MAP(WEAK_CELL_TYPE, WeakCell::kSize, weak_cell)
ALLOCATE_MAP(FILLER_TYPE, kPointerSize, one_pointer_filler)
ALLOCATE_MAP(FILLER_TYPE, 2 * kPointerSize, two_pointer_filler)
@@ -2683,6 +2687,22 @@
cell->set_type(HeapType::None());
return result;
}
+
+
+AllocationResult Heap::AllocateWeakCell(HeapObject* value) {
+ int size = WeakCell::kSize;
+ STATIC_ASSERT(WeakCell::kSize <= Page::kMaxRegularHeapObjectSize);
+ HeapObject* result;
+ {
+ AllocationResult allocation =
+ AllocateRaw(size, OLD_POINTER_SPACE, OLD_POINTER_SPACE);
+ if (!allocation.To(&result)) return allocation;
+ }
+ result->set_map_no_write_barrier(weak_cell_map());
+ WeakCell::cast(result)->initialize(value);
+ WeakCell::cast(result)->set_next(undefined_value(), SKIP_WRITE_BARRIER);
+ return result;
+}
void Heap::CreateApiObjects() {
=======================================
--- /trunk/src/heap/heap.h Tue Oct 14 07:51:07 2014 UTC
+++ /trunk/src/heap/heap.h Wed Oct 15 13:35:30 2014 UTC
@@ -52,6 +52,7 @@
V(Map, fixed_cow_array_map,
FixedCOWArrayMap) \
V(Map, fixed_double_array_map,
FixedDoubleArrayMap) \
V(Map, constant_pool_array_map,
ConstantPoolArrayMap) \
+ V(Map, weak_cell_map,
WeakCellMap) \
V(Oddball, no_interceptor_result_sentinel,
NoInterceptorResultSentinel) \
V(Map, hash_table_map,
HashTableMap) \
V(Map, ordered_hash_table_map,
OrderedHashTableMap) \
@@ -223,6 +224,7 @@
V(fixed_cow_array_map) \
V(fixed_double_array_map) \
V(constant_pool_array_map) \
+ V(weak_cell_map) \
V(no_interceptor_result_sentinel) \
V(hash_table_map) \
V(ordered_hash_table_map) \
@@ -828,6 +830,11 @@
Object* encountered_weak_collections() const {
return encountered_weak_collections_;
}
+
+ void set_encountered_weak_cells(Object* weak_cell) {
+ encountered_weak_cells_ = weak_cell;
+ }
+ Object* encountered_weak_cells() const { return encountered_weak_cells_;
}
// Number of mark-sweeps.
unsigned int ms_count() { return ms_count_; }
@@ -1559,6 +1566,8 @@
// contains Smi(0) while marking is not active.
Object* encountered_weak_collections_;
+ Object* encountered_weak_cells_;
+
StoreBufferRebuilder store_buffer_rebuilder_;
struct StringTypeTable {
@@ -1839,6 +1848,8 @@
// Allocate a tenured JS global property cell initialized with the hole.
MUST_USE_RESULT AllocationResult AllocatePropertyCell();
+ MUST_USE_RESULT AllocationResult AllocateWeakCell(HeapObject* value);
+
// Allocates a new utility object in the old generation.
MUST_USE_RESULT AllocationResult AllocateStruct(InstanceType type);
=======================================
***Additional files exist in this changeset.***
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.