Update V8 to r3121 as required for WebKit update.
Change-Id: Ic53e0aef9a9eb9b71ee7d25a8aef61520bba899c
diff --git a/test/cctest/SConscript b/test/cctest/SConscript
index 9103403..f041041 100644
--- a/test/cctest/SConscript
+++ b/test/cctest/SConscript
@@ -67,7 +67,9 @@
'test-disasm-ia32.cc',
'test-log-stack-tracer.cc'
],
- 'arch:x64': ['test-assembler-x64.cc', 'test-log-stack-tracer.cc'],
+ 'arch:x64': ['test-assembler-x64.cc',
+ 'test-macro-assembler-x64.cc',
+ 'test-log-stack-tracer.cc'],
'os:linux': ['test-platform-linux.cc'],
'os:macos': ['test-platform-macos.cc'],
'os:nullos': ['test-platform-nullos.cc'],
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 8fff769..b43cd64 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -33,6 +33,17 @@
# BUG(382): Weird test. Can't guarantee that it never times out.
test-api/ApplyInterruption: PASS || TIMEOUT
+# This is about to go away anyway since new snapshot code is on the way.
+test-serialize/Deserialize: FAIL
+test-serialize/DeserializeAndRunScript: FAIL
+test-serialize/DeserializeNatives: FAIL
+test-serialize/DeserializeExtensions: FAIL
+
+# These tests always fail. They are here to test test.py. If
+# they don't fail then test.py has failed.
+test-serialize/TestThatAlwaysFails: FAIL
+test-serialize/DependentTestThatAlwaysFails: FAIL
+
[ $arch == arm ]
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index f430cbd..a943f30 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include <stdlib.h>
+#include <limits.h>
#include "v8.h"
@@ -35,6 +35,7 @@
#include "snapshot.h"
#include "platform.h"
#include "top.h"
+#include "utils.h"
#include "cctest.h"
static bool IsNaN(double x) {
@@ -574,6 +575,44 @@
}
+THREADED_TEST(StringConcat) {
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ const char* one_byte_string_1 = "function a_times_t";
+ const char* two_byte_string_1 = "wo_plus_b(a, b) {return ";
+ const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + ";
+ const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + ";
+ const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
+ const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + ";
+ const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);";
+ Local<String> left = v8_str(one_byte_string_1);
+ Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1));
+ Local<String> source = String::Concat(left, right);
+ right = String::NewExternal(
+ new TestAsciiResource(i::StrDup(one_byte_extern_1)));
+ source = String::Concat(source, right);
+ right = String::NewExternal(
+ new TestResource(AsciiToTwoByteString(two_byte_extern_1)));
+ source = String::Concat(source, right);
+ right = v8_str(one_byte_string_2);
+ source = String::Concat(source, right);
+ right = String::New(AsciiToTwoByteString(two_byte_string_2));
+ source = String::Concat(source, right);
+ right = String::NewExternal(
+ new TestResource(AsciiToTwoByteString(two_byte_extern_2)));
+ source = String::Concat(source, right);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> value = script->Run();
+ CHECK(value->IsNumber());
+ CHECK_EQ(68, value->Int32Value());
+ }
+ v8::internal::CompilationCache::Clear();
+ i::Heap::CollectAllGarbage(false);
+ i::Heap::CollectAllGarbage(false);
+}
+
+
THREADED_TEST(GlobalProperties) {
v8::HandleScope scope;
LocalContext env;
@@ -702,6 +741,88 @@
}
+THREADED_TEST(TinyInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ int32_t value = 239;
+ Local<v8::Integer> value_obj = v8::Integer::New(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(BigSmiInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ int32_t value = i::Smi::kMaxValue;
+ // We cannot add one to a Smi::kMaxValue without wrapping.
+ if (i::kSmiValueSize < 32) {
+ CHECK(i::Smi::IsValid(value));
+ CHECK(!i::Smi::IsValid(value + 1));
+ Local<v8::Integer> value_obj = v8::Integer::New(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+ }
+}
+
+
+THREADED_TEST(BigInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ // We cannot add one to a Smi::kMaxValue without wrapping.
+ if (i::kSmiValueSize < 32) {
+ // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1.
+ // The code will not be run in that case, due to the "if" guard.
+ int32_t value =
+ static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1);
+ CHECK(value > i::Smi::kMaxValue);
+ CHECK(!i::Smi::IsValid(value));
+ Local<v8::Integer> value_obj = v8::Integer::New(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+ }
+}
+
+
+THREADED_TEST(TinyUnsignedInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t value = 239;
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(BigUnsignedSmiInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue);
+ CHECK(i::Smi::IsValid(value));
+ CHECK(!i::Smi::IsValid(value + 1));
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(BigUnsignedInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1;
+ CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue));
+ CHECK(!i::Smi::IsValid(value));
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
+THREADED_TEST(OutOfSignedRangeUnsignedInteger) {
+ v8::HandleScope scope;
+ LocalContext env;
+ uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1;
+ uint32_t value = INT32_MAX_AS_UINT + 1;
+ CHECK(value > INT32_MAX_AS_UINT); // No overflow.
+ Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value);
+ CHECK_EQ(static_cast<int64_t>(value), value_obj->Value());
+}
+
+
THREADED_TEST(Number) {
v8::HandleScope scope;
LocalContext env;
@@ -1346,6 +1467,44 @@
}
+THREADED_TEST(InternalFieldsNativePointersAndExternal) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
+ Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
+ instance_templ->SetInternalFieldCount(1);
+ Local<v8::Object> obj = templ->GetFunction()->NewInstance();
+ CHECK_EQ(1, obj->InternalFieldCount());
+ CHECK(obj->GetPointerFromInternalField(0) == NULL);
+
+ char* data = new char[100];
+
+ void* aligned = data;
+ CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1);
+ void* unaligned = data + 1;
+ CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1);
+
+ obj->SetPointerInInternalField(0, aligned);
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0)));
+
+ obj->SetPointerInInternalField(0, unaligned);
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0)));
+
+ obj->SetInternalField(0, v8::External::Wrap(aligned));
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(aligned, obj->GetPointerFromInternalField(0));
+
+ obj->SetInternalField(0, v8::External::Wrap(unaligned));
+ i::Heap::CollectAllGarbage(false);
+ CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0));
+
+ delete[] data;
+}
+
+
THREADED_TEST(IdentityHash) {
v8::HandleScope scope;
LocalContext env;
@@ -1810,7 +1969,7 @@
// Build huge string. This should fail with out of memory exception.
Local<Value> result = CompileRun(
"var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();"
- "for (var i = 0; i < 21; i++) { str = str + str; }");
+ "for (var i = 0; i < 22; i++) { str = str + str; }");
// Check for out of memory state.
CHECK(result.IsEmpty());
@@ -7883,6 +8042,333 @@
}
+template <class ExternalArrayClass, class ElementType>
+static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
+ int64_t low,
+ int64_t high) {
+ v8::HandleScope scope;
+ LocalContext context;
+ const int kElementCount = 40;
+ int element_size = 0;
+ switch (array_type) {
+ case v8::kExternalByteArray:
+ case v8::kExternalUnsignedByteArray:
+ element_size = 1;
+ break;
+ case v8::kExternalShortArray:
+ case v8::kExternalUnsignedShortArray:
+ element_size = 2;
+ break;
+ case v8::kExternalIntArray:
+ case v8::kExternalUnsignedIntArray:
+ case v8::kExternalFloatArray:
+ element_size = 4;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ ElementType* array_data =
+ static_cast<ElementType*>(malloc(kElementCount * element_size));
+ i::Handle<ExternalArrayClass> array =
+ i::Handle<ExternalArrayClass>::cast(
+ i::Factory::NewExternalArray(kElementCount, array_type, array_data));
+ i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
+ for (int i = 0; i < kElementCount; i++) {
+ array->set(i, static_cast<ElementType>(i));
+ }
+ i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
+ for (int i = 0; i < kElementCount; i++) {
+ CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i)));
+ CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i]));
+ }
+
+ v8::Handle<v8::Object> obj = v8::Object::New();
+ i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj);
+ // Set the elements to be the external array.
+ obj->SetIndexedPropertiesToExternalArrayData(array_data,
+ array_type,
+ kElementCount);
+ CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number()));
+ obj->Set(v8_str("field"), v8::Int32::New(1503));
+ context->Global()->Set(v8_str("ext_array"), obj);
+ v8::Handle<v8::Value> result = CompileRun("ext_array.field");
+ CHECK_EQ(1503, result->Int32Value());
+ result = CompileRun("ext_array[1]");
+ CHECK_EQ(1, result->Int32Value());
+
+ // Check pass through of assigned smis
+ result = CompileRun("var sum = 0;"
+ "for (var i = 0; i < 8; i++) {"
+ " sum += ext_array[i] = ext_array[i] = -i;"
+ "}"
+ "sum;");
+ CHECK_EQ(-28, result->Int32Value());
+
+ // Check assigned smis
+ result = CompileRun("for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = i;"
+ "}"
+ "var sum = 0;"
+ "for (var i = 0; i < 8; i++) {"
+ " sum += ext_array[i];"
+ "}"
+ "sum;");
+ CHECK_EQ(28, result->Int32Value());
+
+ // Check assigned smis in reverse order
+ result = CompileRun("for (var i = 8; --i >= 0; ) {"
+ " ext_array[i] = i;"
+ "}"
+ "var sum = 0;"
+ "for (var i = 0; i < 8; i++) {"
+ " sum += ext_array[i];"
+ "}"
+ "sum;");
+ CHECK_EQ(28, result->Int32Value());
+
+ // Check pass through of assigned HeapNumbers
+ result = CompileRun("var sum = 0;"
+ "for (var i = 0; i < 16; i+=2) {"
+ " sum += ext_array[i] = ext_array[i] = (-i * 0.5);"
+ "}"
+ "sum;");
+ CHECK_EQ(-28, result->Int32Value());
+
+ // Check assigned HeapNumbers
+ result = CompileRun("for (var i = 0; i < 16; i+=2) {"
+ " ext_array[i] = (i * 0.5);"
+ "}"
+ "var sum = 0;"
+ "for (var i = 0; i < 16; i+=2) {"
+ " sum += ext_array[i];"
+ "}"
+ "sum;");
+ CHECK_EQ(28, result->Int32Value());
+
+ // Check assigned HeapNumbers in reverse order
+ result = CompileRun("for (var i = 14; i >= 0; i-=2) {"
+ " ext_array[i] = (i * 0.5);"
+ "}"
+ "var sum = 0;"
+ "for (var i = 0; i < 16; i+=2) {"
+ " sum += ext_array[i];"
+ "}"
+ "sum;");
+ CHECK_EQ(28, result->Int32Value());
+
+ i::ScopedVector<char> test_buf(1024);
+
+ // Check legal boundary conditions.
+ // The repeated loads and stores ensure the ICs are exercised.
+ const char* boundary_program =
+ "var res = 0;"
+ "for (var i = 0; i < 16; i++) {"
+ " ext_array[i] = %lld;"
+ " if (i > 8) {"
+ " res = ext_array[i];"
+ " }"
+ "}"
+ "res;";
+ i::OS::SNPrintF(test_buf,
+ boundary_program,
+ low);
+ result = CompileRun(test_buf.start());
+ CHECK_EQ(low, result->IntegerValue());
+
+ i::OS::SNPrintF(test_buf,
+ boundary_program,
+ high);
+ result = CompileRun(test_buf.start());
+ CHECK_EQ(high, result->IntegerValue());
+
+ // Check misprediction of type in IC.
+ result = CompileRun("var tmp_array = ext_array;"
+ "var sum = 0;"
+ "for (var i = 0; i < 8; i++) {"
+ " tmp_array[i] = i;"
+ " sum += tmp_array[i];"
+ " if (i == 4) {"
+ " tmp_array = {};"
+ " }"
+ "}"
+ "sum;");
+ i::Heap::CollectAllGarbage(false); // Force GC to trigger verification.
+ CHECK_EQ(28, result->Int32Value());
+
+ // Make sure out-of-range loads do not throw.
+ i::OS::SNPrintF(test_buf,
+ "var caught_exception = false;"
+ "try {"
+ " ext_array[%d];"
+ "} catch (e) {"
+ " caught_exception = true;"
+ "}"
+ "caught_exception;",
+ kElementCount);
+ result = CompileRun(test_buf.start());
+ CHECK_EQ(false, result->BooleanValue());
+
+ // Make sure out-of-range stores do not throw.
+ i::OS::SNPrintF(test_buf,
+ "var caught_exception = false;"
+ "try {"
+ " ext_array[%d] = 1;"
+ "} catch (e) {"
+ " caught_exception = true;"
+ "}"
+ "caught_exception;",
+ kElementCount);
+ result = CompileRun(test_buf.start());
+ CHECK_EQ(false, result->BooleanValue());
+
+ // Check other boundary conditions, values and operations.
+ result = CompileRun("for (var i = 0; i < 8; i++) {"
+ " ext_array[7] = undefined;"
+ "}"
+ "ext_array[7];");
+ CHECK_EQ(0, result->Int32Value());
+ CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number()));
+
+ result = CompileRun("for (var i = 0; i < 8; i++) {"
+ " ext_array[6] = '2.3';"
+ "}"
+ "ext_array[6];");
+ CHECK_EQ(2, result->Int32Value());
+ CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number()));
+
+ if (array_type != v8::kExternalFloatArray) {
+ // Though the specification doesn't state it, be explicit about
+ // converting NaNs and +/-Infinity to zero.
+ result = CompileRun("for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = 5;"
+ "}"
+ "for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = NaN;"
+ "}"
+ "ext_array[5];");
+ CHECK_EQ(0, result->Int32Value());
+ CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
+
+ result = CompileRun("for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = 5;"
+ "}"
+ "for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = Infinity;"
+ "}"
+ "ext_array[5];");
+ CHECK_EQ(0, result->Int32Value());
+ CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
+
+ result = CompileRun("for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = 5;"
+ "}"
+ "for (var i = 0; i < 8; i++) {"
+ " ext_array[i] = -Infinity;"
+ "}"
+ "ext_array[5];");
+ CHECK_EQ(0, result->Int32Value());
+ CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value());
+ }
+
+ result = CompileRun("ext_array[3] = 33;"
+ "delete ext_array[3];"
+ "ext_array[3];");
+ CHECK_EQ(33, result->Int32Value());
+
+ result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;"
+ "ext_array[2] = 12; ext_array[3] = 13;"
+ "ext_array.__defineGetter__('2',"
+ "function() { return 120; });"
+ "ext_array[2];");
+ CHECK_EQ(12, result->Int32Value());
+
+ result = CompileRun("var js_array = new Array(40);"
+ "js_array[0] = 77;"
+ "js_array;");
+ CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
+
+ result = CompileRun("ext_array[1] = 23;"
+ "ext_array.__proto__ = [];"
+ "js_array.__proto__ = ext_array;"
+ "js_array.concat(ext_array);");
+ CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value());
+ CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value());
+
+ result = CompileRun("ext_array[1] = 23;");
+ CHECK_EQ(23, result->Int32Value());
+
+ free(array_data);
+}
+
+
+THREADED_TEST(ExternalByteArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>(
+ v8::kExternalByteArray,
+ -128,
+ 127);
+}
+
+
+THREADED_TEST(ExternalUnsignedByteArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>(
+ v8::kExternalUnsignedByteArray,
+ 0,
+ 255);
+}
+
+
+THREADED_TEST(ExternalShortArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>(
+ v8::kExternalShortArray,
+ -32768,
+ 32767);
+}
+
+
+THREADED_TEST(ExternalUnsignedShortArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>(
+ v8::kExternalUnsignedShortArray,
+ 0,
+ 65535);
+}
+
+
+THREADED_TEST(ExternalIntArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>(
+ v8::kExternalIntArray,
+ INT_MIN, // -2147483648
+ INT_MAX); // 2147483647
+}
+
+
+THREADED_TEST(ExternalUnsignedIntArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>(
+ v8::kExternalUnsignedIntArray,
+ 0,
+ UINT_MAX); // 4294967295
+}
+
+
+THREADED_TEST(ExternalFloatArray) {
+ ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>(
+ v8::kExternalFloatArray,
+ -500,
+ 500);
+}
+
+
+THREADED_TEST(ExternalArrays) {
+ TestExternalByteArray();
+ TestExternalUnsignedByteArray();
+ TestExternalShortArray();
+ TestExternalUnsignedShortArray();
+ TestExternalIntArray();
+ TestExternalUnsignedIntArray();
+ TestExternalFloatArray();
+}
+
+
THREADED_TEST(ScriptContextDependence) {
v8::HandleScope scope;
LocalContext c1;
@@ -7913,11 +8399,15 @@
}
-// Test that idle notification can be handled when V8 has not yet been
-// set up.
+// Test that idle notification can be handled and eventually returns true.
THREADED_TEST(IdleNotification) {
- for (int i = 0; i < 100; i++) v8::V8::IdleNotification(true);
- for (int i = 0; i < 100; i++) v8::V8::IdleNotification(false);
+ bool rv = false;
+ for (int i = 0; i < 100; i++) {
+ rv = v8::V8::IdleNotification();
+ if (rv)
+ break;
+ }
+ CHECK(rv == true);
}
@@ -7993,3 +8483,15 @@
CHECK(stack_limit == set_limit);
}
}
+
+
+THREADED_TEST(GetHeapStatistics) {
+ v8::HandleScope scope;
+ LocalContext c1;
+ v8::HeapStatistics heap_statistics;
+ CHECK_EQ(heap_statistics.total_heap_size(), 0);
+ CHECK_EQ(heap_statistics.used_heap_size(), 0);
+ v8::V8::GetHeapStatistics(&heap_statistics);
+ CHECK_NE(heap_statistics.total_heap_size(), 0);
+ CHECK_NE(heap_statistics.used_heap_size(), 0);
+}
diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc
index cd750c5..81aa973 100644
--- a/test/cctest/test-assembler-x64.cc
+++ b/test/cctest/test-assembler-x64.cc
@@ -44,6 +44,7 @@
using v8::internal::rax;
using v8::internal::rsi;
using v8::internal::rdi;
+using v8::internal::rcx;
using v8::internal::rdx;
using v8::internal::rbp;
using v8::internal::rsp;
@@ -53,20 +54,28 @@
using v8::internal::not_equal;
using v8::internal::greater;
-
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
-// The AMD64 calling convention is used, with the first five arguments
-// in RSI, RDI, RDX, RCX, R8, and R9, and floating point arguments in
+// The AMD64 calling convention is used, with the first six arguments
+// in RDI, RSI, RDX, RCX, R8, and R9, and floating point arguments in
// the XMM registers. The return value is in RAX.
// This calling convention is used on Linux, with GCC, and on Mac OS,
-// with GCC. A different convention is used on 64-bit windows.
+// with GCC. A different convention is used on 64-bit windows,
+// where the first four integer arguments are passed in RCX, RDX, R8 and R9.
typedef int (*F0)();
typedef int (*F1)(int64_t x);
typedef int (*F2)(int64_t x, int64_t y);
+#ifdef _WIN64
+static const v8::internal::Register arg1 = rcx;
+static const v8::internal::Register arg2 = rdx;
+#else
+static const v8::internal::Register arg1 = rdi;
+static const v8::internal::Register arg2 = rsi;
+#endif
+
#define __ assm.
@@ -80,7 +89,7 @@
Assembler assm(buffer, actual_size);
// Assemble a simple function that copies argument 2 and returns it.
- __ movq(rax, rsi);
+ __ movq(rax, arg2);
__ nop();
__ ret(0);
@@ -105,9 +114,9 @@
// incorrect stack frames when debugging this function (which has them).
__ push(rbp);
__ movq(rbp, rsp);
- __ push(rsi); // Value at (rbp - 8)
- __ push(rsi); // Value at (rbp - 16)
- __ push(rdi); // Value at (rbp - 24)
+ __ push(arg2); // Value at (rbp - 8)
+ __ push(arg2); // Value at (rbp - 16)
+ __ push(arg1); // Value at (rbp - 24)
__ pop(rax);
__ pop(rax);
__ pop(rax);
@@ -132,8 +141,8 @@
Assembler assm(buffer, actual_size);
// Assemble a simple function that adds arguments returning the sum.
- __ movq(rax, rsi);
- __ addq(rax, rdi);
+ __ movq(rax, arg2);
+ __ addq(rax, arg1);
__ ret(0);
CodeDesc desc;
@@ -154,8 +163,8 @@
// Assemble a simple function that multiplies arguments returning the high
// word.
- __ movq(rax, rsi);
- __ imul(rdi);
+ __ movq(rax, arg2);
+ __ imul(arg1);
__ movq(rax, rdx);
__ ret(0);
@@ -182,14 +191,16 @@
// Assemble a simple function that copies argument 2 and returns it.
__ push(rbp);
__ movq(rbp, rsp);
- __ push(rsi); // Value at (rbp - 8)
- __ push(rsi); // Value at (rbp - 16)
- __ push(rdi); // Value at (rbp - 24)
+
+ __ push(arg2); // Value at (rbp - 8)
+ __ push(arg2); // Value at (rbp - 16)
+ __ push(arg1); // Value at (rbp - 24)
+
const int kStackElementSize = 8;
__ movq(rax, Operand(rbp, -3 * kStackElementSize));
- __ pop(rsi);
- __ pop(rsi);
- __ pop(rsi);
+ __ pop(arg2);
+ __ pop(arg2);
+ __ pop(arg2);
__ pop(rbp);
__ nop();
__ ret(0);
@@ -210,13 +221,14 @@
CHECK(buffer);
Assembler assm(buffer, actual_size);
- // Assemble a simple function that copies argument 2 and returns it.
+ // Assemble a simple function that copies argument 1 and returns it.
__ push(rbp);
+
__ movq(rbp, rsp);
- __ movq(rax, rdi);
+ __ movq(rax, arg1);
Label target;
__ jmp(&target);
- __ movq(rax, rsi);
+ __ movq(rax, arg2);
__ bind(&target);
__ pop(rbp);
__ ret(0);
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 1da363c..4ffcee3 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -3539,6 +3539,52 @@
}
+// We match parts of the message to decide if it is a exception message.
+bool IsExceptionEventMessage(char *message) {
+ const char* type_event = "\"type\":\"event\"";
+ const char* event_exception = "\"event\":\"exception\"";
+ // Does the message contain both type:event and event:exception?
+ return strstr(message, type_event) != NULL &&
+ strstr(message, event_exception) != NULL;
+}
+
+
+// We match the message wether it is an evaluate response message.
+bool IsEvaluateResponseMessage(char* message) {
+ const char* type_response = "\"type\":\"response\"";
+ const char* command_evaluate = "\"command\":\"evaluate\"";
+ // Does the message contain both type:response and command:evaluate?
+ return strstr(message, type_response) != NULL &&
+ strstr(message, command_evaluate) != NULL;
+}
+
+
+// We match parts of the message to get evaluate result int value.
+int GetEvaluateIntResult(char *message) {
+ const char* value = "\"value\":";
+ char* pos = strstr(message, value);
+ if (pos == NULL) {
+ return -1;
+ }
+ int res = -1;
+ res = atoi(pos + strlen(value));
+ return res;
+}
+
+
+// We match parts of the message to get hit breakpoint id.
+int GetBreakpointIdFromBreakEventMessage(char *message) {
+ const char* breakpoints = "\"breakpoints\":[";
+ char* pos = strstr(message, breakpoints);
+ if (pos == NULL) {
+ return -1;
+ }
+ int res = -1;
+ res = atoi(pos + strlen(breakpoints));
+ return res;
+}
+
+
/* Test MessageQueues */
/* Tests the message queues that hold debugger commands and
* response messages to the debugger. Fills queues and makes
@@ -3566,8 +3612,6 @@
// Allow message handler to block on a semaphore, to test queueing of
// messages while blocked.
message_queue_barriers.semaphore_1->Wait();
- printf("%s\n", print_buffer);
- fflush(stdout);
}
void MessageQueueDebuggerThread::Run() {
@@ -3822,8 +3866,6 @@
if (IsBreakEventMessage(print_buffer)) {
threaded_debugging_barriers.barrier_2.Wait();
}
- printf("%s\n", print_buffer);
- fflush(stdout);
}
@@ -3911,16 +3953,20 @@
Barriers* breakpoints_barriers;
+int break_event_breakpoint_id;
+int evaluate_int_result;
static void BreakpointsMessageHandler(const v8::Debug::Message& message) {
static char print_buffer[1000];
v8::String::Value json(message.GetJSON());
Utf16ToAscii(*json, json.length(), print_buffer);
- printf("%s\n", print_buffer);
- fflush(stdout);
- // Is break_template a prefix of the message?
if (IsBreakEventMessage(print_buffer)) {
+ break_event_breakpoint_id =
+ GetBreakpointIdFromBreakEventMessage(print_buffer);
+ breakpoints_barriers->semaphore_1->Signal();
+ } else if (IsEvaluateResponseMessage(print_buffer)) {
+ evaluate_int_result = GetEvaluateIntResult(print_buffer);
breakpoints_barriers->semaphore_1->Signal();
}
}
@@ -3930,9 +3976,9 @@
const char* source_1 = "var y_global = 3;\n"
"function cat( new_value ) {\n"
" var x = new_value;\n"
- " y_global = 4;\n"
+ " y_global = y_global + 4;\n"
" x = 3 * x + 1;\n"
- " y_global = 5;\n"
+ " y_global = y_global + 5;\n"
" return x;\n"
"}\n"
"\n"
@@ -3970,59 +4016,76 @@
"\"type\":\"request\","
"\"command\":\"setbreakpoint\","
"\"arguments\":{\"type\":\"function\",\"target\":\"dog\",\"line\":3}}";
- const char* command_3 = "{\"seq\":104,"
+ const char* command_3 = "{\"seq\":103,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
"\"arguments\":{\"expression\":\"dog()\",\"disable_break\":false}}";
- const char* command_4 = "{\"seq\":105,"
+ const char* command_4 = "{\"seq\":104,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
- "\"arguments\":{\"expression\":\"x\",\"disable_break\":true}}";
- const char* command_5 = "{\"seq\":106,"
+ "\"arguments\":{\"expression\":\"x + 1\",\"disable_break\":true}}";
+ const char* command_5 = "{\"seq\":105,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
- const char* command_6 = "{\"seq\":107,"
+ const char* command_6 = "{\"seq\":106,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
- const char* command_7 = "{\"seq\":108,"
+ const char* command_7 = "{\"seq\":107,"
"\"type\":\"request\","
"\"command\":\"evaluate\","
"\"arguments\":{\"expression\":\"dog()\",\"disable_break\":true}}";
- const char* command_8 = "{\"seq\":109,"
+ const char* command_8 = "{\"seq\":108,"
"\"type\":\"request\","
"\"command\":\"continue\"}";
// v8 thread initializes, runs source_1
breakpoints_barriers->barrier_1.Wait();
- // 1:Set breakpoint in cat().
+ // 1:Set breakpoint in cat() (will get id 1).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
- // 2:Set breakpoint in dog()
+ // 2:Set breakpoint in dog() (will get id 2).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
breakpoints_barriers->barrier_2.Wait();
- // v8 thread starts compiling source_2.
+ // V8 thread starts compiling source_2.
// Automatic break happens, to run queued commands
// breakpoints_barriers->semaphore_1->Wait();
// Commands 1 through 3 run, thread continues.
// v8 thread runs source_2 to breakpoint in cat().
// message callback receives break event.
breakpoints_barriers->semaphore_1->Wait();
+ // Must have hit breakpoint #1.
+ CHECK_EQ(1, break_event_breakpoint_id);
// 4:Evaluate dog() (which has a breakpoint).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_3, buffer));
- // v8 thread hits breakpoint in dog()
+ // V8 thread hits breakpoint in dog().
breakpoints_barriers->semaphore_1->Wait(); // wait for break event
- // 5:Evaluate x
+ // Must have hit breakpoint #2.
+ CHECK_EQ(2, break_event_breakpoint_id);
+ // 5:Evaluate (x + 1).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_4, buffer));
- // 6:Continue evaluation of dog()
+ // Evaluate (x + 1) finishes.
+ breakpoints_barriers->semaphore_1->Wait();
+ // Must have result 108.
+ CHECK_EQ(108, evaluate_int_result);
+ // 6:Continue evaluation of dog().
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_5, buffer));
- // dog() finishes.
+ // Evaluate dog() finishes.
+ breakpoints_barriers->semaphore_1->Wait();
+ // Must have result 107.
+ CHECK_EQ(107, evaluate_int_result);
// 7:Continue evaluation of source_2, finish cat(17), hit breakpoint
// in cat(19).
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_6, buffer));
- // message callback gets break event
+ // Message callback gets break event.
breakpoints_barriers->semaphore_1->Wait(); // wait for break event
- // 8: Evaluate dog() with breaks disabled
+ // Must have hit breakpoint #1.
+ CHECK_EQ(1, break_event_breakpoint_id);
+ // 8: Evaluate dog() with breaks disabled.
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_7, buffer));
+ // Evaluate dog() finishes.
+ breakpoints_barriers->semaphore_1->Wait();
+ // Must have result 116.
+ CHECK_EQ(116, evaluate_int_result);
// 9: Continue evaluation of source2, reach end.
v8::Debug::SendCommand(buffer, AsciiToUtf16(command_8, buffer));
}
@@ -4325,7 +4388,13 @@
static void MessageHandlerHitCount(const v8::Debug::Message& message) {
message_handler_hit_count++;
- SendContinueCommand();
+ static char print_buffer[1000];
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
+ if (IsExceptionEventMessage(print_buffer)) {
+ // Send a continue command for exception events.
+ SendContinueCommand();
+ }
}
@@ -4415,8 +4484,6 @@
static char print_buffer[1000];
v8::String::Value json(message.GetJSON());
Utf16ToAscii(*json, json.length(), print_buffer);
- printf("%s\n", print_buffer);
- fflush(stdout);
}
@@ -4776,8 +4843,12 @@
expected_context_data));
message_handler_hit_count++;
+ static char print_buffer[1000];
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
+
// Send a continue command for break events.
- if (message.GetEvent() == v8::Break) {
+ if (IsBreakEventMessage(print_buffer)) {
SendContinueCommand();
}
}
@@ -5016,7 +5087,11 @@
expected_context_data));
message_handler_hit_count++;
- if (message.IsEvent() && message.GetEvent() == v8::Break) {
+ static char print_buffer[1000];
+ v8::String::Value json(message.GetJSON());
+ Utf16ToAscii(*json, json.length(), print_buffer);
+
+ if (IsBreakEventMessage(print_buffer)) {
break_count++;
if (!sent_eval) {
sent_eval = true;
@@ -5038,7 +5113,8 @@
SendContinueCommand();
continue_command_send_count++;
}
- } else if (message.IsResponse() && continue_command_send_count < 2) {
+ } else if (IsEvaluateResponseMessage(print_buffer) &&
+ continue_command_send_count < 2) {
// Response to the evaluation request. We're still on the breakpoint so
// send continue.
SendContinueCommand();
diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc
index af9fb97..74db234 100644
--- a/test/cctest/test-disasm-ia32.cc
+++ b/test/cctest/test-disasm-ia32.cc
@@ -363,7 +363,31 @@
__ divsd(xmm1, xmm0);
__ movdbl(xmm1, Operand(ebx, ecx, times_4, 10000));
__ movdbl(Operand(ebx, ecx, times_4, 10000), xmm1);
+ __ comisd(xmm0, xmm1);
}
+
+ // cmov.
+ {
+ CHECK(CpuFeatures::IsSupported(CpuFeatures::CMOV));
+ CpuFeatures::Scope use_cmov(CpuFeatures::CMOV);
+ __ cmov(overflow, eax, Operand(eax, 0));
+ __ cmov(no_overflow, eax, Operand(eax, 1));
+ __ cmov(below, eax, Operand(eax, 2));
+ __ cmov(above_equal, eax, Operand(eax, 3));
+ __ cmov(equal, eax, Operand(ebx, 0));
+ __ cmov(not_equal, eax, Operand(ebx, 1));
+ __ cmov(below_equal, eax, Operand(ebx, 2));
+ __ cmov(above, eax, Operand(ebx, 3));
+ __ cmov(sign, eax, Operand(ecx, 0));
+ __ cmov(not_sign, eax, Operand(ecx, 1));
+ __ cmov(parity_even, eax, Operand(ecx, 2));
+ __ cmov(parity_odd, eax, Operand(ecx, 3));
+ __ cmov(less, eax, Operand(edx, 0));
+ __ cmov(greater_equal, eax, Operand(edx, 1));
+ __ cmov(less_equal, eax, Operand(edx, 2));
+ __ cmov(greater, eax, Operand(edx, 3));
+ }
+
__ ret(0);
CodeDesc desc;
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index eb32b65..9911ce4 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -132,15 +132,19 @@
CHECK(value->IsNumber());
CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value());
+#ifndef V8_TARGET_ARCH_X64
+ // TODO(lrn): We need a NumberFromIntptr function in order to test this.
value = Heap::NumberFromInt32(Smi::kMinValue - 1);
CHECK(value->IsHeapNumber());
CHECK(value->IsNumber());
CHECK_EQ(static_cast<double>(Smi::kMinValue - 1), value->Number());
+#endif
- value = Heap::NumberFromInt32(Smi::kMaxValue + 1);
+ value = Heap::NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
CHECK(value->IsHeapNumber());
CHECK(value->IsNumber());
- CHECK_EQ(static_cast<double>(Smi::kMaxValue + 1), value->Number());
+ CHECK_EQ(static_cast<double>(static_cast<uint32_t>(Smi::kMaxValue) + 1),
+ value->Number());
// nan oddball checks
CHECK(Heap::nan_value()->IsNumber());
@@ -640,8 +644,9 @@
CHECK_EQ(Smi::FromInt(1), array->length());
CHECK_EQ(array->GetElement(0), name);
- // Set array length with larger than smi value.
- Object* length = Heap::NumberFromInt32(Smi::kMaxValue + 1);
+// Set array length with larger than smi value.
+ Object* length =
+ Heap::NumberFromUint32(static_cast<uint32_t>(Smi::kMaxValue) + 1);
array->SetElementsLength(length);
uint32_t int_length = 0;
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index 65ab50a..3983215 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -430,6 +430,50 @@
#endif // __linux__
+// Test for issue http://crbug.com/23768 in Chromium.
+// Heap can contain scripts with already disposed external sources.
+// We need to verify that LogCompiledFunctions doesn't crash on them.
+namespace {
+
+class SimpleExternalString : public v8::String::ExternalStringResource {
+ public:
+ explicit SimpleExternalString(const char* source)
+ : utf_source_(strlen(source)) {
+ for (int i = 0; i < utf_source_.length(); ++i)
+ utf_source_[i] = source[i];
+ }
+ virtual ~SimpleExternalString() {}
+ virtual size_t length() const { return utf_source_.length(); }
+ virtual const uint16_t* data() const { return utf_source_.start(); }
+ private:
+ i::ScopedVector<uint16_t> utf_source_;
+};
+
+} // namespace
+
+TEST(Issue23768) {
+ v8::HandleScope scope;
+ v8::Handle<v8::Context> env = v8::Context::New();
+ env->Enter();
+
+ SimpleExternalString source_ext_str("(function ext() {})();");
+ v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
+ // Script needs to have a name in order to trigger InitLineEnds execution.
+ v8::Handle<v8::String> origin = v8::String::New("issue-23768-test");
+ v8::Handle<v8::Script> evil_script = v8::Script::Compile(source, origin);
+ CHECK(!evil_script.IsEmpty());
+ CHECK(!evil_script->Run().IsEmpty());
+ i::Handle<i::ExternalTwoByteString> i_source(
+ i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
+ // This situation can happen if source was an external string disposed
+ // by its owner.
+ i_source->set_resource(NULL);
+
+ // Must not crash.
+ i::Logger::LogCompiledFunctions();
+}
+
+
static inline bool IsStringEqualTo(const char* r, const char* s) {
return strncmp(r, s, strlen(r)) == 0;
}
diff --git a/test/cctest/test-macro-assembler-x64.cc b/test/cctest/test-macro-assembler-x64.cc
new file mode 100755
index 0000000..9c1197f
--- /dev/null
+++ b/test/cctest/test-macro-assembler-x64.cc
@@ -0,0 +1,2096 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+
+#include "v8.h"
+
+#include "macro-assembler.h"
+#include "factory.h"
+#include "platform.h"
+#include "serialize.h"
+#include "cctest.h"
+
+using v8::internal::byte;
+using v8::internal::OS;
+using v8::internal::Assembler;
+using v8::internal::Condition;
+using v8::internal::MacroAssembler;
+using v8::internal::HandleScope;
+using v8::internal::Operand;
+using v8::internal::Immediate;
+using v8::internal::SmiIndex;
+using v8::internal::Label;
+using v8::internal::RelocInfo;
+using v8::internal::rax;
+using v8::internal::rbx;
+using v8::internal::rsi;
+using v8::internal::rdi;
+using v8::internal::rcx;
+using v8::internal::rdx;
+using v8::internal::rbp;
+using v8::internal::rsp;
+using v8::internal::r8;
+using v8::internal::r9;
+using v8::internal::r11;
+using v8::internal::r12;
+using v8::internal::r13;
+using v8::internal::r14;
+using v8::internal::r15;
+using v8::internal::FUNCTION_CAST;
+using v8::internal::CodeDesc;
+using v8::internal::less_equal;
+using v8::internal::not_equal;
+using v8::internal::not_zero;
+using v8::internal::greater;
+using v8::internal::greater_equal;
+using v8::internal::carry;
+using v8::internal::not_carry;
+using v8::internal::negative;
+using v8::internal::positive;
+using v8::internal::Smi;
+using v8::internal::kSmiTagMask;
+using v8::internal::kSmiValueSize;
+
+// Test the x64 assembler by compiling some simple functions into
+// a buffer and executing them. These tests do not initialize the
+// V8 library, create a context, or use any V8 objects.
+// The AMD64 calling convention is used, with the first five arguments
+// in RSI, RDI, RDX, RCX, R8, and R9, and floating point arguments in
+// the XMM registers. The return value is in RAX.
+// This calling convention is used on Linux, with GCC, and on Mac OS,
+// with GCC. A different convention is used on 64-bit windows.
+
+typedef int (*F0)();
+
+#define __ masm->
+
+TEST(Smi) {
+ // Check that C++ Smi operations work as expected.
+ intptr_t test_numbers[] = {
+ 0, 1, -1, 127, 128, -128, -129, 255, 256, -256, -257,
+ Smi::kMaxValue, static_cast<intptr_t>(Smi::kMaxValue) + 1,
+ Smi::kMinValue, static_cast<intptr_t>(Smi::kMinValue) - 1
+ };
+ int test_number_count = 15;
+ for (int i = 0; i < test_number_count; i++) {
+ intptr_t number = test_numbers[i];
+ bool is_valid = Smi::IsValid(number);
+ bool is_in_range = number >= Smi::kMinValue && number <= Smi::kMaxValue;
+ CHECK_EQ(is_in_range, is_valid);
+ if (is_valid) {
+ Smi* smi_from_intptr = Smi::FromIntptr(number);
+ if (static_cast<int>(number) == number) { // Is a 32-bit int.
+ Smi* smi_from_int = Smi::FromInt(static_cast<int32_t>(number));
+ CHECK_EQ(smi_from_int, smi_from_intptr);
+ }
+ int smi_value = smi_from_intptr->value();
+ CHECK_EQ(number, static_cast<intptr_t>(smi_value));
+ }
+ }
+}
+
+
+static void TestMoveSmi(MacroAssembler* masm, Label* exit, int id, Smi* value) {
+ __ movl(rax, Immediate(id));
+ __ Move(rcx, Smi::FromInt(0));
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(0)));
+ __ cmpq(rcx, rdx);
+ __ j(not_equal, exit);
+}
+
+
+// Test that we can move a Smi value literally into a register.
+TEST(SmiMove) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+ MacroAssembler* masm = &assembler; // Create a pointer for the __ macro.
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestMoveSmi(masm, &exit, 1, Smi::FromInt(0));
+ TestMoveSmi(masm, &exit, 2, Smi::FromInt(127));
+ TestMoveSmi(masm, &exit, 3, Smi::FromInt(128));
+ TestMoveSmi(masm, &exit, 4, Smi::FromInt(255));
+ TestMoveSmi(masm, &exit, 5, Smi::FromInt(256));
+ TestMoveSmi(masm, &exit, 6, Smi::FromInt(Smi::kMaxValue));
+ TestMoveSmi(masm, &exit, 7, Smi::FromInt(-1));
+ TestMoveSmi(masm, &exit, 8, Smi::FromInt(-128));
+ TestMoveSmi(masm, &exit, 9, Smi::FromInt(-129));
+ TestMoveSmi(masm, &exit, 10, Smi::FromInt(-256));
+ TestMoveSmi(masm, &exit, 11, Smi::FromInt(-257));
+ TestMoveSmi(masm, &exit, 12, Smi::FromInt(Smi::kMinValue));
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiCompare(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r8, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ movq(r9, rdx);
+ __ SmiCompare(rcx, rdx);
+ if (x < y) {
+ __ movl(rax, Immediate(id + 1));
+ __ j(greater_equal, exit);
+ } else if (x > y) {
+ __ movl(rax, Immediate(id + 2));
+ __ j(less_equal, exit);
+ } else {
+ ASSERT_EQ(x, y);
+ __ movl(rax, Immediate(id + 3));
+ __ j(not_equal, exit);
+ }
+ __ movl(rax, Immediate(id + 4));
+ __ cmpq(rcx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ cmpq(rdx, r9);
+ __ j(not_equal, exit);
+
+ if (x != y) {
+ __ SmiCompare(rdx, rcx);
+ if (y < x) {
+ __ movl(rax, Immediate(id + 9));
+ __ j(greater_equal, exit);
+ } else {
+ ASSERT(y > x);
+ __ movl(rax, Immediate(id + 10));
+ __ j(less_equal, exit);
+ }
+ } else {
+ __ SmiCompare(rcx, rcx);
+ __ movl(rax, Immediate(id + 11));
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ cmpq(rcx, r8);
+ __ j(not_equal, exit);
+ }
+}
+
+
+// Test that we can compare smis for equality (and more).
+TEST(SmiCompare) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiCompare(masm, &exit, 0x10, 0, 0);
+ TestSmiCompare(masm, &exit, 0x20, 0, 1);
+ TestSmiCompare(masm, &exit, 0x30, 1, 0);
+ TestSmiCompare(masm, &exit, 0x40, 1, 1);
+ TestSmiCompare(masm, &exit, 0x50, 0, -1);
+ TestSmiCompare(masm, &exit, 0x60, -1, 0);
+ TestSmiCompare(masm, &exit, 0x70, -1, -1);
+ TestSmiCompare(masm, &exit, 0x80, 0, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0x90, Smi::kMinValue, 0);
+ TestSmiCompare(masm, &exit, 0xA0, 0, Smi::kMaxValue);
+ TestSmiCompare(masm, &exit, 0xB0, Smi::kMaxValue, 0);
+ TestSmiCompare(masm, &exit, 0xC0, -1, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0xD0, Smi::kMinValue, -1);
+ TestSmiCompare(masm, &exit, 0xE0, -1, Smi::kMaxValue);
+ TestSmiCompare(masm, &exit, 0xF0, Smi::kMaxValue, -1);
+ TestSmiCompare(masm, &exit, 0x100, Smi::kMinValue, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0x110, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiCompare(masm, &exit, 0x120, Smi::kMaxValue, Smi::kMinValue);
+ TestSmiCompare(masm, &exit, 0x130, Smi::kMaxValue, Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+TEST(Integer32ToSmi) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ __ movq(rax, Immediate(1)); // Test number.
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(0)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(2)); // Test number.
+ __ movl(rcx, Immediate(1024));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(1024)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(3)); // Test number.
+ __ movl(rcx, Immediate(-1));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(-1)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(4)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMaxValue)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(5)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMinValue)));
+ __ SmiCompare(rcx, rdx);
+ __ j(not_equal, &exit);
+
+ // Different target register.
+
+ __ movq(rax, Immediate(6)); // Test number.
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(0)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(7)); // Test number.
+ __ movl(rcx, Immediate(1024));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(1024)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(8)); // Test number.
+ __ movl(rcx, Immediate(-1));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(-1)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(9)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMaxValue)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+ __ movq(rax, Immediate(10)); // Test number.
+ __ movl(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(r8, rcx);
+ __ Set(rdx, reinterpret_cast<intptr_t>(Smi::FromInt(Smi::kMinValue)));
+ __ SmiCompare(r8, rdx);
+ __ j(not_equal, &exit);
+
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestI64PlusConstantToSmi(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int64_t x,
+ int y) {
+ int64_t result = x + y;
+ ASSERT(Smi::IsValid(result));
+ __ movl(rax, Immediate(id));
+ __ Move(r8, Smi::FromInt(result));
+ __ movq(rcx, x, RelocInfo::NONE);
+ __ movq(r11, rcx);
+ __ Integer64PlusConstantToSmi(rdx, rcx, y);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Integer64PlusConstantToSmi(rcx, rcx, y);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+
+TEST(Integer64PlusConstantToSmi) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ int64_t twice_max = static_cast<int64_t>(Smi::kMaxValue) * 2;
+
+ TestI64PlusConstantToSmi(masm, &exit, 0x10, 0, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0x20, 0, 1);
+ TestI64PlusConstantToSmi(masm, &exit, 0x30, 1, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0x40, Smi::kMaxValue - 5, 5);
+ TestI64PlusConstantToSmi(masm, &exit, 0x50, Smi::kMinValue + 5, 5);
+ TestI64PlusConstantToSmi(masm, &exit, 0x60, twice_max, -Smi::kMaxValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0x70, -twice_max, Smi::kMaxValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0x80, 0, Smi::kMinValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0x90, 0, Smi::kMaxValue);
+ TestI64PlusConstantToSmi(masm, &exit, 0xA0, Smi::kMinValue, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0xB0, Smi::kMaxValue, 0);
+ TestI64PlusConstantToSmi(masm, &exit, 0xC0, twice_max, Smi::kMinValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+TEST(SmiCheck) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+ Condition cond;
+
+ __ movl(rax, Immediate(1)); // Test number.
+
+ // CheckSmi
+
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(-1));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckSmi(rcx);
+ __ j(cond, &exit);
+
+ // CheckPositiveSmi
+
+ __ incq(rax);
+ __ movl(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Zero counts as positive.
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckPositiveSmi(rcx); // "zero" non-smi.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(-1));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Negative smis are not positive.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Most negative smi is not positive.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckPositiveSmi(rcx); // "Negative" non-smi.
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckPositiveSmi(rcx); // Most positive smi is positive.
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckPositiveSmi(rcx); // "Positive" non-smi.
+ __ j(cond, &exit);
+
+ // CheckIsMinSmi
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(0));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue + 1));
+ __ Integer32ToSmi(rcx, rcx);
+ cond = masm->CheckIsMinSmi(rcx);
+ __ j(cond, &exit);
+
+ // CheckBothSmi
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ __ Integer32ToSmi(rcx, rcx);
+ __ movq(rdx, Immediate(Smi::kMinValue));
+ __ Integer32ToSmi(rdx, rdx);
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ xor_(rdx, Immediate(kSmiTagMask));
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ cond = masm->CheckBothSmi(rcx, rdx);
+ __ j(cond, &exit);
+
+ __ incq(rax);
+ cond = masm->CheckBothSmi(rcx, rcx);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ cond = masm->CheckBothSmi(rdx, rdx);
+ __ j(cond, &exit);
+
+ // CheckInteger32ValidSmiValue
+ __ incq(rax);
+ __ movq(rcx, Immediate(0));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(-1));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMaxValue));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ __ incq(rax);
+ __ movq(rcx, Immediate(Smi::kMinValue));
+ cond = masm->CheckInteger32ValidSmiValue(rax);
+ __ j(NegateCondition(cond), &exit);
+
+ // Success
+ __ xor_(rax, rax);
+
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+void TestSmiNeg(MacroAssembler* masm, Label* exit, int id, int x) {
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ if (x == Smi::kMinValue || x == 0) {
+ // Negation fails.
+ __ movl(rax, Immediate(id + 8));
+ __ SmiNeg(r9, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiNeg(rcx, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+ } else {
+ Label smi_ok, smi_ok2;
+ int result = -x;
+ __ movl(rax, Immediate(id));
+ __ Move(r8, Smi::FromInt(result));
+
+ __ SmiNeg(r9, rcx, &smi_ok);
+ __ jmp(exit);
+ __ bind(&smi_ok);
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiNeg(rcx, rcx, &smi_ok2);
+ __ jmp(exit);
+ __ bind(&smi_ok2);
+ __ incq(rax);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiNeg) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiNeg(masm, &exit, 0x10, 0);
+ TestSmiNeg(masm, &exit, 0x20, 1);
+ TestSmiNeg(masm, &exit, 0x30, -1);
+ TestSmiNeg(masm, &exit, 0x40, 127);
+ TestSmiNeg(masm, &exit, 0x50, 65535);
+ TestSmiNeg(masm, &exit, 0x60, Smi::kMinValue);
+ TestSmiNeg(masm, &exit, 0x70, Smi::kMaxValue);
+ TestSmiNeg(masm, &exit, 0x80, -Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+
+static void SmiAddTest(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int first,
+ int second) {
+ __ movl(rcx, Immediate(first));
+ __ Integer32ToSmi(rcx, rcx);
+ __ movl(rdx, Immediate(second));
+ __ Integer32ToSmi(rdx, rdx);
+ __ movl(r8, Immediate(first + second));
+ __ Integer32ToSmi(r8, r8);
+
+ __ movl(rax, Immediate(id)); // Test number.
+ __ SmiAdd(r9, rcx, rdx, exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAdd(rcx, rcx, rdx, exit); \
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ movl(rcx, Immediate(first));
+ __ Integer32ToSmi(rcx, rcx);
+
+ __ incq(rax);
+ __ SmiAddConstant(r9, rcx, Smi::FromInt(second));
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ SmiAddConstant(rcx, rcx, Smi::FromInt(second));
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ movl(rcx, Immediate(first));
+ __ Integer32ToSmi(rcx, rcx);
+
+ __ incq(rax);
+ __ SmiAddConstant(r9, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAddConstant(rcx, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+TEST(SmiAdd) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ // No-overflow tests.
+ SmiAddTest(masm, &exit, 0x10, 1, 2);
+ SmiAddTest(masm, &exit, 0x20, 1, -2);
+ SmiAddTest(masm, &exit, 0x30, -1, 2);
+ SmiAddTest(masm, &exit, 0x40, -1, -2);
+ SmiAddTest(masm, &exit, 0x50, 0x1000, 0x2000);
+ SmiAddTest(masm, &exit, 0x60, Smi::kMinValue, 5);
+ SmiAddTest(masm, &exit, 0x70, Smi::kMaxValue, -5);
+ SmiAddTest(masm, &exit, 0x80, Smi::kMaxValue, Smi::kMinValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+static void SmiSubTest(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int first,
+ int second) {
+ __ Move(rcx, Smi::FromInt(first));
+ __ Move(rdx, Smi::FromInt(second));
+ __ Move(r8, Smi::FromInt(first - second));
+
+ __ movl(rax, Immediate(id)); // Test 0.
+ __ SmiSub(r9, rcx, rdx, exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax); // Test 1.
+ __ SmiSub(rcx, rcx, rdx, exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ Move(rcx, Smi::FromInt(first));
+
+ __ incq(rax); // Test 2.
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(second));
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax); // Test 3.
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(second));
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ Move(rcx, Smi::FromInt(first));
+
+ __ incq(rax); // Test 4.
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax); // Test 5.
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(second), exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+static void SmiSubOverflowTest(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int x) {
+ // Subtracts a Smi from x so that the subtraction overflows.
+ ASSERT(x != -1); // Can't overflow by subtracting a Smi.
+ int y_max = (x < 0) ? (Smi::kMaxValue + 0) : (Smi::kMinValue + 0);
+ int y_min = (x < 0) ? (Smi::kMaxValue + x + 2) : (Smi::kMinValue + x);
+
+ __ movl(rax, Immediate(id));
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx); // Store original Smi value of x in r11.
+ __ Move(rdx, Smi::FromInt(y_min));
+ {
+ Label overflow_ok;
+ __ SmiSub(r9, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSub(rcx, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ __ movq(rcx, r11);
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(y_min), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(y_min), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ __ Move(rdx, Smi::FromInt(y_max));
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSub(r9, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSub(rcx, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ __ movq(rcx, r11);
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(r9, rcx, Smi::FromInt(y_max), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+
+ {
+ Label overflow_ok;
+ __ incq(rax);
+ __ SmiSubConstant(rcx, rcx, Smi::FromInt(y_max), &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiSub) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ SmiSubTest(masm, &exit, 0x10, 1, 2);
+ SmiSubTest(masm, &exit, 0x20, 1, -2);
+ SmiSubTest(masm, &exit, 0x30, -1, 2);
+ SmiSubTest(masm, &exit, 0x40, -1, -2);
+ SmiSubTest(masm, &exit, 0x50, 0x1000, 0x2000);
+ SmiSubTest(masm, &exit, 0x60, Smi::kMinValue, -5);
+ SmiSubTest(masm, &exit, 0x70, Smi::kMaxValue, 5);
+ SmiSubTest(masm, &exit, 0x80, -Smi::kMaxValue, Smi::kMinValue);
+ SmiSubTest(masm, &exit, 0x90, 0, Smi::kMaxValue);
+
+ SmiSubOverflowTest(masm, &exit, 0xA0, 1);
+ SmiSubOverflowTest(masm, &exit, 0xB0, 1024);
+ SmiSubOverflowTest(masm, &exit, 0xC0, Smi::kMaxValue);
+ SmiSubOverflowTest(masm, &exit, 0xD0, -2);
+ SmiSubOverflowTest(masm, &exit, 0xE0, -42000);
+ SmiSubOverflowTest(masm, &exit, 0xF0, Smi::kMinValue);
+ SmiSubOverflowTest(masm, &exit, 0x100, 0);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+
+void TestSmiMul(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int64_t result = static_cast<int64_t>(x) * static_cast<int64_t>(y);
+ bool negative_zero = (result == 0) && (x < 0 || y < 0);
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ if (Smi::IsValid(result) && !negative_zero) {
+ __ movl(rax, Immediate(id));
+ __ Move(r8, Smi::FromIntptr(result));
+ __ SmiMul(r9, rcx, rdx, exit);
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiMul(rcx, rcx, rdx, exit);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ } else {
+ __ movl(rax, Immediate(id + 8));
+ Label overflow_ok, overflow_ok2;
+ __ SmiMul(r9, rcx, rdx, &overflow_ok);
+ __ jmp(exit);
+ __ bind(&overflow_ok);
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ SmiMul(rcx, rcx, rdx, &overflow_ok2);
+ __ jmp(exit);
+ __ bind(&overflow_ok2);
+ // 31-bit version doesn't preserve rcx on failure.
+ // __ incq(rax);
+ // __ SmiCompare(r11, rcx);
+ // __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiMul) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiMul(masm, &exit, 0x10, 0, 0);
+ TestSmiMul(masm, &exit, 0x20, -1, 0);
+ TestSmiMul(masm, &exit, 0x30, 0, -1);
+ TestSmiMul(masm, &exit, 0x40, -1, -1);
+ TestSmiMul(masm, &exit, 0x50, 0x10000, 0x10000);
+ TestSmiMul(masm, &exit, 0x60, 0x10000, 0xffff);
+ TestSmiMul(masm, &exit, 0x70, 0x10000, 0xffff);
+ TestSmiMul(masm, &exit, 0x80, Smi::kMaxValue, -1);
+ TestSmiMul(masm, &exit, 0x90, Smi::kMaxValue, -2);
+ TestSmiMul(masm, &exit, 0xa0, Smi::kMaxValue, 2);
+ TestSmiMul(masm, &exit, 0xb0, (Smi::kMaxValue / 2), 2);
+ TestSmiMul(masm, &exit, 0xc0, (Smi::kMaxValue / 2) + 1, 2);
+ TestSmiMul(masm, &exit, 0xd0, (Smi::kMinValue / 2), 2);
+ TestSmiMul(masm, &exit, 0xe0, (Smi::kMinValue / 2) - 1, 2);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiDiv(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ bool division_by_zero = (y == 0);
+ bool negative_zero = (x == 0 && y < 0);
+#ifdef V8_TARGET_ARCH_X64
+ bool overflow = (x == Smi::kMinValue && y < 0); // Safe approx. used.
+#else
+ bool overflow = (x == Smi::kMinValue && y == -1);
+#endif
+ bool fraction = !division_by_zero && !overflow && (x % y != 0);
+ __ Move(r11, Smi::FromInt(x));
+ __ Move(r12, Smi::FromInt(y));
+ if (!fraction && !overflow && !negative_zero && !division_by_zero) {
+ // Division succeeds
+ __ movq(rcx, r11);
+ __ movq(r15, Immediate(id));
+ int result = x / y;
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiDiv(r9, rcx, r12, exit);
+ // Might have destroyed rcx and r12.
+ __ incq(r15);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ movq(rcx, r11);
+ __ Move(r12, Smi::FromInt(y));
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiDiv(rcx, rcx, r12, exit);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ } else {
+ // Division fails.
+ __ movq(r15, Immediate(id + 8));
+
+ Label fail_ok, fail_ok2;
+ __ movq(rcx, r11);
+ __ SmiDiv(r9, rcx, r12, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiDiv(rcx, rcx, r12, &fail_ok2);
+ __ jmp(exit);
+ __ bind(&fail_ok2);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiDiv) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiDiv(masm, &exit, 0x10, 1, 1);
+ TestSmiDiv(masm, &exit, 0x20, 1, 0);
+ TestSmiDiv(masm, &exit, 0x30, -1, 0);
+ TestSmiDiv(masm, &exit, 0x40, 0, 1);
+ TestSmiDiv(masm, &exit, 0x50, 0, -1);
+ TestSmiDiv(masm, &exit, 0x60, 4, 2);
+ TestSmiDiv(masm, &exit, 0x70, -4, 2);
+ TestSmiDiv(masm, &exit, 0x80, 4, -2);
+ TestSmiDiv(masm, &exit, 0x90, -4, -2);
+ TestSmiDiv(masm, &exit, 0xa0, 3, 2);
+ TestSmiDiv(masm, &exit, 0xb0, 3, 4);
+ TestSmiDiv(masm, &exit, 0xc0, 1, Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0xd0, -1, Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0xe0, Smi::kMaxValue, 1);
+ TestSmiDiv(masm, &exit, 0xf0, Smi::kMaxValue, Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0x100, Smi::kMaxValue, -Smi::kMaxValue);
+ TestSmiDiv(masm, &exit, 0x110, Smi::kMaxValue, -1);
+ TestSmiDiv(masm, &exit, 0x120, Smi::kMinValue, 1);
+ TestSmiDiv(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
+ TestSmiDiv(masm, &exit, 0x140, Smi::kMinValue, -1);
+
+ __ xor_(r15, r15); // Success.
+ __ bind(&exit);
+ __ movq(rax, r15);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiMod(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ bool division_by_zero = (y == 0);
+ bool division_overflow = (x == Smi::kMinValue) && (y == -1);
+ bool fraction = !division_by_zero && !division_overflow && ((x % y) != 0);
+ bool negative_zero = (!fraction && x < 0);
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(r12, Smi::FromInt(y));
+ if (!division_overflow && !negative_zero && !division_by_zero) {
+ // Modulo succeeds
+ __ movq(r15, Immediate(id));
+ int result = x % y;
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiMod(r9, rcx, r12, exit);
+
+ __ incq(r15);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiMod(rcx, rcx, r12, exit);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ } else {
+ // Modulo fails.
+ __ movq(r15, Immediate(id + 8));
+
+ Label fail_ok, fail_ok2;
+ __ SmiMod(r9, rcx, r12, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(r15);
+ __ SmiMod(rcx, rcx, r12, &fail_ok2);
+ __ jmp(exit);
+ __ bind(&fail_ok2);
+
+ __ incq(r15);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+ }
+}
+
+
+TEST(SmiMod) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiMod(masm, &exit, 0x10, 1, 1);
+ TestSmiMod(masm, &exit, 0x20, 1, 0);
+ TestSmiMod(masm, &exit, 0x30, -1, 0);
+ TestSmiMod(masm, &exit, 0x40, 0, 1);
+ TestSmiMod(masm, &exit, 0x50, 0, -1);
+ TestSmiMod(masm, &exit, 0x60, 4, 2);
+ TestSmiMod(masm, &exit, 0x70, -4, 2);
+ TestSmiMod(masm, &exit, 0x80, 4, -2);
+ TestSmiMod(masm, &exit, 0x90, -4, -2);
+ TestSmiMod(masm, &exit, 0xa0, 3, 2);
+ TestSmiMod(masm, &exit, 0xb0, 3, 4);
+ TestSmiMod(masm, &exit, 0xc0, 1, Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0xd0, -1, Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0xe0, Smi::kMaxValue, 1);
+ TestSmiMod(masm, &exit, 0xf0, Smi::kMaxValue, Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0x100, Smi::kMaxValue, -Smi::kMaxValue);
+ TestSmiMod(masm, &exit, 0x110, Smi::kMaxValue, -1);
+ TestSmiMod(masm, &exit, 0x120, Smi::kMinValue, 1);
+ TestSmiMod(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
+ TestSmiMod(masm, &exit, 0x140, Smi::kMinValue, -1);
+
+ __ xor_(r15, r15); // Success.
+ __ bind(&exit);
+ __ movq(rax, r15);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiIndex(MacroAssembler* masm, Label* exit, int id, int x) {
+ __ movl(rax, Immediate(id));
+
+ for (int i = 0; i < 8; i++) {
+ __ Move(rcx, Smi::FromInt(x));
+ SmiIndex index = masm->SmiToIndex(rdx, rcx, i);
+ ASSERT(index.reg.is(rcx) || index.reg.is(rdx));
+ __ shl(index.reg, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(x) << i);
+ __ SmiCompare(index.reg, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ index = masm->SmiToIndex(rcx, rcx, i);
+ ASSERT(index.reg.is(rcx));
+ __ shl(rcx, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(x) << i);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+
+ __ Move(rcx, Smi::FromInt(x));
+ index = masm->SmiToNegativeIndex(rdx, rcx, i);
+ ASSERT(index.reg.is(rcx) || index.reg.is(rdx));
+ __ shl(index.reg, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(-x) << i);
+ __ SmiCompare(index.reg, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ index = masm->SmiToNegativeIndex(rcx, rcx, i);
+ ASSERT(index.reg.is(rcx));
+ __ shl(rcx, Immediate(index.scale));
+ __ Set(r8, static_cast<intptr_t>(-x) << i);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ }
+}
+
+TEST(SmiIndex) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiIndex(masm, &exit, 0x10, 0);
+ TestSmiIndex(masm, &exit, 0x20, 1);
+ TestSmiIndex(masm, &exit, 0x30, 100);
+ TestSmiIndex(masm, &exit, 0x40, 1000);
+ TestSmiIndex(masm, &exit, 0x50, Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSelectNonSmi(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ __ movl(rax, Immediate(id));
+ __ Move(rcx, Smi::FromInt(x));
+ __ Move(rdx, Smi::FromInt(y));
+ __ xor_(rdx, Immediate(kSmiTagMask));
+ __ SelectNonSmi(r9, rcx, rdx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, rdx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ __ Move(rdx, Smi::FromInt(y));
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ __ SelectNonSmi(r9, rcx, rdx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ Label fail_ok;
+ __ Move(rcx, Smi::FromInt(x));
+ __ Move(rdx, Smi::FromInt(y));
+ __ xor_(rcx, Immediate(kSmiTagMask));
+ __ xor_(rdx, Immediate(kSmiTagMask));
+ __ SelectNonSmi(r9, rcx, rdx, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+}
+
+
+TEST(SmiSelectNonSmi) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false); // Avoid inline checks.
+ Label exit;
+
+ TestSelectNonSmi(masm, &exit, 0x10, 0, 0);
+ TestSelectNonSmi(masm, &exit, 0x20, 0, 1);
+ TestSelectNonSmi(masm, &exit, 0x30, 1, 0);
+ TestSelectNonSmi(masm, &exit, 0x40, 0, -1);
+ TestSelectNonSmi(masm, &exit, 0x50, -1, 0);
+ TestSelectNonSmi(masm, &exit, 0x60, -1, -1);
+ TestSelectNonSmi(masm, &exit, 0x70, 1, 1);
+ TestSelectNonSmi(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSelectNonSmi(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiAnd(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int result = x & y;
+
+ __ movl(rax, Immediate(id));
+
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiAnd(r9, rcx, rdx);
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAnd(rcx, rcx, rdx);
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+
+ __ movq(rcx, r11);
+ __ incq(rax);
+ __ SmiAndConstant(r9, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiAndConstant(rcx, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiAnd) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiAnd(masm, &exit, 0x10, 0, 0);
+ TestSmiAnd(masm, &exit, 0x20, 0, 1);
+ TestSmiAnd(masm, &exit, 0x30, 1, 0);
+ TestSmiAnd(masm, &exit, 0x40, 0, -1);
+ TestSmiAnd(masm, &exit, 0x50, -1, 0);
+ TestSmiAnd(masm, &exit, 0x60, -1, -1);
+ TestSmiAnd(masm, &exit, 0x70, 1, 1);
+ TestSmiAnd(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiAnd(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+ TestSmiAnd(masm, &exit, 0xA0, Smi::kMinValue, -1);
+ TestSmiAnd(masm, &exit, 0xB0, Smi::kMinValue, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiOr(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int result = x | y;
+
+ __ movl(rax, Immediate(id));
+
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiOr(r9, rcx, rdx);
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiOr(rcx, rcx, rdx);
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+
+ __ movq(rcx, r11);
+ __ incq(rax);
+ __ SmiOrConstant(r9, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiOrConstant(rcx, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiOr) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiOr(masm, &exit, 0x10, 0, 0);
+ TestSmiOr(masm, &exit, 0x20, 0, 1);
+ TestSmiOr(masm, &exit, 0x30, 1, 0);
+ TestSmiOr(masm, &exit, 0x40, 0, -1);
+ TestSmiOr(masm, &exit, 0x50, -1, 0);
+ TestSmiOr(masm, &exit, 0x60, -1, -1);
+ TestSmiOr(masm, &exit, 0x70, 1, 1);
+ TestSmiOr(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiOr(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+ TestSmiOr(masm, &exit, 0xA0, Smi::kMinValue, -1);
+ TestSmiOr(masm, &exit, 0xB0, 0x05555555, 0x01234567);
+ TestSmiOr(masm, &exit, 0xC0, 0x05555555, 0x0fedcba9);
+ TestSmiOr(masm, &exit, 0xD0, Smi::kMinValue, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiXor(MacroAssembler* masm, Label* exit, int id, int x, int y) {
+ int result = x ^ y;
+
+ __ movl(rax, Immediate(id));
+
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ Move(rdx, Smi::FromInt(y));
+ __ Move(r8, Smi::FromInt(result));
+ __ SmiXor(r9, rcx, rdx);
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiXor(rcx, rcx, rdx);
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+
+ __ movq(rcx, r11);
+ __ incq(rax);
+ __ SmiXorConstant(r9, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, r9);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiXorConstant(rcx, rcx, Smi::FromInt(y));
+ __ SmiCompare(r8, rcx);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiXor) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiXor(masm, &exit, 0x10, 0, 0);
+ TestSmiXor(masm, &exit, 0x20, 0, 1);
+ TestSmiXor(masm, &exit, 0x30, 1, 0);
+ TestSmiXor(masm, &exit, 0x40, 0, -1);
+ TestSmiXor(masm, &exit, 0x50, -1, 0);
+ TestSmiXor(masm, &exit, 0x60, -1, -1);
+ TestSmiXor(masm, &exit, 0x70, 1, 1);
+ TestSmiXor(masm, &exit, 0x80, Smi::kMinValue, Smi::kMaxValue);
+ TestSmiXor(masm, &exit, 0x90, Smi::kMinValue, Smi::kMinValue);
+ TestSmiXor(masm, &exit, 0xA0, Smi::kMinValue, -1);
+ TestSmiXor(masm, &exit, 0xB0, 0x5555555, 0x01234567);
+ TestSmiXor(masm, &exit, 0xC0, 0x5555555, 0x0fedcba9);
+ TestSmiXor(masm, &exit, 0xD0, Smi::kMinValue, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiNot(MacroAssembler* masm, Label* exit, int id, int x) {
+ int result = ~x;
+ __ movl(rax, Immediate(id));
+
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+
+ __ SmiNot(r9, rcx);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r11, rcx);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ SmiNot(rcx, rcx);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+}
+
+
+TEST(SmiNot) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiNot(masm, &exit, 0x10, 0);
+ TestSmiNot(masm, &exit, 0x20, 1);
+ TestSmiNot(masm, &exit, 0x30, -1);
+ TestSmiNot(masm, &exit, 0x40, 127);
+ TestSmiNot(masm, &exit, 0x50, 65535);
+ TestSmiNot(masm, &exit, 0x60, Smi::kMinValue);
+ TestSmiNot(masm, &exit, 0x70, Smi::kMaxValue);
+ TestSmiNot(masm, &exit, 0x80, 0x05555555);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiShiftLeft(MacroAssembler* masm, Label* exit, int id, int x) {
+ const int shifts[] = { 0, 1, 7, 24, kSmiValueSize - 1};
+ const int kNumShifts = 5;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < kNumShifts; i++) {
+ // rax == id + i * 10.
+ int shift = shifts[i];
+ int result = x << shift;
+ if (Smi::IsValid(result)) {
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftLeftConstant(r9, rcx, shift, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftLeftConstant(rcx, rcx, shift, exit);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(rcx, Smi::FromInt(shift));
+ __ SmiShiftLeft(r9, rdx, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftLeft(r9, rdx, r11, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftLeft(rdx, rdx, r11, exit);
+
+ __ incq(rax);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ } else {
+ // Cannot happen with long smis.
+ Label fail_ok;
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ SmiShiftLeftConstant(r9, rcx, shift, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ Label fail_ok2;
+ __ SmiShiftLeftConstant(rcx, rcx, shift, &fail_ok2);
+ __ jmp(exit);
+ __ bind(&fail_ok2);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(r8, Smi::FromInt(shift));
+ Label fail_ok3;
+ __ SmiShiftLeft(r9, rcx, r8, &fail_ok3);
+ __ jmp(exit);
+ __ bind(&fail_ok3);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(r8, Smi::FromInt(shift));
+ __ movq(rdx, r11);
+ Label fail_ok4;
+ __ SmiShiftLeft(rdx, rdx, r8, &fail_ok4);
+ __ jmp(exit);
+ __ bind(&fail_ok4);
+
+ __ incq(rax);
+ __ SmiCompare(rdx, r11);
+ __ j(not_equal, exit);
+
+ __ addq(rax, Immediate(3));
+ }
+ }
+}
+
+
+TEST(SmiShiftLeft) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 3,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiShiftLeft(masm, &exit, 0x10, 0);
+ TestSmiShiftLeft(masm, &exit, 0x50, 1);
+ TestSmiShiftLeft(masm, &exit, 0x90, 127);
+ TestSmiShiftLeft(masm, &exit, 0xD0, 65535);
+ TestSmiShiftLeft(masm, &exit, 0x110, Smi::kMaxValue);
+ TestSmiShiftLeft(masm, &exit, 0x150, Smi::kMinValue);
+ TestSmiShiftLeft(masm, &exit, 0x190, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiShiftLogicalRight(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int x) {
+ const int shifts[] = { 0, 1, 7, 24, kSmiValueSize - 1};
+ const int kNumShifts = 5;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < kNumShifts; i++) {
+ int shift = shifts[i];
+ intptr_t result = static_cast<unsigned int>(x) >> shift;
+ if (Smi::IsValid(result)) {
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftLogicalRightConstant(r9, rcx, shift, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(rcx, Smi::FromInt(shift));
+ __ SmiShiftLogicalRight(r9, rdx, rcx, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftLogicalRight(r9, rdx, r11, exit);
+
+ __ incq(rax);
+ __ SmiCompare(r9, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ } else {
+ // Cannot happen with long smis.
+ Label fail_ok;
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ SmiShiftLogicalRightConstant(r9, rcx, shift, &fail_ok);
+ __ jmp(exit);
+ __ bind(&fail_ok);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(r8, Smi::FromInt(shift));
+ Label fail_ok3;
+ __ SmiShiftLogicalRight(r9, rcx, r8, &fail_ok3);
+ __ jmp(exit);
+ __ bind(&fail_ok3);
+
+ __ incq(rax);
+ __ SmiCompare(rcx, r11);
+ __ j(not_equal, exit);
+
+ __ addq(rax, Immediate(3));
+ }
+ }
+}
+
+
+TEST(SmiShiftLogicalRight) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiShiftLogicalRight(masm, &exit, 0x10, 0);
+ TestSmiShiftLogicalRight(masm, &exit, 0x30, 1);
+ TestSmiShiftLogicalRight(masm, &exit, 0x50, 127);
+ TestSmiShiftLogicalRight(masm, &exit, 0x70, 65535);
+ TestSmiShiftLogicalRight(masm, &exit, 0x90, Smi::kMaxValue);
+ TestSmiShiftLogicalRight(masm, &exit, 0xB0, Smi::kMinValue);
+ TestSmiShiftLogicalRight(masm, &exit, 0xD0, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestSmiShiftArithmeticRight(MacroAssembler* masm,
+ Label* exit,
+ int id,
+ int x) {
+ const int shifts[] = { 0, 1, 7, 24, kSmiValueSize - 1};
+ const int kNumShifts = 5;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < kNumShifts; i++) {
+ int shift = shifts[i];
+ // Guaranteed arithmetic shift.
+ int result = (x < 0) ? ~((~x) >> shift) : (x >> shift);
+ __ Move(r8, Smi::FromInt(result));
+ __ Move(rcx, Smi::FromInt(x));
+ __ SmiShiftArithmeticRightConstant(rcx, rcx, shift);
+
+ __ SmiCompare(rcx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ __ Move(rdx, Smi::FromInt(x));
+ __ Move(r11, Smi::FromInt(shift));
+ __ SmiShiftArithmeticRight(rdx, rdx, r11);
+
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+
+ __ incq(rax);
+ }
+}
+
+
+TEST(SmiShiftArithmeticRight) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestSmiShiftArithmeticRight(masm, &exit, 0x10, 0);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x20, 1);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x30, 127);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x40, 65535);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x50, Smi::kMaxValue);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x60, Smi::kMinValue);
+ TestSmiShiftArithmeticRight(masm, &exit, 0x70, -1);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+void TestPositiveSmiPowerUp(MacroAssembler* masm, Label* exit, int id, int x) {
+ ASSERT(x >= 0);
+ int powers[] = { 0, 1, 2, 3, 8, 16, 24, 31 };
+ int power_count = 8;
+ __ movl(rax, Immediate(id));
+ for (int i = 0; i < power_count; i++) {
+ int power = powers[i];
+ intptr_t result = static_cast<intptr_t>(x) << power;
+ __ Set(r8, result);
+ __ Move(rcx, Smi::FromInt(x));
+ __ movq(r11, rcx);
+ __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rcx, power);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ SmiCompare(r11, rcx); // rcx unchanged.
+ __ j(not_equal, exit);
+ __ incq(rax);
+ __ PositiveSmiTimesPowerOfTwoToInteger64(rcx, rcx, power);
+ __ SmiCompare(rdx, r8);
+ __ j(not_equal, exit);
+ __ incq(rax);
+ }
+}
+
+
+TEST(PositiveSmiTimesPowerOfTwoToInteger64) {
+ // Allocate an executable page of memory.
+ size_t actual_size;
+ byte* buffer =
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ &actual_size,
+ true));
+ CHECK(buffer);
+ HandleScope handles;
+ MacroAssembler assembler(buffer, actual_size);
+
+ MacroAssembler* masm = &assembler;
+ masm->set_allow_stub_calls(false);
+ Label exit;
+
+ TestPositiveSmiPowerUp(masm, &exit, 0x20, 0);
+ TestPositiveSmiPowerUp(masm, &exit, 0x40, 1);
+ TestPositiveSmiPowerUp(masm, &exit, 0x60, 127);
+ TestPositiveSmiPowerUp(masm, &exit, 0x80, 128);
+ TestPositiveSmiPowerUp(masm, &exit, 0xA0, 255);
+ TestPositiveSmiPowerUp(masm, &exit, 0xC0, 256);
+ TestPositiveSmiPowerUp(masm, &exit, 0x100, 65535);
+ TestPositiveSmiPowerUp(masm, &exit, 0x120, 65536);
+ TestPositiveSmiPowerUp(masm, &exit, 0x140, Smi::kMaxValue);
+
+ __ xor_(rax, rax); // Success.
+ __ bind(&exit);
+ __ ret(0);
+
+ CodeDesc desc;
+ masm->GetCode(&desc);
+ // Call the function from C++.
+ int result = FUNCTION_CAST<F0>(buffer)();
+ CHECK_EQ(0, result);
+}
+
+
+#undef __
diff --git a/test/cctest/test-mark-compact.cc b/test/cctest/test-mark-compact.cc
index 743375d..e56f0f4 100644
--- a/test/cctest/test-mark-compact.cc
+++ b/test/cctest/test-mark-compact.cc
@@ -71,10 +71,6 @@
TEST(Promotion) {
- // Test the situation that some objects in new space are promoted to the
- // old space
- if (Snapshot::IsEnabled()) return;
-
// Ensure that we get a compacting collection so that objects are promoted
// from new space.
FLAG_gc_global = true;
@@ -106,7 +102,6 @@
TEST(NoPromotion) {
- if (Snapshot::IsEnabled()) return;
Heap::ConfigureHeap(2*256*KB, 4*MB);
// Test the situation that some objects in new space are promoted to
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 6939a80..db37eb3 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -286,3 +286,22 @@
v8::Local<v8::Value> value = script->Run();
CHECK(value->IsUndefined());
}
+
+
+extern "C" void V8_Fatal(const char* file, int line, const char* format, ...);
+
+
+TEST(TestThatAlwaysSucceeds) {
+}
+
+
+TEST(TestThatAlwaysFails) {
+ bool ArtificialFailure = false;
+ CHECK(ArtificialFailure);
+}
+
+
+DEPENDENT_TEST(DependentTestThatAlwaysFails, TestThatAlwaysSucceeds) {
+ bool ArtificialFailure2 = false;
+ CHECK(ArtificialFailure2);
+}
diff --git a/test/cctest/test-spaces.cc b/test/cctest/test-spaces.cc
index d946a7f..1a26883 100644
--- a/test/cctest/test-spaces.cc
+++ b/test/cctest/test-spaces.cc
@@ -99,9 +99,9 @@
TEST(MemoryAllocator) {
CHECK(Heap::ConfigureHeapDefault());
- CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
+ CHECK(MemoryAllocator::Setup(Heap::MaxReserved()));
- OldSpace faked_space(Heap::MaxCapacity(), OLD_POINTER_SPACE, NOT_EXECUTABLE);
+ OldSpace faked_space(Heap::MaxReserved(), OLD_POINTER_SPACE, NOT_EXECUTABLE);
int total_pages = 0;
int requested = 2;
int allocated;
@@ -155,16 +155,16 @@
TEST(NewSpace) {
CHECK(Heap::ConfigureHeapDefault());
- CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
+ CHECK(MemoryAllocator::Setup(Heap::MaxReserved()));
NewSpace new_space;
void* chunk =
- MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize());
+ MemoryAllocator::ReserveInitialChunk(4 * Heap::ReservedSemiSpaceSize());
CHECK(chunk != NULL);
Address start = RoundUp(static_cast<Address>(chunk),
- Heap::YoungGenerationSize());
- CHECK(new_space.Setup(start, Heap::YoungGenerationSize()));
+ 2 * Heap::ReservedSemiSpaceSize());
+ CHECK(new_space.Setup(start, 2 * Heap::ReservedSemiSpaceSize()));
CHECK(new_space.HasBeenSetup());
while (new_space.Available() >= Page::kMaxHeapObjectSize) {
@@ -180,18 +180,18 @@
TEST(OldSpace) {
CHECK(Heap::ConfigureHeapDefault());
- CHECK(MemoryAllocator::Setup(Heap::MaxCapacity()));
+ CHECK(MemoryAllocator::Setup(Heap::MaxReserved()));
- OldSpace* s = new OldSpace(Heap::OldGenerationSize(),
+ OldSpace* s = new OldSpace(Heap::MaxOldGenerationSize(),
OLD_POINTER_SPACE,
NOT_EXECUTABLE);
CHECK(s != NULL);
void* chunk =
- MemoryAllocator::ReserveInitialChunk(2 * Heap::YoungGenerationSize());
+ MemoryAllocator::ReserveInitialChunk(4 * Heap::ReservedSemiSpaceSize());
CHECK(chunk != NULL);
Address start = static_cast<Address>(chunk);
- size_t size = RoundUp(start, Heap::YoungGenerationSize()) - start;
+ size_t size = RoundUp(start, 2 * Heap::ReservedSemiSpaceSize()) - start;
CHECK(s->Setup(start, size));