summaryrefslogtreecommitdiffstats
path: root/test/cctest
diff options
context:
space:
mode:
authorEmily Bernier <ember@google.com>2015-03-24 16:35:39 -0400
committerEmily Bernier <ember@google.com>2015-06-23 16:55:40 -0400
commit958fae7ec3f466955f8e5b50fa5b8d38b9e91675 (patch)
treea63ee37f93192ad427f88ed926743f6bb6014312 /test/cctest
parent57a14c9a8621270b0e6c697dce28a9c453ebe55f (diff)
downloadandroid_external_v8-958fae7ec3f466955f8e5b50fa5b8d38b9e91675.tar.gz
android_external_v8-958fae7ec3f466955f8e5b50fa5b8d38b9e91675.tar.bz2
android_external_v8-958fae7ec3f466955f8e5b50fa5b8d38b9e91675.zip
Update V8 to version 4.1.0.21
This is a cherry-pick of all commits up to and including the 4.1.0.21 cherry-pick in Chromium. Original commit message: Version 4.1.0.21 (cherry-pick) Merged 206e9136bde0f2b5ae8cb77afbb1e7833e5bd412 Unlink pages from the space page list after evacuation. BUG=430201 LOG=N R=jkummerow@chromium.org Review URL: https://codereview.chromium.org/953813002 Cr-Commit-Position: refs/branch-heads/4.1@{#22} Cr-Branched-From: 2e08d2a7aa9d65d269d8c57aba82eb38a8cb0a18-refs/heads/candidates@{#25353} --- Change-Id: I8c23c7bbb70772b4858fe8a47b64fa97ee0d1f8c
Diffstat (limited to 'test/cctest')
-rw-r--r--test/cctest/cctest.cc18
-rw-r--r--test/cctest/cctest.gyp30
-rw-r--r--test/cctest/cctest.h34
-rw-r--r--test/cctest/cctest.status79
-rw-r--r--test/cctest/compiler/call-tester.h38
-rw-r--r--test/cctest/compiler/codegen-tester.cc7
-rw-r--r--test/cctest/compiler/codegen-tester.h17
-rw-r--r--test/cctest/compiler/function-tester.h122
-rw-r--r--test/cctest/compiler/graph-builder-tester.cc8
-rw-r--r--test/cctest/compiler/graph-builder-tester.h5
-rw-r--r--test/cctest/compiler/simplified-graph-builder.cc23
-rw-r--r--test/cctest/compiler/simplified-graph-builder.h16
-rw-r--r--test/cctest/compiler/test-basic-block-profiler.cc114
-rw-r--r--test/cctest/compiler/test-changes-lowering.cc54
-rw-r--r--test/cctest/compiler/test-codegen-deopt.cc65
-rw-r--r--test/cctest/compiler/test-control-reducer.cc1678
-rw-r--r--test/cctest/compiler/test-gap-resolver.cc7
-rw-r--r--test/cctest/compiler/test-graph-reducer.cc85
-rw-r--r--test/cctest/compiler/test-graph-visualizer.cc90
-rw-r--r--test/cctest/compiler/test-instruction.cc153
-rw-r--r--test/cctest/compiler/test-js-constant-cache.cc193
-rw-r--r--test/cctest/compiler/test-js-context-specialization.cc23
-rw-r--r--test/cctest/compiler/test-js-typed-lowering.cc309
-rw-r--r--test/cctest/compiler/test-jump-threading.cc764
-rw-r--r--test/cctest/compiler/test-linkage.cc24
-rw-r--r--test/cctest/compiler/test-loop-analysis.cc862
-rw-r--r--test/cctest/compiler/test-loop-assignment-analysis.cc294
-rw-r--r--test/cctest/compiler/test-machine-operator-reducer.cc111
-rw-r--r--test/cctest/compiler/test-node-algorithm.cc260
-rw-r--r--test/cctest/compiler/test-node-cache.cc71
-rw-r--r--test/cctest/compiler/test-node.cc31
-rw-r--r--test/cctest/compiler/test-operator.cc179
-rw-r--r--test/cctest/compiler/test-phi-reducer.cc230
-rw-r--r--test/cctest/compiler/test-pipeline.cc6
-rw-r--r--test/cctest/compiler/test-representation-change.cc338
-rw-r--r--test/cctest/compiler/test-run-inlining.cc341
-rw-r--r--test/cctest/compiler/test-run-intrinsics.cc76
-rw-r--r--test/cctest/compiler/test-run-jsbranches.cc75
-rw-r--r--test/cctest/compiler/test-run-machops.cc594
-rw-r--r--test/cctest/compiler/test-run-stackcheck.cc18
-rw-r--r--test/cctest/compiler/test-schedule.cc86
-rw-r--r--test/cctest/compiler/test-scheduler.cc849
-rw-r--r--test/cctest/compiler/test-simplified-lowering.cc805
-rw-r--r--test/cctest/compiler/test-typer.cc380
-rw-r--r--test/cctest/compiler/value-helper.h42
-rw-r--r--test/cctest/test-accessors.cc34
-rw-r--r--test/cctest/test-alloc.cc9
-rw-r--r--test/cctest/test-api.cc2331
-rw-r--r--test/cctest/test-assembler-arm.cc439
-rw-r--r--test/cctest/test-assembler-arm64.cc89
-rw-r--r--test/cctest/test-assembler-ia32.cc482
-rw-r--r--test/cctest/test-assembler-x64.cc450
-rw-r--r--test/cctest/test-ast.cc4
-rw-r--r--test/cctest/test-bit-vector.cc (renamed from test/cctest/test-dataflow.cc)4
-rw-r--r--test/cctest/test-code-stubs-mips64.cc2
-rw-r--r--test/cctest/test-compiler.cc27
-rw-r--r--test/cctest/test-cpu-profiler.cc98
-rw-r--r--test/cctest/test-debug.cc227
-rw-r--r--test/cctest/test-decls.cc530
-rw-r--r--test/cctest/test-disasm-arm.cc64
-rw-r--r--test/cctest/test-disasm-arm64.cc4
-rw-r--r--test/cctest/test-disasm-ia32.cc103
-rw-r--r--test/cctest/test-disasm-x64.cc143
-rw-r--r--test/cctest/test-disasm-x87.cc6
-rw-r--r--test/cctest/test-feedback-vector.cc308
-rw-r--r--test/cctest/test-func-name-inference.cc18
-rw-r--r--test/cctest/test-heap-profiler.cc71
-rw-r--r--test/cctest/test-heap.cc643
-rw-r--r--test/cctest/test-lockers.cc1
-rw-r--r--test/cctest/test-log-stack-tracer.cc4
-rw-r--r--test/cctest/test-log.cc28
-rw-r--r--test/cctest/test-mark-compact.cc2
-rw-r--r--test/cctest/test-object-observe.cc126
-rw-r--r--test/cctest/test-ostreams.cc148
-rw-r--r--test/cctest/test-parsing.cc929
-rw-r--r--test/cctest/test-platform.cc2
-rw-r--r--test/cctest/test-regexp.cc12
-rw-r--r--test/cctest/test-sampler-api.cc245
-rw-r--r--test/cctest/test-serialize.cc764
-rw-r--r--test/cctest/test-spaces.cc65
-rw-r--r--test/cctest/test-strings.cc87
-rw-r--r--test/cctest/test-transitions.cc301
-rw-r--r--test/cctest/test-types.cc666
-rw-r--r--test/cctest/test-unboxed-doubles.cc1162
-rw-r--r--test/cctest/test-utils.cc44
-rw-r--r--test/cctest/test-weakmaps.cc19
-rw-r--r--test/cctest/trace-extension.cc3
-rw-r--r--test/cctest/types-fuzz.h311
88 files changed, 17179 insertions, 3430 deletions
diff --git a/test/cctest/cctest.cc b/test/cctest/cctest.cc
index f03710a3..170aa5a8 100644
--- a/test/cctest/cctest.cc
+++ b/test/cctest/cctest.cc
@@ -34,12 +34,12 @@
#include "test/cctest/profiler-extension.h"
#include "test/cctest/trace-extension.h"
-#if (defined(_WIN32) || defined(_WIN64))
+#if V8_OS_WIN
#include <windows.h> // NOLINT
-#if defined(_MSC_VER)
+#if V8_CC_MSVC
#include <crtdbg.h>
-#endif // defined(_MSC_VER)
-#endif // defined(_WIN32) || defined(_WIN64)
+#endif
+#endif
enum InitializationState {kUnset, kUnintialized, kInitialized};
static InitializationState initialization_state_ = kUnset;
@@ -47,7 +47,7 @@ static bool disable_automatic_dispose_ = false;
CcTest* CcTest::last_ = NULL;
bool CcTest::initialize_called_ = false;
-bool CcTest::isolate_used_ = false;
+v8::base::Atomic32 CcTest::isolate_used_ = 0;
v8::Isolate* CcTest::isolate_ = NULL;
@@ -145,12 +145,12 @@ static void SuggestTestHarness(int tests) {
int main(int argc, char* argv[]) {
-#if (defined(_WIN32) || defined(_WIN64))
+#if V8_OS_WIN
UINT new_flags =
SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
UINT existing_flags = SetErrorMode(new_flags);
SetErrorMode(existing_flags | new_flags);
-#if defined(_MSC_VER)
+#if V8_CC_MSVC
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
@@ -158,8 +158,8 @@ int main(int argc, char* argv[]) {
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_set_error_mode(_OUT_TO_STDERR);
-#endif // _MSC_VER
-#endif // defined(_WIN32) || defined(_WIN64)
+#endif // V8_CC_MSVC
+#endif // V8_OS_WIN
v8::V8::InitializeICU();
v8::Platform* platform = v8::platform::CreateDefaultPlatform();
diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp
index 6a57763e..4d1c467e 100644
--- a/test/cctest/cctest.gyp
+++ b/test/cctest/cctest.gyp
@@ -53,22 +53,27 @@
'compiler/graph-tester.h',
'compiler/simplified-graph-builder.cc',
'compiler/simplified-graph-builder.h',
+ 'compiler/test-basic-block-profiler.cc',
'compiler/test-branch-combine.cc',
'compiler/test-changes-lowering.cc',
'compiler/test-codegen-deopt.cc',
+ 'compiler/test-control-reducer.cc',
'compiler/test-gap-resolver.cc',
'compiler/test-graph-reducer.cc',
+ 'compiler/test-graph-visualizer.cc',
'compiler/test-instruction.cc',
'compiler/test-js-context-specialization.cc',
'compiler/test-js-constant-cache.cc',
'compiler/test-js-typed-lowering.cc',
+ 'compiler/test-jump-threading.cc',
'compiler/test-linkage.cc',
+ 'compiler/test-loop-assignment-analysis.cc',
+ 'compiler/test-loop-analysis.cc',
'compiler/test-machine-operator-reducer.cc',
'compiler/test-node-algorithm.cc',
'compiler/test-node-cache.cc',
'compiler/test-node.cc',
'compiler/test-operator.cc',
- 'compiler/test-phi-reducer.cc',
'compiler/test-pipeline.cc',
'compiler/test-representation-change.cc',
'compiler/test-run-deopt.cc',
@@ -80,10 +85,12 @@
'compiler/test-run-jsops.cc',
'compiler/test-run-machops.cc',
'compiler/test-run-properties.cc',
+ 'compiler/test-run-stackcheck.cc',
'compiler/test-run-variables.cc',
'compiler/test-schedule.cc',
'compiler/test-scheduler.cc',
'compiler/test-simplified-lowering.cc',
+ 'compiler/test-typer.cc',
'cctest.cc',
'gay-fixed.cc',
'gay-precision.cc',
@@ -97,13 +104,13 @@
'test-atomicops.cc',
'test-bignum.cc',
'test-bignum-dtoa.cc',
+ 'test-bit-vector.cc',
'test-checks.cc',
'test-circular-queue.cc',
'test-compiler.cc',
'test-constantpool.cc',
'test-conversions.cc',
'test-cpu-profiler.cc',
- 'test-dataflow.cc',
'test-date.cc',
'test-debug.cc',
'test-declarative-accessors.cc',
@@ -114,6 +121,7 @@
'test-double.cc',
'test-dtoa.cc',
'test-fast-dtoa.cc',
+ 'test-feedback-vector.cc',
'test-fixed-dtoa.cc',
'test-flags.cc',
'test-func-name-inference.cc',
@@ -134,7 +142,6 @@
'test-mementos.cc',
'test-object-observe.cc',
'test-ordered-hash-table.cc',
- 'test-ostreams.cc',
'test-parsing.cc',
'test-platform.cc',
'test-profile-generator.cc',
@@ -142,6 +149,7 @@
'test-regexp.cc',
'test-reloc-info.cc',
'test-representation.cc',
+ 'test-sampler-api.cc',
'test-serialize.cc',
'test-spaces.cc',
'test-strings.cc',
@@ -149,8 +157,10 @@
'test-strtod.cc',
'test-thread-termination.cc',
'test-threads.cc',
+ 'test-transitions.cc',
'test-types.cc',
'test-unbound-queue.cc',
+ 'test-unboxed-doubles.cc',
'test-unique.cc',
'test-unscopables-hidden-prototype.cc',
'test-utils.cc',
@@ -250,13 +260,14 @@
# cctest can't be built against a shared library, so we need to
# depend on the underlying static target in that case.
'conditions': [
- ['v8_use_snapshot=="true"', {
+ ['v8_use_snapshot=="true" and v8_use_external_startup_data==0', {
'dependencies': ['../../tools/gyp/v8.gyp:v8_snapshot'],
- },
- {
- 'dependencies': [
- '../../tools/gyp/v8.gyp:v8_nosnapshot',
- ],
+ }],
+ ['v8_use_snapshot=="true" and v8_use_external_startup_data==1', {
+ 'dependencies': ['../../tools/gyp/v8.gyp:v8_external_snapshot'],
+ }],
+ ['v8_use_snapshot!="true"', {
+ 'dependencies': ['../../tools/gyp/v8.gyp:v8_nosnapshot'],
}],
],
}, {
@@ -294,7 +305,6 @@
'../../tools/js2c.py',
'<@(_outputs)',
'TEST', # type
- 'off', # compression
'<@(file_list)',
],
}
diff --git a/test/cctest/cctest.h b/test/cctest/cctest.h
index 6d27074a..a8239d26 100644
--- a/test/cctest/cctest.h
+++ b/test/cctest/cctest.h
@@ -117,7 +117,7 @@ class CcTest {
static v8::Isolate* isolate() {
CHECK(isolate_ != NULL);
- isolate_used_ = true;
+ v8::base::NoBarrier_Store(&isolate_used_, 1);
return isolate_;
}
@@ -149,7 +149,7 @@ class CcTest {
// TODO(dcarney): Remove.
// This must be called first in a test.
static void InitializeVM() {
- CHECK(!isolate_used_);
+ CHECK(!v8::base::NoBarrier_Load(&isolate_used_));
CHECK(!initialize_called_);
initialize_called_ = true;
v8::HandleScope handle_scope(CcTest::isolate());
@@ -181,7 +181,7 @@ class CcTest {
static CcTest* last_;
static v8::Isolate* isolate_;
static bool initialize_called_;
- static bool isolate_used_;
+ static v8::base::Atomic32 isolate_used_;
};
// Switches between all the Api tests using the threading support.
@@ -481,15 +481,31 @@ static inline void ExpectUndefined(const char* code) {
// Helper function that simulates a full new-space in the heap.
-static inline void SimulateFullSpace(v8::internal::NewSpace* space) {
- int new_linear_size = static_cast<int>(
- *space->allocation_limit_address() - *space->allocation_top_address());
- if (new_linear_size == 0) return;
+static inline bool FillUpOnePage(v8::internal::NewSpace* space) {
v8::internal::AllocationResult allocation =
- space->AllocateRaw(new_linear_size);
+ space->AllocateRaw(v8::internal::Page::kMaxRegularHeapObjectSize);
+ if (allocation.IsRetry()) return false;
v8::internal::FreeListNode* node =
v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
- node->set_size(space->heap(), new_linear_size);
+ node->set_size(space->heap(), v8::internal::Page::kMaxRegularHeapObjectSize);
+ return true;
+}
+
+
+static inline void SimulateFullSpace(v8::internal::NewSpace* space) {
+ int new_linear_size = static_cast<int>(*space->allocation_limit_address() -
+ *space->allocation_top_address());
+ if (new_linear_size > 0) {
+ // Fill up the current page.
+ v8::internal::AllocationResult allocation =
+ space->AllocateRaw(new_linear_size);
+ v8::internal::FreeListNode* node =
+ v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
+ node->set_size(space->heap(), new_linear_size);
+ }
+ // Fill up all remaining pages.
+ while (FillUpOnePage(space))
+ ;
}
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 5198af6f..cc5414da 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -29,6 +29,7 @@
[ALWAYS, {
# All tests prefixed with 'Bug' are expected to fail.
'test-api/Bug*': [FAIL],
+ 'test-serialize/Bug*': [FAIL],
##############################################################################
@@ -80,58 +81,14 @@
##############################################################################
# TurboFan compiler failures.
- # TODO(sigurds): The schedule is borked with multiple inlinees,
- # and cannot handle free-floating loops yet
- 'test-run-inlining/InlineTwiceDependentDiamond': [SKIP],
- 'test-run-inlining/InlineTwiceDependentDiamondDifferent': [SKIP],
- 'test-run-inlining/InlineLoop': [SKIP],
-
# Some tests are just too slow to run for now.
'test-api/Threading*': [PASS, NO_VARIANTS],
'test-heap/IncrementalMarkingStepMakesBigProgressWithLargeObjects': [PASS, NO_VARIANTS],
'test-heap-profiler/ManyLocalsInSharedContext': [PASS, NO_VARIANTS],
'test-debug/ThreadedDebugging': [PASS, NO_VARIANTS],
'test-debug/DebugBreakLoop': [PASS, NO_VARIANTS],
-
- # Support for breakpoints requires using LoadICs and StoreICs.
- 'test-debug/BreakPointICStore': [PASS, NO_VARIANTS],
- 'test-debug/BreakPointICLoad': [PASS, NO_VARIANTS],
- 'test-debug/BreakPointICCall': [PASS, NO_VARIANTS],
- 'test-debug/BreakPointICCallWithGC': [PASS, NO_VARIANTS],
- 'test-debug/BreakPointConstructCallWithGC': [PASS, NO_VARIANTS],
- 'test-debug/BreakPointReturn': [PASS, NO_VARIANTS],
- 'test-debug/BreakPointThroughJavaScript': [PASS, NO_VARIANTS],
- 'test-debug/ScriptBreakPointByNameThroughJavaScript': [PASS, NO_VARIANTS],
- 'test-debug/ScriptBreakPointByIdThroughJavaScript': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepLinear': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepKeyedLoadLoop': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepKeyedStoreLoop': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepNamedLoadLoop': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepNamedStoreLoop': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepLinearMixedICs': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepDeclarations': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepLocals': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepIf': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepSwitch': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepWhile': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepDoWhile': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepFor': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepForContinue': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepForBreak': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepForIn': [PASS, NO_VARIANTS],
- 'test-debug/DebugStepWith': [PASS, NO_VARIANTS],
- 'test-debug/DebugConditional': [PASS, NO_VARIANTS],
- 'test-debug/StepInOutSimple': [PASS, NO_VARIANTS],
- 'test-debug/StepInOutTree': [PASS, NO_VARIANTS],
- 'test-debug/StepInOutBranch': [PASS, NO_VARIANTS],
- 'test-debug/DebugBreak': [PASS, NO_VARIANTS],
- 'test-debug/DebugBreakStackInspection': [PASS, NO_VARIANTS],
- 'test-debug/BreakMessageWhenMessageHandlerIsReset': [PASS, NO_VARIANTS],
- 'test-debug/NoDebugBreakInAfterCompileMessageHandler': [PASS, NO_VARIANTS],
- 'test-debug/DisableBreak': [PASS, NO_VARIANTS],
- 'test-debug/RegExpDebugBreak': [PASS, NO_VARIANTS],
- 'test-debug/DebugBreakFunctionApply': [PASS, NO_VARIANTS],
- 'test-debug/DeoptimizeDuringDebugBreak': [PASS, NO_VARIANTS],
+ # BUG(3742).
+ 'test-mark-compact/MarkCompactCollector': [PASS, ['arch==arm', NO_VARIANTS]],
# Support for %GetFrameDetails is missing and requires checkpoints.
'test-api/Regress385349': [PASS, NO_VARIANTS],
@@ -148,6 +105,17 @@
'test-debug/CallingContextIsNotDebugContext': [PASS, NO_VARIANTS],
'test-debug/DebugEventContext': [PASS, NO_VARIANTS],
'test-debug/DebugBreakInline': [PASS, NO_VARIANTS],
+ 'test-debug/BreakMessageWhenMessageHandlerIsReset': [PASS, NO_VARIANTS],
+ 'test-debug/DebugBreak': [PASS, NO_VARIANTS],
+ 'test-debug/DebugBreakFunctionApply': [PASS, NO_VARIANTS],
+ 'test-debug/DebugBreakStackInspection': [PASS, NO_VARIANTS],
+ 'test-debug/DeoptimizeDuringDebugBreak': [PASS, NO_VARIANTS],
+ 'test-debug/DisableBreak': [PASS, NO_VARIANTS],
+ 'test-debug/NoDebugBreakInAfterCompileMessageHandler': [PASS, NO_VARIANTS],
+ 'test-debug/RegExpDebugBreak': [PASS, NO_VARIANTS],
+
+ # TODO(titzer): Triggers bug in late control reduction.
+ 'test-run-inlining/InlineLoopGuardedEmpty': [SKIP],
############################################################################
# Slow tests.
@@ -298,6 +266,19 @@
}], # 'arch == mipsel or arch == mips'
##############################################################################
+['arch == mips', {
+ # Too slow with TF.
+ 'test-api/ExternalArrays': [PASS, NO_VARIANTS],
+
+ # TODO(mips-team): Currently fails on mips board.
+ 'test-simplified-lowering/RunNumberMultiply_TruncatingToUint32': [SKIP],
+ 'test-parsing/TooManyArguments': [SKIP],
+ 'test-api/Threading3': [SKIP],
+ 'test-api/RequestInterruptTestWithNativeAccessor': [SKIP],
+ 'test-api/RequestInterruptTestWithAccessor': [SKIP],
+}], # 'arch == mips'
+
+##############################################################################
['arch == mips64el', {
# BUG(2657): Test sometimes times out on MIPS simulator.
@@ -334,12 +315,6 @@
'test-log/LogAccessorCallbacks': [SKIP],
'test-log/LogCallbacks': [SKIP],
'test-log/ProfLazyMode': [SKIP],
-
- # platform-tls.h does not contain an ANDROID-related header.
- 'test-platform-tls/FastTLS': [SKIP],
-
- # This test times out.
- 'test-threads/ThreadJoinSelf': [SKIP],
}], # 'arch == android_arm or arch == android_ia32'
##############################################################################
diff --git a/test/cctest/compiler/call-tester.h b/test/cctest/compiler/call-tester.h
index e8641602..cad171e6 100644
--- a/test/cctest/compiler/call-tester.h
+++ b/test/cctest/compiler/call-tester.h
@@ -207,7 +207,43 @@ class CallHelper {
Simulator::CallArgument::End()};
return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
}
-#elif USE_SIMULATOR && V8_TARGET_ARCH_ARM
+#elif USE_SIMULATOR && V8_TARGET_ARCH_MIPS64
+ uintptr_t CallSimulator(byte* f, int64_t p1 = 0, int64_t p2 = 0,
+ int64_t p3 = 0, int64_t p4 = 0) {
+ Simulator* simulator = Simulator::current(isolate_);
+ return static_cast<uintptr_t>(simulator->Call(f, 4, p1, p2, p3, p4));
+ }
+
+ template <typename R, typename F>
+ R DoCall(F* f) {
+ return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f)));
+ }
+ template <typename R, typename F, typename P1>
+ R DoCall(F* f, P1 p1) {
+ return ReturnValueTraits<R>::Cast(
+ CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1)));
+ }
+ template <typename R, typename F, typename P1, typename P2>
+ R DoCall(F* f, P1 p1, P2 p2) {
+ return ReturnValueTraits<R>::Cast(
+ CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+ ParameterTraits<P2>::Cast(p2)));
+ }
+ template <typename R, typename F, typename P1, typename P2, typename P3>
+ R DoCall(F* f, P1 p1, P2 p2, P3 p3) {
+ return ReturnValueTraits<R>::Cast(CallSimulator(
+ FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+ ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3)));
+ }
+ template <typename R, typename F, typename P1, typename P2, typename P3,
+ typename P4>
+ R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) {
+ return ReturnValueTraits<R>::Cast(CallSimulator(
+ FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+ ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3),
+ ParameterTraits<P4>::Cast(p4)));
+ }
+#elif USE_SIMULATOR && (V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS)
uintptr_t CallSimulator(byte* f, int32_t p1 = 0, int32_t p2 = 0,
int32_t p3 = 0, int32_t p4 = 0) {
Simulator* simulator = Simulator::current(isolate_);
diff --git a/test/cctest/compiler/codegen-tester.cc b/test/cctest/compiler/codegen-tester.cc
index b1874f55..53110017 100644
--- a/test/cctest/compiler/codegen-tester.cc
+++ b/test/cctest/compiler/codegen-tester.cc
@@ -14,7 +14,6 @@ using namespace v8::internal::compiler;
TEST(CompareWrapper) {
// Who tests the testers?
// If CompareWrapper is broken, then test expectations will be broken.
- RawMachineAssemblerTester<int32_t> m;
CompareWrapper wWord32Equal(IrOpcode::kWord32Equal);
CompareWrapper wInt32LessThan(IrOpcode::kInt32LessThan);
CompareWrapper wInt32LessThanOrEqual(IrOpcode::kInt32LessThanOrEqual);
@@ -477,10 +476,10 @@ TEST(RunHeapConstant) {
TEST(RunHeapNumberConstant) {
- RawMachineAssemblerTester<Object*> m;
- Handle<Object> number = m.isolate()->factory()->NewHeapNumber(100.5);
+ RawMachineAssemblerTester<HeapObject*> m;
+ Handle<HeapObject> number = m.isolate()->factory()->NewHeapNumber(100.5);
m.Return(m.HeapConstant(number));
- Object* result = m.Call();
+ HeapObject* result = m.Call();
CHECK_EQ(result, *number);
}
diff --git a/test/cctest/compiler/codegen-tester.h b/test/cctest/compiler/codegen-tester.h
index 6aa5bae5..283d5339 100644
--- a/test/cctest/compiler/codegen-tester.h
+++ b/test/cctest/compiler/codegen-tester.h
@@ -7,6 +7,7 @@
#include "src/v8.h"
+#include "src/compiler/instruction-selector.h"
#include "src/compiler/pipeline.h"
#include "src/compiler/raw-machine-assembler.h"
#include "src/simulator.h"
@@ -23,7 +24,9 @@ class MachineAssemblerTester : public HandleAndZoneScope,
public:
MachineAssemblerTester(MachineType return_type, MachineType p0,
MachineType p1, MachineType p2, MachineType p3,
- MachineType p4)
+ MachineType p4,
+ MachineOperatorBuilder::Flags flags =
+ MachineOperatorBuilder::Flag::kNoFlags)
: HandleAndZoneScope(),
CallHelper(
main_isolate(),
@@ -31,7 +34,7 @@ class MachineAssemblerTester : public HandleAndZoneScope,
MachineAssembler(
new (main_zone()) Graph(main_zone()),
MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4),
- kMachPtr) {}
+ kMachPtr, flags) {}
Node* LoadFromPointer(void* address, MachineType rep, int32_t offset = 0) {
return this->Load(rep, this->PointerConstant(address),
@@ -65,10 +68,8 @@ class MachineAssemblerTester : public HandleAndZoneScope,
Schedule* schedule = this->Export();
CallDescriptor* call_descriptor = this->call_descriptor();
Graph* graph = this->graph();
- CompilationInfo info(graph->zone()->isolate(), graph->zone());
- Linkage linkage(&info, call_descriptor);
- Pipeline pipeline(&info);
- code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph, schedule);
+ code_ =
+ Pipeline::GenerateCodeForTesting(call_descriptor, graph, schedule);
}
return this->code_.ToHandleChecked()->entry();
}
@@ -89,8 +90,8 @@ class RawMachineAssemblerTester
MachineType p3 = kMachNone,
MachineType p4 = kMachNone)
: MachineAssemblerTester<RawMachineAssembler>(
- ReturnValueTraits<ReturnType>::Representation(), p0, p1, p2, p3,
- p4) {}
+ ReturnValueTraits<ReturnType>::Representation(), p0, p1, p2, p3, p4,
+ InstructionSelector::SupportedMachineOperatorFlags()) {}
template <typename Ci, typename Fn>
void Run(const Ci& ci, const Fn& fn) {
diff --git a/test/cctest/compiler/function-tester.h b/test/cctest/compiler/function-tester.h
index c869f00d..7e16eead 100644
--- a/test/cctest/compiler/function-tester.h
+++ b/test/cctest/compiler/function-tester.h
@@ -8,7 +8,9 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
+#include "src/ast-numbering.h"
#include "src/compiler.h"
+#include "src/compiler/linkage.h"
#include "src/compiler/pipeline.h"
#include "src/execution.h"
#include "src/full-codegen.h"
@@ -37,52 +39,16 @@ class FunctionTester : public InitializedHandleScope {
CHECK_EQ(0, flags_ & ~supported_flags);
}
+ explicit FunctionTester(Graph* graph)
+ : isolate(main_isolate()),
+ function(NewFunction("(function(a,b){})")),
+ flags_(0) {
+ CompileGraph(graph);
+ }
+
Isolate* isolate;
Handle<JSFunction> function;
- Handle<JSFunction> Compile(Handle<JSFunction> function) {
-#if V8_TURBOFAN_TARGET
- CompilationInfoWithZone info(function);
-
- CHECK(Parser::Parse(&info));
- info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
- if (flags_ & CompilationInfo::kContextSpecializing) {
- info.MarkAsContextSpecializing();
- }
- if (flags_ & CompilationInfo::kInliningEnabled) {
- info.MarkAsInliningEnabled();
- }
- if (flags_ & CompilationInfo::kTypingEnabled) {
- info.MarkAsTypingEnabled();
- }
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
- CHECK(Compiler::EnsureDeoptimizationSupport(&info));
-
- Pipeline pipeline(&info);
- Handle<Code> code = pipeline.GenerateCode();
- if (FLAG_turbo_deoptimization) {
- info.context()->native_context()->AddOptimizedCode(*code);
- }
-
- CHECK(!code.is_null());
- function->ReplaceCode(*code);
-#elif USE_CRANKSHAFT
- Handle<Code> unoptimized = Handle<Code>(function->code());
- Handle<Code> code = Compiler::GetOptimizedCode(function, unoptimized,
- Compiler::NOT_CONCURRENT);
- CHECK(!code.is_null());
-#if ENABLE_DISASSEMBLER
- if (FLAG_print_opt_code) {
- CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
- code->Disassemble("test code", tracing_scope.file());
- }
-#endif
- function->ReplaceCode(*code);
-#endif
- return function;
- }
-
MaybeHandle<Object> Call(Handle<Object> a, Handle<Object> b) {
Handle<Object> args[] = {a, b};
return Execution::Call(isolate, function, undefined(), 2, args, false);
@@ -183,8 +149,78 @@ class FunctionTester : public InitializedHandleScope {
Handle<Object> false_value() { return isolate->factory()->false_value(); }
+ Handle<JSFunction> Compile(Handle<JSFunction> function) {
+// TODO(titzer): make this method private.
+#if V8_TURBOFAN_TARGET
+ CompilationInfoWithZone info(function);
+
+ CHECK(Parser::Parse(&info));
+ info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
+ if (flags_ & CompilationInfo::kContextSpecializing) {
+ info.MarkAsContextSpecializing();
+ }
+ if (flags_ & CompilationInfo::kInliningEnabled) {
+ info.MarkAsInliningEnabled();
+ }
+ if (flags_ & CompilationInfo::kTypingEnabled) {
+ info.MarkAsTypingEnabled();
+ }
+ CHECK(Compiler::Analyze(&info));
+ CHECK(Compiler::EnsureDeoptimizationSupport(&info));
+
+ Pipeline pipeline(&info);
+ Handle<Code> code = pipeline.GenerateCode();
+ if (FLAG_turbo_deoptimization) {
+ info.context()->native_context()->AddOptimizedCode(*code);
+ }
+
+ CHECK(!code.is_null());
+ function->ReplaceCode(*code);
+#elif USE_CRANKSHAFT
+ Handle<Code> unoptimized = Handle<Code>(function->code());
+ Handle<Code> code = Compiler::GetOptimizedCode(function, unoptimized,
+ Compiler::NOT_CONCURRENT);
+ CHECK(!code.is_null());
+#if ENABLE_DISASSEMBLER
+ if (FLAG_print_opt_code) {
+ CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
+ code->Disassemble("test code", tracing_scope.file());
+ }
+#endif
+ function->ReplaceCode(*code);
+#endif
+ return function;
+ }
+
+ static Handle<JSFunction> ForMachineGraph(Graph* graph) {
+ JSFunction* p = NULL;
+ { // because of the implicit handle scope of FunctionTester.
+ FunctionTester f(graph);
+ p = *f.function;
+ }
+ return Handle<JSFunction>(p); // allocated in outer handle scope.
+ }
+
private:
uint32_t flags_;
+
+ // Compile the given machine graph instead of the source of the function
+ // and replace the JSFunction's code with the result.
+ Handle<JSFunction> CompileGraph(Graph* graph) {
+ CHECK(Pipeline::SupportedTarget());
+ CompilationInfoWithZone info(function);
+
+ CHECK(Parser::Parse(&info));
+ info.SetOptimizing(BailoutId::None(),
+ Handle<Code>(function->shared()->code()));
+ CHECK(Compiler::Analyze(&info));
+ CHECK(Compiler::EnsureDeoptimizationSupport(&info));
+
+ Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, graph);
+ CHECK(!code.is_null());
+ function->ReplaceCode(*code);
+ return function;
+ }
};
}
}
diff --git a/test/cctest/compiler/graph-builder-tester.cc b/test/cctest/compiler/graph-builder-tester.cc
index bfa82264..b0f470b0 100644
--- a/test/cctest/compiler/graph-builder-tester.cc
+++ b/test/cctest/compiler/graph-builder-tester.cc
@@ -35,11 +35,9 @@ byte* MachineCallHelper::Generate() {
if (!Pipeline::SupportedBackend()) return NULL;
if (code_.is_null()) {
Zone* zone = graph_->zone();
- CompilationInfo info(zone->isolate(), zone);
- Linkage linkage(&info,
- Linkage::GetSimplifiedCDescriptor(zone, machine_sig_));
- Pipeline pipeline(&info);
- code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph_);
+ CallDescriptor* desc =
+ Linkage::GetSimplifiedCDescriptor(zone, machine_sig_);
+ code_ = Pipeline::GenerateCodeForTesting(desc, graph_);
}
return code_.ToHandleChecked()->entry();
}
diff --git a/test/cctest/compiler/graph-builder-tester.h b/test/cctest/compiler/graph-builder-tester.h
index df792508..772de4d1 100644
--- a/test/cctest/compiler/graph-builder-tester.h
+++ b/test/cctest/compiler/graph-builder-tester.h
@@ -27,8 +27,8 @@ class DirectGraphBuilder : public GraphBuilder {
protected:
virtual Node* MakeNode(const Operator* op, int value_input_count,
- Node** value_inputs) FINAL {
- return graph()->NewNode(op, value_input_count, value_inputs);
+ Node** value_inputs, bool incomplete) FINAL {
+ return graph()->NewNode(op, value_input_count, value_inputs, incomplete);
}
};
@@ -61,6 +61,7 @@ class GraphAndBuilders {
explicit GraphAndBuilders(Zone* zone)
: main_graph_(new (zone) Graph(zone)),
main_common_(zone),
+ main_machine_(zone),
main_simplified_(zone) {}
protected:
diff --git a/test/cctest/compiler/simplified-graph-builder.cc b/test/cctest/compiler/simplified-graph-builder.cc
index c44d5ed5..baa03fbb 100644
--- a/test/cctest/compiler/simplified-graph-builder.cc
+++ b/test/cctest/compiler/simplified-graph-builder.cc
@@ -5,7 +5,6 @@
#include "test/cctest/compiler/simplified-graph-builder.h"
#include "src/compiler/operator-properties.h"
-#include "src/compiler/operator-properties-inl.h"
namespace v8 {
namespace internal {
@@ -45,20 +44,20 @@ void SimplifiedGraphBuilder::End() {
Node* SimplifiedGraphBuilder::MakeNode(const Operator* op,
int value_input_count,
- Node** value_inputs) {
- DCHECK(op->InputCount() == value_input_count);
+ Node** value_inputs, bool incomplete) {
+ DCHECK(op->ValueInputCount() == value_input_count);
DCHECK(!OperatorProperties::HasContextInput(op));
DCHECK(!OperatorProperties::HasFrameStateInput(op));
- bool has_control = OperatorProperties::GetControlInputCount(op) == 1;
- bool has_effect = OperatorProperties::GetEffectInputCount(op) == 1;
+ bool has_control = op->ControlInputCount() == 1;
+ bool has_effect = op->EffectInputCount() == 1;
- DCHECK(OperatorProperties::GetControlInputCount(op) < 2);
- DCHECK(OperatorProperties::GetEffectInputCount(op) < 2);
+ DCHECK(op->ControlInputCount() < 2);
+ DCHECK(op->EffectInputCount() < 2);
Node* result = NULL;
if (!has_control && !has_effect) {
- result = graph()->NewNode(op, value_input_count, value_inputs);
+ result = graph()->NewNode(op, value_input_count, value_inputs, incomplete);
} else {
int input_count_with_deps = value_input_count;
if (has_control) ++input_count_with_deps;
@@ -72,14 +71,12 @@ Node* SimplifiedGraphBuilder::MakeNode(const Operator* op,
if (has_control) {
*current_input++ = graph()->start();
}
- result = graph()->NewNode(op, input_count_with_deps, buffer);
+ result = graph()->NewNode(op, input_count_with_deps, buffer, incomplete);
if (has_effect) {
effect_ = result;
}
- if (OperatorProperties::HasControlOutput(result->op())) {
- // This graph builder does not support control flow.
- UNREACHABLE();
- }
+ // This graph builder does not support control flow.
+ CHECK_EQ(0, op->ControlOutputCount());
}
return result;
diff --git a/test/cctest/compiler/simplified-graph-builder.h b/test/cctest/compiler/simplified-graph-builder.h
index 1b637b76..537094a3 100644
--- a/test/cctest/compiler/simplified-graph-builder.h
+++ b/test/cctest/compiler/simplified-graph-builder.h
@@ -45,8 +45,8 @@ class SimplifiedGraphBuilder : public GraphBuilder {
Node* Int32Constant(int32_t value) {
return NewNode(common()->Int32Constant(value));
}
- Node* HeapConstant(Handle<Object> object) {
- Unique<Object> val = Unique<Object>::CreateUninitialized(object);
+ Node* HeapConstant(Handle<HeapObject> object) {
+ Unique<HeapObject> val = Unique<HeapObject>::CreateUninitialized(object);
return NewNode(common()->HeapConstant(val));
}
@@ -127,19 +127,17 @@ class SimplifiedGraphBuilder : public GraphBuilder {
Node* StoreField(const FieldAccess& access, Node* object, Node* value) {
return NewNode(simplified()->StoreField(access), object, value);
}
- Node* LoadElement(const ElementAccess& access, Node* object, Node* index,
- Node* length) {
- return NewNode(simplified()->LoadElement(access), object, index, length);
+ Node* LoadElement(const ElementAccess& access, Node* object, Node* index) {
+ return NewNode(simplified()->LoadElement(access), object, index);
}
Node* StoreElement(const ElementAccess& access, Node* object, Node* index,
- Node* length, Node* value) {
- return NewNode(simplified()->StoreElement(access), object, index, length,
- value);
+ Node* value) {
+ return NewNode(simplified()->StoreElement(access), object, index, value);
}
protected:
virtual Node* MakeNode(const Operator* op, int value_input_count,
- Node** value_inputs) FINAL;
+ Node** value_inputs, bool incomplete) FINAL;
private:
Node* effect_;
diff --git a/test/cctest/compiler/test-basic-block-profiler.cc b/test/cctest/compiler/test-basic-block-profiler.cc
new file mode 100644
index 00000000..703fc176
--- /dev/null
+++ b/test/cctest/compiler/test-basic-block-profiler.cc
@@ -0,0 +1,114 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/basic-block-profiler.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+typedef RawMachineAssembler::Label MLabel;
+
+class BasicBlockProfilerTest : public RawMachineAssemblerTester<int32_t> {
+ public:
+ BasicBlockProfilerTest() : RawMachineAssemblerTester<int32_t>(kMachInt32) {
+ FLAG_turbo_profiling = true;
+ }
+
+ void ResetCounts() { isolate()->basic_block_profiler()->ResetCounts(); }
+
+ void Expect(size_t size, uint32_t* expected) {
+ CHECK_NE(NULL, isolate()->basic_block_profiler());
+ const BasicBlockProfiler::DataList* l =
+ isolate()->basic_block_profiler()->data_list();
+ CHECK_NE(0, static_cast<int>(l->size()));
+ const BasicBlockProfiler::Data* data = l->back();
+ CHECK_EQ(static_cast<int>(size), static_cast<int>(data->n_blocks()));
+ const uint32_t* counts = data->counts();
+ for (size_t i = 0; i < size; ++i) {
+ CHECK_EQ(static_cast<int>(expected[i]), static_cast<int>(counts[i]));
+ }
+ }
+};
+
+
+TEST(ProfileDiamond) {
+ BasicBlockProfilerTest m;
+
+ MLabel blocka, blockb, end;
+ m.Branch(m.Parameter(0), &blocka, &blockb);
+ m.Bind(&blocka);
+ m.Goto(&end);
+ m.Bind(&blockb);
+ m.Goto(&end);
+ m.Bind(&end);
+ m.Return(m.Int32Constant(0));
+
+ m.GenerateCode();
+ {
+ uint32_t expected[] = {0, 0, 0, 0};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.Call(0);
+ {
+ uint32_t expected[] = {1, 1, 0, 1};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.ResetCounts();
+
+ m.Call(1);
+ {
+ uint32_t expected[] = {1, 0, 1, 1};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.Call(0);
+ {
+ uint32_t expected[] = {2, 1, 1, 2};
+ m.Expect(arraysize(expected), expected);
+ }
+}
+
+
+TEST(ProfileLoop) {
+ BasicBlockProfilerTest m;
+
+ MLabel header, body, end;
+ Node* one = m.Int32Constant(1);
+ m.Goto(&header);
+
+ m.Bind(&header);
+ Node* count = m.Phi(kMachInt32, m.Parameter(0), one);
+ m.Branch(count, &body, &end);
+
+ m.Bind(&body);
+ count->ReplaceInput(1, m.Int32Sub(count, one));
+ m.Goto(&header);
+
+ m.Bind(&end);
+ m.Return(one);
+
+ m.GenerateCode();
+ {
+ uint32_t expected[] = {0, 0, 0, 0};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ uint32_t runs[] = {0, 1, 500, 10000};
+ for (size_t i = 0; i < arraysize(runs); i++) {
+ m.ResetCounts();
+ CHECK_EQ(1, m.Call(static_cast<int>(runs[i])));
+ uint32_t expected[] = {1, runs[i] + 1, runs[i], 1};
+ m.Expect(arraysize(expected), expected);
+ }
+}
+
+#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-changes-lowering.cc b/test/cctest/compiler/test-changes-lowering.cc
index 06308a0b..5795754c 100644
--- a/test/cctest/compiler/test-changes-lowering.cc
+++ b/test/cctest/compiler/test-changes-lowering.cc
@@ -6,10 +6,11 @@
#include "src/compiler/change-lowering.h"
#include "src/compiler/control-builders.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/pipeline.h"
+#include "src/compiler/select-lowering.h"
+#include "src/compiler/simplified-lowering.h"
#include "src/compiler/typer.h"
#include "src/compiler/verifier.h"
#include "src/execution.h"
@@ -19,6 +20,7 @@
#include "src/scopes.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/function-tester.h"
#include "test/cctest/compiler/graph-builder-tester.h"
#include "test/cctest/compiler/value-helper.h"
@@ -30,13 +32,10 @@ class ChangesLoweringTester : public GraphBuilderTester<ReturnType> {
public:
explicit ChangesLoweringTester(MachineType p0 = kMachNone)
: GraphBuilderTester<ReturnType>(p0),
- typer(this->zone()),
javascript(this->zone()),
- jsgraph(this->graph(), this->common(), &javascript, &typer,
- this->machine()),
+ jsgraph(this->graph(), this->common(), &javascript, this->machine()),
function(Handle<JSFunction>::null()) {}
- Typer typer;
JSOperatorBuilder javascript;
JSGraph jsgraph;
Handle<JSFunction> function;
@@ -45,29 +44,10 @@ class ChangesLoweringTester : public GraphBuilderTester<ReturnType> {
template <typename T>
T* CallWithPotentialGC() {
- // TODO(titzer): we need to wrap the code in a JSFunction and call it via
- // Execution::Call() so that the GC knows about the frame, can walk it,
- // relocate the code object if necessary, etc.
- // This is pretty ugly and at the least should be moved up to helpers.
+ // TODO(titzer): we wrap the code in a JSFunction here to reuse the
+ // JSEntryStub; that could be done with a special prologue or other stub.
if (function.is_null()) {
- function =
- v8::Utils::OpenHandle(*v8::Handle<v8::Function>::Cast(CompileRun(
- "(function() { 'use strict'; return 2.7123; })")));
- CompilationInfoWithZone info(function);
- CHECK(Parser::Parse(&info));
- info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
- CHECK_NE(NULL, info.scope());
- Handle<ScopeInfo> scope_info =
- ScopeInfo::Create(info.scope(), info.zone());
- info.shared_info()->set_scope_info(*scope_info);
- Pipeline pipeline(&info);
- Linkage linkage(&info);
- Handle<Code> code =
- pipeline.GenerateCodeForMachineGraph(&linkage, this->graph());
- CHECK(!code.is_null());
- function->ReplaceCode(*code);
+ function = FunctionTester::ForMachineGraph(this->graph());
}
Handle<Object>* args = NULL;
MaybeHandle<Object> result =
@@ -132,9 +112,9 @@ class ChangesLoweringTester : public GraphBuilderTester<ReturnType> {
void* location) {
// We build a graph by hand here, because the raw machine assembler
// does not add the correct control and effect nodes.
- Node* load =
- this->graph()->NewNode(load_op, this->PointerConstant(location),
- this->Int32Constant(0), this->start());
+ Node* load = this->graph()->NewNode(
+ load_op, this->PointerConstant(location), this->Int32Constant(0),
+ this->start(), this->start());
Node* change = this->graph()->NewNode(op, load);
Node* ret = this->graph()->NewNode(this->common()->Return(), change,
this->start(), this->start());
@@ -146,12 +126,16 @@ class ChangesLoweringTester : public GraphBuilderTester<ReturnType> {
void LowerChange(Node* change) {
// Run the graph reducer with changes lowering on a single node.
CompilationInfo info(this->isolate(), this->zone());
- Linkage linkage(&info);
- ChangeLowering lowering(&jsgraph, &linkage);
- GraphReducer reducer(this->graph());
- reducer.AddReducer(&lowering);
+ Linkage linkage(this->zone(), &info);
+ Typer typer(this->graph(), info.context());
+ typer.Run();
+ ChangeLowering change_lowering(&jsgraph, &linkage);
+ SelectLowering select_lowering(this->graph(), this->common());
+ GraphReducer reducer(this->graph(), this->zone());
+ reducer.AddReducer(&change_lowering);
+ reducer.AddReducer(&select_lowering);
reducer.ReduceNode(change);
- Verifier::Run(this->graph());
+ Verifier::Run(this->graph(), Verifier::UNTYPED);
}
Factory* factory() { return this->isolate()->factory(); }
diff --git a/test/cctest/compiler/test-codegen-deopt.cc b/test/cctest/compiler/test-codegen-deopt.cc
index 8217229b..56afe7b6 100644
--- a/test/cctest/compiler/test-codegen-deopt.cc
+++ b/test/cctest/compiler/test-codegen-deopt.cc
@@ -16,6 +16,7 @@
#include "src/compiler/register-allocator.h"
#include "src/compiler/schedule.h"
+#include "src/ast-numbering.h"
#include "src/full-codegen.h"
#include "src/parser.h"
#include "src/rewriter.h"
@@ -30,6 +31,7 @@ using namespace v8::internal::compiler;
#if V8_TURBOFAN_TARGET
typedef RawMachineAssembler::Label MLabel;
+typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
static Handle<JSFunction> NewFunction(const char* source) {
return v8::Utils::OpenHandle(
@@ -46,8 +48,7 @@ class DeoptCodegenTester {
bailout_id(-1) {
CHECK(Parser::Parse(&info));
info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
+ CHECK(Compiler::Analyze(&info));
CHECK(Compiler::EnsureDeoptimizationSupport(&info));
DCHECK(info.shared_info()->has_deoptimization_support());
@@ -55,38 +56,14 @@ class DeoptCodegenTester {
graph = new (scope_->main_zone()) Graph(scope_->main_zone());
}
- virtual ~DeoptCodegenTester() { delete code; }
+ virtual ~DeoptCodegenTester() {}
void GenerateCodeFromSchedule(Schedule* schedule) {
OFStream os(stdout);
if (FLAG_trace_turbo) {
os << *schedule;
}
-
- // Initialize the codegen and generate code.
- Linkage* linkage = new (scope_->main_zone()) Linkage(&info);
- code = new v8::internal::compiler::InstructionSequence(linkage, graph,
- schedule);
- SourcePositionTable source_positions(graph);
- InstructionSelector selector(code, &source_positions);
- selector.SelectInstructions();
-
- if (FLAG_trace_turbo) {
- os << "----- Instruction sequence before register allocation -----\n"
- << *code;
- }
-
- RegisterAllocator allocator(code);
- CHECK(allocator.Allocate());
-
- if (FLAG_trace_turbo) {
- os << "----- Instruction sequence after register allocation -----\n"
- << *code;
- }
-
- compiler::CodeGenerator generator(code);
- result_code = generator.GenerateCode();
-
+ result_code = Pipeline::GenerateCodeForTesting(&info, graph, schedule);
#ifdef OBJECT_PRINT
if (FLAG_print_opt_code || FLAG_trace_turbo) {
result_code->Print();
@@ -101,7 +78,7 @@ class DeoptCodegenTester {
CompilationInfo info;
BailoutId bailout_id;
Handle<Code> result_code;
- v8::internal::compiler::InstructionSequence* code;
+ TestInstrSeq* code;
Graph* graph;
};
@@ -129,13 +106,13 @@ class TrivialDeoptCodegenTester : public DeoptCodegenTester {
Handle<JSFunction> deopt_function =
NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
- Unique<Object> deopt_fun_constant =
- Unique<Object>::CreateUninitialized(deopt_function);
+ Unique<JSFunction> deopt_fun_constant =
+ Unique<JSFunction>::CreateUninitialized(deopt_function);
Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
Handle<Context> caller_context(function->context(), CcTest::i_isolate());
- Unique<Object> caller_context_constant =
- Unique<Object>::CreateUninitialized(caller_context);
+ Unique<Context> caller_context_constant =
+ Unique<Context>::CreateUninitialized(caller_context);
Node* caller_context_node =
m.NewNode(common.HeapConstant(caller_context_constant));
@@ -145,12 +122,13 @@ class TrivialDeoptCodegenTester : public DeoptCodegenTester {
Node* stack = m.NewNode(common.StateValues(0));
Node* state_node = m.NewNode(
- common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
- locals, stack, caller_context_node, m.UndefinedConstant());
+ common.FrameState(JS_FRAME, bailout_id,
+ OutputFrameStateCombine::Ignore()),
+ parameters, locals, stack, caller_context_node, m.UndefinedConstant());
Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
- Unique<Object> context_constant =
- Unique<Object>::CreateUninitialized(context);
+ Unique<Context> context_constant =
+ Unique<Context>::CreateUninitialized(context);
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
@@ -244,13 +222,13 @@ class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
CSignature1<Object*, Object*> sig;
RawMachineAssembler m(graph, &sig);
- Unique<Object> this_fun_constant =
- Unique<Object>::CreateUninitialized(function);
+ Unique<HeapObject> this_fun_constant =
+ Unique<HeapObject>::CreateUninitialized(function);
Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
Handle<Context> context(function->context(), CcTest::i_isolate());
- Unique<Object> context_constant =
- Unique<Object>::CreateUninitialized(context);
+ Unique<HeapObject> context_constant =
+ Unique<HeapObject>::CreateUninitialized(context);
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
bailout_id = GetCallBailoutId();
@@ -259,8 +237,9 @@ class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
Node* stack = m.NewNode(common.StateValues(0));
Node* state_node = m.NewNode(
- common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
- locals, stack, context_node, m.UndefinedConstant());
+ common.FrameState(JS_FRAME, bailout_id,
+ OutputFrameStateCombine::Ignore()),
+ parameters, locals, stack, context_node, m.UndefinedConstant());
m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
state_node);
diff --git a/test/cctest/compiler/test-control-reducer.cc b/test/cctest/compiler/test-control-reducer.cc
new file mode 100644
index 00000000..03aa50b3
--- /dev/null
+++ b/test/cctest/compiler/test-control-reducer.cc
@@ -0,0 +1,1678 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/base/bits.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/control-reducer.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/node-properties-inl.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static const size_t kNumLeafs = 4;
+
+// TODO(titzer): convert this whole file into unit tests.
+
+static int CheckInputs(Node* node, Node* i0 = NULL, Node* i1 = NULL,
+ Node* i2 = NULL) {
+ int count = 3;
+ if (i2 == NULL) count = 2;
+ if (i1 == NULL) count = 1;
+ if (i0 == NULL) count = 0;
+ CHECK_EQ(count, node->InputCount());
+ if (i0 != NULL) CHECK_EQ(i0, node->InputAt(0));
+ if (i1 != NULL) CHECK_EQ(i1, node->InputAt(1));
+ if (i2 != NULL) CHECK_EQ(i2, node->InputAt(2));
+ return count;
+}
+
+
+static int CheckMerge(Node* node, Node* i0 = NULL, Node* i1 = NULL,
+ Node* i2 = NULL) {
+ CHECK_EQ(IrOpcode::kMerge, node->opcode());
+ int count = CheckInputs(node, i0, i1, i2);
+ CHECK_EQ(count, node->op()->ControlInputCount());
+ return count;
+}
+
+
+static int CheckLoop(Node* node, Node* i0 = NULL, Node* i1 = NULL,
+ Node* i2 = NULL) {
+ CHECK_EQ(IrOpcode::kLoop, node->opcode());
+ int count = CheckInputs(node, i0, i1, i2);
+ CHECK_EQ(count, node->op()->ControlInputCount());
+ return count;
+}
+
+
+bool IsUsedBy(Node* a, Node* b) {
+ for (UseIter i = a->uses().begin(); i != a->uses().end(); ++i) {
+ if (b == *i) return true;
+ }
+ return false;
+}
+
+
+// A helper for all tests dealing with ControlTester.
+class ControlReducerTester : HandleAndZoneScope {
+ public:
+ ControlReducerTester()
+ : isolate(main_isolate()),
+ common(main_zone()),
+ graph(main_zone()),
+ jsgraph(&graph, &common, NULL, NULL),
+ start(graph.NewNode(common.Start(1))),
+ end(graph.NewNode(common.End(), start)),
+ p0(graph.NewNode(common.Parameter(0), start)),
+ zero(jsgraph.Int32Constant(0)),
+ one(jsgraph.OneConstant()),
+ half(jsgraph.Constant(0.5)),
+ self(graph.NewNode(common.Int32Constant(0xaabbccdd))),
+ dead(graph.NewNode(common.Dead())) {
+ graph.SetEnd(end);
+ graph.SetStart(start);
+ leaf[0] = zero;
+ leaf[1] = one;
+ leaf[2] = half;
+ leaf[3] = p0;
+ }
+
+ Isolate* isolate;
+ CommonOperatorBuilder common;
+ Graph graph;
+ JSGraph jsgraph;
+ Node* start;
+ Node* end;
+ Node* p0;
+ Node* zero;
+ Node* one;
+ Node* half;
+ Node* self;
+ Node* dead;
+ Node* leaf[kNumLeafs];
+
+ Node* Phi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, false), a, start));
+ }
+
+ Node* Phi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, false), a, b, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, false), a, b, c, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, false), a, b, c, d, start));
+ }
+
+ Node* EffectPhi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, true), a, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, true), a, b, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, true), a, b, c, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, true), a, b, c, d, start));
+ }
+
+ Node* SetSelfReferences(Node* node) {
+ for (Edge edge : node->input_edges()) {
+ if (edge.to() == self) node->ReplaceInput(edge.index(), node);
+ }
+ return node;
+ }
+
+ const Operator* op(int count, bool effect) {
+ return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count);
+ }
+
+ void Trim() { ControlReducer::TrimGraph(main_zone(), &jsgraph); }
+
+ void ReduceGraph() {
+ ControlReducer::ReduceGraph(main_zone(), &jsgraph, &common);
+ }
+
+ // Checks one-step reduction of a phi.
+ void ReducePhi(Node* expect, Node* phi) {
+ Node* result = ControlReducer::ReducePhiForTesting(&jsgraph, &common, phi);
+ CHECK_EQ(expect, result);
+ ReducePhiIterative(expect, phi); // iterative should give the same result.
+ }
+
+ void ReducePhiIterative(Node* expect, Node* phi) {
+ p0->ReplaceInput(0, start); // hack: parameters may be trimmed.
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret);
+ graph.SetEnd(end);
+ ControlReducer::ReduceGraph(main_zone(), &jsgraph, &common);
+ CheckInputs(end, ret);
+ CheckInputs(ret, expect, start, start);
+ }
+
+ void ReduceMerge(Node* expect, Node* merge) {
+ Node* result =
+ ControlReducer::ReduceMergeForTesting(&jsgraph, &common, merge);
+ CHECK_EQ(expect, result);
+ }
+
+ void ReduceMergeIterative(Node* expect, Node* merge) {
+ p0->ReplaceInput(0, start); // hack: parameters may be trimmed.
+ Node* end = graph.NewNode(common.End(), merge);
+ graph.SetEnd(end);
+ ReduceGraph();
+ CheckInputs(end, expect);
+ }
+
+ void ReduceBranch(Node* expect, Node* branch) {
+ Node* result =
+ ControlReducer::ReduceBranchForTesting(&jsgraph, &common, branch);
+ CHECK_EQ(expect, result);
+ }
+
+ Node* Return(Node* val, Node* effect, Node* control) {
+ Node* ret = graph.NewNode(common.Return(), val, effect, control);
+ end->ReplaceInput(0, ret);
+ return ret;
+ }
+};
+
+
+TEST(Trim1_live) {
+ ControlReducerTester T;
+ CHECK(IsUsedBy(T.start, T.p0));
+ T.graph.SetEnd(T.p0);
+ T.Trim();
+ CHECK(IsUsedBy(T.start, T.p0));
+ CheckInputs(T.p0, T.start);
+}
+
+
+TEST(Trim1_dead) {
+ ControlReducerTester T;
+ CHECK(IsUsedBy(T.start, T.p0));
+ T.Trim();
+ CHECK(!IsUsedBy(T.start, T.p0));
+ CHECK_EQ(NULL, T.p0->InputAt(0));
+}
+
+
+TEST(Trim2_live) {
+ ControlReducerTester T;
+ Node* phi =
+ T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
+ CHECK(IsUsedBy(T.one, phi));
+ CHECK(IsUsedBy(T.half, phi));
+ CHECK(IsUsedBy(T.start, phi));
+ T.graph.SetEnd(phi);
+ T.Trim();
+ CHECK(IsUsedBy(T.one, phi));
+ CHECK(IsUsedBy(T.half, phi));
+ CHECK(IsUsedBy(T.start, phi));
+ CheckInputs(phi, T.one, T.half, T.start);
+}
+
+
+TEST(Trim2_dead) {
+ ControlReducerTester T;
+ Node* phi =
+ T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
+ CHECK(IsUsedBy(T.one, phi));
+ CHECK(IsUsedBy(T.half, phi));
+ CHECK(IsUsedBy(T.start, phi));
+ T.Trim();
+ CHECK(!IsUsedBy(T.one, phi));
+ CHECK(!IsUsedBy(T.half, phi));
+ CHECK(!IsUsedBy(T.start, phi));
+ CHECK_EQ(NULL, phi->InputAt(0));
+ CHECK_EQ(NULL, phi->InputAt(1));
+ CHECK_EQ(NULL, phi->InputAt(2));
+}
+
+
+TEST(Trim_chain1) {
+ ControlReducerTester T;
+ const int kDepth = 15;
+ Node* live[kDepth];
+ Node* dead[kDepth];
+ Node* end = T.start;
+ for (int i = 0; i < kDepth; i++) {
+ live[i] = end = T.graph.NewNode(T.common.Merge(1), end);
+ dead[i] = T.graph.NewNode(T.common.Merge(1), end);
+ }
+ // end -> live[last] -> live[last-1] -> ... -> start
+ // dead[last] ^ dead[last-1] ^ ... ^
+ T.graph.SetEnd(end);
+ T.Trim();
+ for (int i = 0; i < kDepth; i++) {
+ CHECK(!IsUsedBy(live[i], dead[i]));
+ CHECK_EQ(NULL, dead[i]->InputAt(0));
+ CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
+ }
+}
+
+
+TEST(Trim_chain2) {
+ ControlReducerTester T;
+ const int kDepth = 15;
+ Node* live[kDepth];
+ Node* dead[kDepth];
+ Node* l = T.start;
+ Node* d = T.start;
+ for (int i = 0; i < kDepth; i++) {
+ live[i] = l = T.graph.NewNode(T.common.Merge(1), l);
+ dead[i] = d = T.graph.NewNode(T.common.Merge(1), d);
+ }
+ // end -> live[last] -> live[last-1] -> ... -> start
+ // dead[last] -> dead[last-1] -> ... -> start
+ T.graph.SetEnd(l);
+ T.Trim();
+ CHECK(!IsUsedBy(T.start, dead[0]));
+ for (int i = 0; i < kDepth; i++) {
+ CHECK_EQ(i == 0 ? NULL : dead[i - 1], dead[i]->InputAt(0));
+ CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
+ }
+}
+
+
+TEST(Trim_cycle1) {
+ ControlReducerTester T;
+ Node* loop = T.graph.NewNode(T.common.Loop(1), T.start, T.start);
+ loop->ReplaceInput(1, loop);
+ Node* end = T.graph.NewNode(T.common.End(), loop);
+ T.graph.SetEnd(end);
+
+ CHECK(IsUsedBy(T.start, loop));
+ CHECK(IsUsedBy(loop, end));
+ CHECK(IsUsedBy(loop, loop));
+
+ T.Trim();
+
+ // nothing should have happened to the loop itself.
+ CHECK(IsUsedBy(T.start, loop));
+ CHECK(IsUsedBy(loop, end));
+ CHECK(IsUsedBy(loop, loop));
+ CheckInputs(loop, T.start, loop);
+ CheckInputs(end, loop);
+}
+
+
+TEST(Trim_cycle2) {
+ ControlReducerTester T;
+ Node* loop = T.graph.NewNode(T.common.Loop(2), T.start, T.start);
+ loop->ReplaceInput(1, loop);
+ Node* end = T.graph.NewNode(T.common.End(), loop);
+ Node* phi =
+ T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, loop);
+ T.graph.SetEnd(end);
+
+ CHECK(IsUsedBy(T.start, loop));
+ CHECK(IsUsedBy(loop, end));
+ CHECK(IsUsedBy(loop, loop));
+ CHECK(IsUsedBy(loop, phi));
+ CHECK(IsUsedBy(T.one, phi));
+ CHECK(IsUsedBy(T.half, phi));
+
+ T.Trim();
+
+ // nothing should have happened to the loop itself.
+ CHECK(IsUsedBy(T.start, loop));
+ CHECK(IsUsedBy(loop, end));
+ CHECK(IsUsedBy(loop, loop));
+ CheckInputs(loop, T.start, loop);
+ CheckInputs(end, loop);
+
+ // phi should have been trimmed away.
+ CHECK(!IsUsedBy(loop, phi));
+ CHECK(!IsUsedBy(T.one, phi));
+ CHECK(!IsUsedBy(T.half, phi));
+ CHECK_EQ(NULL, phi->InputAt(0));
+ CHECK_EQ(NULL, phi->InputAt(1));
+ CHECK_EQ(NULL, phi->InputAt(2));
+}
+
+
+void CheckTrimConstant(ControlReducerTester* T, Node* k) {
+ Node* phi = T->graph.NewNode(T->common.Phi(kMachInt32, 1), k, T->start);
+ CHECK(IsUsedBy(k, phi));
+ T->Trim();
+ CHECK(!IsUsedBy(k, phi));
+ CHECK_EQ(NULL, phi->InputAt(0));
+ CHECK_EQ(NULL, phi->InputAt(1));
+}
+
+
+TEST(Trim_constants) {
+ ControlReducerTester T;
+ int32_t int32_constants[] = {
+ 0, -1, -2, 2, 2, 3, 3, 4, 4, 5, 5, 4, 5, 6, 6, 7, 8, 7, 8, 9,
+ 0, -11, -12, 12, 12, 13, 13, 14, 14, 15, 15, 14, 15, 6, 6, 7, 8, 7, 8, 9};
+
+ for (size_t i = 0; i < arraysize(int32_constants); i++) {
+ CheckTrimConstant(&T, T.jsgraph.Int32Constant(int32_constants[i]));
+ CheckTrimConstant(&T, T.jsgraph.Float64Constant(int32_constants[i]));
+ CheckTrimConstant(&T, T.jsgraph.Constant(int32_constants[i]));
+ }
+
+ Node* other_constants[] = {
+ T.jsgraph.UndefinedConstant(), T.jsgraph.TheHoleConstant(),
+ T.jsgraph.TrueConstant(), T.jsgraph.FalseConstant(),
+ T.jsgraph.NullConstant(), T.jsgraph.ZeroConstant(),
+ T.jsgraph.OneConstant(), T.jsgraph.NaNConstant(),
+ T.jsgraph.Constant(21), T.jsgraph.Constant(22.2)};
+
+ for (size_t i = 0; i < arraysize(other_constants); i++) {
+ CheckTrimConstant(&T, other_constants[i]);
+ }
+}
+
+
+TEST(CReducePhi1) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.Phi(R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.Phi(R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.Phi(R.leaf[3]));
+}
+
+
+TEST(CReducePhi1_dead) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[1], R.Phi(R.leaf[1], R.dead));
+ R.ReducePhi(R.leaf[2], R.Phi(R.leaf[2], R.dead));
+ R.ReducePhi(R.leaf[3], R.Phi(R.leaf[3], R.dead));
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.dead, R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.Phi(R.dead, R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.Phi(R.dead, R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.Phi(R.dead, R.leaf[3]));
+}
+
+
+TEST(CReducePhi1_dead2) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.leaf[0], R.dead, R.dead));
+ R.ReducePhi(R.leaf[0], R.Phi(R.dead, R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[0], R.Phi(R.dead, R.dead, R.leaf[0]));
+}
+
+
+TEST(CReducePhi2a) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a));
+ }
+}
+
+
+TEST(CReducePhi2b) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a));
+ R.ReducePhi(a, R.Phi(a, R.self));
+ }
+}
+
+
+TEST(CReducePhi2c) {
+ ControlReducerTester R;
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b);
+ R.ReducePhi(phi2, phi2);
+ }
+}
+
+
+TEST(CReducePhi2_dead) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a, R.dead));
+ R.ReducePhi(a, R.Phi(a, R.dead, a));
+ R.ReducePhi(a, R.Phi(R.dead, a, a));
+ }
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a));
+ R.ReducePhi(a, R.Phi(a, R.self));
+ R.ReducePhi(a, R.Phi(R.self, a, R.dead));
+ R.ReducePhi(a, R.Phi(a, R.self, R.dead));
+ }
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a, R.dead);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b, R.dead);
+ R.ReducePhi(phi2, phi2);
+ }
+}
+
+
+TEST(CReducePhi3) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a, a));
+ }
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a, a));
+ R.ReducePhi(a, R.Phi(a, R.self, a));
+ R.ReducePhi(a, R.Phi(a, a, R.self));
+ }
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a, a);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b, a);
+ R.ReducePhi(phi2, phi2);
+
+ Node* phi3 = R.Phi(a, a, b);
+ R.ReducePhi(phi3, phi3);
+ }
+}
+
+
+TEST(CReducePhi4) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a, a, a));
+ }
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a, a, a));
+ R.ReducePhi(a, R.Phi(a, R.self, a, a));
+ R.ReducePhi(a, R.Phi(a, a, R.self, a));
+ R.ReducePhi(a, R.Phi(a, a, a, R.self));
+
+ R.ReducePhi(a, R.Phi(R.self, R.self, a, a));
+ R.ReducePhi(a, R.Phi(a, R.self, R.self, a));
+ R.ReducePhi(a, R.Phi(a, a, R.self, R.self));
+ R.ReducePhi(a, R.Phi(R.self, a, a, R.self));
+ }
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a, a, a);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b, a, a);
+ R.ReducePhi(phi2, phi2);
+
+ Node* phi3 = R.Phi(a, a, b, a);
+ R.ReducePhi(phi3, phi3);
+
+ Node* phi4 = R.Phi(a, a, a, b);
+ R.ReducePhi(phi4, phi4);
+ }
+}
+
+
+TEST(CReducePhi_iterative1) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.leaf[0], R.Phi(R.leaf[0])));
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0]), R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative2) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0]), R.Phi(R.leaf[0])));
+}
+
+
+TEST(CReducePhi_iterative3) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0],
+ R.Phi(R.leaf[0], R.Phi(R.leaf[0], R.leaf[0])));
+ R.ReducePhiIterative(R.leaf[0],
+ R.Phi(R.Phi(R.leaf[0], R.leaf[0]), R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative4) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0], R.leaf[0]),
+ R.Phi(R.leaf[0], R.leaf[0])));
+
+ Node* p1 = R.Phi(R.leaf[0], R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p1, p1));
+
+ Node* p2 = R.Phi(R.leaf[0], R.leaf[0], R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p2, p2, p2));
+
+ Node* p3 = R.Phi(R.leaf[0], R.leaf[0], R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p3, p3, R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative_self1) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.leaf[0], R.Phi(R.leaf[0], R.self)));
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0], R.self), R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative_self2) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(
+ R.leaf[0], R.Phi(R.Phi(R.leaf[0], R.self), R.Phi(R.leaf[0], R.self)));
+ R.ReducePhiIterative(
+ R.leaf[0], R.Phi(R.Phi(R.self, R.leaf[0]), R.Phi(R.self, R.leaf[0])));
+
+ Node* p1 = R.Phi(R.leaf[0], R.self);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p1, p1));
+
+ Node* p2 = R.Phi(R.self, R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p2, p2));
+}
+
+
+TEST(EReducePhi1) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.EffectPhi(R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.EffectPhi(R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.EffectPhi(R.leaf[3]));
+}
+
+
+TEST(EReducePhi1_dead) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[1], R.EffectPhi(R.leaf[1], R.dead));
+ R.ReducePhi(R.leaf[2], R.EffectPhi(R.leaf[2], R.dead));
+ R.ReducePhi(R.leaf[3], R.EffectPhi(R.leaf[3], R.dead));
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.dead, R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.EffectPhi(R.dead, R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.EffectPhi(R.dead, R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.EffectPhi(R.dead, R.leaf[3]));
+}
+
+
+TEST(EReducePhi1_dead2) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.leaf[0], R.dead, R.dead));
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.dead, R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.dead, R.dead, R.leaf[0]));
+}
+
+
+TEST(CMergeReduce_simple1) {
+ ControlReducerTester R;
+
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.start);
+ R.ReduceMerge(R.start, merge);
+}
+
+
+TEST(CMergeReduce_simple2) {
+ ControlReducerTester R;
+
+ Node* merge1 = R.graph.NewNode(R.common.Merge(1), R.start);
+ Node* merge2 = R.graph.NewNode(R.common.Merge(1), merge1);
+ R.ReduceMerge(merge1, merge2);
+ R.ReduceMergeIterative(R.start, merge2);
+}
+
+
+TEST(CMergeReduce_none1) {
+ ControlReducerTester R;
+
+ Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, R.start);
+ R.ReduceMerge(merge, merge);
+}
+
+
+TEST(CMergeReduce_none2) {
+ ControlReducerTester R;
+
+ Node* t = R.graph.NewNode(R.common.IfTrue(), R.start);
+ Node* f = R.graph.NewNode(R.common.IfFalse(), R.start);
+ Node* merge = R.graph.NewNode(R.common.Merge(2), t, f);
+ R.ReduceMerge(merge, merge);
+}
+
+
+TEST(CMergeReduce_self3) {
+ ControlReducerTester R;
+
+ Node* merge =
+ R.SetSelfReferences(R.graph.NewNode(R.common.Merge(2), R.start, R.self));
+ R.ReduceMerge(merge, merge);
+}
+
+
+TEST(CMergeReduce_dead1) {
+ ControlReducerTester R;
+
+ Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, R.dead);
+ R.ReduceMerge(R.start, merge);
+}
+
+
+TEST(CMergeReduce_dead2) {
+ ControlReducerTester R;
+
+ Node* merge1 = R.graph.NewNode(R.common.Merge(1), R.start);
+ Node* merge2 = R.graph.NewNode(R.common.Merge(2), merge1, R.dead);
+ R.ReduceMerge(merge1, merge2);
+ R.ReduceMergeIterative(R.start, merge2);
+}
+
+
+TEST(CMergeReduce_dead_rm1a) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ R.ReduceMerge(merge, merge);
+ CheckMerge(merge, R.start, R.start);
+ }
+}
+
+
+TEST(CMergeReduce_dead_rm1b) {
+ ControlReducerTester R;
+
+ Node* t = R.graph.NewNode(R.common.IfTrue(), R.start);
+ Node* f = R.graph.NewNode(R.common.IfFalse(), R.start);
+ for (int i = 0; i < 2; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
+ for (int j = i + 1; j < 3; j++) {
+ merge->ReplaceInput(i, t);
+ merge->ReplaceInput(j, f);
+ R.ReduceMerge(merge, merge);
+ CheckMerge(merge, t, f);
+ }
+ }
+}
+
+
+TEST(CMergeReduce_dead_rm2) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
+ merge->ReplaceInput(i, R.start);
+ R.ReduceMerge(R.start, merge);
+ }
+}
+
+
+TEST(CLoopReduce_dead_rm1) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* loop = R.graph.NewNode(R.common.Loop(3), R.dead, R.start, R.start);
+ R.ReduceMerge(loop, loop);
+ CheckLoop(loop, R.start, R.start);
+ }
+}
+
+
+TEST(CMergeReduce_edit_phi1) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ Node* phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 3), R.leaf[0],
+ R.leaf[1], R.leaf[2], merge);
+ R.ReduceMerge(merge, merge);
+ CHECK_EQ(IrOpcode::kPhi, phi->opcode());
+ CHECK_EQ(2, phi->op()->ValueInputCount());
+ CHECK_EQ(3, phi->InputCount());
+ CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0));
+ CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1));
+ CHECK_EQ(merge, phi->InputAt(2));
+ }
+}
+
+
+TEST(CMergeReduce_edit_effect_phi1) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ Node* phi = R.graph.NewNode(R.common.EffectPhi(3), R.leaf[0], R.leaf[1],
+ R.leaf[2], merge);
+ R.ReduceMerge(merge, merge);
+ CHECK_EQ(IrOpcode::kEffectPhi, phi->opcode());
+ CHECK_EQ(0, phi->op()->ValueInputCount());
+ CHECK_EQ(2, phi->op()->EffectInputCount());
+ CHECK_EQ(3, phi->InputCount());
+ CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0));
+ CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1));
+ CHECK_EQ(merge, phi->InputAt(2));
+ }
+}
+
+
+static const int kSelectorSize = 4;
+
+// Helper to select K of N nodes according to a mask, useful for the test below.
+struct Selector {
+ int mask;
+ int count;
+ explicit Selector(int m) {
+ mask = m;
+ count = v8::base::bits::CountPopulation32(m);
+ }
+ bool is_selected(int i) { return (mask & (1 << i)) != 0; }
+ void CheckNode(Node* node, IrOpcode::Value opcode, Node** inputs,
+ Node* control) {
+ CHECK_EQ(opcode, node->opcode());
+ CHECK_EQ(count + (control != NULL ? 1 : 0), node->InputCount());
+ int index = 0;
+ for (int i = 0; i < kSelectorSize; i++) {
+ if (mask & (1 << i)) {
+ CHECK_EQ(inputs[i], node->InputAt(index++));
+ }
+ }
+ CHECK_EQ(count, index);
+ if (control != NULL) CHECK_EQ(control, node->InputAt(index++));
+ }
+ int single_index() {
+ CHECK_EQ(1, count);
+ return WhichPowerOf2(mask);
+ }
+};
+
+
+TEST(CMergeReduce_exhaustive_4) {
+ ControlReducerTester R;
+ Node* controls[] = {
+ R.graph.NewNode(R.common.Start(1)), R.graph.NewNode(R.common.Start(2)),
+ R.graph.NewNode(R.common.Start(3)), R.graph.NewNode(R.common.Start(4))};
+ Node* values[] = {R.jsgraph.Int32Constant(11), R.jsgraph.Int32Constant(22),
+ R.jsgraph.Int32Constant(33), R.jsgraph.Int32Constant(44)};
+ Node* effects[] = {
+ R.jsgraph.Float64Constant(123.4), R.jsgraph.Float64Constant(223.4),
+ R.jsgraph.Float64Constant(323.4), R.jsgraph.Float64Constant(423.4)};
+
+ for (int mask = 0; mask < (1 << (kSelectorSize - 1)); mask++) {
+ // Reduce a single merge with a given mask.
+ Node* merge = R.graph.NewNode(R.common.Merge(4), controls[0], controls[1],
+ controls[2], controls[3]);
+ Node* phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 4), values[0],
+ values[1], values[2], values[3], merge);
+ Node* ephi = R.graph.NewNode(R.common.EffectPhi(4), effects[0], effects[1],
+ effects[2], effects[3], merge);
+
+ Node* phi_use =
+ R.graph.NewNode(R.common.Phi(kMachAnyTagged, 1), phi, R.start);
+ Node* ephi_use = R.graph.NewNode(R.common.EffectPhi(1), ephi, R.start);
+
+ Selector selector(mask);
+
+ for (int i = 0; i < kSelectorSize; i++) { // set up dead merge inputs.
+ if (!selector.is_selected(i)) merge->ReplaceInput(i, R.dead);
+ }
+
+ Node* result =
+ ControlReducer::ReduceMergeForTesting(&R.jsgraph, &R.common, merge);
+
+ int count = selector.count;
+ if (count == 0) {
+ // result should be dead.
+ CHECK_EQ(IrOpcode::kDead, result->opcode());
+ } else if (count == 1) {
+ // merge should be replaced with one of the controls.
+ CHECK_EQ(controls[selector.single_index()], result);
+ // Phis should have been directly replaced.
+ CHECK_EQ(values[selector.single_index()], phi_use->InputAt(0));
+ CHECK_EQ(effects[selector.single_index()], ephi_use->InputAt(0));
+ } else {
+ // Otherwise, nodes should be edited in place.
+ CHECK_EQ(merge, result);
+ selector.CheckNode(merge, IrOpcode::kMerge, controls, NULL);
+ selector.CheckNode(phi, IrOpcode::kPhi, values, merge);
+ selector.CheckNode(ephi, IrOpcode::kEffectPhi, effects, merge);
+ CHECK_EQ(phi, phi_use->InputAt(0));
+ CHECK_EQ(ephi, ephi_use->InputAt(0));
+ CHECK_EQ(count, phi->op()->ValueInputCount());
+ CHECK_EQ(count + 1, phi->InputCount());
+ CHECK_EQ(count, ephi->op()->EffectInputCount());
+ CHECK_EQ(count + 1, ephi->InputCount());
+ }
+ }
+}
+
+
+TEST(CMergeReduce_edit_many_phis1) {
+ ControlReducerTester R;
+
+ const int kPhiCount = 10;
+ Node* phis[kPhiCount];
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ for (int j = 0; j < kPhiCount; j++) {
+ phis[j] = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 3), R.leaf[0],
+ R.leaf[1], R.leaf[2], merge);
+ }
+ R.ReduceMerge(merge, merge);
+ for (int j = 0; j < kPhiCount; j++) {
+ Node* phi = phis[j];
+ CHECK_EQ(IrOpcode::kPhi, phi->opcode());
+ CHECK_EQ(2, phi->op()->ValueInputCount());
+ CHECK_EQ(3, phi->InputCount());
+ CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0));
+ CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1));
+ CHECK_EQ(merge, phi->InputAt(2));
+ }
+ }
+}
+
+
+TEST(CMergeReduce_simple_chain1) {
+ ControlReducerTester R;
+ for (int i = 0; i < 5; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.start);
+ for (int j = 0; j < i; j++) {
+ merge = R.graph.NewNode(R.common.Merge(1), merge);
+ }
+ R.ReduceMergeIterative(R.start, merge);
+ }
+}
+
+
+TEST(CMergeReduce_dead_chain1) {
+ ControlReducerTester R;
+ for (int i = 0; i < 5; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.dead);
+ for (int j = 0; j < i; j++) {
+ merge = R.graph.NewNode(R.common.Merge(1), merge);
+ }
+ Node* end = R.graph.NewNode(R.common.End(), merge);
+ R.graph.SetEnd(end);
+ R.ReduceGraph();
+ CHECK(merge->IsDead());
+ CHECK_EQ(NULL, end->InputAt(0)); // end dies.
+ }
+}
+
+
+TEST(CMergeReduce_dead_chain2) {
+ ControlReducerTester R;
+ for (int i = 0; i < 5; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.start);
+ for (int j = 0; j < i; j++) {
+ merge = R.graph.NewNode(R.common.Merge(2), merge, R.dead);
+ }
+ R.ReduceMergeIterative(R.start, merge);
+ }
+}
+
+
+struct Branch {
+ Node* branch;
+ Node* if_true;
+ Node* if_false;
+
+ Branch(ControlReducerTester& R, Node* cond, Node* control = NULL) {
+ if (control == NULL) control = R.start;
+ branch = R.graph.NewNode(R.common.Branch(), cond, control);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ if_false = R.graph.NewNode(R.common.IfFalse(), branch);
+ }
+};
+
+
+// TODO(titzer): use the diamonds from src/compiler/diamond.h here.
+struct Diamond {
+ Node* branch;
+ Node* if_true;
+ Node* if_false;
+ Node* merge;
+ Node* phi;
+
+ Diamond(ControlReducerTester& R, Node* cond) {
+ branch = R.graph.NewNode(R.common.Branch(), cond, R.start);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ if_false = R.graph.NewNode(R.common.IfFalse(), branch);
+ merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false);
+ phi = NULL;
+ }
+
+ Diamond(ControlReducerTester& R, Node* cond, Node* tv, Node* fv) {
+ branch = R.graph.NewNode(R.common.Branch(), cond, R.start);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ if_false = R.graph.NewNode(R.common.IfFalse(), branch);
+ merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false);
+ phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 2), tv, fv, merge);
+ }
+
+ void chain(Diamond& that) { branch->ReplaceInput(1, that.merge); }
+
+ // Nest {this} into either the if_true or if_false branch of {that}.
+ void nest(Diamond& that, bool if_true) {
+ if (if_true) {
+ branch->ReplaceInput(1, that.if_true);
+ that.merge->ReplaceInput(0, merge);
+ } else {
+ branch->ReplaceInput(1, that.if_false);
+ that.merge->ReplaceInput(1, merge);
+ }
+ }
+};
+
+
+struct While {
+ Node* branch;
+ Node* if_true;
+ Node* exit;
+ Node* loop;
+
+ While(ControlReducerTester& R, Node* cond) {
+ loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ branch = R.graph.NewNode(R.common.Branch(), cond, loop);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ exit = R.graph.NewNode(R.common.IfFalse(), branch);
+ loop->ReplaceInput(1, if_true);
+ }
+
+ void chain(Node* control) { loop->ReplaceInput(0, control); }
+};
+
+
+TEST(CBranchReduce_none1) {
+ ControlReducerTester R;
+ Diamond d(R, R.p0);
+ R.ReduceBranch(d.branch, d.branch);
+}
+
+
+TEST(CBranchReduce_none2) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0);
+ Diamond d2(R, R.p0);
+ d2.chain(d1);
+ R.ReduceBranch(d2.branch, d2.branch);
+}
+
+
+TEST(CBranchReduce_true) {
+ ControlReducerTester R;
+ Node* true_values[] = {
+ R.one, R.jsgraph.Int32Constant(2),
+ R.jsgraph.Int32Constant(0x7fffffff), R.jsgraph.Constant(1.0),
+ R.jsgraph.Constant(22.1), R.jsgraph.TrueConstant()};
+
+ for (size_t i = 0; i < arraysize(true_values); i++) {
+ Diamond d(R, true_values[i]);
+ Node* true_use = R.graph.NewNode(R.common.Merge(1), d.if_true);
+ Node* false_use = R.graph.NewNode(R.common.Merge(1), d.if_false);
+ R.ReduceBranch(R.start, d.branch);
+ CHECK_EQ(R.start, true_use->InputAt(0));
+ CHECK_EQ(IrOpcode::kDead, false_use->InputAt(0)->opcode());
+ CHECK(d.if_true->IsDead()); // replaced
+ CHECK(d.if_false->IsDead()); // replaced
+ }
+}
+
+
+TEST(CBranchReduce_false) {
+ ControlReducerTester R;
+ Node* false_values[] = {R.zero, R.jsgraph.Constant(0.0),
+ R.jsgraph.Constant(-0.0), R.jsgraph.FalseConstant()};
+
+ for (size_t i = 0; i < arraysize(false_values); i++) {
+ Diamond d(R, false_values[i]);
+ Node* true_use = R.graph.NewNode(R.common.Merge(1), d.if_true);
+ Node* false_use = R.graph.NewNode(R.common.Merge(1), d.if_false);
+ R.ReduceBranch(R.start, d.branch);
+ CHECK_EQ(R.start, false_use->InputAt(0));
+ CHECK_EQ(IrOpcode::kDead, true_use->InputAt(0)->opcode());
+ CHECK(d.if_true->IsDead()); // replaced
+ CHECK(d.if_false->IsDead()); // replaced
+ }
+}
+
+
+TEST(CDiamondReduce_true) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ R.ReduceMergeIterative(R.start, d1.merge);
+}
+
+
+TEST(CDiamondReduce_false) {
+ ControlReducerTester R;
+ Diamond d2(R, R.zero);
+ R.ReduceMergeIterative(R.start, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_true_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ Diamond d2(R, R.zero);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(R.start, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_x_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0);
+ Diamond d2(R, R.zero);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(d1.merge, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_false_x) {
+ ControlReducerTester R;
+ Diamond d1(R, R.zero);
+ Diamond d2(R, R.p0);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(d2.merge, d2.merge);
+ CheckInputs(d2.branch, R.p0, R.start);
+}
+
+
+TEST(CChainedDiamondsReduce_phi1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.zero, R.one, R.zero); // foldable branch, phi.
+ Diamond d2(R, d1.phi);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(R.start, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_phi2) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0, R.one, R.one); // redundant phi.
+ Diamond d2(R, d1.phi);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(d1.merge, d2.merge);
+}
+
+
+TEST(CNestedDiamondsReduce_true_true_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ Diamond d2(R, R.zero);
+ d2.nest(d1, true);
+
+ R.ReduceMergeIterative(R.start, d1.merge);
+}
+
+
+TEST(CNestedDiamondsReduce_false_true_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ Diamond d2(R, R.zero);
+ d2.nest(d1, false);
+
+ R.ReduceMergeIterative(R.start, d1.merge);
+}
+
+
+TEST(CNestedDiamonds_xyz) {
+ ControlReducerTester R;
+
+ for (int a = 0; a < 2; a++) {
+ for (int b = 0; b < 2; b++) {
+ for (int c = 0; c < 2; c++) {
+ Diamond d1(R, R.jsgraph.Int32Constant(a));
+ Diamond d2(R, R.jsgraph.Int32Constant(b));
+ d2.nest(d1, c);
+
+ R.ReduceMergeIterative(R.start, d1.merge);
+ }
+ }
+ }
+}
+
+
+TEST(CDeadLoop1) {
+ ControlReducerTester R;
+
+ Node* loop = R.graph.NewNode(R.common.Loop(1), R.start);
+ Branch b(R, R.p0, loop);
+ loop->ReplaceInput(0, b.if_true); // loop is not connected to start.
+ Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, b.if_false);
+ R.ReduceMergeIterative(R.start, merge);
+ CHECK(b.if_true->IsDead());
+ CHECK(b.if_false->IsDead());
+}
+
+
+TEST(CDeadLoop2) {
+ ControlReducerTester R;
+
+ While w(R, R.p0);
+ Diamond d(R, R.zero);
+ // if (0) { while (p0) ; } else { }
+ w.branch->ReplaceInput(1, d.if_true);
+ d.merge->ReplaceInput(0, w.exit);
+
+ R.ReduceMergeIterative(R.start, d.merge);
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+}
+
+
+TEST(CNonTermLoop1) {
+ ControlReducerTester R;
+ Node* loop =
+ R.SetSelfReferences(R.graph.NewNode(R.common.Loop(2), R.start, R.self));
+ R.ReduceGraph();
+ Node* end = R.graph.end();
+ CheckLoop(loop, R.start, loop);
+ Node* merge = end->InputAt(0);
+ CheckMerge(merge, R.start, loop);
+}
+
+
+TEST(CNonTermLoop2) {
+ ControlReducerTester R;
+ Diamond d(R, R.p0);
+ Node* loop = R.SetSelfReferences(
+ R.graph.NewNode(R.common.Loop(2), d.if_false, R.self));
+ d.merge->ReplaceInput(1, R.dead);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, d.merge);
+ R.ReduceGraph();
+ CHECK_EQ(end, R.graph.end());
+ CheckLoop(loop, d.if_false, loop);
+ Node* merge = end->InputAt(0);
+ CheckMerge(merge, d.if_true, loop);
+}
+
+
+TEST(NonTermLoop3) {
+ ControlReducerTester R;
+ Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ Branch b(R, R.one, loop);
+ loop->ReplaceInput(1, b.if_true);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, b.if_false);
+
+ R.ReduceGraph();
+
+ CHECK_EQ(end, R.graph.end());
+ CheckInputs(end, loop);
+ CheckInputs(loop, R.start, loop);
+}
+
+
+TEST(CNonTermLoop_terminate1) {
+ ControlReducerTester R;
+ Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ Node* effect = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ Branch b(R, R.one, loop);
+ loop->ReplaceInput(1, b.if_true);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, b.if_false);
+
+ R.ReduceGraph();
+
+ CHECK_EQ(end, R.graph.end());
+ CheckLoop(loop, R.start, loop);
+ Node* terminate = end->InputAt(0);
+ CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
+ CHECK_EQ(2, terminate->InputCount());
+ CHECK_EQ(1, terminate->op()->EffectInputCount());
+ CHECK_EQ(1, terminate->op()->ControlInputCount());
+ CheckInputs(terminate, effect, loop);
+}
+
+
+TEST(CNonTermLoop_terminate2) {
+ ControlReducerTester R;
+ Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ Node* effect1 = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ Node* effect2 = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ Branch b(R, R.one, loop);
+ loop->ReplaceInput(1, b.if_true);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, b.if_false);
+
+ R.ReduceGraph();
+
+ CheckLoop(loop, R.start, loop);
+ CHECK_EQ(end, R.graph.end());
+ Node* terminate = end->InputAt(0);
+ CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
+ CHECK_EQ(3, terminate->InputCount());
+ CHECK_EQ(2, terminate->op()->EffectInputCount());
+ CHECK_EQ(1, terminate->op()->ControlInputCount());
+ Node* e0 = terminate->InputAt(0);
+ Node* e1 = terminate->InputAt(1);
+ CHECK(e0 == effect1 || e1 == effect1);
+ CHECK(e0 == effect2 || e1 == effect2);
+ CHECK_EQ(loop, terminate->InputAt(2));
+}
+
+
+TEST(CNonTermLoop_terminate_m1) {
+ ControlReducerTester R;
+ Node* loop =
+ R.SetSelfReferences(R.graph.NewNode(R.common.Loop(2), R.start, R.self));
+ Node* effect = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ R.ReduceGraph();
+ Node* end = R.graph.end();
+ CHECK_EQ(R.start, loop->InputAt(0));
+ CHECK_EQ(loop, loop->InputAt(1));
+ Node* merge = end->InputAt(0);
+ CHECK_EQ(IrOpcode::kMerge, merge->opcode());
+ CHECK_EQ(2, merge->InputCount());
+ CHECK_EQ(2, merge->op()->ControlInputCount());
+ CHECK_EQ(R.start, merge->InputAt(0));
+
+ Node* terminate = merge->InputAt(1);
+ CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
+ CHECK_EQ(2, terminate->InputCount());
+ CHECK_EQ(1, terminate->op()->EffectInputCount());
+ CHECK_EQ(1, terminate->op()->ControlInputCount());
+ CHECK_EQ(effect, terminate->InputAt(0));
+ CHECK_EQ(loop, terminate->InputAt(1));
+}
+
+
+TEST(CNonTermLoop_big1) {
+ ControlReducerTester R;
+ Branch b1(R, R.p0);
+ Node* rt = R.graph.NewNode(R.common.Return(), R.one, R.start, b1.if_true);
+
+ Branch b2(R, R.p0, b1.if_false);
+ Node* rf = R.graph.NewNode(R.common.Return(), R.zero, R.start, b2.if_true);
+ Node* loop = R.SetSelfReferences(
+ R.graph.NewNode(R.common.Loop(2), b2.if_false, R.self));
+ Node* merge = R.graph.NewNode(R.common.Merge(2), rt, rf);
+ R.end->ReplaceInput(0, merge);
+
+ R.ReduceGraph();
+
+ CheckInputs(R.end, merge);
+ CheckInputs(merge, rt, rf, loop);
+ CheckInputs(loop, b2.if_false, loop);
+}
+
+
+TEST(CNonTermLoop_big2) {
+ ControlReducerTester R;
+ Branch b1(R, R.p0);
+ Node* rt = R.graph.NewNode(R.common.Return(), R.one, R.start, b1.if_true);
+
+ Branch b2(R, R.zero, b1.if_false);
+ Node* rf = R.graph.NewNode(R.common.Return(), R.zero, R.start, b2.if_true);
+ Node* loop = R.SetSelfReferences(
+ R.graph.NewNode(R.common.Loop(2), b2.if_false, R.self));
+ Node* merge = R.graph.NewNode(R.common.Merge(2), rt, rf);
+ R.end->ReplaceInput(0, merge);
+
+ R.ReduceGraph();
+
+ Node* new_merge = R.end->InputAt(0); // old merge was reduced.
+ CHECK_NE(merge, new_merge);
+ CheckInputs(new_merge, rt, loop);
+ CheckInputs(loop, b1.if_false, loop);
+ CHECK(merge->IsDead());
+ CHECK(rf->IsDead());
+ CHECK(b2.if_true->IsDead());
+}
+
+
+TEST(Return1) {
+ ControlReducerTester R;
+ Node* ret = R.Return(R.one, R.start, R.start);
+ R.ReduceGraph();
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.one, R.start, R.start);
+}
+
+
+TEST(Return2) {
+ ControlReducerTester R;
+ Diamond d(R, R.one);
+ Node* ret = R.Return(R.half, R.start, d.merge);
+ R.ReduceGraph();
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.half, R.start, R.start);
+}
+
+
+TEST(Return_true1) {
+ ControlReducerTester R;
+ Diamond d(R, R.one, R.half, R.zero);
+ Node* ret = R.Return(d.phi, R.start, d.merge);
+ R.ReduceGraph();
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+ CHECK(d.phi->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.half, R.start, R.start);
+}
+
+
+TEST(Return_false1) {
+ ControlReducerTester R;
+ Diamond d(R, R.zero, R.one, R.half);
+ Node* ret = R.Return(d.phi, R.start, d.merge);
+ R.ReduceGraph();
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+ CHECK(d.phi->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.half, R.start, R.start);
+}
+
+
+void CheckDeadDiamond(Diamond& d) {
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+ if (d.phi != NULL) CHECK(d.phi->IsDead());
+}
+
+
+void CheckLiveDiamond(Diamond& d, bool live_phi = true) {
+ CheckInputs(d.merge, d.if_true, d.if_false);
+ CheckInputs(d.if_true, d.branch);
+ CheckInputs(d.if_false, d.branch);
+ if (d.phi != NULL) {
+ if (live_phi) {
+ CHECK_EQ(3, d.phi->InputCount());
+ CHECK_EQ(d.merge, d.phi->InputAt(2));
+ } else {
+ CHECK(d.phi->IsDead());
+ }
+ }
+}
+
+
+TEST(Return_effect1) {
+ ControlReducerTester R;
+ Diamond d(R, R.one);
+ Node* e1 = R.jsgraph.Float64Constant(-100.1);
+ Node* e2 = R.jsgraph.Float64Constant(+100.1);
+ Node* effect = R.graph.NewNode(R.common.EffectPhi(2), e1, e2, d.merge);
+ Node* ret = R.Return(R.p0, effect, d.merge);
+ R.ReduceGraph();
+ CheckDeadDiamond(d);
+ CHECK(effect->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.p0, e1, R.start);
+}
+
+
+TEST(Return_nested_diamonds1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0, R.one, R.zero);
+ Diamond d2(R, R.p0);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // nothing should happen.
+
+ CheckInputs(ret, d1.phi, R.start, d1.merge);
+ CheckInputs(d1.phi, R.one, R.zero, d1.merge);
+ CheckInputs(d1.merge, d2.merge, d3.merge);
+ CheckLiveDiamond(d2);
+ CheckLiveDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one, R.one, R.zero);
+ Diamond d2(R, R.p0);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, R.one, R.start, d2.merge);
+ CheckInputs(d2.branch, R.p0, R.start);
+ CheckDeadDiamond(d1);
+ CheckLiveDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_false1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.zero, R.one, R.zero);
+ Diamond d2(R, R.p0);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded false.
+
+ CheckInputs(ret, R.zero, R.start, d3.merge);
+ CheckInputs(d3.branch, R.p0, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckLiveDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_true1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one, R.one, R.zero);
+ Diamond d2(R, R.one);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 and d2 both get folded true.
+
+ CheckInputs(ret, R.one, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_false1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one, R.one, R.zero);
+ Diamond d2(R, R.zero);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true and d2 gets folded false.
+
+ CheckInputs(ret, R.one, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.p0, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.p0, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // nothing should happen.
+
+ CheckInputs(ret, d1.phi, R.start, d1.merge);
+ CheckInputs(d1.phi, d2.phi, d3.phi, d1.merge);
+ CheckInputs(d1.merge, d2.merge, d3.merge);
+ CheckLiveDiamond(d2);
+ CheckLiveDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.p0, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.one, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, d2.phi, R.start, d2.merge);
+ CheckInputs(d2.branch, R.p0, R.start);
+ CheckDeadDiamond(d1);
+ CheckLiveDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_true2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.one, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.one, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, x2, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_false2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.zero, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.one, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, y2, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
diff --git a/test/cctest/compiler/test-gap-resolver.cc b/test/cctest/compiler/test-gap-resolver.cc
index 6239f2a4..ea6f4ee8 100644
--- a/test/cctest/compiler/test-gap-resolver.cc
+++ b/test/cctest/compiler/test-gap-resolver.cc
@@ -58,13 +58,16 @@ class InterpreterState {
return Value(op->kind(), op->index());
}
- friend OStream& operator<<(OStream& os, const InterpreterState& is) {
+ friend std::ostream& operator<<(std::ostream& os,
+ const InterpreterState& is) {
for (OperandMap::const_iterator it = is.values_.begin();
it != is.values_.end(); ++it) {
if (it != is.values_.begin()) os << " ";
InstructionOperand source(it->first.first, it->first.second);
InstructionOperand destination(it->second.first, it->second.second);
- os << MoveOperands(&source, &destination);
+ MoveOperands mo(&source, &destination);
+ PrintableMoveOperands pmo = {RegisterConfiguration::ArchDefault(), &mo};
+ os << pmo;
}
return os;
}
diff --git a/test/cctest/compiler/test-graph-reducer.cc b/test/cctest/compiler/test-graph-reducer.cc
index eabfd22e..70b57b9d 100644
--- a/test/cctest/compiler/test-graph-reducer.cc
+++ b/test/cctest/compiler/test-graph-reducer.cc
@@ -5,7 +5,6 @@
#include "src/v8.h"
#include "graph-tester.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph-reducer.h"
using namespace v8::internal;
@@ -21,15 +20,15 @@ const uint8_t OPCODE_C0 = 30;
const uint8_t OPCODE_C1 = 31;
const uint8_t OPCODE_C2 = 32;
-static SimpleOperator OPA0(OPCODE_A0, Operator::kNoWrite, 0, 0, "opa0");
-static SimpleOperator OPA1(OPCODE_A1, Operator::kNoWrite, 1, 0, "opa1");
-static SimpleOperator OPA2(OPCODE_A2, Operator::kNoWrite, 2, 0, "opa2");
-static SimpleOperator OPB0(OPCODE_B0, Operator::kNoWrite, 0, 0, "opa0");
-static SimpleOperator OPB1(OPCODE_B1, Operator::kNoWrite, 1, 0, "opa1");
-static SimpleOperator OPB2(OPCODE_B2, Operator::kNoWrite, 2, 0, "opa2");
-static SimpleOperator OPC0(OPCODE_C0, Operator::kNoWrite, 0, 0, "opc0");
-static SimpleOperator OPC1(OPCODE_C1, Operator::kNoWrite, 1, 0, "opc1");
-static SimpleOperator OPC2(OPCODE_C2, Operator::kNoWrite, 2, 0, "opc2");
+static Operator OPA0(OPCODE_A0, Operator::kNoWrite, "opa0", 0, 0, 0, 0, 0, 0);
+static Operator OPA1(OPCODE_A1, Operator::kNoWrite, "opa1", 1, 0, 0, 0, 0, 0);
+static Operator OPA2(OPCODE_A2, Operator::kNoWrite, "opa2", 2, 0, 0, 0, 0, 0);
+static Operator OPB0(OPCODE_B0, Operator::kNoWrite, "opa0", 0, 0, 0, 0, 0, 0);
+static Operator OPB1(OPCODE_B1, Operator::kNoWrite, "opa1", 1, 0, 0, 0, 0, 0);
+static Operator OPB2(OPCODE_B2, Operator::kNoWrite, "opa2", 2, 0, 0, 0, 0, 0);
+static Operator OPC0(OPCODE_C0, Operator::kNoWrite, "opc0", 0, 0, 0, 0, 0, 0);
+static Operator OPC1(OPCODE_C1, Operator::kNoWrite, "opc1", 1, 0, 0, 0, 0, 0);
+static Operator OPC2(OPCODE_C2, Operator::kNoWrite, "opc2", 2, 0, 0, 0, 0, 0);
// Replaces all "A" operators with "B" operators without creating new nodes.
@@ -202,7 +201,7 @@ TEST(ReduceGraphFromEnd1) {
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
ReducerRecorder recorder(graph.zone());
reducer.AddReducer(&recorder);
reducer.ReduceGraph();
@@ -220,7 +219,7 @@ TEST(ReduceGraphFromEnd2) {
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
ReducerRecorder recorder(graph.zone());
reducer.AddReducer(&recorder);
reducer.ReduceGraph();
@@ -238,7 +237,7 @@ TEST(ReduceInPlace1) {
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer r;
reducer.AddReducer(&r);
@@ -263,7 +262,7 @@ TEST(ReduceInPlace2) {
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer r;
reducer.AddReducer(&r);
@@ -293,7 +292,7 @@ TEST(ReduceNew1) {
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
NewABReducer r(&graph);
reducer.AddReducer(&r);
@@ -330,7 +329,7 @@ TEST(Wrapping1) {
graph.SetEnd(end);
CHECK_EQ(1, graph.NodeCount());
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A0Wrapper r(&graph);
reducer.AddReducer(&r);
@@ -352,7 +351,7 @@ TEST(Wrapping2) {
graph.SetEnd(end);
CHECK_EQ(1, graph.NodeCount());
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
B0Wrapper r(&graph);
reducer.AddReducer(&r);
@@ -379,7 +378,7 @@ TEST(Forwarding1) {
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A1Forwarder r;
reducer.AddReducer(&r);
@@ -403,7 +402,7 @@ TEST(Forwarding2) {
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A1Forwarder r;
reducer.AddReducer(&r);
@@ -434,7 +433,7 @@ TEST(Forwarding3) {
}
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A1Forwarder r;
reducer.AddReducer(&r);
@@ -458,7 +457,7 @@ TEST(ReduceForward1) {
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer r;
B1Forwarder f;
reducer.AddReducer(&r);
@@ -501,7 +500,7 @@ TEST(Sorter1) {
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
reducer.AddReducer(&r);
int before = graph.NodeCount();
@@ -560,7 +559,7 @@ TEST(SortForwardReduce) {
GenDAG(&graph, p3, p2, p1);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
AB2Sorter r1;
A1Forwarder r2;
InPlaceABReducer r3;
@@ -599,7 +598,7 @@ TEST(Order) {
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer abr;
InPlaceBCReducer bcr;
if (i == 0) {
@@ -621,41 +620,3 @@ TEST(Order) {
}
}
}
-
-
-// Tests that a reducer is only applied once.
-class OneTimeReducer : public Reducer {
- public:
- OneTimeReducer(Reducer* reducer, Zone* zone)
- : reducer_(reducer),
- nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {}
- virtual Reduction Reduce(Node* node) {
- CHECK_EQ(0, static_cast<int>(nodes_.count(node)));
- nodes_.insert(node);
- return reducer_->Reduce(node);
- }
- Reducer* reducer_;
- NodeSet nodes_;
-};
-
-
-TEST(OneTimeReduce1) {
- GraphTester graph;
-
- Node* n1 = graph.NewNode(&OPA0);
- Node* end = graph.NewNode(&OPA1, n1);
- graph.SetEnd(end);
-
- GraphReducer reducer(&graph);
- InPlaceABReducer r;
- OneTimeReducer once(&r, graph.zone());
- reducer.AddReducer(&once);
-
- // Tests A* => B* with in-place updates. Should only be applied once.
- int before = graph.NodeCount();
- reducer.ReduceGraph();
- CHECK_EQ(before, graph.NodeCount());
- CHECK_EQ(&OPB0, n1->op());
- CHECK_EQ(&OPB1, end->op());
- CHECK_EQ(n1, end->InputAt(0));
-}
diff --git a/test/cctest/compiler/test-graph-visualizer.cc b/test/cctest/compiler/test-graph-visualizer.cc
new file mode 100644
index 00000000..ce3e6b71
--- /dev/null
+++ b/test/cctest/compiler/test-graph-visualizer.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "src/compiler/scheduler.h"
+#include "src/compiler/verifier.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(NodeWithNullInputReachableFromEnd) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* k = graph.NewNode(common.Int32Constant(0));
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 1), k, start);
+ phi->ReplaceInput(0, NULL);
+ graph.SetEnd(phi);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
+
+
+TEST(NodeWithNullControlReachableFromEnd) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* k = graph.NewNode(common.Int32Constant(0));
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 1), k, start);
+ phi->ReplaceInput(1, NULL);
+ graph.SetEnd(phi);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
+
+
+TEST(NodeWithNullInputReachableFromStart) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* k = graph.NewNode(common.Int32Constant(0));
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 1), k, start);
+ phi->ReplaceInput(0, NULL);
+ graph.SetEnd(start);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
+
+
+TEST(NodeWithNullControlReachableFromStart) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* merge = graph.NewNode(common.Merge(2), start, start);
+ merge->ReplaceInput(1, NULL);
+ graph.SetEnd(merge);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
diff --git a/test/cctest/compiler/test-instruction.cc b/test/cctest/compiler/test-instruction.cc
index a9feaac2..294812fd 100644
--- a/test/cctest/compiler/test-instruction.cc
+++ b/test/cctest/compiler/test-instruction.cc
@@ -31,12 +31,11 @@ class InstructionTester : public HandleAndZoneScope {
graph(zone()),
schedule(zone()),
info(static_cast<HydrogenCodeStub*>(NULL), main_isolate()),
- linkage(&info),
+ linkage(zone(), &info),
common(zone()),
+ machine(zone()),
code(NULL) {}
- ~InstructionTester() { delete code; }
-
Isolate* isolate;
Graph graph;
Schedule schedule;
@@ -51,10 +50,12 @@ class InstructionTester : public HandleAndZoneScope {
void allocCode() {
if (schedule.rpo_order()->size() == 0) {
// Compute the RPO order.
- Scheduler::ComputeSpecialRPO(&schedule);
+ Scheduler::ComputeSpecialRPO(main_zone(), &schedule);
DCHECK(schedule.rpo_order()->size() > 0);
}
- code = new TestInstrSeq(&linkage, &graph, &schedule);
+ InstructionBlocks* instruction_blocks =
+ TestInstrSeq::InstructionBlocksFor(main_zone(), &schedule);
+ code = new (main_zone()) TestInstrSeq(main_zone(), instruction_blocks);
}
Node* Int32Constant(int32_t val) {
@@ -81,10 +82,10 @@ class InstructionTester : public HandleAndZoneScope {
return node;
}
- int NewInstr(BasicBlock* block) {
+ int NewInstr() {
InstructionCode opcode = static_cast<InstructionCode>(110);
TestInstr* instr = TestInstr::New(zone(), opcode);
- return code->AddInstruction(instr, block);
+ return code->AddInstruction(instr);
}
UnallocatedOperand* NewUnallocated(int vreg) {
@@ -93,6 +94,21 @@ class InstructionTester : public HandleAndZoneScope {
unallocated->set_virtual_register(vreg);
return unallocated;
}
+
+ InstructionBlock* BlockAt(BasicBlock* block) {
+ return code->InstructionBlockAt(block->GetRpoNumber());
+ }
+ BasicBlock* GetBasicBlock(int instruction_index) {
+ const InstructionBlock* block =
+ code->GetInstructionBlock(instruction_index);
+ return schedule.rpo_order()->at(block->rpo_number().ToSize());
+ }
+ int first_instruction_index(BasicBlock* block) {
+ return BlockAt(block)->first_instruction_index();
+ }
+ int last_instruction_index(BasicBlock* block) {
+ return BlockAt(block)->last_instruction_index();
+ }
};
@@ -112,17 +128,16 @@ TEST(InstructionBasic) {
R.allocCode();
- CHECK_EQ(R.graph.NodeCount(), R.code->ValueCount());
-
BasicBlockVector* blocks = R.schedule.rpo_order();
- CHECK_EQ(static_cast<int>(blocks->size()), R.code->BasicBlockCount());
+ CHECK_EQ(static_cast<int>(blocks->size()), R.code->InstructionBlockCount());
int index = 0;
for (BasicBlockVectorIter i = blocks->begin(); i != blocks->end();
i++, index++) {
BasicBlock* block = *i;
- CHECK_EQ(block, R.code->BlockAt(index));
- CHECK_EQ(-1, R.code->GetLoopEnd(block));
+ CHECK_EQ(block->rpo_number(), R.BlockAt(block)->rpo_number().ToInt());
+ CHECK_EQ(block->id().ToInt(), R.BlockAt(block)->id().ToInt());
+ CHECK_EQ(NULL, block->loop_end());
}
}
@@ -141,47 +156,47 @@ TEST(InstructionGetBasicBlock) {
R.allocCode();
- R.code->StartBlock(b0);
- int i0 = R.NewInstr(b0);
- int i1 = R.NewInstr(b0);
- R.code->EndBlock(b0);
- R.code->StartBlock(b1);
- int i2 = R.NewInstr(b1);
- int i3 = R.NewInstr(b1);
- int i4 = R.NewInstr(b1);
- int i5 = R.NewInstr(b1);
- R.code->EndBlock(b1);
- R.code->StartBlock(b2);
- int i6 = R.NewInstr(b2);
- int i7 = R.NewInstr(b2);
- int i8 = R.NewInstr(b2);
- R.code->EndBlock(b2);
- R.code->StartBlock(b3);
- R.code->EndBlock(b3);
-
- CHECK_EQ(b0, R.code->GetBasicBlock(i0));
- CHECK_EQ(b0, R.code->GetBasicBlock(i1));
-
- CHECK_EQ(b1, R.code->GetBasicBlock(i2));
- CHECK_EQ(b1, R.code->GetBasicBlock(i3));
- CHECK_EQ(b1, R.code->GetBasicBlock(i4));
- CHECK_EQ(b1, R.code->GetBasicBlock(i5));
-
- CHECK_EQ(b2, R.code->GetBasicBlock(i6));
- CHECK_EQ(b2, R.code->GetBasicBlock(i7));
- CHECK_EQ(b2, R.code->GetBasicBlock(i8));
-
- CHECK_EQ(b0, R.code->GetBasicBlock(b0->first_instruction_index()));
- CHECK_EQ(b0, R.code->GetBasicBlock(b0->last_instruction_index()));
-
- CHECK_EQ(b1, R.code->GetBasicBlock(b1->first_instruction_index()));
- CHECK_EQ(b1, R.code->GetBasicBlock(b1->last_instruction_index()));
-
- CHECK_EQ(b2, R.code->GetBasicBlock(b2->first_instruction_index()));
- CHECK_EQ(b2, R.code->GetBasicBlock(b2->last_instruction_index()));
-
- CHECK_EQ(b3, R.code->GetBasicBlock(b3->first_instruction_index()));
- CHECK_EQ(b3, R.code->GetBasicBlock(b3->last_instruction_index()));
+ R.code->StartBlock(b0->GetRpoNumber());
+ int i0 = R.NewInstr();
+ int i1 = R.NewInstr();
+ R.code->EndBlock(b0->GetRpoNumber());
+ R.code->StartBlock(b1->GetRpoNumber());
+ int i2 = R.NewInstr();
+ int i3 = R.NewInstr();
+ int i4 = R.NewInstr();
+ int i5 = R.NewInstr();
+ R.code->EndBlock(b1->GetRpoNumber());
+ R.code->StartBlock(b2->GetRpoNumber());
+ int i6 = R.NewInstr();
+ int i7 = R.NewInstr();
+ int i8 = R.NewInstr();
+ R.code->EndBlock(b2->GetRpoNumber());
+ R.code->StartBlock(b3->GetRpoNumber());
+ R.code->EndBlock(b3->GetRpoNumber());
+
+ CHECK_EQ(b0, R.GetBasicBlock(i0));
+ CHECK_EQ(b0, R.GetBasicBlock(i1));
+
+ CHECK_EQ(b1, R.GetBasicBlock(i2));
+ CHECK_EQ(b1, R.GetBasicBlock(i3));
+ CHECK_EQ(b1, R.GetBasicBlock(i4));
+ CHECK_EQ(b1, R.GetBasicBlock(i5));
+
+ CHECK_EQ(b2, R.GetBasicBlock(i6));
+ CHECK_EQ(b2, R.GetBasicBlock(i7));
+ CHECK_EQ(b2, R.GetBasicBlock(i8));
+
+ CHECK_EQ(b0, R.GetBasicBlock(R.first_instruction_index(b0)));
+ CHECK_EQ(b0, R.GetBasicBlock(R.last_instruction_index(b0)));
+
+ CHECK_EQ(b1, R.GetBasicBlock(R.first_instruction_index(b1)));
+ CHECK_EQ(b1, R.GetBasicBlock(R.last_instruction_index(b1)));
+
+ CHECK_EQ(b2, R.GetBasicBlock(R.first_instruction_index(b2)));
+ CHECK_EQ(b2, R.GetBasicBlock(R.last_instruction_index(b2)));
+
+ CHECK_EQ(b3, R.GetBasicBlock(R.first_instruction_index(b3)));
+ CHECK_EQ(b3, R.GetBasicBlock(R.last_instruction_index(b3)));
}
@@ -194,10 +209,10 @@ TEST(InstructionIsGapAt) {
R.allocCode();
TestInstr* i0 = TestInstr::New(R.zone(), 100);
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
- R.code->StartBlock(b0);
- R.code->AddInstruction(i0, b0);
- R.code->AddInstruction(g, b0);
- R.code->EndBlock(b0);
+ R.code->StartBlock(b0->GetRpoNumber());
+ R.code->AddInstruction(i0);
+ R.code->AddInstruction(g);
+ R.code->EndBlock(b0->GetRpoNumber());
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
@@ -221,17 +236,17 @@ TEST(InstructionIsGapAt2) {
R.allocCode();
TestInstr* i0 = TestInstr::New(R.zone(), 100);
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
- R.code->StartBlock(b0);
- R.code->AddInstruction(i0, b0);
- R.code->AddInstruction(g, b0);
- R.code->EndBlock(b0);
+ R.code->StartBlock(b0->GetRpoNumber());
+ R.code->AddInstruction(i0);
+ R.code->AddInstruction(g);
+ R.code->EndBlock(b0->GetRpoNumber());
TestInstr* i1 = TestInstr::New(R.zone(), 102);
TestInstr* g1 = TestInstr::New(R.zone(), 104)->MarkAsControl();
- R.code->StartBlock(b1);
- R.code->AddInstruction(i1, b1);
- R.code->AddInstruction(g1, b1);
- R.code->EndBlock(b1);
+ R.code->StartBlock(b1->GetRpoNumber());
+ R.code->AddInstruction(i1);
+ R.code->AddInstruction(g1);
+ R.code->EndBlock(b1->GetRpoNumber());
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
@@ -262,10 +277,10 @@ TEST(InstructionAddGapMove) {
R.allocCode();
TestInstr* i0 = TestInstr::New(R.zone(), 100);
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
- R.code->StartBlock(b0);
- R.code->AddInstruction(i0, b0);
- R.code->AddInstruction(g, b0);
- R.code->EndBlock(b0);
+ R.code->StartBlock(b0->GetRpoNumber());
+ R.code->AddInstruction(i0);
+ R.code->AddInstruction(g);
+ R.code->EndBlock(b0->GetRpoNumber());
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
diff --git a/test/cctest/compiler/test-js-constant-cache.cc b/test/cctest/compiler/test-js-constant-cache.cc
index eb0975ef..8588f66f 100644
--- a/test/cctest/compiler/test-js-constant-cache.cc
+++ b/test/cctest/compiler/test-js-constant-cache.cc
@@ -4,6 +4,7 @@
#include "src/v8.h"
+#include "src/assembler.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/typer.h"
@@ -20,8 +21,8 @@ class JSCacheTesterHelper {
: main_graph_(zone),
main_common_(zone),
main_javascript_(zone),
- main_typer_(zone),
- main_machine_() {}
+ main_typer_(&main_graph_, MaybeHandle<Context>()),
+ main_machine_(zone) {}
Graph main_graph_;
CommonOperatorBuilder main_common_;
JSOperatorBuilder main_javascript_;
@@ -30,14 +31,19 @@ class JSCacheTesterHelper {
};
+// TODO(dcarney): JSConstantCacheTester inherits from JSGraph???
class JSConstantCacheTester : public HandleAndZoneScope,
public JSCacheTesterHelper,
public JSGraph {
public:
JSConstantCacheTester()
: JSCacheTesterHelper(main_zone()),
- JSGraph(&main_graph_, &main_common_, &main_javascript_, &main_typer_,
- &main_machine_) {}
+ JSGraph(&main_graph_, &main_common_, &main_javascript_,
+ &main_machine_) {
+ main_graph_.SetStart(main_graph_.NewNode(common()->Start(0)));
+ main_graph_.SetEnd(main_graph_.NewNode(common()->End()));
+ main_typer_.Run();
+ }
Type* upper(Node* node) { return NodeProperties::GetBounds(node).upper; }
@@ -227,7 +233,7 @@ TEST(NumberTypes) {
FOR_FLOAT64_INPUTS(i) {
double value = *i;
Node* node = T.Constant(value);
- CHECK(T.upper(node)->Equals(Type::Of(value, T.main_zone())));
+ CHECK(T.upper(node)->Is(Type::Of(value, T.main_zone())));
}
}
@@ -289,3 +295,180 @@ TEST(OddballTypes) {
TEST(ExternalReferences) {
// TODO(titzer): test canonicalization of external references.
}
+
+
+static bool Contains(NodeVector* nodes, Node* n) {
+ for (size_t i = 0; i < nodes->size(); i++) {
+ if (nodes->at(i) == n) return true;
+ }
+ return false;
+}
+
+
+static void CheckGetCachedNodesContains(JSConstantCacheTester* T, Node* n) {
+ NodeVector nodes(T->main_zone());
+ T->GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, n));
+}
+
+
+TEST(JSGraph_GetCachedNodes1) {
+ JSConstantCacheTester T;
+ CheckGetCachedNodesContains(&T, T.TrueConstant());
+ CheckGetCachedNodesContains(&T, T.UndefinedConstant());
+ CheckGetCachedNodesContains(&T, T.TheHoleConstant());
+ CheckGetCachedNodesContains(&T, T.TrueConstant());
+ CheckGetCachedNodesContains(&T, T.FalseConstant());
+ CheckGetCachedNodesContains(&T, T.NullConstant());
+ CheckGetCachedNodesContains(&T, T.ZeroConstant());
+ CheckGetCachedNodesContains(&T, T.OneConstant());
+ CheckGetCachedNodesContains(&T, T.NaNConstant());
+}
+
+
+TEST(JSGraph_GetCachedNodes_int32) {
+ JSConstantCacheTester T;
+
+ int32_t constants[] = {0, 1, 1, 1, 1, 2, 3, 4, 11, 12, 13,
+ 14, 55, -55, -44, -33, -22, -11, 16, 16, 17, 17,
+ 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 24,
+ 25, 15, 30, 31, 45, 46, 47, 48};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Int32Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_float64) {
+ JSConstantCacheTester T;
+
+ double constants[] = {0, 11.1, 12.2, 13, 14, 55.5, -55.5, -44.4,
+ -33, -22, -11, 0, 11.1, 11.1, 12.3, 12.3,
+ 11, 11, -33.3, -33.3, -22, -11};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Float64Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_int64) {
+ JSConstantCacheTester T;
+
+ int32_t constants[] = {0, 11, 12, 13, 14, 55, -55, -44, -33,
+ -22, -11, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 23, 24, 25};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Int64Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_number) {
+ JSConstantCacheTester T;
+
+ double constants[] = {0, 11.1, 12.2, 13, 14, 55.5, -55.5, -44.4,
+ -33, -22, -11, 0, 11.1, 11.1, 12.3, 12.3,
+ 11, 11, -33.3, -33.3, -22, -11};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_external) {
+ JSConstantCacheTester T;
+
+ ExternalReference constants[] = {ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_one_half(),
+ ExternalReference::address_of_one_half(),
+ ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_the_hole_nan(),
+ ExternalReference::address_of_one_half()};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.ExternalConstant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_together) {
+ JSConstantCacheTester T;
+
+ Node* constants[] = {
+ T.TrueConstant(),
+ T.UndefinedConstant(),
+ T.TheHoleConstant(),
+ T.TrueConstant(),
+ T.FalseConstant(),
+ T.NullConstant(),
+ T.ZeroConstant(),
+ T.OneConstant(),
+ T.NaNConstant(),
+ T.Int32Constant(0),
+ T.Int32Constant(1),
+ T.Int64Constant(-2),
+ T.Int64Constant(-4),
+ T.Float64Constant(0.9),
+ T.Float64Constant(V8_INFINITY),
+ T.Constant(0.99),
+ T.Constant(1.11),
+ T.ExternalConstant(ExternalReference::address_of_one_half())};
+
+ NodeVector nodes(T.main_zone());
+ T.GetCachedNodes(&nodes);
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ CHECK(Contains(&nodes, constants[i]));
+ }
+}
diff --git a/test/cctest/compiler/test-js-context-specialization.cc b/test/cctest/compiler/test-js-context-specialization.cc
index 47c660ae..fb7bd946 100644
--- a/test/cctest/compiler/test-js-context-specialization.cc
+++ b/test/cctest/compiler/test-js-context-specialization.cc
@@ -7,7 +7,6 @@
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/source-position.h"
-#include "src/compiler/typer.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/function-tester.h"
#include "test/cctest/compiler/graph-builder-tester.h"
@@ -22,10 +21,9 @@ class ContextSpecializationTester : public HandleAndZoneScope,
: DirectGraphBuilder(new (main_zone()) Graph(main_zone())),
common_(main_zone()),
javascript_(main_zone()),
- machine_(),
+ machine_(main_zone()),
simplified_(main_zone()),
- typer_(main_zone()),
- jsgraph_(graph(), common(), &javascript_, &typer_, &machine_),
+ jsgraph_(graph(), common(), &javascript_, &machine_),
info_(main_isolate(), main_zone()) {}
Factory* factory() { return main_isolate()->factory(); }
@@ -40,7 +38,6 @@ class ContextSpecializationTester : public HandleAndZoneScope,
JSOperatorBuilder javascript_;
MachineOperatorBuilder machine_;
SimplifiedOperatorBuilder simplified_;
- Typer typer_;
JSGraph jsgraph_;
CompilationInfo info_;
};
@@ -95,8 +92,8 @@ TEST(ReduceJSLoadContext) {
HeapObjectMatcher<Context> match(new_context_input);
CHECK_EQ(*native, *match.Value().handle());
ContextAccess access = OpParameter<ContextAccess>(r.replacement());
- CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
- CHECK_EQ(0, access.depth());
+ CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
+ CHECK_EQ(0, static_cast<int>(access.depth()));
CHECK_EQ(false, access.immutable());
}
@@ -175,8 +172,8 @@ TEST(ReduceJSStoreContext) {
HeapObjectMatcher<Context> match(new_context_input);
CHECK_EQ(*native, *match.Value().handle());
ContextAccess access = OpParameter<ContextAccess>(r.replacement());
- CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
- CHECK_EQ(0, access.depth());
+ CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
+ CHECK_EQ(0, static_cast<int>(access.depth()));
CHECK_EQ(false, access.immutable());
}
}
@@ -206,7 +203,7 @@ TEST(SpecializeToContext) {
JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
{
- // Check that SpecializeToContext() replaces values and forwards effects
+ // Check that specialization replaces values and forwards effects
// correctly, and folds values from constant and non-constant contexts
Node* effect_in = start;
Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
@@ -232,8 +229,10 @@ TEST(SpecializeToContext) {
CheckEffectInput(effect_in, load);
CheckEffectInput(load, effect_use);
- // Perform the substitution on the entire graph.
- spec.SpecializeToContext();
+ // Perform the reduction on the entire graph.
+ GraphReducer graph_reducer(t.graph(), t.main_zone());
+ graph_reducer.AddReducer(&spec);
+ graph_reducer.ReduceGraph();
// Effects should have been forwarded (not replaced with a value).
CheckEffectInput(effect_in, effect_use);
diff --git a/test/cctest/compiler/test-js-typed-lowering.cc b/test/cctest/compiler/test-js-typed-lowering.cc
index cf126c2c..3023837f 100644
--- a/test/cctest/compiler/test-js-typed-lowering.cc
+++ b/test/cctest/compiler/test-js-typed-lowering.cc
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "src/v8.h"
-#include "test/cctest/cctest.h"
-
#include "src/compiler/graph-inl.h"
+#include "src/compiler/js-graph.h"
#include "src/compiler/js-typed-lowering.h"
+#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/typer.h"
+#include "test/cctest/cctest.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
@@ -21,14 +21,15 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
binop(NULL),
unop(NULL),
javascript(main_zone()),
+ machine(main_zone()),
simplified(main_zone()),
common(main_zone()),
graph(main_zone()),
- typer(main_zone()),
+ typer(&graph, MaybeHandle<Context>()),
context_node(NULL) {
- typer.DecorateGraph(&graph);
- Node* s = graph.NewNode(common.Start(num_parameters));
- graph.SetStart(s);
+ graph.SetStart(graph.NewNode(common.Start(num_parameters)));
+ graph.SetEnd(graph.NewNode(common.End()));
+ typer.Run();
}
Isolate* isolate;
@@ -49,13 +50,14 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
}
Node* UndefinedConstant() {
- Unique<Object> unique =
- Unique<Object>::CreateImmovable(isolate->factory()->undefined_value());
+ Unique<HeapObject> unique = Unique<HeapObject>::CreateImmovable(
+ isolate->factory()->undefined_value());
return graph.NewNode(common.HeapConstant(unique));
}
- Node* HeapConstant(Handle<Object> constant) {
- Unique<Object> unique = Unique<Object>::CreateUninitialized(constant);
+ Node* HeapConstant(Handle<HeapObject> constant) {
+ Unique<HeapObject> unique =
+ Unique<HeapObject>::CreateUninitialized(constant);
return graph.NewNode(common.HeapConstant(unique));
}
@@ -65,15 +67,16 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
Node* stack = graph.NewNode(common.StateValues(0));
Node* state_node =
- graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0), kIgnoreOutput),
+ graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0),
+ OutputFrameStateCombine::Ignore()),
parameters, locals, stack, context, UndefinedConstant());
return state_node;
}
Node* reduce(Node* node) {
- JSGraph jsgraph(&graph, &common, &javascript, &typer, &machine);
- JSTypedLowering reducer(&jsgraph);
+ JSGraph jsgraph(&graph, &common, &javascript, &machine);
+ JSTypedLowering reducer(&jsgraph, main_zone());
Reduction reduction = reducer.Reduce(node);
if (reduction.Changed()) return reduction.replacement();
return node;
@@ -164,17 +167,19 @@ static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(),
static Type* kInt32Types[] = {
- Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
- Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
- Type::Signed32(), Type::Unsigned32(), Type::Integral32()};
+ Type::UnsignedSmall(), Type::NegativeSigned32(),
+ Type::NonNegativeSigned32(), Type::SignedSmall(),
+ Type::Signed32(), Type::Unsigned32(),
+ Type::Integral32()};
static Type* kNumberTypes[] = {
- Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
- Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
- Type::Signed32(), Type::Unsigned32(), Type::Integral32(),
- Type::MinusZero(), Type::NaN(), Type::OtherNumber(),
- Type::OrderedNumber(), Type::Number()};
+ Type::UnsignedSmall(), Type::NegativeSigned32(),
+ Type::NonNegativeSigned32(), Type::SignedSmall(),
+ Type::Signed32(), Type::Unsigned32(),
+ Type::Integral32(), Type::MinusZero(),
+ Type::NaN(), Type::OrderedNumber(),
+ Type::PlainNumber(), Type::Number()};
static Type* kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
@@ -260,16 +265,15 @@ TEST(NumberBinops) {
static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
Type* old_type = NodeProperties::GetBounds(old_input).upper;
+ Type* new_type = NodeProperties::GetBounds(new_input).upper;
Type* expected_type = I32Type(is_signed);
+ CHECK(new_type->Is(expected_type));
if (old_type->Is(expected_type)) {
CHECK_EQ(old_input, new_input);
} else if (new_input->opcode() == IrOpcode::kNumberConstant) {
- CHECK(NodeProperties::GetBounds(new_input).upper->Is(expected_type));
double v = OpParameter<double>(new_input);
double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
CHECK_EQ(e, v);
- } else {
- CHECK_EQ(NumberToI32(is_signed), new_input->opcode());
}
}
@@ -302,12 +306,13 @@ class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
TEST(Int32BitwiseShifts) {
JSBitwiseShiftTypedLoweringTester R;
- Type* types[] = {
- Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
- Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
- Type::NaN(), Type::OtherNumber(), Type::Undefined(),
- Type::Null(), Type::Boolean(), Type::Number(),
- Type::String(), Type::Object()};
+ Type* types[] = {Type::SignedSmall(), Type::UnsignedSmall(),
+ Type::NegativeSigned32(), Type::NonNegativeSigned32(),
+ Type::Unsigned32(), Type::Signed32(),
+ Type::MinusZero(), Type::NaN(),
+ Type::Undefined(), Type::Null(),
+ Type::Boolean(), Type::Number(),
+ Type::PlainNumber(), Type::String()};
for (size_t i = 0; i < arraysize(types); ++i) {
Node* p0 = R.Parameter(types[i], 0);
@@ -325,9 +330,13 @@ TEST(Int32BitwiseShifts) {
CheckToI32(p0, r0, R.signedness[k]);
- R.CheckPureBinop(IrOpcode::kWord32And, r1);
- CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
- R.CheckInt32Constant(0x1F, r1->InputAt(1));
+ if (r1->opcode() == IrOpcode::kWord32And) {
+ R.CheckPureBinop(IrOpcode::kWord32And, r1);
+ CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
+ R.CheckInt32Constant(0x1F, r1->InputAt(1));
+ } else {
+ CheckToI32(p1, r1, R.signedness[k]);
+ }
}
}
}
@@ -363,11 +372,11 @@ TEST(Int32BitwiseBinops) {
JSBitwiseTypedLoweringTester R;
Type* types[] = {
- Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
- Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
- Type::NaN(), Type::OtherNumber(), Type::Undefined(),
- Type::Null(), Type::Boolean(), Type::Number(),
- Type::String(), Type::Object()};
+ Type::SignedSmall(), Type::UnsignedSmall(), Type::Unsigned32(),
+ Type::Signed32(), Type::MinusZero(), Type::NaN(),
+ Type::OrderedNumber(), Type::PlainNumber(), Type::Undefined(),
+ Type::Null(), Type::Boolean(), Type::Number(),
+ Type::String()};
for (size_t i = 0; i < arraysize(types); ++i) {
Node* p0 = R.Parameter(types[i], 0);
@@ -498,20 +507,6 @@ TEST(JSToBoolean) {
CHECK_EQ(IrOpcode::kParameter, r->opcode());
}
- { // ToBoolean(ordered-number)
- Node* r = R.ReduceUnop(op, Type::OrderedNumber());
- CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
- Node* i = r->InputAt(0);
- CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
- // ToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
- }
-
- { // ToBoolean(string)
- Node* r = R.ReduceUnop(op, Type::String());
- // TODO(titzer): test will break with better js-typed-lowering
- CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- }
-
{ // ToBoolean(object)
Node* r = R.ReduceUnop(op, Type::DetectableObject());
R.CheckTrue(r);
@@ -524,39 +519,7 @@ TEST(JSToBoolean) {
{ // ToBoolean(object)
Node* r = R.ReduceUnop(op, Type::Object());
- CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- }
-}
-
-
-TEST(JSToBoolean_replacement) {
- JSTypedLoweringTester R;
-
- Type* types[] = {Type::Null(), Type::Undefined(),
- Type::Boolean(), Type::OrderedNumber(),
- Type::DetectableObject(), Type::Undetectable()};
-
- for (size_t i = 0; i < arraysize(types); i++) {
- Node* n = R.Parameter(types[i]);
- Node* c = R.graph.NewNode(R.javascript.ToBoolean(), n, R.context(),
- R.start(), R.start());
- Node* effect_use = R.UseForEffect(c);
- Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
-
- R.CheckEffectInput(c, effect_use);
- Node* r = R.reduce(c);
-
- if (types[i]->Is(Type::Boolean())) {
- CHECK_EQ(n, r);
- } else if (types[i]->Is(Type::OrderedNumber())) {
- CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
- } else {
- CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
- }
-
- CHECK_EQ(n, add->InputAt(0));
- CHECK_EQ(r, add->InputAt(1));
- R.CheckEffectInput(R.start(), effect_use);
+ CHECK_EQ(IrOpcode::kAnyToBoolean, r->opcode());
}
}
@@ -690,33 +653,22 @@ TEST(NumberComparison) {
R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
- for (size_t i = 0; i < arraysize(kJSTypes); i++) {
- Type* t0 = kJSTypes[i];
- // Skip Type::String and Type::Receiver which might coerce into a string.
- if (t0->Is(Type::String()) || t0->Is(Type::Receiver())) continue;
- Node* p0 = R.Parameter(t0, 0);
+ Node* const p0 = R.Parameter(Type::Number(), 0);
+ Node* const p1 = R.Parameter(Type::Number(), 1);
- for (size_t j = 0; j < arraysize(kJSTypes); j++) {
- Type* t1 = kJSTypes[j];
- // Skip Type::String and Type::Receiver which might coerce into a string.
- if (t1->Is(Type::String()) || t0->Is(Type::Receiver())) continue;
- Node* p1 = R.Parameter(t1, 1);
+ for (size_t k = 0; k < arraysize(ops); k += 2) {
+ Node* cmp = R.Binop(ops[k], p0, p1);
+ Node* r = R.reduce(cmp);
- for (size_t k = 0; k < arraysize(ops); k += 2) {
- Node* cmp = R.Binop(ops[k], p0, p1);
- Node* r = R.reduce(cmp);
-
- R.CheckPureBinop(ops[k + 1], r);
- if (k >= 4) {
- // GreaterThan and GreaterThanOrEqual commute the inputs
- // and use the LessThan and LessThanOrEqual operators.
- CheckIsConvertedToNumber(p1, r->InputAt(0));
- CheckIsConvertedToNumber(p0, r->InputAt(1));
- } else {
- CheckIsConvertedToNumber(p0, r->InputAt(0));
- CheckIsConvertedToNumber(p1, r->InputAt(1));
- }
- }
+ R.CheckPureBinop(ops[k + 1], r);
+ if (k >= 4) {
+ // GreaterThan and GreaterThanOrEqual commute the inputs
+ // and use the LessThan and LessThanOrEqual operators.
+ CheckIsConvertedToNumber(p1, r->InputAt(0));
+ CheckIsConvertedToNumber(p0, r->InputAt(1));
+ } else {
+ CheckIsConvertedToNumber(p0, r->InputAt(0));
+ CheckIsConvertedToNumber(p1, r->InputAt(1));
}
}
}
@@ -753,51 +705,18 @@ TEST(MixedComparison1) {
}
-TEST(ObjectComparison) {
- JSTypedLoweringTester R;
-
- Node* p0 = R.Parameter(Type::Number(), 0);
- Node* p1 = R.Parameter(Type::Object(), 1);
-
- Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
- Node* effect_use = R.UseForEffect(cmp);
-
- R.CheckEffectInput(R.start(), cmp);
- R.CheckEffectInput(cmp, effect_use);
-
- Node* r = R.reduce(cmp);
-
- R.CheckPureBinop(R.simplified.NumberLessThan(), r);
-
- Node* i0 = r->InputAt(0);
- Node* i1 = r->InputAt(1);
-
- CHECK_EQ(p0, i0);
- CHECK_NE(p1, i1);
- CHECK_EQ(IrOpcode::kParameter, i0->opcode());
- CHECK_EQ(IrOpcode::kJSToNumber, i1->opcode());
-
- // Check effect chain is correct.
- R.CheckEffectInput(R.start(), i1);
- R.CheckEffectInput(i1, effect_use);
-}
-
-
TEST(UnaryNot) {
JSTypedLoweringTester R;
const Operator* opnot = R.javascript.UnaryNot();
for (size_t i = 0; i < arraysize(kJSTypes); i++) {
Node* orig = R.Unop(opnot, R.Parameter(kJSTypes[i]));
- Node* use = R.graph.NewNode(R.common.Return(), orig);
Node* r = R.reduce(orig);
- // TODO(titzer): test will break if/when js-typed-lowering constant folds.
- CHECK_EQ(IrOpcode::kBooleanNot, use->InputAt(0)->opcode());
if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
// The original node was turned into a ToBoolean.
CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- } else {
+ } else if (r->opcode() != IrOpcode::kHeapConstant) {
CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
}
}
@@ -852,7 +771,7 @@ TEST(RemoveToNumberEffects) {
if (effect_use != NULL) {
R.CheckEffectInput(R.start(), effect_use);
// Check that value uses of ToNumber() do not go to start().
- for (int i = 0; i < effect_use->op()->InputCount(); i++) {
+ for (int i = 0; i < effect_use->op()->ValueInputCount(); i++) {
CHECK_NE(R.start(), effect_use->InputAt(i));
}
}
@@ -904,9 +823,9 @@ class BinopEffectsTester {
Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
CHECK_EQ(opcode, node->opcode());
if (effects) {
- CHECK_LT(0, OperatorProperties::GetEffectInputCount(node->op()));
+ CHECK_LT(0, node->op()->EffectInputCount());
} else {
- CHECK_EQ(0, OperatorProperties::GetEffectInputCount(node->op()));
+ CHECK_EQ(0, node->op()->EffectInputCount());
}
return node;
}
@@ -1030,7 +949,7 @@ TEST(OrderNumberBinopEffects1) {
};
for (size_t j = 0; j < arraysize(ops); j += 2) {
- BinopEffectsTester B(ops[j], Type::Object(), Type::String());
+ BinopEffectsTester B(ops[j], Type::Symbol(), Type::Symbol());
CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
@@ -1103,8 +1022,8 @@ TEST(OrderCompareEffects) {
CHECK_EQ(B.p1, i0->InputAt(0));
CHECK_EQ(B.p0, i1->InputAt(0));
- // But effects should be ordered start -> i1 -> i0 -> effect_use
- B.CheckEffectOrdering(i1, i0);
+ // But effects should be ordered start -> i1 -> effect_use
+ B.CheckEffectOrdering(i1);
}
for (size_t j = 0; j < arraysize(ops); j += 2) {
@@ -1166,7 +1085,7 @@ TEST(Int32BinopEffects) {
for (int j = 0; j < R.kNumberOps; j += 2) {
bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
- BinopEffectsTester B(R.ops[j], Type::Number(), Type::Object());
+ BinopEffectsTester B(R.ops[j], Type::Number(), Type::Primitive());
B.R.CheckPureBinop(B.result->opcode(), B.result);
@@ -1183,7 +1102,7 @@ TEST(Int32BinopEffects) {
for (int j = 0; j < R.kNumberOps; j += 2) {
bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
- BinopEffectsTester B(R.ops[j], Type::Object(), Type::Number());
+ BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Number());
B.R.CheckPureBinop(B.result->opcode(), B.result);
@@ -1200,7 +1119,7 @@ TEST(Int32BinopEffects) {
for (int j = 0; j < R.kNumberOps; j += 2) {
bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
- BinopEffectsTester B(R.ops[j], Type::Object(), Type::Object());
+ BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Primitive());
B.R.CheckPureBinop(B.result->opcode(), B.result);
@@ -1218,33 +1137,6 @@ TEST(Int32BinopEffects) {
}
-TEST(UnaryNotEffects) {
- JSTypedLoweringTester R;
- const Operator* opnot = R.javascript.UnaryNot();
-
- for (size_t i = 0; i < arraysize(kJSTypes); i++) {
- Node* p0 = R.Parameter(kJSTypes[i], 0);
- Node* orig = R.Unop(opnot, p0);
- Node* effect_use = R.UseForEffect(orig);
- Node* value_use = R.graph.NewNode(R.common.Return(), orig);
- Node* r = R.reduce(orig);
- // TODO(titzer): test will break if/when js-typed-lowering constant folds.
- CHECK_EQ(IrOpcode::kBooleanNot, value_use->InputAt(0)->opcode());
-
- if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
- // The original node was turned into a ToBoolean, which has an effect.
- CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- R.CheckEffectInput(R.start(), orig);
- R.CheckEffectInput(orig, effect_use);
- } else {
- // effect should have been removed from this node.
- CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
- R.CheckEffectInput(R.start(), effect_use);
- }
- }
-}
-
-
TEST(Int32AddNarrowing) {
{
JSBitwiseTypedLoweringTester R;
@@ -1263,11 +1155,7 @@ TEST(Int32AddNarrowing) {
Node* r = R.reduce(or_node);
CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
- CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
- bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
-
- Type* add_type = NodeProperties::GetBounds(add_node).upper;
- CHECK(add_type->Is(I32Type(is_signed)));
+ CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
}
}
}
@@ -1290,40 +1178,33 @@ TEST(Int32AddNarrowing) {
Node* r = R.reduce(or_node);
CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
- CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
- bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
-
- Type* add_type = NodeProperties::GetBounds(add_node).upper;
- CHECK(add_type->Is(I32Type(is_signed)));
+ CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
}
}
}
}
}
-}
-
-
-TEST(Int32AddNarrowingNotOwned) {
- JSBitwiseTypedLoweringTester R;
+ {
+ JSBitwiseTypedLoweringTester R;
- for (int o = 0; o < R.kNumberOps; o += 2) {
- Node* n0 = R.Parameter(I32Type(R.signedness[o]));
- Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
- Node* one = R.graph.NewNode(R.common.NumberConstant(1));
-
- Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
- Node* or_node = R.Binop(R.ops[o], add_node, one);
- Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
- Node* r = R.reduce(or_node);
- CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
- // Should not be reduced to Int32Add because of the other number add.
- CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
- // Conversion to int32 should be done.
- CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
- CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
- // The other use should also not be touched.
- CHECK_EQ(add_node, other_use->InputAt(0));
- CHECK_EQ(one, other_use->InputAt(1));
+ for (int o = 0; o < R.kNumberOps; o += 2) {
+ Node* n0 = R.Parameter(I32Type(R.signedness[o]));
+ Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
+ Node* one = R.graph.NewNode(R.common.NumberConstant(1));
+
+ Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
+ Node* or_node = R.Binop(R.ops[o], add_node, one);
+ Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
+ Node* r = R.reduce(or_node);
+ CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
+ CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
+ // Conversion to int32 should be done.
+ CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
+ CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
+ // The other use should also not be touched.
+ CHECK_EQ(add_node, other_use->InputAt(0));
+ CHECK_EQ(one, other_use->InputAt(1));
+ }
}
}
diff --git a/test/cctest/compiler/test-jump-threading.cc b/test/cctest/compiler/test-jump-threading.cc
new file mode 100644
index 00000000..74bf43d2
--- /dev/null
+++ b/test/cctest/compiler/test-jump-threading.cc
@@ -0,0 +1,764 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/instruction.h"
+#include "src/compiler/instruction-codes.h"
+#include "src/compiler/jump-threading.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+typedef BasicBlock::RpoNumber RpoNumber;
+
+class TestCode : public HandleAndZoneScope {
+ public:
+ TestCode()
+ : HandleAndZoneScope(),
+ blocks_(main_zone()),
+ sequence_(main_zone(), &blocks_),
+ rpo_number_(RpoNumber::FromInt(0)),
+ current_(NULL) {}
+
+ ZoneVector<InstructionBlock*> blocks_;
+ InstructionSequence sequence_;
+ RpoNumber rpo_number_;
+ InstructionBlock* current_;
+
+ int Jump(int target) {
+ Start();
+ InstructionOperand* ops[] = {UseRpo(target)};
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchJmp, 0, NULL, 1,
+ ops, 0, NULL)->MarkAsControl());
+ int pos = static_cast<int>(sequence_.instructions().size() - 1);
+ End();
+ return pos;
+ }
+ void Fallthru() {
+ Start();
+ End();
+ }
+ int Branch(int ttarget, int ftarget) {
+ Start();
+ InstructionOperand* ops[] = {UseRpo(ttarget), UseRpo(ftarget)};
+ InstructionCode code = 119 | FlagsModeField::encode(kFlags_branch) |
+ FlagsConditionField::encode(kEqual);
+ sequence_.AddInstruction(Instruction::New(main_zone(), code, 0, NULL, 2,
+ ops, 0, NULL)->MarkAsControl());
+ int pos = static_cast<int>(sequence_.instructions().size() - 1);
+ End();
+ return pos;
+ }
+ void Nop() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop));
+ }
+ void RedundantMoves() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop));
+ int index = static_cast<int>(sequence_.instructions().size()) - 1;
+ sequence_.AddGapMove(index, RegisterOperand::Create(13, main_zone()),
+ RegisterOperand::Create(13, main_zone()));
+ }
+ void NonRedundantMoves() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop));
+ int index = static_cast<int>(sequence_.instructions().size()) - 1;
+ sequence_.AddGapMove(index, ImmediateOperand::Create(11, main_zone()),
+ RegisterOperand::Create(11, main_zone()));
+ }
+ void Other() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), 155));
+ }
+ void End() {
+ Start();
+ sequence_.EndBlock(current_->rpo_number());
+ current_ = NULL;
+ rpo_number_ = RpoNumber::FromInt(rpo_number_.ToInt() + 1);
+ }
+ InstructionOperand* UseRpo(int num) {
+ int index = sequence_.AddImmediate(Constant(RpoNumber::FromInt(num)));
+ return ImmediateOperand::Create(index, main_zone());
+ }
+ void Start(bool deferred = false) {
+ if (current_ == NULL) {
+ current_ = new (main_zone()) InstructionBlock(
+ main_zone(), BasicBlock::Id::FromInt(rpo_number_.ToInt()),
+ rpo_number_, RpoNumber::Invalid(), RpoNumber::Invalid(), deferred);
+ blocks_.push_back(current_);
+ sequence_.StartBlock(rpo_number_);
+ }
+ }
+ void Defer() {
+ CHECK(current_ == NULL);
+ Start(true);
+ }
+};
+
+
+void VerifyForwarding(TestCode& code, int count, int* expected) {
+ Zone local_zone(code.main_isolate());
+ ZoneVector<RpoNumber> result(&local_zone);
+ JumpThreading::ComputeForwarding(&local_zone, result, &code.sequence_);
+
+ CHECK(count == static_cast<int>(result.size()));
+ for (int i = 0; i < count; i++) {
+ CHECK(expected[i] == result[i].ToInt());
+ }
+}
+
+
+TEST(FwEmpty1) {
+ TestCode code;
+
+ // B0
+ code.Jump(1);
+ // B1
+ code.Jump(2);
+ // B2
+ code.End();
+
+ static int expected[] = {2, 2, 2};
+ VerifyForwarding(code, 3, expected);
+}
+
+
+TEST(FwEmptyN) {
+ for (int i = 0; i < 9; i++) {
+ TestCode code;
+
+ // B0
+ code.Jump(1);
+ // B1
+ for (int j = 0; j < i; j++) code.Nop();
+ code.Jump(2);
+ // B2
+ code.End();
+
+ static int expected[] = {2, 2, 2};
+ VerifyForwarding(code, 3, expected);
+ }
+}
+
+
+TEST(FwNone1) {
+ TestCode code;
+
+ // B0
+ code.End();
+
+ static int expected[] = {0};
+ VerifyForwarding(code, 1, expected);
+}
+
+
+TEST(FwMoves1) {
+ TestCode code;
+
+ // B0
+ code.RedundantMoves();
+ code.End();
+
+ static int expected[] = {0};
+ VerifyForwarding(code, 1, expected);
+}
+
+
+TEST(FwMoves2) {
+ TestCode code;
+
+ // B0
+ code.RedundantMoves();
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwMoves2b) {
+ TestCode code;
+
+ // B0
+ code.NonRedundantMoves();
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {0, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwOther2) {
+ TestCode code;
+
+ // B0
+ code.Other();
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {0, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwNone2a) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwNone2b) {
+ TestCode code;
+
+ // B0
+ code.Jump(1);
+ // B1
+ code.End();
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwLoop1) {
+ TestCode code;
+
+ // B0
+ code.Jump(0);
+
+ static int expected[] = {0};
+ VerifyForwarding(code, 1, expected);
+}
+
+
+TEST(FwLoop2) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Jump(0);
+
+ static int expected[] = {0, 0};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwLoop3) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(0);
+
+ static int expected[] = {0, 0, 0};
+ VerifyForwarding(code, 3, expected);
+}
+
+
+TEST(FwLoop1b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Jump(1);
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwLoop2b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1};
+ VerifyForwarding(code, 3, expected);
+}
+
+
+TEST(FwLoop3b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Fallthru();
+ // B3
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1, 1};
+ VerifyForwarding(code, 4, expected);
+}
+
+
+TEST(FwLoop2_1a) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Fallthru();
+ // B3
+ code.Jump(1);
+ // B4
+ code.Jump(2);
+
+ static int expected[] = {1, 1, 1, 1, 1};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop2_1b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(4);
+ // B3
+ code.Jump(1);
+ // B4
+ code.Jump(2);
+
+ static int expected[] = {2, 2, 2, 2, 2};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop2_1c) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(4);
+ // B3
+ code.Jump(2);
+ // B4
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1, 1, 1};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop2_1d) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(1);
+ // B3
+ code.Jump(1);
+ // B4
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1, 1, 1};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop3_1a) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Fallthru();
+ // B3
+ code.Jump(2);
+ // B4
+ code.Jump(1);
+ // B5
+ code.Jump(0);
+
+ static int expected[] = {2, 2, 2, 2, 2, 2};
+ VerifyForwarding(code, 6, expected);
+}
+
+
+TEST(FwDiamonds) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ TestCode code;
+ // B0
+ code.Branch(1, 2);
+ // B1
+ if (i) code.Other();
+ code.Jump(3);
+ // B2
+ if (j) code.Other();
+ code.Jump(3);
+ // B3
+ code.End();
+
+ int expected[] = {0, i ? 1 : 3, j ? 2 : 3, 3};
+ VerifyForwarding(code, 4, expected);
+ }
+ }
+}
+
+
+TEST(FwDiamonds2) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ TestCode code;
+ // B0
+ code.Branch(1, 2);
+ // B1
+ if (i) code.Other();
+ code.Jump(3);
+ // B2
+ if (j) code.Other();
+ code.Jump(3);
+ // B3
+ if (k) code.NonRedundantMoves();
+ code.Jump(4);
+ // B4
+ code.End();
+
+ int merge = k ? 3 : 4;
+ int expected[] = {0, i ? 1 : merge, j ? 2 : merge, merge, 4};
+ VerifyForwarding(code, 5, expected);
+ }
+ }
+ }
+}
+
+
+TEST(FwDoubleDiamonds) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int x = 0; x < 2; x++) {
+ for (int y = 0; y < 2; y++) {
+ TestCode code;
+ // B0
+ code.Branch(1, 2);
+ // B1
+ if (i) code.Other();
+ code.Jump(3);
+ // B2
+ if (j) code.Other();
+ code.Jump(3);
+ // B3
+ code.Branch(4, 5);
+ // B4
+ if (x) code.Other();
+ code.Jump(6);
+ // B5
+ if (y) code.Other();
+ code.Jump(6);
+ // B6
+ code.End();
+
+ int expected[] = {0, i ? 1 : 3, j ? 2 : 3, 3,
+ x ? 4 : 6, y ? 5 : 6, 6};
+ VerifyForwarding(code, 7, expected);
+ }
+ }
+ }
+ }
+}
+
+template <int kSize>
+void RunPermutationsRecursive(int outer[kSize], int start,
+ void (*run)(int*, int)) {
+ int permutation[kSize];
+
+ for (int i = 0; i < kSize; i++) permutation[i] = outer[i];
+
+ int count = kSize - start;
+ if (count == 0) return run(permutation, kSize);
+ for (int i = start; i < kSize; i++) {
+ permutation[start] = outer[i];
+ permutation[i] = outer[start];
+ RunPermutationsRecursive<kSize>(permutation, start + 1, run);
+ permutation[i] = outer[i];
+ permutation[start] = outer[start];
+ }
+}
+
+
+template <int kSize>
+void RunAllPermutations(void (*run)(int*, int)) {
+ int permutation[kSize];
+ for (int i = 0; i < kSize; i++) permutation[i] = i;
+ RunPermutationsRecursive<kSize>(permutation, 0, run);
+}
+
+
+void PrintPermutation(int* permutation, int size) {
+ printf("{ ");
+ for (int i = 0; i < size; i++) {
+ if (i > 0) printf(", ");
+ printf("%d", permutation[i]);
+ }
+ printf(" }\n");
+}
+
+
+int find(int x, int* permutation, int size) {
+ for (int i = 0; i < size; i++) {
+ if (permutation[i] == x) return i;
+ }
+ return size;
+}
+
+
+void RunPermutedChain(int* permutation, int size) {
+ TestCode code;
+ int cur = -1;
+ for (int i = 0; i < size; i++) {
+ code.Jump(find(cur + 1, permutation, size) + 1);
+ cur = permutation[i];
+ }
+ code.Jump(find(cur + 1, permutation, size) + 1);
+ code.End();
+
+ int expected[] = {size + 1, size + 1, size + 1, size + 1,
+ size + 1, size + 1, size + 1};
+ VerifyForwarding(code, size + 2, expected);
+}
+
+
+TEST(FwPermuted_chain) {
+ RunAllPermutations<3>(RunPermutedChain);
+ RunAllPermutations<4>(RunPermutedChain);
+ RunAllPermutations<5>(RunPermutedChain);
+}
+
+
+void RunPermutedDiamond(int* permutation, int size) {
+ TestCode code;
+ int br = 1 + find(0, permutation, size);
+ code.Jump(br);
+ for (int i = 0; i < size; i++) {
+ switch (permutation[i]) {
+ case 0:
+ code.Branch(1 + find(1, permutation, size),
+ 1 + find(2, permutation, size));
+ break;
+ case 1:
+ code.Jump(1 + find(3, permutation, size));
+ break;
+ case 2:
+ code.Jump(1 + find(3, permutation, size));
+ break;
+ case 3:
+ code.Jump(5);
+ break;
+ }
+ }
+ code.End();
+
+ int expected[] = {br, 5, 5, 5, 5, 5};
+ expected[br] = br;
+ VerifyForwarding(code, 6, expected);
+}
+
+
+TEST(FwPermuted_diamond) { RunAllPermutations<4>(RunPermutedDiamond); }
+
+
+void ApplyForwarding(TestCode& code, int size, int* forward) {
+ ZoneVector<RpoNumber> vector(code.main_zone());
+ for (int i = 0; i < size; i++) {
+ vector.push_back(RpoNumber::FromInt(forward[i]));
+ }
+ JumpThreading::ApplyForwarding(vector, &code.sequence_);
+}
+
+
+void CheckJump(TestCode& code, int pos, int target) {
+ Instruction* instr = code.sequence_.InstructionAt(pos);
+ CHECK_EQ(kArchJmp, instr->arch_opcode());
+ CHECK_EQ(1, static_cast<int>(instr->InputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->TempCount()));
+ CHECK_EQ(target, code.sequence_.InputRpo(instr, 0).ToInt());
+}
+
+
+void CheckNop(TestCode& code, int pos) {
+ Instruction* instr = code.sequence_.InstructionAt(pos);
+ CHECK_EQ(kArchNop, instr->arch_opcode());
+ CHECK_EQ(0, static_cast<int>(instr->InputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->TempCount()));
+}
+
+
+void CheckBranch(TestCode& code, int pos, int t1, int t2) {
+ Instruction* instr = code.sequence_.InstructionAt(pos);
+ CHECK_EQ(2, static_cast<int>(instr->InputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->TempCount()));
+ CHECK_EQ(t1, code.sequence_.InputRpo(instr, 0).ToInt());
+ CHECK_EQ(t2, code.sequence_.InputRpo(instr, 1).ToInt());
+}
+
+
+void CheckAssemblyOrder(TestCode& code, int size, int* expected) {
+ int i = 0;
+ for (auto const block : code.sequence_.instruction_blocks()) {
+ CHECK_EQ(expected[i++], block->ao_number().ToInt());
+ }
+}
+
+
+TEST(Rewire1) {
+ TestCode code;
+
+ // B0
+ int j1 = code.Jump(1);
+ // B1
+ int j2 = code.Jump(2);
+ // B2
+ code.End();
+
+ static int forward[] = {2, 2, 2};
+ ApplyForwarding(code, 3, forward);
+ CheckJump(code, j1, 2);
+ CheckNop(code, j2);
+
+ static int assembly[] = {0, 1, 1};
+ CheckAssemblyOrder(code, 3, assembly);
+}
+
+
+TEST(Rewire1_deferred) {
+ TestCode code;
+
+ // B0
+ int j1 = code.Jump(1);
+ // B1
+ int j2 = code.Jump(2);
+ // B2
+ code.Defer();
+ int j3 = code.Jump(3);
+ // B3
+ code.End();
+
+ static int forward[] = {3, 3, 3, 3};
+ ApplyForwarding(code, 4, forward);
+ CheckJump(code, j1, 3);
+ CheckNop(code, j2);
+ CheckNop(code, j3);
+
+ static int assembly[] = {0, 1, 2, 1};
+ CheckAssemblyOrder(code, 4, assembly);
+}
+
+
+TEST(Rewire2_deferred) {
+ TestCode code;
+
+ // B0
+ code.Other();
+ int j1 = code.Jump(1);
+ // B1
+ code.Defer();
+ code.Fallthru();
+ // B2
+ code.Defer();
+ int j2 = code.Jump(3);
+ // B3
+ code.End();
+
+ static int forward[] = {0, 1, 2, 3};
+ ApplyForwarding(code, 4, forward);
+ CheckJump(code, j1, 1);
+ CheckJump(code, j2, 3);
+
+ static int assembly[] = {0, 2, 3, 1};
+ CheckAssemblyOrder(code, 4, assembly);
+}
+
+
+TEST(Rewire_diamond) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ TestCode code;
+ // B0
+ int j1 = code.Jump(1);
+ // B1
+ int b1 = code.Branch(2, 3);
+ // B2
+ int j2 = code.Jump(4);
+ // B3
+ int j3 = code.Jump(4);
+ // B5
+ code.End();
+
+ int forward[] = {0, 1, i ? 4 : 2, j ? 4 : 3, 4};
+ ApplyForwarding(code, 5, forward);
+ CheckJump(code, j1, 1);
+ CheckBranch(code, b1, i ? 4 : 2, j ? 4 : 3);
+ if (i) {
+ CheckNop(code, j2);
+ } else {
+ CheckJump(code, j2, 4);
+ }
+ if (j) {
+ CheckNop(code, j3);
+ } else {
+ CheckJump(code, j3, 4);
+ }
+
+ int assembly[] = {0, 1, 2, 3, 4};
+ if (i) {
+ for (int k = 3; k < 5; k++) assembly[k]--;
+ }
+ if (j) {
+ for (int k = 4; k < 5; k++) assembly[k]--;
+ }
+ CheckAssemblyOrder(code, 5, assembly);
+ }
+ }
+}
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8
diff --git a/test/cctest/compiler/test-linkage.cc b/test/cctest/compiler/test-linkage.cc
index ff65d6e4..117caf22 100644
--- a/test/cctest/compiler/test-linkage.cc
+++ b/test/cctest/compiler/test-linkage.cc
@@ -8,7 +8,6 @@
#include "src/zone.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/machine-operator.h"
@@ -23,8 +22,8 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 0, 0, 0);
// So we can get a real JS function.
static Handle<JSFunction> Compile(const char* source) {
@@ -45,7 +44,7 @@ TEST(TestLinkageCreate) {
InitializedHandleScope handles;
Handle<JSFunction> function = Compile("a + b");
CompilationInfoWithZone info(function);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
}
@@ -60,13 +59,13 @@ TEST(TestLinkageJSFunctionIncoming) {
Handle<JSFunction> function = v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CompileRun(sources[i])));
CompilationInfoWithZone info(function);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
CallDescriptor* descriptor = linkage.GetIncomingDescriptor();
CHECK_NE(NULL, descriptor);
- CHECK_EQ(1 + i, descriptor->JSParameterCount());
- CHECK_EQ(1, descriptor->ReturnCount());
+ CHECK_EQ(1 + i, static_cast<int>(descriptor->JSParameterCount()));
+ CHECK_EQ(1, static_cast<int>(descriptor->ReturnCount()));
CHECK_EQ(Operator::kNoProperties, descriptor->properties());
CHECK_EQ(true, descriptor->IsJSFunctionCall());
}
@@ -76,7 +75,7 @@ TEST(TestLinkageJSFunctionIncoming) {
TEST(TestLinkageCodeStubIncoming) {
Isolate* isolate = CcTest::InitIsolateOnce();
CompilationInfoWithZone info(static_cast<HydrogenCodeStub*>(NULL), isolate);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
// TODO(titzer): test linkage creation with a bonafide code stub.
// this just checks current behavior.
CHECK_EQ(NULL, linkage.GetIncomingDescriptor());
@@ -87,13 +86,14 @@ TEST(TestLinkageJSCall) {
HandleAndZoneScope handles;
Handle<JSFunction> function = Compile("a + c");
CompilationInfoWithZone info(function);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
for (int i = 0; i < 32; i++) {
- CallDescriptor* descriptor = linkage.GetJSCallDescriptor(i);
+ CallDescriptor* descriptor =
+ linkage.GetJSCallDescriptor(i, CallDescriptor::kNoFlags);
CHECK_NE(NULL, descriptor);
- CHECK_EQ(i, descriptor->JSParameterCount());
- CHECK_EQ(1, descriptor->ReturnCount());
+ CHECK_EQ(i, static_cast<int>(descriptor->JSParameterCount()));
+ CHECK_EQ(1, static_cast<int>(descriptor->ReturnCount()));
CHECK_EQ(Operator::kNoProperties, descriptor->properties());
CHECK_EQ(true, descriptor->IsJSFunctionCall());
}
diff --git a/test/cctest/compiler/test-loop-analysis.cc b/test/cctest/compiler/test-loop-analysis.cc
new file mode 100644
index 00000000..9c112681
--- /dev/null
+++ b/test/cctest/compiler/test-loop-analysis.cc
@@ -0,0 +1,862 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "src/compiler/access-builder.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/loop-analysis.h"
+#include "src/compiler/node.h"
+#include "src/compiler/opcodes.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "src/compiler/scheduler.h"
+#include "src/compiler/simplified-operator.h"
+#include "src/compiler/verifier.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
+ 0, 1, 0, 0);
+static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure,
+ "Int32LessThan", 2, 0, 0, 1, 0, 0);
+static Operator kStore(IrOpcode::kStore, Operator::kNoProperties, "Store", 0, 2,
+ 1, 0, 1, 0);
+
+static const int kNumLeafs = 4;
+
+// A helper for all tests dealing with LoopFinder.
+class LoopFinderTester : HandleAndZoneScope {
+ public:
+ LoopFinderTester()
+ : isolate(main_isolate()),
+ common(main_zone()),
+ graph(main_zone()),
+ jsgraph(&graph, &common, NULL, NULL),
+ start(graph.NewNode(common.Start(1))),
+ end(graph.NewNode(common.End(), start)),
+ p0(graph.NewNode(common.Parameter(0), start)),
+ zero(jsgraph.Int32Constant(0)),
+ one(jsgraph.OneConstant()),
+ half(jsgraph.Constant(0.5)),
+ self(graph.NewNode(common.Int32Constant(0xaabbccdd))),
+ dead(graph.NewNode(common.Dead())),
+ loop_tree(NULL) {
+ graph.SetEnd(end);
+ graph.SetStart(start);
+ leaf[0] = zero;
+ leaf[1] = one;
+ leaf[2] = half;
+ leaf[3] = p0;
+ }
+
+ Isolate* isolate;
+ CommonOperatorBuilder common;
+ Graph graph;
+ JSGraph jsgraph;
+ Node* start;
+ Node* end;
+ Node* p0;
+ Node* zero;
+ Node* one;
+ Node* half;
+ Node* self;
+ Node* dead;
+ Node* leaf[kNumLeafs];
+ LoopTree* loop_tree;
+
+ Node* Phi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, false), a, start));
+ }
+
+ Node* Phi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, false), a, b, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, false), a, b, c, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, false), a, b, c, d, start));
+ }
+
+ Node* EffectPhi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, true), a, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, true), a, b, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, true), a, b, c, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, true), a, b, c, d, start));
+ }
+
+ Node* SetSelfReferences(Node* node) {
+ for (Edge edge : node->input_edges()) {
+ if (edge.to() == self) node->ReplaceInput(edge.index(), node);
+ }
+ return node;
+ }
+
+ const Operator* op(int count, bool effect) {
+ return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count);
+ }
+
+ Node* Return(Node* val, Node* effect, Node* control) {
+ Node* ret = graph.NewNode(common.Return(), val, effect, control);
+ end->ReplaceInput(0, ret);
+ return ret;
+ }
+
+ LoopTree* GetLoopTree() {
+ if (loop_tree == NULL) {
+ if (FLAG_trace_turbo_graph) {
+ OFStream os(stdout);
+ os << AsRPO(graph);
+ }
+ Zone zone(isolate);
+ loop_tree = LoopFinder::BuildLoopTree(&graph, &zone);
+ }
+ return loop_tree;
+ }
+
+ void CheckLoop(Node** header, int header_count, Node** body, int body_count) {
+ LoopTree* tree = GetLoopTree();
+ LoopTree::Loop* loop = tree->ContainingLoop(header[0]);
+ CHECK_NE(NULL, loop);
+
+ CHECK(header_count == static_cast<int>(loop->HeaderSize()));
+ for (int i = 0; i < header_count; i++) {
+ // Each header node should be in the loop.
+ CHECK_EQ(loop, tree->ContainingLoop(header[i]));
+ CheckRangeContains(tree->HeaderNodes(loop), header[i]);
+ }
+
+ CHECK_EQ(body_count, static_cast<int>(loop->BodySize()));
+ for (int i = 0; i < body_count; i++) {
+ // Each body node should be contained in the loop.
+ CHECK(tree->Contains(loop, body[i]));
+ CheckRangeContains(tree->BodyNodes(loop), body[i]);
+ }
+ }
+
+ void CheckRangeContains(NodeRange range, Node* node) {
+ // O(n) ftw.
+ CHECK_NE(range.end(), std::find(range.begin(), range.end(), node));
+ }
+
+ void CheckNestedLoops(Node** chain, int chain_count) {
+ LoopTree* tree = GetLoopTree();
+ for (int i = 0; i < chain_count; i++) {
+ Node* header = chain[i];
+ // Each header should be in a loop.
+ LoopTree::Loop* loop = tree->ContainingLoop(header);
+ CHECK_NE(NULL, loop);
+ // Check parentage.
+ LoopTree::Loop* parent =
+ i == 0 ? NULL : tree->ContainingLoop(chain[i - 1]);
+ CHECK_EQ(parent, loop->parent());
+ for (int j = i - 1; j >= 0; j--) {
+ // This loop should be nested inside all the outer loops.
+ Node* outer_header = chain[j];
+ LoopTree::Loop* outer = tree->ContainingLoop(outer_header);
+ CHECK(tree->Contains(outer, header));
+ CHECK(!tree->Contains(loop, outer_header));
+ }
+ }
+ }
+};
+
+
+struct While {
+ LoopFinderTester& t;
+ Node* branch;
+ Node* if_true;
+ Node* exit;
+ Node* loop;
+
+ While(LoopFinderTester& R, Node* cond) : t(R) {
+ loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ branch = t.graph.NewNode(t.common.Branch(), cond, loop);
+ if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ exit = t.graph.NewNode(t.common.IfFalse(), branch);
+ loop->ReplaceInput(1, if_true);
+ }
+
+ void chain(Node* control) { loop->ReplaceInput(0, control); }
+ void nest(While& that) {
+ that.loop->ReplaceInput(1, exit);
+ this->loop->ReplaceInput(0, that.if_true);
+ }
+};
+
+
+struct Counter {
+ Node* base;
+ Node* inc;
+ Node* phi;
+ Node* add;
+
+ Counter(While& w, int32_t b, int32_t k)
+ : base(w.t.jsgraph.Int32Constant(b)), inc(w.t.jsgraph.Int32Constant(k)) {
+ Build(w);
+ }
+
+ Counter(While& w, Node* b, Node* k) : base(b), inc(k) { Build(w); }
+
+ void Build(While& w) {
+ phi = w.t.graph.NewNode(w.t.op(2, false), base, base, w.loop);
+ add = w.t.graph.NewNode(&kIntAdd, phi, inc);
+ phi->ReplaceInput(1, add);
+ }
+};
+
+
+struct StoreLoop {
+ Node* base;
+ Node* val;
+ Node* phi;
+ Node* store;
+
+ explicit StoreLoop(While& w)
+ : base(w.t.jsgraph.Int32Constant(12)),
+ val(w.t.jsgraph.Int32Constant(13)) {
+ Build(w);
+ }
+
+ StoreLoop(While& w, Node* b, Node* v) : base(b), val(v) { Build(w); }
+
+ void Build(While& w) {
+ phi = w.t.graph.NewNode(w.t.op(2, true), base, base, w.loop);
+ store = w.t.graph.NewNode(&kStore, phi, val, w.loop);
+ phi->ReplaceInput(1, store);
+ }
+};
+
+
+TEST(LaLoop1) {
+ // One loop.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ t.Return(t.p0, t.start, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop};
+ Node* body[] = {w.branch, w.if_true};
+ t.CheckLoop(header, 1, body, 2);
+}
+
+
+TEST(LaLoop1c) {
+ // One loop with a counter.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ Counter c(w, 0, 1);
+ t.Return(c.phi, t.start, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop, c.phi};
+ Node* body[] = {w.branch, w.if_true, c.add};
+ t.CheckLoop(header, 2, body, 3);
+}
+
+
+TEST(LaLoop1e) {
+ // One loop with an effect phi.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ StoreLoop c(w);
+ t.Return(t.p0, c.phi, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop, c.phi};
+ Node* body[] = {w.branch, w.if_true, c.store};
+ t.CheckLoop(header, 2, body, 3);
+}
+
+
+TEST(LaLoop1d) {
+ // One loop with two counters.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ Counter c1(w, 0, 1);
+ Counter c2(w, 1, 1);
+ t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop, c1.phi, c2.phi};
+ Node* body[] = {w.branch, w.if_true, c1.add, c2.add};
+ t.CheckLoop(header, 3, body, 4);
+}
+
+
+TEST(LaLoop2) {
+ // One loop following another.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ w2.chain(w1.exit);
+ t.Return(t.p0, t.start, w2.exit);
+
+ {
+ Node* chain[] = {w1.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w1.loop};
+ Node* body[] = {w1.branch, w1.if_true};
+ t.CheckLoop(header, 1, body, 2);
+ }
+
+ {
+ Node* chain[] = {w2.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w2.loop};
+ Node* body[] = {w2.branch, w2.if_true};
+ t.CheckLoop(header, 1, body, 2);
+ }
+}
+
+
+TEST(LaLoop2c) {
+ // One loop following another, each with counters.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ Counter c1(w1, 0, 1);
+ Counter c2(w2, 0, 1);
+ w2.chain(w1.exit);
+ t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w2.exit);
+
+ {
+ Node* chain[] = {w1.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w1.loop, c1.phi};
+ Node* body[] = {w1.branch, w1.if_true, c1.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+
+ {
+ Node* chain[] = {w2.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w2.loop, c2.phi};
+ Node* body[] = {w2.branch, w2.if_true, c2.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+}
+
+
+TEST(LaLoop2cc) {
+ // One loop following another; second loop uses phi from first.
+ for (int i = 0; i < 8; i++) {
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ Counter c1(w1, 0, 1);
+
+ // various usage scenarios for the second loop.
+ Counter c2(w2, i & 1 ? t.p0 : c1.phi, i & 2 ? t.p0 : c1.phi);
+ if (i & 3) w2.branch->ReplaceInput(0, c1.phi);
+
+ w2.chain(w1.exit);
+ t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w2.exit);
+
+ {
+ Node* chain[] = {w1.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w1.loop, c1.phi};
+ Node* body[] = {w1.branch, w1.if_true, c1.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+
+ {
+ Node* chain[] = {w2.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w2.loop, c2.phi};
+ Node* body[] = {w2.branch, w2.if_true, c2.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+ }
+}
+
+
+TEST(LaNestedLoop1) {
+ // One loop nested in another.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ w2.nest(w1);
+ t.Return(t.p0, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop};
+ t.CheckNestedLoops(chain, 2);
+
+ Node* h1[] = {w1.loop};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true, w2.exit};
+ t.CheckLoop(h1, 1, b1, 6);
+
+ Node* h2[] = {w2.loop};
+ Node* b2[] = {w2.branch, w2.if_true};
+ t.CheckLoop(h2, 1, b2, 2);
+}
+
+
+TEST(LaNestedLoop1c) {
+ // One loop nested in another, each with a counter.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ Counter c1(w1, 0, 1);
+ Counter c2(w2, 0, 1);
+ w2.branch->ReplaceInput(0, c2.phi);
+ w2.nest(w1);
+ t.Return(c1.phi, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop};
+ t.CheckNestedLoops(chain, 2);
+
+ Node* h1[] = {w1.loop, c1.phi};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true,
+ w2.exit, c2.phi, c1.add, c2.add};
+ t.CheckLoop(h1, 2, b1, 9);
+
+ Node* h2[] = {w2.loop, c2.phi};
+ Node* b2[] = {w2.branch, w2.if_true, c2.add};
+ t.CheckLoop(h2, 2, b2, 3);
+}
+
+
+TEST(LaNestedLoop2) {
+ // Two loops nested in an outer loop.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ While w3(t, t.p0);
+ w2.nest(w1);
+ w3.nest(w1);
+ w3.chain(w2.exit);
+ t.Return(t.p0, t.start, w1.exit);
+
+ Node* chain1[] = {w1.loop, w2.loop};
+ t.CheckNestedLoops(chain1, 2);
+
+ Node* chain2[] = {w1.loop, w3.loop};
+ t.CheckNestedLoops(chain2, 2);
+
+ Node* h1[] = {w1.loop};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true,
+ w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h1, 1, b1, 10);
+
+ Node* h2[] = {w2.loop};
+ Node* b2[] = {w2.branch, w2.if_true};
+ t.CheckLoop(h2, 1, b2, 2);
+
+ Node* h3[] = {w3.loop};
+ Node* b3[] = {w3.branch, w3.if_true};
+ t.CheckLoop(h3, 1, b3, 2);
+}
+
+
+TEST(LaNestedLoop3) {
+ // Three nested loops.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ While w3(t, t.p0);
+ w2.loop->ReplaceInput(0, w1.if_true);
+ w3.loop->ReplaceInput(0, w2.if_true);
+ w2.loop->ReplaceInput(1, w3.exit);
+ w1.loop->ReplaceInput(1, w2.exit);
+ t.Return(t.p0, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop, w3.loop};
+ t.CheckNestedLoops(chain, 3);
+
+ Node* h1[] = {w1.loop};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true,
+ w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h1, 1, b1, 10);
+
+ Node* h2[] = {w2.loop};
+ Node* b2[] = {w2.branch, w2.if_true, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h2, 1, b2, 6);
+
+ Node* h3[] = {w3.loop};
+ Node* b3[] = {w3.branch, w3.if_true};
+ t.CheckLoop(h3, 1, b3, 2);
+}
+
+
+TEST(LaNestedLoop3c) {
+ // Three nested loops with counters.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ Counter c1(w1, 0, 1);
+ While w2(t, t.p0);
+ Counter c2(w2, 0, 1);
+ While w3(t, t.p0);
+ Counter c3(w3, 0, 1);
+ w2.loop->ReplaceInput(0, w1.if_true);
+ w3.loop->ReplaceInput(0, w2.if_true);
+ w2.loop->ReplaceInput(1, w3.exit);
+ w1.loop->ReplaceInput(1, w2.exit);
+ w1.branch->ReplaceInput(0, c1.phi);
+ w2.branch->ReplaceInput(0, c2.phi);
+ w3.branch->ReplaceInput(0, c3.phi);
+ t.Return(c1.phi, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop, w3.loop};
+ t.CheckNestedLoops(chain, 3);
+
+ Node* h1[] = {w1.loop, c1.phi};
+ Node* b1[] = {w1.branch, w1.if_true, c1.add, c2.add, c2.add,
+ c2.phi, c3.phi, w2.loop, w2.branch, w2.if_true,
+ w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h1, 2, b1, 15);
+
+ Node* h2[] = {w2.loop, c2.phi};
+ Node* b2[] = {w2.branch, w2.if_true, c2.add, c3.add, c3.phi,
+ w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h2, 2, b2, 9);
+
+ Node* h3[] = {w3.loop, c3.phi};
+ Node* b3[] = {w3.branch, w3.if_true, c3.add};
+ t.CheckLoop(h3, 2, b3, 3);
+}
+
+
+TEST(LaMultipleExit1) {
+ const int kMaxExits = 10;
+ Node* merge[1 + kMaxExits];
+ Node* body[2 * kMaxExits];
+
+ // A single loop with {i} exits.
+ for (int i = 1; i < kMaxExits; i++) {
+ LoopFinderTester t;
+ Node* cond = t.p0;
+
+ int merge_count = 0;
+ int body_count = 0;
+ Node* loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* last = loop;
+
+ for (int e = 0; e < i; e++) {
+ Node* branch = t.graph.NewNode(t.common.Branch(), cond, last);
+ Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ Node* exit = t.graph.NewNode(t.common.IfFalse(), branch);
+ last = if_true;
+
+ body[body_count++] = branch;
+ body[body_count++] = if_true;
+ merge[merge_count++] = exit;
+ }
+
+ loop->ReplaceInput(1, last); // form loop backedge.
+ Node* end = t.graph.NewNode(t.common.Merge(i), i, merge); // form exit.
+ t.graph.SetEnd(end);
+
+ Node* h[] = {loop};
+ t.CheckLoop(h, 1, body, body_count);
+ }
+}
+
+
+TEST(LaMultipleBackedge1) {
+ const int kMaxBackedges = 10;
+ Node* loop_inputs[1 + kMaxBackedges];
+ Node* body[3 * kMaxBackedges];
+
+ // A single loop with {i} backedges.
+ for (int i = 1; i < kMaxBackedges; i++) {
+ LoopFinderTester t;
+
+ for (int j = 0; j <= i; j++) loop_inputs[j] = t.start;
+ Node* loop = t.graph.NewNode(t.common.Loop(1 + i), 1 + i, loop_inputs);
+
+ Node* cond = t.p0;
+ int body_count = 0;
+ Node* exit = loop;
+
+ for (int b = 0; b < i; b++) {
+ Node* branch = t.graph.NewNode(t.common.Branch(), cond, exit);
+ Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ Node* if_false = t.graph.NewNode(t.common.IfFalse(), branch);
+ exit = if_false;
+
+ body[body_count++] = branch;
+ body[body_count++] = if_true;
+ if (b != (i - 1)) body[body_count++] = if_false;
+
+ loop->ReplaceInput(1 + b, if_true);
+ }
+
+ t.graph.SetEnd(exit);
+
+ Node* h[] = {loop};
+ t.CheckLoop(h, 1, body, body_count);
+ }
+}
+
+
+TEST(LaEdgeMatrix1) {
+ // Test various kinds of extra edges added to a simple loop.
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ for (int k = 0; k < 3; k++) {
+ LoopFinderTester t;
+
+ Node* p1 = t.jsgraph.Int32Constant(11);
+ Node* p2 = t.jsgraph.Int32Constant(22);
+ Node* p3 = t.jsgraph.Int32Constant(33);
+
+ Node* loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* phi =
+ t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p1, loop);
+ Node* cond = t.graph.NewNode(&kIntAdd, phi, p2);
+ Node* branch = t.graph.NewNode(t.common.Branch(), cond, loop);
+ Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ Node* exit = t.graph.NewNode(t.common.IfFalse(), branch);
+ loop->ReplaceInput(1, if_true);
+ Node* ret = t.graph.NewNode(t.common.Return(), p3, t.start, exit);
+ t.graph.SetEnd(ret);
+
+ Node* choices[] = {p1, phi, cond};
+ p1->ReplaceUses(choices[i]);
+ p2->ReplaceUses(choices[j]);
+ p3->ReplaceUses(choices[k]);
+
+ Node* header[] = {loop, phi};
+ Node* body[] = {cond, branch, if_true};
+ t.CheckLoop(header, 2, body, 3);
+ }
+ }
+ }
+}
+
+
+void RunEdgeMatrix2(int i) {
+ DCHECK(i >= 0 && i < 5);
+ for (int j = 0; j < 5; j++) {
+ for (int k = 0; k < 5; k++) {
+ LoopFinderTester t;
+
+ Node* p1 = t.jsgraph.Int32Constant(11);
+ Node* p2 = t.jsgraph.Int32Constant(22);
+ Node* p3 = t.jsgraph.Int32Constant(33);
+
+ // outer loop.
+ Node* loop1 = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* phi1 =
+ t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p1, loop1);
+ Node* cond1 = t.graph.NewNode(&kIntAdd, phi1, t.one);
+ Node* branch1 = t.graph.NewNode(t.common.Branch(), cond1, loop1);
+ Node* if_true1 = t.graph.NewNode(t.common.IfTrue(), branch1);
+ Node* exit1 = t.graph.NewNode(t.common.IfFalse(), branch1);
+
+ // inner loop.
+ Node* loop2 = t.graph.NewNode(t.common.Loop(2), if_true1, t.start);
+ Node* phi2 =
+ t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p2, loop2);
+ Node* cond2 = t.graph.NewNode(&kIntAdd, phi2, p3);
+ Node* branch2 = t.graph.NewNode(t.common.Branch(), cond2, loop2);
+ Node* if_true2 = t.graph.NewNode(t.common.IfTrue(), branch2);
+ Node* exit2 = t.graph.NewNode(t.common.IfFalse(), branch2);
+ loop2->ReplaceInput(1, if_true2);
+ loop1->ReplaceInput(1, exit2);
+
+ Node* ret = t.graph.NewNode(t.common.Return(), phi1, t.start, exit1);
+ t.graph.SetEnd(ret);
+
+ Node* choices[] = {p1, phi1, cond1, phi2, cond2};
+ p1->ReplaceUses(choices[i]);
+ p2->ReplaceUses(choices[j]);
+ p3->ReplaceUses(choices[k]);
+
+ Node* header1[] = {loop1, phi1};
+ Node* body1[] = {cond1, branch1, if_true1, exit2, loop2,
+ phi2, cond2, branch2, if_true2};
+ t.CheckLoop(header1, 2, body1, 9);
+
+ Node* header2[] = {loop2, phi2};
+ Node* body2[] = {cond2, branch2, if_true2};
+ t.CheckLoop(header2, 2, body2, 3);
+
+ Node* chain[] = {loop1, loop2};
+ t.CheckNestedLoops(chain, 2);
+ }
+ }
+}
+
+
+TEST(LaEdgeMatrix2_0) { RunEdgeMatrix2(0); }
+
+
+TEST(LaEdgeMatrix2_1) { RunEdgeMatrix2(1); }
+
+
+TEST(LaEdgeMatrix2_2) { RunEdgeMatrix2(2); }
+
+
+TEST(LaEdgeMatrix2_3) { RunEdgeMatrix2(3); }
+
+
+TEST(LaEdgeMatrix2_4) { RunEdgeMatrix2(4); }
+
+
+// Generates a triply-nested loop with extra edges between the phis and
+// conditions according to the edge choice parameters.
+void RunEdgeMatrix3(int c1a, int c1b, int c1c, // line break
+ int c2a, int c2b, int c2c, // line break
+ int c3a, int c3b, int c3c) { // line break
+ LoopFinderTester t;
+
+ Node* p1a = t.jsgraph.Int32Constant(11);
+ Node* p1b = t.jsgraph.Int32Constant(22);
+ Node* p1c = t.jsgraph.Int32Constant(33);
+ Node* p2a = t.jsgraph.Int32Constant(44);
+ Node* p2b = t.jsgraph.Int32Constant(55);
+ Node* p2c = t.jsgraph.Int32Constant(66);
+ Node* p3a = t.jsgraph.Int32Constant(77);
+ Node* p3b = t.jsgraph.Int32Constant(88);
+ Node* p3c = t.jsgraph.Int32Constant(99);
+
+ // L1 depth = 0
+ Node* loop1 = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* phi1 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p1a, p1c, loop1);
+ Node* cond1 = t.graph.NewNode(&kIntAdd, phi1, p1b);
+ Node* branch1 = t.graph.NewNode(t.common.Branch(), cond1, loop1);
+ Node* if_true1 = t.graph.NewNode(t.common.IfTrue(), branch1);
+ Node* exit1 = t.graph.NewNode(t.common.IfFalse(), branch1);
+
+ // L2 depth = 1
+ Node* loop2 = t.graph.NewNode(t.common.Loop(2), if_true1, t.start);
+ Node* phi2 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p2a, p2c, loop2);
+ Node* cond2 = t.graph.NewNode(&kIntAdd, phi2, p2b);
+ Node* branch2 = t.graph.NewNode(t.common.Branch(), cond2, loop2);
+ Node* if_true2 = t.graph.NewNode(t.common.IfTrue(), branch2);
+ Node* exit2 = t.graph.NewNode(t.common.IfFalse(), branch2);
+
+ // L3 depth = 2
+ Node* loop3 = t.graph.NewNode(t.common.Loop(2), if_true2, t.start);
+ Node* phi3 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p3a, p3c, loop3);
+ Node* cond3 = t.graph.NewNode(&kIntAdd, phi3, p3b);
+ Node* branch3 = t.graph.NewNode(t.common.Branch(), cond3, loop3);
+ Node* if_true3 = t.graph.NewNode(t.common.IfTrue(), branch3);
+ Node* exit3 = t.graph.NewNode(t.common.IfFalse(), branch3);
+
+ loop3->ReplaceInput(1, if_true3);
+ loop2->ReplaceInput(1, exit3);
+ loop1->ReplaceInput(1, exit2);
+
+ Node* ret = t.graph.NewNode(t.common.Return(), phi1, t.start, exit1);
+ t.graph.SetEnd(ret);
+
+ // Mutate the graph according to the edge choices.
+
+ Node* o1[] = {t.one};
+ Node* o2[] = {t.one, phi1, cond1};
+ Node* o3[] = {t.one, phi1, cond1, phi2, cond2};
+
+ p1a->ReplaceUses(o1[c1a]);
+ p1b->ReplaceUses(o1[c1b]);
+
+ p2a->ReplaceUses(o2[c2a]);
+ p2b->ReplaceUses(o2[c2b]);
+
+ p3a->ReplaceUses(o3[c3a]);
+ p3b->ReplaceUses(o3[c3b]);
+
+ Node* l2[] = {phi1, cond1, phi2, cond2};
+ Node* l3[] = {phi1, cond1, phi2, cond2, phi3, cond3};
+
+ p1c->ReplaceUses(l2[c1c]);
+ p2c->ReplaceUses(l3[c2c]);
+ p3c->ReplaceUses(l3[c3c]);
+
+ // Run the tests and verify loop structure.
+
+ Node* chain[] = {loop1, loop2, loop3};
+ t.CheckNestedLoops(chain, 3);
+
+ Node* header1[] = {loop1, phi1};
+ Node* body1[] = {cond1, branch1, if_true1, exit2, loop2,
+ phi2, cond2, branch2, if_true2, exit3,
+ loop3, phi3, cond3, branch3, if_true3};
+ t.CheckLoop(header1, 2, body1, 15);
+
+ Node* header2[] = {loop2, phi2};
+ Node* body2[] = {cond2, branch2, if_true2, exit3, loop3,
+ phi3, cond3, branch3, if_true3};
+ t.CheckLoop(header2, 2, body2, 9);
+
+ Node* header3[] = {loop3, phi3};
+ Node* body3[] = {cond3, branch3, if_true3};
+ t.CheckLoop(header3, 2, body3, 3);
+}
+
+
+// Runs all combinations with a fixed {i}.
+void RunEdgeMatrix3_i(int i) {
+ for (int a = 0; a < 1; a++) {
+ for (int b = 0; b < 1; b++) {
+ for (int c = 0; c < 4; c++) {
+ for (int d = 0; d < 3; d++) {
+ for (int e = 0; e < 3; e++) {
+ for (int f = 0; f < 6; f++) {
+ for (int g = 0; g < 5; g++) {
+ for (int h = 0; h < 5; h++) {
+ RunEdgeMatrix3(a, b, c, d, e, f, g, h, i);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// Test all possible legal triply-nested loops with conditions and phis.
+TEST(LaEdgeMatrix3_0) { RunEdgeMatrix3_i(0); }
+
+
+TEST(LaEdgeMatrix3_1) { RunEdgeMatrix3_i(1); }
+
+
+TEST(LaEdgeMatrix3_2) { RunEdgeMatrix3_i(2); }
+
+
+TEST(LaEdgeMatrix3_3) { RunEdgeMatrix3_i(3); }
+
+
+TEST(LaEdgeMatrix3_4) { RunEdgeMatrix3_i(4); }
+
+
+TEST(LaEdgeMatrix3_5) { RunEdgeMatrix3_i(5); }
diff --git a/test/cctest/compiler/test-loop-assignment-analysis.cc b/test/cctest/compiler/test-loop-assignment-analysis.cc
new file mode 100644
index 00000000..aabd95bc
--- /dev/null
+++ b/test/cctest/compiler/test-loop-assignment-analysis.cc
@@ -0,0 +1,294 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/ast-loop-assignment-analyzer.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+namespace {
+const int kBufferSize = 1024;
+
+struct TestHelper : public HandleAndZoneScope {
+ Handle<JSFunction> function;
+ LoopAssignmentAnalysis* result;
+
+ explicit TestHelper(const char* body)
+ : function(Handle<JSFunction>::null()), result(NULL) {
+ ScopedVector<char> program(kBufferSize);
+ SNPrintF(program, "function f(a,b,c) { %s; } f;", body);
+ v8::Local<v8::Value> v = CompileRun(program.start());
+ Handle<Object> obj = v8::Utils::OpenHandle(*v);
+ function = Handle<JSFunction>::cast(obj);
+ }
+
+ void CheckLoopAssignedCount(int expected, const char* var_name) {
+ // TODO(titzer): don't scope analyze every single time.
+ CompilationInfo info(function, main_zone());
+
+ CHECK(Parser::Parse(&info));
+ CHECK(Rewriter::Rewrite(&info));
+ CHECK(Scope::Analyze(&info));
+
+ Scope* scope = info.function()->scope();
+ AstValueFactory* factory = info.ast_value_factory();
+ CHECK_NE(NULL, scope);
+
+ if (result == NULL) {
+ AstLoopAssignmentAnalyzer analyzer(main_zone(), &info);
+ result = analyzer.Analyze();
+ CHECK_NE(NULL, result);
+ }
+
+ const i::AstRawString* name = factory->GetOneByteString(var_name);
+
+ i::Variable* var = scope->Lookup(name);
+ CHECK_NE(NULL, var);
+
+ if (var->location() == Variable::UNALLOCATED) {
+ CHECK_EQ(0, expected);
+ } else {
+ CHECK(var->IsStackAllocated());
+ CHECK_EQ(expected, result->GetAssignmentCountForTesting(scope, var));
+ }
+ }
+};
+}
+
+
+TEST(SimpleLoop1) {
+ TestHelper f("var x = 0; while (x) ;");
+
+ f.CheckLoopAssignedCount(0, "x");
+}
+
+
+TEST(SimpleLoop2) {
+ const char* loops[] = {
+ "while (x) { var x = 0; }", "for(;;) { var x = 0; }",
+ "for(;x;) { var x = 0; }", "for(;x;x) { var x = 0; }",
+ "for(var i = x; x; x) { var x = 0; }", "for(y in 0) { var x = 0; }",
+ "for(y of 0) { var x = 0; }", "for(var x = 0; x; x++) { }",
+ "for(var x = 0; x++;) { }", "var x; for(;x;x++) { }",
+ "var x; do { x = 1; } while (0);", "do { var x = 1; } while (0);"};
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "x");
+ }
+}
+
+
+TEST(ForInOf1) {
+ const char* loops[] = {
+ "for(x in 0) { }", "for(x of 0) { }",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(0, "x");
+ }
+}
+
+
+TEST(Param1) {
+ TestHelper f("while (1) a = 0;");
+
+ f.CheckLoopAssignedCount(1, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param2) {
+ TestHelper f("for (;;) b = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(1, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param2b) {
+ TestHelper f("a; b; c; for (;;) b = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(1, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param3) {
+ TestHelper f("for(x in 0) c = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(1, "c");
+}
+
+
+TEST(Param3b) {
+ TestHelper f("a; b; c; for(x in 0) c = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(1, "c");
+}
+
+
+TEST(NestedLoop1) {
+ TestHelper f("while (x) { while (x) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+}
+
+
+TEST(NestedLoop2) {
+ TestHelper f("while (0) { while (0) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+}
+
+
+TEST(NestedLoop3) {
+ TestHelper f("while (0) { var y = 1; while (0) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+ f.CheckLoopAssignedCount(1, "y");
+}
+
+
+TEST(NestedInc1) {
+ const char* loops[] = {
+ "while (1) a(b++);",
+ "while (1) a(0, b++);",
+ "while (1) a(0, 0, b++);",
+ "while (1) a(b++, 1, 1);",
+ "while (1) a(++b);",
+ "while (1) a + (b++);",
+ "while (1) (b++) + a;",
+ "while (1) a + c(b++);",
+ "while (1) throw b++;",
+ "while (1) switch (b++) {} ;",
+ "while (1) switch (a) {case (b++): 0; } ;",
+ "while (1) switch (a) {case b: b++; } ;",
+ "while (1) a == (b++);",
+ "while (1) a === (b++);",
+ "while (1) +(b++);",
+ "while (1) ~(b++);",
+ "while (1) new a(b++);",
+ "while (1) (b++).f;",
+ "while (1) a[b++];",
+ "while (1) (b++)();",
+ "while (1) [b++];",
+ "while (1) [0,b++];",
+ "while (1) var y = [11,b++,12];",
+ "while (1) var y = {f:11,g:(b++),h:12};",
+ "while (1) try {b++;} finally {};",
+ "while (1) try {} finally {b++};",
+ "while (1) try {b++;} catch (e) {};",
+ "while (1) try {} catch (e) {b++};",
+ "while (1) return b++;",
+ "while (1) (b++) ? b : b;",
+ "while (1) b ? (b++) : b;",
+ "while (1) b ? b : (b++);",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "b");
+ }
+}
+
+
+TEST(NestedAssign1) {
+ const char* loops[] = {
+ "while (1) a(b=1);",
+ "while (1) a(0, b=1);",
+ "while (1) a(0, 0, b=1);",
+ "while (1) a(b=1, 1, 1);",
+ "while (1) a + (b=1);",
+ "while (1) (b=1) + a;",
+ "while (1) a + c(b=1);",
+ "while (1) throw b=1;",
+ "while (1) switch (b=1) {} ;",
+ "while (1) switch (a) {case b=1: 0; } ;",
+ "while (1) switch (a) {case b: b=1; } ;",
+ "while (1) a == (b=1);",
+ "while (1) a === (b=1);",
+ "while (1) +(b=1);",
+ "while (1) ~(b=1);",
+ "while (1) new a(b=1);",
+ "while (1) (b=1).f;",
+ "while (1) a[b=1];",
+ "while (1) (b=1)();",
+ "while (1) [b=1];",
+ "while (1) [0,b=1];",
+ "while (1) var z = [11,b=1,12];",
+ "while (1) var y = {f:11,g:(b=1),h:12};",
+ "while (1) try {b=1;} finally {};",
+ "while (1) try {} finally {b=1};",
+ "while (1) try {b=1;} catch (e) {};",
+ "while (1) try {} catch (e) {b=1};",
+ "while (1) return b=1;",
+ "while (1) (b=1) ? b : b;",
+ "while (1) b ? (b=1) : b;",
+ "while (1) b ? b : (b=1);",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "b");
+ }
+}
+
+
+TEST(NestedLoops3) {
+ TestHelper f("var x, y, z, w; while (x++) while (y++) while (z++) ; w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(2, "y");
+ f.CheckLoopAssignedCount(3, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
+
+
+TEST(NestedLoops3b) {
+ TestHelper f(
+ "var x, y, z, w;"
+ "while (1) { x=1; while (1) { y=1; while (1) z=1; } }"
+ "w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(2, "y");
+ f.CheckLoopAssignedCount(3, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
+
+
+TEST(NestedLoops3c) {
+ TestHelper f(
+ "var x, y, z, w;"
+ "while (1) {"
+ " x++;"
+ " while (1) {"
+ " y++;"
+ " while (1) z++;"
+ " }"
+ " while (1) {"
+ " y++;"
+ " while (1) z++;"
+ " }"
+ "}"
+ "w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(3, "y");
+ f.CheckLoopAssignedCount(5, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
diff --git a/test/cctest/compiler/test-machine-operator-reducer.cc b/test/cctest/compiler/test-machine-operator-reducer.cc
index eca1f3cf..648e1b92 100644
--- a/test/cctest/compiler/test-machine-operator-reducer.cc
+++ b/test/cctest/compiler/test-machine-operator-reducer.cc
@@ -5,9 +5,11 @@
#include "test/cctest/cctest.h"
#include "src/base/utils/random-number-generator.h"
+#include "src/codegen.h"
#include "src/compiler/graph-inl.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator-reducer.h"
+#include "src/compiler/operator-properties.h"
#include "src/compiler/typer.h"
#include "test/cctest/compiler/value-helper.h"
@@ -49,15 +51,18 @@ double ValueOfOperator<double>(const Operator* op) {
class ReducerTester : public HandleAndZoneScope {
public:
- explicit ReducerTester(int num_parameters = 0)
+ explicit ReducerTester(
+ int num_parameters = 0,
+ MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags)
: isolate(main_isolate()),
binop(NULL),
unop(NULL),
+ machine(main_zone(), kMachPtr, flags),
common(main_zone()),
graph(main_zone()),
javascript(main_zone()),
- typer(main_zone()),
- jsgraph(&graph, &common, &javascript, &typer, &machine),
+ typer(&graph, MaybeHandle<Context>()),
+ jsgraph(&graph, &common, &javascript, &machine),
maxuint32(Constant<int32_t>(kMaxUInt32)) {
Node* s = graph.NewNode(common.Start(num_parameters));
graph.SetStart(s);
@@ -96,7 +101,7 @@ class ReducerTester : public HandleAndZoneScope {
template <typename T>
void CheckFoldBinop(volatile T expect, Node* a, Node* b) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, a, b);
+ Node* n = CreateBinopNode(a, b);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(reduction.Changed());
@@ -108,7 +113,7 @@ class ReducerTester : public HandleAndZoneScope {
// the {expect} node.
void CheckBinop(Node* expect, Node* a, Node* b) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, a, b);
+ Node* n = CreateBinopNode(a, b);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(reduction.Changed());
@@ -120,7 +125,7 @@ class ReducerTester : public HandleAndZoneScope {
void CheckFoldBinop(Node* left_expect, Node* right_expect, Node* left,
Node* right) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, left, right);
+ Node* n = CreateBinopNode(left, right);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(reduction.Changed());
@@ -135,7 +140,7 @@ class ReducerTester : public HandleAndZoneScope {
void CheckFoldBinop(volatile T left_expect, const Operator* op_expect,
Node* right_expect, Node* left, Node* right) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, left, right);
+ Node* n = CreateBinopNode(left, right);
MachineOperatorReducer reducer(&jsgraph);
Reduction r = reducer.Reduce(n);
CHECK(r.Changed());
@@ -150,11 +155,13 @@ class ReducerTester : public HandleAndZoneScope {
void CheckFoldBinop(Node* left_expect, const Operator* op_expect,
volatile T right_expect, Node* left, Node* right) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, left, right);
+ Node* n = CreateBinopNode(left, right);
MachineOperatorReducer reducer(&jsgraph);
Reduction r = reducer.Reduce(n);
CHECK(r.Changed());
CHECK_EQ(op_expect->opcode(), r.replacement()->op()->opcode());
+ CHECK_EQ(OperatorProperties::GetTotalInputCount(op_expect),
+ r.replacement()->InputCount());
CHECK_EQ(left_expect, r.replacement()->InputAt(0));
CHECK_EQ(right_expect, ValueOf<T>(r.replacement()->InputAt(1)->op()));
}
@@ -167,7 +174,7 @@ class ReducerTester : public HandleAndZoneScope {
Node* p = Parameter();
Node* k = Constant<T>(constant);
{
- Node* n = graph.NewNode(binop, k, p);
+ Node* n = CreateBinopNode(k, p);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(!reduction.Changed() || reduction.replacement() == n);
@@ -175,7 +182,7 @@ class ReducerTester : public HandleAndZoneScope {
CHECK_EQ(k, n->InputAt(1));
}
{
- Node* n = graph.NewNode(binop, p, k);
+ Node* n = CreateBinopNode(p, k);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(!reduction.Changed());
@@ -191,7 +198,7 @@ class ReducerTester : public HandleAndZoneScope {
CHECK(!binop->HasProperty(Operator::kCommutative));
Node* p = Parameter();
Node* k = Constant<T>(constant);
- Node* n = graph.NewNode(binop, k, p);
+ Node* n = CreateBinopNode(k, p);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(!reduction.Changed());
@@ -202,6 +209,15 @@ class ReducerTester : public HandleAndZoneScope {
Node* Parameter(int32_t index = 0) {
return graph.NewNode(common.Parameter(index), graph.start());
}
+
+ private:
+ Node* CreateBinopNode(Node* left, Node* right) {
+ if (binop->ControlInputCount() > 0) {
+ return graph.NewNode(binop, left, right, graph.start());
+ } else {
+ return graph.NewNode(binop, left, right);
+ }
+ }
};
@@ -343,7 +359,36 @@ TEST(ReduceWord32Sar) {
}
-TEST(ReduceWord32Equal) {
+static void CheckJsShift(ReducerTester* R) {
+ DCHECK(R->machine.Word32ShiftIsSafe());
+
+ Node* x = R->Parameter(0);
+ Node* y = R->Parameter(1);
+ Node* thirty_one = R->Constant<int32_t>(0x1f);
+ Node* y_and_thirty_one =
+ R->graph.NewNode(R->machine.Word32And(), y, thirty_one);
+
+ // If the underlying machine shift instructions 'and' their right operand
+ // with 0x1f then: x << (y & 0x1f) => x << y
+ R->CheckFoldBinop(x, y, x, y_and_thirty_one);
+}
+
+
+TEST(ReduceJsShifts) {
+ ReducerTester R(0, MachineOperatorBuilder::kWord32ShiftIsSafe);
+
+ R.binop = R.machine.Word32Shl();
+ CheckJsShift(&R);
+
+ R.binop = R.machine.Word32Shr();
+ CheckJsShift(&R);
+
+ R.binop = R.machine.Word32Sar();
+ CheckJsShift(&R);
+}
+
+
+TEST(Word32Equal) {
ReducerTester R;
R.binop = R.machine.Word32Equal();
@@ -476,9 +521,9 @@ TEST(ReduceInt32Div) {
}
-TEST(ReduceInt32UDiv) {
+TEST(ReduceUint32Div) {
ReducerTester R;
- R.binop = R.machine.Int32UDiv();
+ R.binop = R.machine.Uint32Div();
FOR_UINT32_INPUTS(pl) {
FOR_UINT32_INPUTS(pr) {
@@ -529,9 +574,9 @@ TEST(ReduceInt32Mod) {
}
-TEST(ReduceInt32UMod) {
+TEST(ReduceUint32Mod) {
ReducerTester R;
- R.binop = R.machine.Int32UMod();
+ R.binop = R.machine.Uint32Mod();
FOR_INT32_INPUTS(pl) {
FOR_INT32_INPUTS(pr) {
@@ -687,9 +732,9 @@ static void CheckNans(ReducerTester* R) {
pr != nans.end(); ++pr) {
Node* nan1 = R->Constant<double>(*pl);
Node* nan2 = R->Constant<double>(*pr);
- R->CheckBinop(nan1, x, nan1); // x % NaN => NaN
- R->CheckBinop(nan1, nan1, x); // NaN % x => NaN
- R->CheckBinop(nan1, nan2, nan1); // NaN % NaN => NaN
+ R->CheckBinop(nan1, x, nan1); // x op NaN => NaN
+ R->CheckBinop(nan1, nan1, x); // NaN op x => NaN
+ R->CheckBinop(nan1, nan2, nan1); // NaN op NaN => NaN
}
}
}
@@ -706,8 +751,15 @@ TEST(ReduceFloat64Add) {
}
}
- FOR_FLOAT64_INPUTS(i) { R.CheckPutConstantOnRight(*i); }
- // TODO(titzer): CheckNans(&R);
+ FOR_FLOAT64_INPUTS(i) {
+ Double tmp(*i);
+ if (!tmp.IsSpecial() || tmp.IsInfinite()) {
+ // Don't check NaNs as they are reduced more.
+ R.CheckPutConstantOnRight(*i);
+ }
+ }
+
+ CheckNans(&R);
}
@@ -721,7 +773,13 @@ TEST(ReduceFloat64Sub) {
R.CheckFoldBinop<double>(x - y, x, y);
}
}
- // TODO(titzer): CheckNans(&R);
+
+ Node* zero = R.Constant<double>(0.0);
+ Node* x = R.Parameter();
+
+ R.CheckBinop(x, x, zero); // x - 0.0 => x
+
+ CheckNans(&R);
}
@@ -783,6 +841,11 @@ TEST(ReduceFloat64Mod) {
}
}
+ Node* x = R.Parameter();
+ Node* zero = R.Constant<double>(0.0);
+
+ R.CheckFoldBinop<double>(v8::base::OS::nan_value(), x, zero);
+
CheckNans(&R);
}
@@ -800,9 +863,9 @@ TEST(ReduceFloat64Mod) {
// TODO(titzer): test MachineOperatorReducer for Int64Mul
// TODO(titzer): test MachineOperatorReducer for Int64UMul
// TODO(titzer): test MachineOperatorReducer for Int64Div
-// TODO(titzer): test MachineOperatorReducer for Int64UDiv
+// TODO(titzer): test MachineOperatorReducer for Uint64Div
// TODO(titzer): test MachineOperatorReducer for Int64Mod
-// TODO(titzer): test MachineOperatorReducer for Int64UMod
+// TODO(titzer): test MachineOperatorReducer for Uint64Mod
// TODO(titzer): test MachineOperatorReducer for Int64Neg
// TODO(titzer): test MachineOperatorReducer for ChangeInt32ToFloat64
// TODO(titzer): test MachineOperatorReducer for ChangeFloat64ToInt32
diff --git a/test/cctest/compiler/test-node-algorithm.cc b/test/cctest/compiler/test-node-algorithm.cc
index 10f98a66..842d1827 100644
--- a/test/cctest/compiler/test-node-algorithm.cc
+++ b/test/cctest/compiler/test-node-algorithm.cc
@@ -8,25 +8,23 @@
#include "graph-tester.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/graph-inl.h"
#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/node.h"
#include "src/compiler/operator.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 1, 0, 0);
class PreNodeVisitor : public NullNodeVisitor {
public:
- GenericGraphVisit::Control Pre(Node* node) {
+ void Pre(Node* node) {
printf("NODE ID: %d\n", node->id());
nodes_.push_back(node);
- return GenericGraphVisit::CONTINUE;
}
std::vector<Node*> nodes_;
};
@@ -34,45 +32,14 @@ class PreNodeVisitor : public NullNodeVisitor {
class PostNodeVisitor : public NullNodeVisitor {
public:
- GenericGraphVisit::Control Post(Node* node) {
+ void Post(Node* node) {
printf("NODE ID: %d\n", node->id());
nodes_.push_back(node);
- return GenericGraphVisit::CONTINUE;
}
std::vector<Node*> nodes_;
};
-TEST(TestUseNodeVisitEmpty) {
- GraphWithStartNodeTester graph;
-
- PreNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(1, static_cast<int>(node_visitor.nodes_.size()));
-}
-
-
-TEST(TestUseNodePreOrderVisitSimple) {
- GraphWithStartNodeTester graph;
- Node* n2 = graph.NewNode(&dummy_operator, graph.start());
- Node* n3 = graph.NewNode(&dummy_operator, n2);
- Node* n4 = graph.NewNode(&dummy_operator, n2, n3);
- Node* n5 = graph.NewNode(&dummy_operator, n4, n2);
- graph.SetEnd(n5);
-
- PreNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(5, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(graph.start()->id() == node_visitor.nodes_[0]->id());
- CHECK(n2->id() == node_visitor.nodes_[1]->id());
- CHECK(n3->id() == node_visitor.nodes_[2]->id());
- CHECK(n4->id() == node_visitor.nodes_[3]->id());
- CHECK(n5->id() == node_visitor.nodes_[4]->id());
-}
-
-
TEST(TestInputNodePreOrderVisitSimple) {
GraphWithStartNodeTester graph;
Node* n2 = graph.NewNode(&dummy_operator, graph.start());
@@ -92,223 +59,6 @@ TEST(TestInputNodePreOrderVisitSimple) {
}
-TEST(TestUseNodePostOrderVisitSimple) {
- GraphWithStartNodeTester graph;
- Node* n2 = graph.NewNode(&dummy_operator, graph.start());
- Node* n3 = graph.NewNode(&dummy_operator, graph.start());
- Node* n4 = graph.NewNode(&dummy_operator, n2);
- Node* n5 = graph.NewNode(&dummy_operator, n2);
- Node* n6 = graph.NewNode(&dummy_operator, n2);
- Node* n7 = graph.NewNode(&dummy_operator, n3);
- Node* end_dependencies[4] = {n4, n5, n6, n7};
- Node* n8 = graph.NewNode(&dummy_operator, 4, end_dependencies);
- graph.SetEnd(n8);
-
- PostNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(8, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(graph.end()->id() == node_visitor.nodes_[0]->id());
- CHECK(n4->id() == node_visitor.nodes_[1]->id());
- CHECK(n5->id() == node_visitor.nodes_[2]->id());
- CHECK(n6->id() == node_visitor.nodes_[3]->id());
- CHECK(n2->id() == node_visitor.nodes_[4]->id());
- CHECK(n7->id() == node_visitor.nodes_[5]->id());
- CHECK(n3->id() == node_visitor.nodes_[6]->id());
- CHECK(graph.start()->id() == node_visitor.nodes_[7]->id());
-}
-
-
-TEST(TestUseNodePostOrderVisitLong) {
- GraphWithStartNodeTester graph;
- Node* n2 = graph.NewNode(&dummy_operator, graph.start());
- Node* n3 = graph.NewNode(&dummy_operator, graph.start());
- Node* n4 = graph.NewNode(&dummy_operator, n2);
- Node* n5 = graph.NewNode(&dummy_operator, n2);
- Node* n6 = graph.NewNode(&dummy_operator, n3);
- Node* n7 = graph.NewNode(&dummy_operator, n3);
- Node* n8 = graph.NewNode(&dummy_operator, n5);
- Node* n9 = graph.NewNode(&dummy_operator, n5);
- Node* n10 = graph.NewNode(&dummy_operator, n9);
- Node* n11 = graph.NewNode(&dummy_operator, n9);
- Node* end_dependencies[6] = {n4, n8, n10, n11, n6, n7};
- Node* n12 = graph.NewNode(&dummy_operator, 6, end_dependencies);
- graph.SetEnd(n12);
-
- PostNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(12, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(graph.end()->id() == node_visitor.nodes_[0]->id());
- CHECK(n4->id() == node_visitor.nodes_[1]->id());
- CHECK(n8->id() == node_visitor.nodes_[2]->id());
- CHECK(n10->id() == node_visitor.nodes_[3]->id());
- CHECK(n11->id() == node_visitor.nodes_[4]->id());
- CHECK(n9->id() == node_visitor.nodes_[5]->id());
- CHECK(n5->id() == node_visitor.nodes_[6]->id());
- CHECK(n2->id() == node_visitor.nodes_[7]->id());
- CHECK(n6->id() == node_visitor.nodes_[8]->id());
- CHECK(n7->id() == node_visitor.nodes_[9]->id());
- CHECK(n3->id() == node_visitor.nodes_[10]->id());
- CHECK(graph.start()->id() == node_visitor.nodes_[11]->id());
-}
-
-
-TEST(TestUseNodePreOrderVisitCycle) {
- GraphWithStartNodeTester graph;
- Node* n0 = graph.start_node();
- Node* n1 = graph.NewNode(&dummy_operator, n0);
- Node* n2 = graph.NewNode(&dummy_operator, n1);
- n0->AppendInput(graph.main_zone(), n2);
- graph.SetStart(n0);
- graph.SetEnd(n2);
-
- PreNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(3, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(n0->id() == node_visitor.nodes_[0]->id());
- CHECK(n1->id() == node_visitor.nodes_[1]->id());
- CHECK(n2->id() == node_visitor.nodes_[2]->id());
-}
-
-
-struct ReenterNodeVisitor : NullNodeVisitor {
- GenericGraphVisit::Control Pre(Node* node) {
- printf("[%d] PRE NODE: %d\n", static_cast<int>(nodes_.size()), node->id());
- nodes_.push_back(node->id());
- int size = static_cast<int>(nodes_.size());
- switch (node->id()) {
- case 0:
- return size < 6 ? GenericGraphVisit::REENTER : GenericGraphVisit::SKIP;
- case 1:
- return size < 4 ? GenericGraphVisit::DEFER
- : GenericGraphVisit::CONTINUE;
- default:
- return GenericGraphVisit::REENTER;
- }
- }
-
- GenericGraphVisit::Control Post(Node* node) {
- printf("[%d] POST NODE: %d\n", static_cast<int>(nodes_.size()), node->id());
- nodes_.push_back(-node->id());
- return node->id() == 4 ? GenericGraphVisit::REENTER
- : GenericGraphVisit::CONTINUE;
- }
-
- void PreEdge(Node* from, int index, Node* to) {
- printf("[%d] PRE EDGE: %d-%d\n", static_cast<int>(edges_.size()),
- from->id(), to->id());
- edges_.push_back(std::make_pair(from->id(), to->id()));
- }
-
- void PostEdge(Node* from, int index, Node* to) {
- printf("[%d] POST EDGE: %d-%d\n", static_cast<int>(edges_.size()),
- from->id(), to->id());
- edges_.push_back(std::make_pair(-from->id(), -to->id()));
- }
-
- std::vector<int> nodes_;
- std::vector<std::pair<int, int> > edges_;
-};
-
-
-TEST(TestUseNodeReenterVisit) {
- GraphWithStartNodeTester graph;
- Node* n0 = graph.start_node();
- Node* n1 = graph.NewNode(&dummy_operator, n0);
- Node* n2 = graph.NewNode(&dummy_operator, n0);
- Node* n3 = graph.NewNode(&dummy_operator, n2);
- Node* n4 = graph.NewNode(&dummy_operator, n0);
- Node* n5 = graph.NewNode(&dummy_operator, n4);
- n0->AppendInput(graph.main_zone(), n3);
- graph.SetStart(n0);
- graph.SetEnd(n5);
-
- ReenterNodeVisitor visitor;
- graph.VisitNodeUsesFromStart(&visitor);
-
- CHECK_EQ(22, static_cast<int>(visitor.nodes_.size()));
- CHECK_EQ(24, static_cast<int>(visitor.edges_.size()));
-
- CHECK(n0->id() == visitor.nodes_[0]);
- CHECK(n0->id() == visitor.edges_[0].first);
- CHECK(n1->id() == visitor.edges_[0].second);
- CHECK(n1->id() == visitor.nodes_[1]);
- // N1 is deferred.
- CHECK(-n1->id() == visitor.edges_[1].second);
- CHECK(-n0->id() == visitor.edges_[1].first);
- CHECK(n0->id() == visitor.edges_[2].first);
- CHECK(n2->id() == visitor.edges_[2].second);
- CHECK(n2->id() == visitor.nodes_[2]);
- CHECK(n2->id() == visitor.edges_[3].first);
- CHECK(n3->id() == visitor.edges_[3].second);
- CHECK(n3->id() == visitor.nodes_[3]);
- // Circle back to N0, which we may reenter for now.
- CHECK(n3->id() == visitor.edges_[4].first);
- CHECK(n0->id() == visitor.edges_[4].second);
- CHECK(n0->id() == visitor.nodes_[4]);
- CHECK(n0->id() == visitor.edges_[5].first);
- CHECK(n1->id() == visitor.edges_[5].second);
- CHECK(n1->id() == visitor.nodes_[5]);
- // This time N1 is no longer deferred.
- CHECK(-n1->id() == visitor.nodes_[6]);
- CHECK(-n1->id() == visitor.edges_[6].second);
- CHECK(-n0->id() == visitor.edges_[6].first);
- CHECK(n0->id() == visitor.edges_[7].first);
- CHECK(n2->id() == visitor.edges_[7].second);
- CHECK(n2->id() == visitor.nodes_[7]);
- CHECK(n2->id() == visitor.edges_[8].first);
- CHECK(n3->id() == visitor.edges_[8].second);
- CHECK(n3->id() == visitor.nodes_[8]);
- CHECK(n3->id() == visitor.edges_[9].first);
- CHECK(n0->id() == visitor.edges_[9].second);
- CHECK(n0->id() == visitor.nodes_[9]);
- // This time we break at N0 and skip it.
- CHECK(-n0->id() == visitor.edges_[10].second);
- CHECK(-n3->id() == visitor.edges_[10].first);
- CHECK(-n3->id() == visitor.nodes_[10]);
- CHECK(-n3->id() == visitor.edges_[11].second);
- CHECK(-n2->id() == visitor.edges_[11].first);
- CHECK(-n2->id() == visitor.nodes_[11]);
- CHECK(-n2->id() == visitor.edges_[12].second);
- CHECK(-n0->id() == visitor.edges_[12].first);
- CHECK(n0->id() == visitor.edges_[13].first);
- CHECK(n4->id() == visitor.edges_[13].second);
- CHECK(n4->id() == visitor.nodes_[12]);
- CHECK(n4->id() == visitor.edges_[14].first);
- CHECK(n5->id() == visitor.edges_[14].second);
- CHECK(n5->id() == visitor.nodes_[13]);
- CHECK(-n5->id() == visitor.nodes_[14]);
- CHECK(-n5->id() == visitor.edges_[15].second);
- CHECK(-n4->id() == visitor.edges_[15].first);
- CHECK(-n4->id() == visitor.nodes_[15]);
- CHECK(-n4->id() == visitor.edges_[16].second);
- CHECK(-n0->id() == visitor.edges_[16].first);
- CHECK(-n0->id() == visitor.nodes_[16]);
- CHECK(-n0->id() == visitor.edges_[17].second);
- CHECK(-n3->id() == visitor.edges_[17].first);
- CHECK(-n3->id() == visitor.nodes_[17]);
- CHECK(-n3->id() == visitor.edges_[18].second);
- CHECK(-n2->id() == visitor.edges_[18].first);
- CHECK(-n2->id() == visitor.nodes_[18]);
- CHECK(-n2->id() == visitor.edges_[19].second);
- CHECK(-n0->id() == visitor.edges_[19].first);
- // N4 may be reentered.
- CHECK(n0->id() == visitor.edges_[20].first);
- CHECK(n4->id() == visitor.edges_[20].second);
- CHECK(n4->id() == visitor.nodes_[19]);
- CHECK(n4->id() == visitor.edges_[21].first);
- CHECK(n5->id() == visitor.edges_[21].second);
- CHECK(-n5->id() == visitor.edges_[22].second);
- CHECK(-n4->id() == visitor.edges_[22].first);
- CHECK(-n4->id() == visitor.nodes_[20]);
- CHECK(-n4->id() == visitor.edges_[23].second);
- CHECK(-n0->id() == visitor.edges_[23].first);
- CHECK(-n0->id() == visitor.nodes_[21]);
-}
-
-
TEST(TestPrintNodeGraphToNodeGraphviz) {
GraphWithStartNodeTester graph;
Node* n2 = graph.NewNode(&dummy_operator, graph.start());
diff --git a/test/cctest/compiler/test-node-cache.cc b/test/cctest/compiler/test-node-cache.cc
index 3569386c..a48adb9a 100644
--- a/test/cctest/compiler/test-node-cache.cc
+++ b/test/cctest/compiler/test-node-cache.cc
@@ -115,46 +115,61 @@ TEST(Int64Constant_hits) {
}
-TEST(PtrConstant_back_to_back) {
+static bool Contains(NodeVector* nodes, Node* n) {
+ for (size_t i = 0; i < nodes->size(); i++) {
+ if (nodes->at(i) == n) return true;
+ }
+ return false;
+}
+
+
+TEST(NodeCache_GetCachedNodes_int32) {
GraphTester graph;
- PtrNodeCache cache;
- int32_t buffer[50];
+ Int32NodeCache cache;
+ CommonOperatorBuilder common(graph.zone());
- for (int32_t* p = buffer;
- (p - buffer) < static_cast<ptrdiff_t>(arraysize(buffer)); p++) {
- Node** pos = cache.Find(graph.zone(), p);
- CHECK_NE(NULL, pos);
- for (int j = 0; j < 3; j++) {
- Node** npos = cache.Find(graph.zone(), p);
- CHECK_EQ(pos, npos);
+ int32_t constants[] = {0, 311, 12, 13, 14, 555, -555, -44, -33, -22, -11,
+ 0, 311, 311, 412, 412, 11, 11, -33, -33, -22, -11};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ Node** pos = cache.Find(graph.zone(), k);
+ if (*pos != NULL) {
+ NodeVector nodes(graph.zone());
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, *pos));
+ } else {
+ NodeVector nodes(graph.zone());
+ Node* n = graph.NewNode(common.Int32Constant(k));
+ *pos = n;
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, n));
}
}
}
-TEST(PtrConstant_hits) {
+TEST(NodeCache_GetCachedNodes_int64) {
GraphTester graph;
- PtrNodeCache cache;
- const int32_t kSize = 50;
- int32_t buffer[kSize];
- Node* nodes[kSize];
+ Int64NodeCache cache;
CommonOperatorBuilder common(graph.zone());
- for (size_t i = 0; i < arraysize(buffer); i++) {
- int k = static_cast<int>(i);
- int32_t* p = &buffer[i];
- nodes[i] = graph.NewNode(common.Int32Constant(k));
- *cache.Find(graph.zone(), p) = nodes[i];
- }
+ int64_t constants[] = {0, 311, 12, 13, 14, 555, -555, -44, -33, -22, -11,
+ 0, 311, 311, 412, 412, 11, 11, -33, -33, -22, -11};
- int hits = 0;
- for (size_t i = 0; i < arraysize(buffer); i++) {
- int32_t* p = &buffer[i];
- Node** pos = cache.Find(graph.zone(), p);
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int64_t k = constants[i];
+ Node** pos = cache.Find(graph.zone(), k);
if (*pos != NULL) {
- CHECK_EQ(nodes[i], *pos);
- hits++;
+ NodeVector nodes(graph.zone());
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, *pos));
+ } else {
+ NodeVector nodes(graph.zone());
+ Node* n = graph.NewNode(common.Int64Constant(k));
+ *pos = n;
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, n));
}
}
- CHECK_LT(4, hits);
}
diff --git a/test/cctest/compiler/test-node.cc b/test/cctest/compiler/test-node.cc
index 28d807e4..eafabd35 100644
--- a/test/cctest/compiler/test-node.cc
+++ b/test/cctest/compiler/test-node.cc
@@ -7,15 +7,14 @@
#include "src/v8.h"
#include "graph-tester.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 1, 0, 0);
TEST(NodeAllocation) {
GraphTester graph;
@@ -93,10 +92,8 @@ TEST(NodeInputIteratorOne) {
TEST(NodeUseIteratorEmpty) {
GraphTester graph;
Node* n1 = graph.NewNode(&dummy_operator);
- Node::Uses::iterator i(n1->uses().begin());
int use_count = 0;
- for (; i != n1->uses().end(); ++i) {
- Node::Edge edge(i.edge());
+ for (Edge const edge : n1->use_edges()) {
USE(edge);
use_count++;
}
@@ -366,31 +363,31 @@ TEST(AppendInputsAndIterator) {
Node* n1 = graph.NewNode(&dummy_operator, n0);
Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
- Node::Inputs inputs(n2->inputs());
- Node::Inputs::iterator current = inputs.begin();
+ Node::InputEdges inputs(n2->input_edges());
+ Node::InputEdges::iterator current = inputs.begin();
CHECK(current != inputs.end());
- CHECK(*current == n0);
+ CHECK((*current).to() == n0);
++current;
CHECK(current != inputs.end());
- CHECK(*current == n1);
+ CHECK((*current).to() == n1);
++current;
CHECK(current == inputs.end());
Node* n3 = graph.NewNode(&dummy_operator);
n2->AppendInput(graph.zone(), n3);
- inputs = n2->inputs();
+ inputs = n2->input_edges();
current = inputs.begin();
CHECK(current != inputs.end());
- CHECK(*current == n0);
- CHECK_EQ(0, current.index());
+ CHECK((*current).to() == n0);
+ CHECK_EQ(0, (*current).index());
++current;
CHECK(current != inputs.end());
- CHECK(*current == n1);
- CHECK_EQ(1, current.index());
+ CHECK((*current).to() == n1);
+ CHECK_EQ(1, (*current).index());
++current;
CHECK(current != inputs.end());
- CHECK(*current == n3);
- CHECK_EQ(2, current.index());
+ CHECK((*current).to() == n3);
+ CHECK_EQ(2, (*current).index());
++current;
CHECK(current == inputs.end());
}
diff --git a/test/cctest/compiler/test-operator.cc b/test/cctest/compiler/test-operator.cc
index af75d676..39f660fe 100644
--- a/test/cctest/compiler/test-operator.cc
+++ b/test/cctest/compiler/test-operator.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <sstream>
+
#include "src/v8.h"
#include "src/compiler/operator.h"
@@ -10,44 +12,45 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-#define NaN (v8::base::OS::nan_value())
-#define Infinity (std::numeric_limits<double>::infinity())
+#define NONE Operator::kNoProperties
+#define FOLD Operator::kFoldable
+
-TEST(TestOperatorMnemonic) {
- SimpleOperator op1(10, Operator::kNoProperties, 0, 0, "ThisOne");
+TEST(TestOperator_Mnemonic) {
+ Operator op1(10, NONE, "ThisOne", 0, 0, 0, 0, 0, 0);
CHECK_EQ(0, strcmp(op1.mnemonic(), "ThisOne"));
- SimpleOperator op2(11, Operator::kNoProperties, 0, 0, "ThatOne");
+ Operator op2(11, NONE, "ThatOne", 0, 0, 0, 0, 0, 0);
CHECK_EQ(0, strcmp(op2.mnemonic(), "ThatOne"));
- Operator1<int> op3(12, Operator::kNoProperties, 0, 1, "Mnemonic1", 12333);
+ Operator1<int> op3(12, NONE, "Mnemonic1", 0, 0, 0, 1, 0, 0, 12333);
CHECK_EQ(0, strcmp(op3.mnemonic(), "Mnemonic1"));
- Operator1<double> op4(13, Operator::kNoProperties, 0, 1, "TheOther", 99.9);
+ Operator1<double> op4(13, NONE, "TheOther", 0, 0, 0, 1, 0, 0, 99.9);
CHECK_EQ(0, strcmp(op4.mnemonic(), "TheOther"));
}
-TEST(TestSimpleOperatorHash) {
- SimpleOperator op1(17, Operator::kNoProperties, 0, 0, "Another");
- CHECK_EQ(17, op1.HashCode());
+TEST(TestOperator_Hash) {
+ Operator op1(17, NONE, "Another", 0, 0, 0, 0, 0, 0);
+ CHECK_EQ(17, static_cast<int>(op1.HashCode()));
- SimpleOperator op2(18, Operator::kNoProperties, 0, 0, "Falsch");
- CHECK_EQ(18, op2.HashCode());
+ Operator op2(18, NONE, "Falsch", 0, 0, 0, 0, 0, 0);
+ CHECK_EQ(18, static_cast<int>(op2.HashCode()));
}
-TEST(TestSimpleOperatorEquals) {
- SimpleOperator op1a(19, Operator::kNoProperties, 0, 0, "Another1");
- SimpleOperator op1b(19, Operator::kFoldable, 2, 2, "Another2");
+TEST(TestOperator_Equals) {
+ Operator op1a(19, NONE, "Another1", 0, 0, 0, 0, 0, 0);
+ Operator op1b(19, FOLD, "Another2", 2, 0, 0, 2, 0, 0);
CHECK(op1a.Equals(&op1a));
CHECK(op1a.Equals(&op1b));
CHECK(op1b.Equals(&op1a));
CHECK(op1b.Equals(&op1b));
- SimpleOperator op2a(20, Operator::kNoProperties, 0, 0, "Falsch1");
- SimpleOperator op2b(20, Operator::kFoldable, 1, 1, "Falsch2");
+ Operator op2a(20, NONE, "Falsch1", 0, 0, 0, 0, 0, 0);
+ Operator op2b(20, FOLD, "Falsch2", 1, 0, 0, 1, 0, 0);
CHECK(op2a.Equals(&op2a));
CHECK(op2a.Equals(&op2b));
@@ -67,52 +70,52 @@ TEST(TestSimpleOperatorEquals) {
static SmartArrayPointer<const char> OperatorToString(Operator* op) {
- OStringStream os;
+ std::ostringstream os;
os << *op;
- return SmartArrayPointer<const char>(StrDup(os.c_str()));
+ return SmartArrayPointer<const char>(StrDup(os.str().c_str()));
}
-TEST(TestSimpleOperatorPrint) {
- SimpleOperator op1a(19, Operator::kNoProperties, 0, 0, "Another1");
- SimpleOperator op1b(19, Operator::kFoldable, 2, 2, "Another2");
+TEST(TestOperator_Print) {
+ Operator op1a(19, NONE, "Another1", 0, 0, 0, 0, 0, 0);
+ Operator op1b(19, FOLD, "Another2", 2, 0, 0, 2, 0, 0);
CHECK_EQ("Another1", OperatorToString(&op1a).get());
CHECK_EQ("Another2", OperatorToString(&op1b).get());
- SimpleOperator op2a(20, Operator::kNoProperties, 0, 0, "Flog1");
- SimpleOperator op2b(20, Operator::kFoldable, 1, 1, "Flog2");
+ Operator op2a(20, NONE, "Flog1", 0, 0, 0, 0, 0, 0);
+ Operator op2b(20, FOLD, "Flog2", 1, 0, 0, 1, 0, 0);
CHECK_EQ("Flog1", OperatorToString(&op2a).get());
CHECK_EQ("Flog2", OperatorToString(&op2b).get());
}
-TEST(TestOperator1intHash) {
- Operator1<int> op1a(23, Operator::kNoProperties, 0, 0, "Wolfie", 11);
- Operator1<int> op1b(23, Operator::kFoldable, 2, 2, "Doggie", 11);
+TEST(TestOperator1int_Hash) {
+ Operator1<int> op1a(23, NONE, "Wolfie", 0, 0, 0, 0, 0, 0, 11);
+ Operator1<int> op1b(23, FOLD, "Doggie", 2, 0, 0, 2, 0, 0, 11);
- CHECK_EQ(op1a.HashCode(), op1b.HashCode());
+ CHECK(op1a.HashCode() == op1b.HashCode());
- Operator1<int> op2a(24, Operator::kNoProperties, 0, 0, "Arfie", 3);
- Operator1<int> op2b(24, Operator::kNoProperties, 0, 0, "Arfie", 4);
+ Operator1<int> op2a(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, 3);
+ Operator1<int> op2b(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, 4);
- CHECK_NE(op1a.HashCode(), op2a.HashCode());
- CHECK_NE(op2a.HashCode(), op2b.HashCode());
+ CHECK(op1a.HashCode() != op2a.HashCode());
+ CHECK(op2a.HashCode() != op2b.HashCode());
}
-TEST(TestOperator1intEquals) {
- Operator1<int> op1a(23, Operator::kNoProperties, 0, 0, "Scratchy", 11);
- Operator1<int> op1b(23, Operator::kFoldable, 2, 2, "Scratchy", 11);
+TEST(TestOperator1int_Equals) {
+ Operator1<int> op1a(23, NONE, "Scratchy", 0, 0, 0, 0, 0, 0, 11);
+ Operator1<int> op1b(23, FOLD, "Scratchy", 2, 0, 0, 2, 0, 0, 11);
CHECK(op1a.Equals(&op1a));
CHECK(op1a.Equals(&op1b));
CHECK(op1b.Equals(&op1a));
CHECK(op1b.Equals(&op1b));
- Operator1<int> op2a(24, Operator::kNoProperties, 0, 0, "Im", 3);
- Operator1<int> op2b(24, Operator::kNoProperties, 0, 0, "Im", 4);
+ Operator1<int> op2a(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 3);
+ Operator1<int> op2b(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 4);
CHECK(op2a.Equals(&op2a));
CHECK(!op2a.Equals(&op2b));
@@ -129,7 +132,7 @@ TEST(TestOperator1intEquals) {
CHECK(!op2b.Equals(&op1a));
CHECK(!op2b.Equals(&op1b));
- SimpleOperator op3(25, Operator::kNoProperties, 0, 0, "Weepy");
+ Operator op3(25, NONE, "Weepy", 0, 0, 0, 0, 0, 0);
CHECK(!op1a.Equals(&op3));
CHECK(!op1b.Equals(&op3));
@@ -143,46 +146,55 @@ TEST(TestOperator1intEquals) {
}
-TEST(TestOperator1intPrint) {
- Operator1<int> op1(12, Operator::kNoProperties, 0, 1, "Op1Test", 0);
+TEST(TestOperator1int_Print) {
+ Operator1<int> op1(12, NONE, "Op1Test", 0, 0, 0, 1, 0, 0, 0);
CHECK_EQ("Op1Test[0]", OperatorToString(&op1).get());
- Operator1<int> op2(12, Operator::kNoProperties, 0, 1, "Op1Test", 66666666);
+ Operator1<int> op2(12, NONE, "Op1Test", 0, 0, 0, 1, 0, 0, 66666666);
CHECK_EQ("Op1Test[66666666]", OperatorToString(&op2).get());
- Operator1<int> op3(12, Operator::kNoProperties, 0, 1, "FooBar", 2347);
+ Operator1<int> op3(12, NONE, "FooBar", 0, 0, 0, 1, 0, 0, 2347);
CHECK_EQ("FooBar[2347]", OperatorToString(&op3).get());
- Operator1<int> op4(12, Operator::kNoProperties, 0, 1, "BarFoo", -879);
+ Operator1<int> op4(12, NONE, "BarFoo", 0, 0, 0, 1, 0, 0, -879);
CHECK_EQ("BarFoo[-879]", OperatorToString(&op4).get());
}
-TEST(TestOperator1doubleHash) {
- Operator1<double> op1a(23, Operator::kNoProperties, 0, 0, "Wolfie", 11.77);
- Operator1<double> op1b(23, Operator::kFoldable, 2, 2, "Doggie", 11.77);
+TEST(TestOperator1double_Hash) {
+ Operator1<double> op1a(23, NONE, "Wolfie", 0, 0, 0, 0, 0, 0, 11.77);
+ Operator1<double> op1b(23, FOLD, "Doggie", 2, 0, 0, 2, 0, 0, 11.77);
- CHECK_EQ(op1a.HashCode(), op1b.HashCode());
+ CHECK(op1a.HashCode() == op1b.HashCode());
- Operator1<double> op2a(24, Operator::kNoProperties, 0, 0, "Arfie", -6.7);
- Operator1<double> op2b(24, Operator::kNoProperties, 0, 0, "Arfie", -6.8);
+ Operator1<double> op2a(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, -6.7);
+ Operator1<double> op2b(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, -6.8);
- CHECK_NE(op1a.HashCode(), op2a.HashCode());
- CHECK_NE(op2a.HashCode(), op2b.HashCode());
+ CHECK(op1a.HashCode() != op2a.HashCode());
+ CHECK(op2a.HashCode() != op2b.HashCode());
}
-TEST(TestOperator1doubleEquals) {
- Operator1<double> op1a(23, Operator::kNoProperties, 0, 0, "Scratchy", 11.77);
- Operator1<double> op1b(23, Operator::kFoldable, 2, 2, "Scratchy", 11.77);
+TEST(TestOperator1doublePrint) {
+ Operator1<double> op1a(23, NONE, "Canary", 0, 0, 0, 0, 0, 0, 0.5);
+ Operator1<double> op1b(23, FOLD, "Finch", 2, 0, 0, 2, 0, 0, -1.5);
+
+ CHECK_EQ("Canary[0.5]", OperatorToString(&op1a).get());
+ CHECK_EQ("Finch[-1.5]", OperatorToString(&op1b).get());
+}
+
+
+TEST(TestOperator1double_Equals) {
+ Operator1<double> op1a(23, NONE, "Scratchy", 0, 0, 0, 0, 0, 0, 11.77);
+ Operator1<double> op1b(23, FOLD, "Scratchy", 2, 0, 0, 2, 0, 0, 11.77);
CHECK(op1a.Equals(&op1a));
CHECK(op1a.Equals(&op1b));
CHECK(op1b.Equals(&op1a));
CHECK(op1b.Equals(&op1b));
- Operator1<double> op2a(24, Operator::kNoProperties, 0, 0, "Im", 3.1);
- Operator1<double> op2b(24, Operator::kNoProperties, 0, 0, "Im", 3.2);
+ Operator1<double> op2a(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 3.1);
+ Operator1<double> op2b(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 3.2);
CHECK(op2a.Equals(&op2a));
CHECK(!op2a.Equals(&op2b));
@@ -199,7 +211,7 @@ TEST(TestOperator1doubleEquals) {
CHECK(!op2b.Equals(&op1a));
CHECK(!op2b.Equals(&op1b));
- SimpleOperator op3(25, Operator::kNoProperties, 0, 0, "Weepy");
+ Operator op3(25, NONE, "Weepy", 0, 0, 0, 0, 0, 0);
CHECK(!op1a.Equals(&op3));
CHECK(!op1b.Equals(&op3));
@@ -211,8 +223,8 @@ TEST(TestOperator1doubleEquals) {
CHECK(!op3.Equals(&op2a));
CHECK(!op3.Equals(&op2b));
- Operator1<double> op4a(24, Operator::kNoProperties, 0, 0, "Bashful", NaN);
- Operator1<double> op4b(24, Operator::kNoProperties, 0, 0, "Bashful", NaN);
+ Operator1<double> op4a(24, NONE, "Bashful", 0, 0, 0, 0, 0, 0, 1.0);
+ Operator1<double> op4b(24, NONE, "Bashful", 0, 0, 0, 0, 0, 0, 1.0);
CHECK(op4a.Equals(&op4a));
CHECK(op4a.Equals(&op4b));
@@ -226,19 +238,46 @@ TEST(TestOperator1doubleEquals) {
}
-TEST(TestOperator1doublePrint) {
- Operator1<double> op1(12, Operator::kNoProperties, 0, 1, "Op1Test", 0);
- CHECK_EQ("Op1Test[0]", OperatorToString(&op1).get());
+TEST(TestOpParameter_Operator1double) {
+ double values[] = {7777.5, -66, 0, 11, 0.1};
- Operator1<double> op2(12, Operator::kNoProperties, 0, 1, "Op1Test", 7.3);
- CHECK_EQ("Op1Test[7.3]", OperatorToString(&op2).get());
+ for (size_t i = 0; i < arraysize(values); i++) {
+ Operator1<double> op(33, NONE, "Scurvy", 0, 0, 0, 0, 0, 0, values[i]);
+ CHECK_EQ(values[i], OpParameter<double>(&op));
+ }
+}
+
+
+TEST(TestOpParameter_Operator1float) {
+ float values[] = {// thanks C++.
+ static_cast<float>(7777.5), static_cast<float>(-66),
+ static_cast<float>(0), static_cast<float>(11),
+ static_cast<float>(0.1)};
+
+ for (size_t i = 0; i < arraysize(values); i++) {
+ Operator1<float> op(33, NONE, "Scurvy", 0, 0, 0, 0, 0, 0, values[i]);
+ CHECK_EQ(values[i], OpParameter<float>(&op));
+ }
+}
+
+
+TEST(TestOpParameter_Operator1int) {
+ int values[] = {7777, -66, 0, 11, 1, 0x666aff};
+
+ for (size_t i = 0; i < arraysize(values); i++) {
+ Operator1<int> op(33, NONE, "Scurvy", 0, 0, 0, 0, 0, 0, values[i]);
+ CHECK_EQ(values[i], OpParameter<int>(&op));
+ }
+}
- Operator1<double> op3(12, Operator::kNoProperties, 0, 1, "FooBar", 2e+123);
- CHECK_EQ("FooBar[2e+123]", OperatorToString(&op3).get());
- Operator1<double> op4(12, Operator::kNoProperties, 0, 1, "BarFoo", Infinity);
- CHECK_EQ("BarFoo[inf]", OperatorToString(&op4).get());
+TEST(Operator_CountsOrder) {
+ Operator op(29, NONE, "Flashy", 11, 22, 33, 44, 55, 66);
+ CHECK_EQ(11, op.ValueInputCount());
+ CHECK_EQ(22, op.EffectInputCount());
+ CHECK_EQ(33, op.ControlInputCount());
- Operator1<double> op5(12, Operator::kNoProperties, 0, 1, "BarFoo", NaN);
- CHECK_EQ("BarFoo[nan]", OperatorToString(&op5).get());
+ CHECK_EQ(44, op.ValueOutputCount());
+ CHECK_EQ(55, op.EffectOutputCount());
+ CHECK_EQ(66, op.ControlOutputCount());
}
diff --git a/test/cctest/compiler/test-phi-reducer.cc b/test/cctest/compiler/test-phi-reducer.cc
deleted file mode 100644
index 7d2fab67..00000000
--- a/test/cctest/compiler/test-phi-reducer.cc
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "src/v8.h"
-#include "test/cctest/cctest.h"
-
-#include "src/compiler/common-operator.h"
-#include "src/compiler/graph-inl.h"
-#include "src/compiler/phi-reducer.h"
-
-using namespace v8::internal;
-using namespace v8::internal::compiler;
-
-class PhiReducerTester : HandleAndZoneScope {
- public:
- explicit PhiReducerTester(int num_parameters = 0)
- : isolate(main_isolate()),
- common(main_zone()),
- graph(main_zone()),
- self(graph.NewNode(common.Start(num_parameters))),
- dead(graph.NewNode(common.Dead())) {
- graph.SetStart(self);
- }
-
- Isolate* isolate;
- CommonOperatorBuilder common;
- Graph graph;
- Node* self;
- Node* dead;
-
- void CheckReduce(Node* expect, Node* phi) {
- PhiReducer reducer;
- Reduction reduction = reducer.Reduce(phi);
- if (expect == phi) {
- CHECK(!reduction.Changed());
- } else {
- CHECK(reduction.Changed());
- CHECK_EQ(expect, reduction.replacement());
- }
- }
-
- Node* Int32Constant(int32_t val) {
- return graph.NewNode(common.Int32Constant(val));
- }
-
- Node* Float64Constant(double val) {
- return graph.NewNode(common.Float64Constant(val));
- }
-
- Node* Parameter(int32_t index = 0) {
- return graph.NewNode(common.Parameter(index), graph.start());
- }
-
- Node* Phi(Node* a) {
- return SetSelfReferences(graph.NewNode(common.Phi(kMachAnyTagged, 1), a));
- }
-
- Node* Phi(Node* a, Node* b) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 2), a, b));
- }
-
- Node* Phi(Node* a, Node* b, Node* c) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 3), a, b, c));
- }
-
- Node* Phi(Node* a, Node* b, Node* c, Node* d) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 4), a, b, c, d));
- }
-
- Node* PhiWithControl(Node* a, Node* control) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 1), a, control));
- }
-
- Node* PhiWithControl(Node* a, Node* b, Node* control) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 2), a, b, control));
- }
-
- Node* SetSelfReferences(Node* node) {
- Node::Inputs inputs = node->inputs();
- for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end();
- ++iter) {
- Node* input = *iter;
- if (input == self) node->ReplaceInput(iter.index(), node);
- }
- return node;
- }
-};
-
-
-TEST(PhiReduce1) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- R.CheckReduce(singles[i], R.Phi(singles[i]));
- }
-}
-
-
-TEST(PhiReduce2) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(a, a));
- }
-
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(R.self, a));
- R.CheckReduce(a, R.Phi(a, R.self));
- }
-
- for (size_t i = 1; i < arraysize(singles); i++) {
- Node* a = singles[i], *b = singles[0];
- Node* phi1 = R.Phi(b, a);
- R.CheckReduce(phi1, phi1);
-
- Node* phi2 = R.Phi(a, b);
- R.CheckReduce(phi2, phi2);
- }
-}
-
-
-TEST(PhiReduce3) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(a, a, a));
- }
-
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(R.self, a, a));
- R.CheckReduce(a, R.Phi(a, R.self, a));
- R.CheckReduce(a, R.Phi(a, a, R.self));
- }
-
- for (size_t i = 1; i < arraysize(singles); i++) {
- Node* a = singles[i], *b = singles[0];
- Node* phi1 = R.Phi(b, a, a);
- R.CheckReduce(phi1, phi1);
-
- Node* phi2 = R.Phi(a, b, a);
- R.CheckReduce(phi2, phi2);
-
- Node* phi3 = R.Phi(a, a, b);
- R.CheckReduce(phi3, phi3);
- }
-}
-
-
-TEST(PhiReduce4) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(a, a, a, a));
- }
-
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(R.self, a, a, a));
- R.CheckReduce(a, R.Phi(a, R.self, a, a));
- R.CheckReduce(a, R.Phi(a, a, R.self, a));
- R.CheckReduce(a, R.Phi(a, a, a, R.self));
-
- R.CheckReduce(a, R.Phi(R.self, R.self, a, a));
- R.CheckReduce(a, R.Phi(a, R.self, R.self, a));
- R.CheckReduce(a, R.Phi(a, a, R.self, R.self));
- R.CheckReduce(a, R.Phi(R.self, a, a, R.self));
- }
-
- for (size_t i = 1; i < arraysize(singles); i++) {
- Node* a = singles[i], *b = singles[0];
- Node* phi1 = R.Phi(b, a, a, a);
- R.CheckReduce(phi1, phi1);
-
- Node* phi2 = R.Phi(a, b, a, a);
- R.CheckReduce(phi2, phi2);
-
- Node* phi3 = R.Phi(a, a, b, a);
- R.CheckReduce(phi3, phi3);
-
- Node* phi4 = R.Phi(a, a, a, b);
- R.CheckReduce(phi4, phi4);
- }
-}
-
-
-TEST(PhiReduceShouldIgnoreControlNodes) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); ++i) {
- R.CheckReduce(singles[i], R.PhiWithControl(singles[i], R.dead));
- R.CheckReduce(singles[i], R.PhiWithControl(R.self, singles[i], R.dead));
- R.CheckReduce(singles[i], R.PhiWithControl(singles[i], R.self, R.dead));
- }
-}
diff --git a/test/cctest/compiler/test-pipeline.cc b/test/cctest/compiler/test-pipeline.cc
index f0b750a0..98b0baee 100644
--- a/test/cctest/compiler/test-pipeline.cc
+++ b/test/cctest/compiler/test-pipeline.cc
@@ -5,6 +5,7 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
+#include "src/ast-numbering.h"
#include "src/compiler.h"
#include "src/compiler/pipeline.h"
#include "src/handles.h"
@@ -22,10 +23,7 @@ TEST(PipelineAdd) {
*v8::Handle<v8::Function>::Cast(CompileRun(source)));
CompilationInfoWithZone info(function);
- CHECK(Parser::Parse(&info));
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
- CHECK_NE(NULL, info.scope());
+ CHECK(Compiler::ParseAndAnalyze(&info));
Pipeline pipeline(&info);
#if V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-representation-change.cc b/test/cctest/compiler/test-representation-change.cc
index 6c9026b2..2dc30294 100644
--- a/test/cctest/compiler/test-representation-change.cc
+++ b/test/cctest/compiler/test-representation-change.cc
@@ -7,10 +7,10 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/graph-builder-tester.h"
+#include "test/cctest/compiler/value-helper.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/representation-change.h"
-#include "src/compiler/typer.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
@@ -24,16 +24,13 @@ class RepresentationChangerTester : public HandleAndZoneScope,
public:
explicit RepresentationChangerTester(int num_parameters = 0)
: GraphAndBuilders(main_zone()),
- typer_(main_zone()),
javascript_(main_zone()),
- jsgraph_(main_graph_, &main_common_, &javascript_, &typer_,
- &main_machine_),
+ jsgraph_(main_graph_, &main_common_, &javascript_, &main_machine_),
changer_(&jsgraph_, &main_simplified_, main_isolate()) {
Node* s = graph()->NewNode(common()->Start(num_parameters));
graph()->SetStart(s);
}
- Typer typer_;
JSOperatorBuilder javascript_;
JSGraph jsgraph_;
RepresentationChanger changer_;
@@ -51,6 +48,24 @@ class RepresentationChangerTester : public HandleAndZoneScope,
CHECK_EQ(expected, m.Value());
}
+ void CheckUint32Constant(Node* n, uint32_t expected) {
+ Uint32Matcher m(n);
+ CHECK(m.HasValue());
+ CHECK_EQ(static_cast<int>(expected), static_cast<int>(m.Value()));
+ }
+
+ void CheckFloat64Constant(Node* n, double expected) {
+ Float64Matcher m(n);
+ CHECK(m.HasValue());
+ CHECK_EQ(expected, m.Value());
+ }
+
+ void CheckFloat32Constant(Node* n, float expected) {
+ CHECK_EQ(IrOpcode::kFloat32Constant, n->opcode());
+ float fval = OpParameter<float>(n->op());
+ CHECK_EQ(expected, fval);
+ }
+
void CheckHeapConstant(Node* n, HeapObject* expected) {
HeapObjectMatcher<HeapObject> m(n);
CHECK(m.HasValue());
@@ -88,28 +103,8 @@ class RepresentationChangerTester : public HandleAndZoneScope,
} // namespace v8::internal::compiler
-// TODO(titzer): add kRepFloat32 when fully supported.
-static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
- kRepFloat64, kRepTagged};
-
-
-// TODO(titzer): lift this to ValueHelper
-static const double double_inputs[] = {
- 0.0, -0.0, 1.0, -1.0, 0.1, 1.4, -1.7,
- 2, 5, 6, 982983, 888, -999.8, 3.1e7,
- -2e66, 2.3e124, -12e73, V8_INFINITY, -V8_INFINITY};
-
-
-static const int32_t int32_inputs[] = {
- 0, 1, -1,
- 2, 5, 6,
- 982983, 888, -999,
- 65535, static_cast<int32_t>(0xFFFFFFFF), static_cast<int32_t>(0x80000000)};
-
-
-static const uint32_t uint32_inputs[] = {
- 0, 1, static_cast<uint32_t>(-1), 2, 5, 6,
- 982983, 888, static_cast<uint32_t>(-999), 65535, 0xFFFFFFFF, 0x80000000};
+static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
+ kRepFloat32, kRepFloat64, kRepTagged};
TEST(BoolToBit_constant) {
@@ -142,24 +137,234 @@ TEST(BitToBool_constant) {
TEST(ToTagged_constant) {
RepresentationChangerTester r;
- for (size_t i = 0; i < arraysize(double_inputs); i++) {
- Node* n = r.jsgraph()->Float64Constant(double_inputs[i]);
- Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
- r.CheckNumberConstant(c, double_inputs[i]);
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+}
+
+
+TEST(ToFloat64_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepFloat64);
+ CHECK_EQ(n, c);
+ }
+ }
+
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged, kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+}
+
+
+static bool IsFloat32Int32(int32_t val) {
+ return val >= -(1 << 23) && val <= (1 << 23);
+}
+
+
+static bool IsFloat32Uint32(uint32_t val) { return val <= (1 << 23); }
+
+
+TEST(ToFloat32_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepFloat32);
+ CHECK_EQ(n, c);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged, kRepFloat32);
+ r.CheckFloat32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepFloat32);
+ r.CheckFloat32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ if (!IsFloat32Int32(*i)) continue;
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepFloat32);
+ r.CheckFloat32Constant(c, static_cast<float>(*i));
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ if (!IsFloat32Uint32(*i)) continue;
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepFloat32);
+ r.CheckFloat32Constant(c, static_cast<float>(*i));
+ }
+ }
+}
+
+
+TEST(ToInt32_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
}
- for (size_t i = 0; i < arraysize(int32_inputs); i++) {
- Node* n = r.jsgraph()->Int32Constant(int32_inputs[i]);
- Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
- kRepTagged);
- r.CheckNumberConstant(c, static_cast<double>(int32_inputs[i]));
+ {
+ FOR_INT32_INPUTS(i) {
+ if (!IsFloat32Int32(*i)) continue;
+ Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32 | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
}
- for (size_t i = 0; i < arraysize(uint32_inputs); i++) {
- Node* n = r.jsgraph()->Int32Constant(uint32_inputs[i]);
- Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
- kRepTagged);
- r.CheckNumberConstant(c, static_cast<double>(uint32_inputs[i]));
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64 | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
+ }
+}
+
+
+TEST(ToUint32_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ if (!IsFloat32Uint32(*i)) continue;
+ Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32 | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64 | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(static_cast<double>(*i));
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
}
}
@@ -177,6 +382,23 @@ static void CheckChange(IrOpcode::Value expected, MachineTypeUnion from,
}
+static void CheckTwoChanges(IrOpcode::Value expected2,
+ IrOpcode::Value expected1, MachineTypeUnion from,
+ MachineTypeUnion to) {
+ RepresentationChangerTester r;
+
+ Node* n = r.Parameter();
+ Node* c1 = r.changer()->GetRepresentationFor(n, from, to);
+
+ CHECK_NE(c1, n);
+ CHECK_EQ(expected1, c1->opcode());
+ Node* c2 = c1->InputAt(0);
+ CHECK_NE(c2, n);
+ CHECK_EQ(expected2, c2->opcode());
+ CHECK_EQ(n, c2->InputAt(0));
+}
+
+
TEST(SingleChanges) {
CheckChange(IrOpcode::kChangeBoolToBit, kRepTagged, kRepBit);
CheckChange(IrOpcode::kChangeBitToBool, kRepBit, kRepTagged);
@@ -202,6 +424,28 @@ TEST(SingleChanges) {
kRepWord32);
CheckChange(IrOpcode::kChangeFloat64ToUint32, kRepFloat64 | kTypeUint32,
kRepWord32);
+
+ CheckChange(IrOpcode::kTruncateFloat64ToFloat32, kRepFloat64, kRepFloat32);
+
+ // Int32,Uint32 <-> Float32 require two changes.
+ CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeInt32,
+ kRepFloat32);
+ CheckTwoChanges(IrOpcode::kChangeUint32ToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeUint32,
+ kRepFloat32);
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToInt32, kRepFloat32 | kTypeInt32,
+ kRepWord32);
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToUint32, kRepFloat32 | kTypeUint32,
+ kRepWord32);
+
+ // Float32 <-> Tagged require two changes.
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToTagged, kRepFloat32, kRepTagged);
+ CheckTwoChanges(IrOpcode::kChangeTaggedToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepTagged, kRepFloat32);
}
@@ -215,6 +459,11 @@ TEST(SignednessInWord32) {
kRepWord32 | kTypeUint32);
CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32, kRepFloat64);
CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64, kRepWord32);
+
+ CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepWord32, kRepFloat32);
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToInt32, kRepFloat32, kRepWord32);
}
@@ -295,11 +544,4 @@ TEST(TypeErrors) {
r.CheckTypeError(all_reps[i] | all_reps[j], kRepTagged);
}
}
-
- // TODO(titzer): Float32 representation changes trigger type errors now.
- // Enforce current behavior to test all paths through representation changer.
- for (size_t i = 0; i < arraysize(all_reps); i++) {
- r.CheckTypeError(all_reps[i], kRepFloat32);
- r.CheckTypeError(kRepFloat32, all_reps[i]);
- }
}
diff --git a/test/cctest/compiler/test-run-inlining.cc b/test/cctest/compiler/test-run-inlining.cc
index ad82feca..19b96bad 100644
--- a/test/cctest/compiler/test-run-inlining.cc
+++ b/test/cctest/compiler/test-run-inlining.cc
@@ -25,7 +25,8 @@ static void AssertInlineCount(const v8::FunctionCallbackInfo<v8::Value>& args) {
frames_seen++;
it.Advance();
}
- CHECK_EQ(args[0]->ToInt32()->Value(), topmost->GetInlineCount());
+ CHECK_EQ(args[0]->ToInt32(args.GetIsolate())->Value(),
+ topmost->GetInlineCount());
}
@@ -37,16 +38,20 @@ static void InstallAssertInlineCountHelper(v8::Isolate* isolate) {
}
+static uint32_t kInlineFlags = CompilationInfo::kInliningEnabled |
+ CompilationInfo::kContextSpecializing |
+ CompilationInfo::kTypingEnabled;
+
+
TEST(SimpleInlining) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function(){"
- "function foo(s) { AssertInlineCount(2); return s; };"
- "function bar(s, t) { return foo(s); };"
- "return bar;})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ " function foo(s) { AssertInlineCount(2); return s; };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
@@ -57,13 +62,11 @@ TEST(SimpleInliningDeopt) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function(){"
- "function foo(s) { %DeoptimizeFunction(bar); return "
- "s; };"
- "function bar(s, t) { return foo(s); };"
- "return bar;})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ " function foo(s) { %DeoptimizeFunction(bar); return s; };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
@@ -74,13 +77,11 @@ TEST(SimpleInliningContext) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };"
- "function bar(s, t) { return foo(s); };"
- "return bar;"
+ " function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
@@ -91,16 +92,14 @@ TEST(SimpleInliningContextDeopt) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { "
- " AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;"
- " return s + x;"
- "};"
- "function bar(s, t) { return foo(s); };"
- "return bar;"
+ " function foo(s) {"
+ " AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;"
+ " return s + x;"
+ " };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
@@ -111,14 +110,12 @@ TEST(CaptureContext) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"var f = (function () {"
- "var x = 42;"
- "function bar(s) { return x + s; };"
- "return (function (s) { return bar(s); });"
+ " var x = 42;"
+ " function bar(s) { return x + s; };"
+ " return (function (s) { return bar(s); });"
"})();"
- "(function (s) { return f(s)})",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ "(function (s) { return f(s) })",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
@@ -132,12 +129,10 @@ TEST(DontInlineEval) {
FunctionTester T(
"var x = 42;"
"(function () {"
- "function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };"
- "return bar;"
+ " function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.Val("x"), T.undefined());
@@ -148,13 +143,11 @@ TEST(InlineOmitArguments) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
- "return (function (s,t) { return bar(s); });"
+ " var x = 42;"
+ " function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
+ " return (function (s,t) { return bar(s); });"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
@@ -165,16 +158,14 @@ TEST(InlineOmitArgumentsDeopt) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s,t,u,v) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
- "return baz(); };"
- "function bar() { return foo(11); };"
- "function baz() { return foo.arguments.length == 1 && "
- " foo.arguments[0] == 11 ; }"
- "return bar;"
+ " function foo(s,t,u,v) { AssertInlineCount(2);"
+ " %DeoptimizeFunction(bar); return baz(); };"
+ " function bar() { return foo(11); };"
+ " function baz() { return foo.arguments.length == 1 &&"
+ " foo.arguments[0] == 11; }"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
@@ -185,14 +176,12 @@ TEST(InlineSurplusArguments) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function foo(s) { AssertInlineCount(2); return x + s; };"
- "function bar(s,t) { return foo(s,t,13); };"
- "return bar;"
+ " var x = 42;"
+ " function foo(s) { AssertInlineCount(2); return x + s; };"
+ " function bar(s,t) { return foo(s,t,13); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
@@ -203,18 +192,16 @@ TEST(InlineSurplusArgumentsDeopt) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
- "return baz(); };"
- "function bar() { return foo(13, 14, 15); };"
- "function baz() { return foo.arguments.length == 3 && "
- " foo.arguments[0] == 13 && "
- " foo.arguments[1] == 14 && "
- " foo.arguments[2] == 15; }"
- "return bar;"
+ " function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar);"
+ " return baz(); };"
+ " function bar() { return foo(13, 14, 15); };"
+ " function baz() { return foo.arguments.length == 3 &&"
+ " foo.arguments[0] == 13 &&"
+ " foo.arguments[1] == 14 &&"
+ " foo.arguments[2] == 15; }"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
@@ -225,13 +212,11 @@ TEST(InlineTwice) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function bar(s) { AssertInlineCount(2); return x + s; };"
- "return (function (s,t) { return bar(s) + bar(t); });"
+ " var x = 42;"
+ " function bar(s) { AssertInlineCount(2); return x + s; };"
+ " return (function (s,t) { return bar(s) + bar(t); });"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(2 * 42 + 12 + 4), T.Val(12), T.Val(4));
@@ -242,14 +227,12 @@ TEST(InlineTwiceDependent) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function foo(s) { AssertInlineCount(2); return x + s; };"
- "function bar(s,t) { return foo(foo(s)); };"
- "return bar;"
+ " var x = 42;"
+ " function foo(s) { AssertInlineCount(2); return x + s; };"
+ " function bar(s,t) { return foo(foo(s)); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 42 + 12), T.Val(12), T.Val(4));
@@ -260,15 +243,13 @@ TEST(InlineTwiceDependentDiamond) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 41;"
- "function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {"
- " return x - s } else { return x + s; } };"
- "function bar(s,t) { return foo(foo(s)); };"
- "return bar;"
+ " var x = 41;"
+ " function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {"
+ " return x - s } else { return x + s; } };"
+ " function bar(s,t) { return foo(foo(s)); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(-11), T.Val(11), T.Val(4));
@@ -279,34 +260,60 @@ TEST(InlineTwiceDependentDiamondDifferent) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 41;"
- "function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {"
- " return x - s * t } else { return x + s * t; } };"
- "function bar(s,t) { return foo(foo(s, 3), 5); };"
- "return bar;"
+ " var x = 41;"
+ " function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {"
+ " return x - s * t } else { return x + s * t; } };"
+ " function bar(s,t) { return foo(foo(s, 3), 5); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(-329), T.Val(11), T.Val(4));
}
-TEST(InlineLoop) {
+TEST(InlineLoopGuardedEmpty) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " function foo(s) { AssertInlineCount(2); if (s) while (s); return s; };"
+ " function bar(s,t) { return foo(s); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(4));
+}
+
+
+TEST(InlineLoopGuardedOnce) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 41;"
- "function foo(s) { AssertInlineCount(2); while (s > 0) {"
- " s = s - 1; }; return s; };"
- "function bar(s,t) { return foo(foo(s)); };"
- "return bar;"
+ " function foo(s,t) { AssertInlineCount(2); if (t > 0) while (s > 0) {"
+ " s = s - 1; }; return s; };"
+ " function bar(s,t) { return foo(s,t); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4));
+}
+
+
+TEST(InlineLoopGuardedTwice) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " function foo(s,t) { AssertInlineCount(2); if (t > 0) while (s > 0) {"
+ " s = s - 1; }; return s; };"
+ " function bar(s,t) { return foo(foo(s,t),t); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4));
@@ -317,15 +324,13 @@ TEST(InlineStrictIntoNonStrict) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = Object.create({}, { y: { value:42, writable:false } });"
- "function foo(s) { 'use strict';"
- " x.y = 9; };"
- "function bar(s,t) { return foo(s); };"
- "return bar;"
+ " var x = Object.create({}, { y: { value:42, writable:false } });"
+ " function foo(s) { 'use strict';"
+ " x.y = 9; };"
+ " function bar(s,t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckThrows(T.undefined(), T.undefined());
@@ -336,18 +341,100 @@ TEST(InlineNonStrictIntoStrict) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = Object.create({}, { y: { value:42, writable:false } });"
- "function foo(s) { x.y = 9; return x.y; };"
- "function bar(s,t) { \'use strict\'; return foo(s); };"
- "return bar;"
+ " var x = Object.create({}, { y: { value:42, writable:false } });"
+ " function foo(s) { x.y = 9; return x.y; };"
+ " function bar(s,t) { \'use strict\'; return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.undefined(), T.undefined());
}
+TEST(InlineIntrinsicIsSmi) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " var x = 42;"
+ " function bar(s,t) { return %_IsSmi(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineIntrinsicIsNonNegativeSmi) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " var x = 42;"
+ " function bar(s,t) { return %_IsNonNegativeSmi(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineIntrinsicIsArray) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " var x = [1,2,3];"
+ " function bar(s,t) { return %_IsArray(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(4));
+
+ FunctionTester T2(
+ "(function () {"
+ " var x = 32;"
+ " function bar(s,t) { return %_IsArray(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ T2.CheckCall(T.false_value(), T.Val(12), T.Val(4));
+
+ FunctionTester T3(
+ "(function () {"
+ " var x = bar;"
+ " function bar(s,t) { return %_IsArray(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ T3.CheckCall(T.false_value(), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineWithArguments) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " function foo(s,t,u) { AssertInlineCount(2);"
+ " return foo.arguments.length == 3 &&"
+ " foo.arguments[0] == 13 &&"
+ " foo.arguments[1] == 14 &&"
+ " foo.arguments[2] == 15;"
+ " }"
+ " function bar() { return foo(13, 14, 15); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
+}
+
#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-run-intrinsics.cc b/test/cctest/compiler/test-run-intrinsics.cc
index a1b56761..76cbb8fc 100644
--- a/test/cctest/compiler/test-run-intrinsics.cc
+++ b/test/cctest/compiler/test-run-intrinsics.cc
@@ -8,10 +8,12 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-
+uint32_t flags = CompilationInfo::kInliningEnabled;
TEST(IsSmi) {
- FunctionTester T("(function(a) { return %_IsSmi(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsSmi(a); })", flags);
T.CheckTrue(T.Val(1));
T.CheckFalse(T.Val(1.1));
@@ -23,7 +25,9 @@ TEST(IsSmi) {
TEST(IsNonNegativeSmi) {
- FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })", flags);
T.CheckTrue(T.Val(1));
T.CheckFalse(T.Val(1.1));
@@ -35,7 +39,9 @@ TEST(IsNonNegativeSmi) {
TEST(IsMinusZero) {
- FunctionTester T("(function(a) { return %_IsMinusZero(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsMinusZero(a); })", flags);
T.CheckFalse(T.Val(1));
T.CheckFalse(T.Val(1.1));
@@ -47,7 +53,9 @@ TEST(IsMinusZero) {
TEST(IsArray) {
- FunctionTester T("(function(a) { return %_IsArray(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsArray(a); })", flags);
T.CheckFalse(T.NewObject("(function() {})"));
T.CheckTrue(T.NewObject("([1])"));
@@ -61,7 +69,9 @@ TEST(IsArray) {
TEST(IsObject) {
- FunctionTester T("(function(a) { return %_IsObject(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsObject(a); })", flags);
T.CheckFalse(T.NewObject("(function() {})"));
T.CheckTrue(T.NewObject("([1])"));
@@ -75,7 +85,9 @@ TEST(IsObject) {
TEST(IsFunction) {
- FunctionTester T("(function(a) { return %_IsFunction(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsFunction(a); })", flags);
T.CheckTrue(T.NewObject("(function() {})"));
T.CheckFalse(T.NewObject("([1])"));
@@ -89,7 +101,9 @@ TEST(IsFunction) {
TEST(IsRegExp) {
- FunctionTester T("(function(a) { return %_IsRegExp(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsRegExp(a); })", flags);
T.CheckFalse(T.NewObject("(function() {})"));
T.CheckFalse(T.NewObject("([1])"));
@@ -103,7 +117,9 @@ TEST(IsRegExp) {
TEST(ClassOf) {
- FunctionTester T("(function(a) { return %_ClassOf(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_ClassOf(a); })", flags);
T.CheckCall(T.Val("Function"), T.NewObject("(function() {})"));
T.CheckCall(T.Val("Array"), T.NewObject("([1])"));
@@ -117,7 +133,9 @@ TEST(ClassOf) {
TEST(ObjectEquals) {
- FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })", flags);
CompileRun("var o = {}");
T.CheckTrue(T.NewObject("(o)"), T.NewObject("(o)"));
@@ -130,7 +148,9 @@ TEST(ObjectEquals) {
TEST(ValueOf) {
- FunctionTester T("(function(a) { return %_ValueOf(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_ValueOf(a); })", flags);
T.CheckCall(T.Val("a"), T.Val("a"));
T.CheckCall(T.Val("b"), T.NewObject("(new String('b'))"));
@@ -140,7 +160,9 @@ TEST(ValueOf) {
TEST(SetValueOf) {
- FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })", flags);
T.CheckCall(T.Val("a"), T.NewObject("(new String)"), T.Val("a"));
T.CheckCall(T.Val(123), T.NewObject("(new Number)"), T.Val(123));
@@ -149,7 +171,9 @@ TEST(SetValueOf) {
TEST(StringCharFromCode) {
- FunctionTester T("(function(a) { return %_StringCharFromCode(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_StringCharFromCode(a); })", flags);
T.CheckCall(T.Val("a"), T.Val(97));
T.CheckCall(T.Val("\xE2\x9D\x8A"), T.Val(0x274A));
@@ -158,7 +182,9 @@ TEST(StringCharFromCode) {
TEST(StringCharAt) {
- FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })", flags);
T.CheckCall(T.Val("e"), T.Val("huge fan!"), T.Val(3));
T.CheckCall(T.Val("f"), T.Val("\xE2\x9D\x8A fan!"), T.Val(2));
@@ -167,7 +193,10 @@ TEST(StringCharAt) {
TEST(StringCharCodeAt) {
- FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })",
+ flags);
T.CheckCall(T.Val('e'), T.Val("huge fan!"), T.Val(3));
T.CheckCall(T.Val('f'), T.Val("\xE2\x9D\x8A fan!"), T.Val(2));
@@ -176,7 +205,9 @@ TEST(StringCharCodeAt) {
TEST(StringAdd) {
- FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })", flags);
T.CheckCall(T.Val("aaabbb"), T.Val("aaa"), T.Val("bbb"));
T.CheckCall(T.Val("aaa"), T.Val("aaa"), T.Val(""));
@@ -185,7 +216,9 @@ TEST(StringAdd) {
TEST(StringSubString) {
- FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })", flags);
T.CheckCall(T.Val("aaa"), T.Val("aaabbb"), T.Val(0.0));
T.CheckCall(T.Val("abb"), T.Val("aaabbb"), T.Val(2));
@@ -194,7 +227,9 @@ TEST(StringSubString) {
TEST(StringCompare) {
- FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })", flags);
T.CheckCall(T.Val(-1), T.Val("aaa"), T.Val("bbb"));
T.CheckCall(T.Val(0.0), T.Val("bbb"), T.Val("bbb"));
@@ -203,7 +238,10 @@ TEST(StringCompare) {
TEST(CallFunction) {
- FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })",
+ flags);
CompileRun("function f(a,b,c) { return a + b + c + this.d; }");
T.CheckCall(T.Val(129), T.NewObject("({d:123})"), T.NewObject("f"));
diff --git a/test/cctest/compiler/test-run-jsbranches.cc b/test/cctest/compiler/test-run-jsbranches.cc
index df2fcdcb..7a4a0b33 100644
--- a/test/cctest/compiler/test-run-jsbranches.cc
+++ b/test/cctest/compiler/test-run-jsbranches.cc
@@ -280,3 +280,78 @@ TEST(NestedForConditional) {
T.CheckCall(T.Val(2), T.Val(2), T.false_value());
T.CheckCall(T.undefined(), T.Val(1), T.null());
}
+
+
+TEST(IfTrue) {
+ FunctionTester T("(function(a,b) { if (true) return a; return b; })");
+
+ T.CheckCall(T.Val(55), T.Val(55), T.Val(11));
+ T.CheckCall(T.Val(666), T.Val(666), T.Val(-444));
+}
+
+
+TEST(TernaryTrue) {
+ FunctionTester T("(function(a,b) { return true ? a : b; })");
+
+ T.CheckCall(T.Val(77), T.Val(77), T.Val(11));
+ T.CheckCall(T.Val(111), T.Val(111), T.Val(-444));
+}
+
+
+TEST(IfFalse) {
+ FunctionTester T("(function(a,b) { if (false) return a; return b; })");
+
+ T.CheckCall(T.Val(11), T.Val(22), T.Val(11));
+ T.CheckCall(T.Val(-555), T.Val(333), T.Val(-555));
+}
+
+
+TEST(TernaryFalse) {
+ FunctionTester T("(function(a,b) { return false ? a : b; })");
+
+ T.CheckCall(T.Val(99), T.Val(33), T.Val(99));
+ T.CheckCall(T.Val(-99), T.Val(-33), T.Val(-99));
+}
+
+
+TEST(WhileTrue) {
+ FunctionTester T("(function(a,b) { while (true) return a; return b; })");
+
+ T.CheckCall(T.Val(551), T.Val(551), T.Val(111));
+ T.CheckCall(T.Val(661), T.Val(661), T.Val(-444));
+}
+
+
+TEST(WhileFalse) {
+ FunctionTester T("(function(a,b) { while (false) return a; return b; })");
+
+ T.CheckCall(T.Val(115), T.Val(551), T.Val(115));
+ T.CheckCall(T.Val(-445), T.Val(661), T.Val(-445));
+}
+
+
+TEST(DoWhileTrue) {
+ FunctionTester T(
+ "(function(a,b) { do { return a; } while (true); return b; })");
+
+ T.CheckCall(T.Val(7551), T.Val(7551), T.Val(7111));
+ T.CheckCall(T.Val(7661), T.Val(7661), T.Val(-7444));
+}
+
+
+TEST(DoWhileFalse) {
+ FunctionTester T(
+ "(function(a,b) { do { "
+ "; } while (false); return b; })");
+
+ T.CheckCall(T.Val(8115), T.Val(8551), T.Val(8115));
+ T.CheckCall(T.Val(-8445), T.Val(8661), T.Val(-8445));
+}
+
+
+TEST(EmptyFor) {
+ FunctionTester T("(function(a,b) { if (a) for(;;) ; return b; })");
+
+ T.CheckCall(T.Val(8126.1), T.Val(0.0), T.Val(8126.1));
+ T.CheckCall(T.Val(1123.1), T.Val(0.0), T.Val(1123.1));
+}
diff --git a/test/cctest/compiler/test-run-machops.cc b/test/cctest/compiler/test-run-machops.cc
index 985e0f8f..974d4cef 100644
--- a/test/cctest/compiler/test-run-machops.cc
+++ b/test/cctest/compiler/test-run-machops.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <cmath>
#include <functional>
#include <limits>
#include "src/base/bits.h"
-#include "src/compiler/generic-node-inl.h"
+#include "src/codegen.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#include "test/cctest/compiler/value-helper.h"
@@ -58,25 +59,25 @@ static Node* Int32Input(RawMachineAssemblerTester<int32_t>* m, int index) {
TEST(CodeGenInt32Binop) {
RawMachineAssemblerTester<void> m;
- const Operator* ops[] = {
+ const Operator* kOps[] = {
m.machine()->Word32And(), m.machine()->Word32Or(),
m.machine()->Word32Xor(), m.machine()->Word32Shl(),
m.machine()->Word32Shr(), m.machine()->Word32Sar(),
m.machine()->Word32Equal(), m.machine()->Int32Add(),
m.machine()->Int32Sub(), m.machine()->Int32Mul(),
- m.machine()->Int32Div(), m.machine()->Int32UDiv(),
- m.machine()->Int32Mod(), m.machine()->Int32UMod(),
+ m.machine()->Int32MulHigh(), m.machine()->Int32Div(),
+ m.machine()->Uint32Div(), m.machine()->Int32Mod(),
+ m.machine()->Uint32Mod(), m.machine()->Uint32MulHigh(),
m.machine()->Int32LessThan(), m.machine()->Int32LessThanOrEqual(),
- m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual(),
- NULL};
+ m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual()};
- for (int i = 0; ops[i] != NULL; i++) {
+ for (size_t i = 0; i < arraysize(kOps); ++i) {
for (int j = 0; j < 8; j++) {
for (int k = 0; k < 8; k++) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
Node* a = Int32Input(&m, j);
Node* b = Int32Input(&m, k);
- m.Return(m.NewNode(ops[i], a, b));
+ m.Return(m.NewNode(kOps[i], a, b));
m.GenerateCode();
}
}
@@ -127,51 +128,6 @@ TEST(RunBranch) {
}
-TEST(RunRedundantBranch1) {
- RawMachineAssemblerTester<int32_t> m;
- int constant = 944777;
-
- MLabel blocka;
- m.Branch(m.Int32Constant(0), &blocka, &blocka);
- m.Bind(&blocka);
- m.Return(m.Int32Constant(constant));
-
- CHECK_EQ(constant, m.Call());
-}
-
-
-TEST(RunRedundantBranch2) {
- RawMachineAssemblerTester<int32_t> m;
- int constant = 955777;
-
- MLabel blocka, blockb;
- m.Branch(m.Int32Constant(0), &blocka, &blocka);
- m.Bind(&blockb);
- m.Goto(&blocka);
- m.Bind(&blocka);
- m.Return(m.Int32Constant(constant));
-
- CHECK_EQ(constant, m.Call());
-}
-
-
-TEST(RunRedundantBranch3) {
- RawMachineAssemblerTester<int32_t> m;
- int constant = 966777;
-
- MLabel blocka, blockb, blockc;
- m.Branch(m.Int32Constant(0), &blocka, &blockc);
- m.Bind(&blocka);
- m.Branch(m.Int32Constant(0), &blockb, &blockb);
- m.Bind(&blockc);
- m.Goto(&blockb);
- m.Bind(&blockb);
- m.Return(m.Int32Constant(constant));
-
- CHECK_EQ(constant, m.Call());
-}
-
-
TEST(RunDiamond2) {
RawMachineAssemblerTester<int32_t> m;
@@ -571,6 +527,142 @@ TEST(RunInt32AddP) {
}
+TEST(RunInt32AddAndWord32EqualP) {
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Parameter(0),
+ m.Word32Equal(m.Parameter(1), m.Parameter(2))));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32Equal(m.Parameter(0), m.Parameter(1)),
+ m.Parameter(2)));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(RunInt32AddAndWord32EqualImm) {
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Int32Constant(*i),
+ m.Word32Equal(m.Parameter(0), m.Parameter(1))));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32Equal(m.Int32Constant(*i), m.Parameter(0)),
+ m.Parameter(1)));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(RunInt32AddAndWord32NotEqualP) {
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Parameter(0),
+ m.Word32NotEqual(m.Parameter(1), m.Parameter(2))));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32NotEqual(m.Parameter(0), m.Parameter(1)),
+ m.Parameter(2)));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(RunInt32AddAndWord32NotEqualImm) {
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Int32Constant(*i),
+ m.Word32NotEqual(m.Parameter(0), m.Parameter(1))));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32NotEqual(m.Int32Constant(*i), m.Parameter(0)),
+ m.Parameter(1)));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+}
+
+
TEST(RunInt32AddAndWord32SarP) {
{
RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
@@ -1233,6 +1325,20 @@ TEST(RunInt32MulP) {
}
+TEST(RunInt32MulHighP) {
+ RawMachineAssemblerTester<int32_t> m;
+ Int32BinopTester bt(&m);
+ bt.AddReturn(m.Int32MulHigh(bt.param0, bt.param1));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ int32_t expected = static_cast<int32_t>(
+ (static_cast<int64_t>(*i) * static_cast<int64_t>(*j)) >> 32);
+ CHECK_EQ(expected, bt.call(*i, *j));
+ }
+ }
+}
+
+
TEST(RunInt32MulImm) {
{
FOR_UINT32_INPUTS(i) {
@@ -1259,6 +1365,22 @@ TEST(RunInt32MulImm) {
TEST(RunInt32MulAndInt32AddP) {
{
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32);
+ int32_t p0 = *i;
+ int32_t p1 = *j;
+ m.Return(m.Int32Add(m.Int32Constant(p0),
+ m.Int32Mul(m.Parameter(0), m.Int32Constant(p1))));
+ FOR_INT32_INPUTS(k) {
+ int32_t p2 = *k;
+ int expected = p0 + static_cast<int32_t>(p1 * p2);
+ CHECK_EQ(expected, m.Call(p2));
+ }
+ }
+ }
+ }
+ {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
m.Return(
m.Int32Add(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
@@ -1347,6 +1469,20 @@ TEST(RunInt32MulAndInt32SubP) {
}
+TEST(RunUint32MulHighP) {
+ RawMachineAssemblerTester<int32_t> m;
+ Int32BinopTester bt(&m);
+ bt.AddReturn(m.Uint32MulHigh(bt.param0, bt.param1));
+ FOR_UINT32_INPUTS(i) {
+ FOR_UINT32_INPUTS(j) {
+ int32_t expected = bit_cast<int32_t>(static_cast<uint32_t>(
+ (static_cast<uint64_t>(*i) * static_cast<uint64_t>(*j)) >> 32));
+ CHECK_EQ(expected, bt.call(bit_cast<int32_t>(*i), bit_cast<int32_t>(*j)));
+ }
+ }
+}
+
+
TEST(RunInt32DivP) {
{
RawMachineAssemblerTester<int32_t> m;
@@ -1381,11 +1517,11 @@ TEST(RunInt32DivP) {
}
-TEST(RunInt32UDivP) {
+TEST(RunUint32DivP) {
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32UDiv(bt.param0, bt.param1));
+ bt.AddReturn(m.Uint32Div(bt.param0, bt.param1));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -1400,7 +1536,7 @@ TEST(RunInt32UDivP) {
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32Add(bt.param0, m.Int32UDiv(bt.param0, bt.param1)));
+ bt.AddReturn(m.Int32Add(bt.param0, m.Uint32Div(bt.param0, bt.param1)));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -1449,11 +1585,11 @@ TEST(RunInt32ModP) {
}
-TEST(RunInt32UModP) {
+TEST(RunUint32ModP) {
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32UMod(bt.param0, bt.param1));
+ bt.AddReturn(m.Uint32Mod(bt.param0, bt.param1));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -1468,7 +1604,7 @@ TEST(RunInt32UModP) {
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32Add(bt.param0, m.Int32UMod(bt.param0, bt.param1)));
+ bt.AddReturn(m.Int32Add(bt.param0, m.Uint32Mod(bt.param0, bt.param1)));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -2658,22 +2794,23 @@ TEST(RunDeadNodes) {
TEST(RunDeadInt32Binops) {
RawMachineAssemblerTester<int32_t> m;
- const Operator* ops[] = {
- m.machine()->Word32And(), m.machine()->Word32Or(),
- m.machine()->Word32Xor(), m.machine()->Word32Shl(),
- m.machine()->Word32Shr(), m.machine()->Word32Sar(),
- m.machine()->Word32Ror(), m.machine()->Word32Equal(),
- m.machine()->Int32Add(), m.machine()->Int32Sub(),
- m.machine()->Int32Mul(), m.machine()->Int32Div(),
- m.machine()->Int32UDiv(), m.machine()->Int32Mod(),
- m.machine()->Int32UMod(), m.machine()->Int32LessThan(),
- m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(),
- m.machine()->Uint32LessThanOrEqual(), NULL};
-
- for (int i = 0; ops[i] != NULL; i++) {
+ const Operator* kOps[] = {
+ m.machine()->Word32And(), m.machine()->Word32Or(),
+ m.machine()->Word32Xor(), m.machine()->Word32Shl(),
+ m.machine()->Word32Shr(), m.machine()->Word32Sar(),
+ m.machine()->Word32Ror(), m.machine()->Word32Equal(),
+ m.machine()->Int32Add(), m.machine()->Int32Sub(),
+ m.machine()->Int32Mul(), m.machine()->Int32MulHigh(),
+ m.machine()->Int32Div(), m.machine()->Uint32Div(),
+ m.machine()->Int32Mod(), m.machine()->Uint32Mod(),
+ m.machine()->Uint32MulHigh(), m.machine()->Int32LessThan(),
+ m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(),
+ m.machine()->Uint32LessThanOrEqual()};
+
+ for (size_t i = 0; i < arraysize(kOps); ++i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
- int constant = 0x55555 + i;
- m.NewNode(ops[i], m.Parameter(0), m.Parameter(1));
+ int32_t constant = static_cast<int32_t>(0x55555 + i);
+ m.NewNode(kOps[i], m.Parameter(0), m.Parameter(1));
m.Return(m.Int32Constant(constant));
CHECK_EQ(constant, m.Call(1, 1));
@@ -3102,6 +3239,38 @@ TEST(RunChangeUint32ToFloat64_B) {
}
+TEST(RunChangeUint32ToFloat64_spilled) {
+ RawMachineAssemblerTester<int32_t> m;
+ const int kNumInputs = 32;
+ int32_t magic = 0x786234;
+ uint32_t input[kNumInputs];
+ double result[kNumInputs];
+ Node* input_node[kNumInputs];
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input_node[i] =
+ m.Load(kMachUint32, m.PointerConstant(&input), m.Int32Constant(i * 4));
+ }
+
+ for (int i = 0; i < kNumInputs; i++) {
+ m.Store(kMachFloat64, m.PointerConstant(&result), m.Int32Constant(i * 8),
+ m.ChangeUint32ToFloat64(input_node[i]));
+ }
+
+ m.Return(m.Int32Constant(magic));
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input[i] = 100 + i;
+ }
+
+ CHECK_EQ(magic, m.Call());
+
+ for (int i = 0; i < kNumInputs; i++) {
+ CHECK_EQ(result[i], static_cast<double>(100 + i));
+ }
+}
+
+
TEST(RunChangeFloat64ToInt32_A) {
RawMachineAssemblerTester<int32_t> m;
int32_t magic = 0x786234;
@@ -3272,6 +3441,38 @@ TEST(RunChangeFloat64ToUint32_spilled) {
}
+TEST(RunTruncateFloat64ToFloat32_spilled) {
+ RawMachineAssemblerTester<uint32_t> m;
+ const int kNumInputs = 32;
+ int32_t magic = 0x786234;
+ double input[kNumInputs];
+ float result[kNumInputs];
+ Node* input_node[kNumInputs];
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input_node[i] =
+ m.Load(kMachFloat64, m.PointerConstant(&input), m.Int32Constant(i * 8));
+ }
+
+ for (int i = 0; i < kNumInputs; i++) {
+ m.Store(kMachFloat32, m.PointerConstant(&result), m.Int32Constant(i * 4),
+ m.TruncateFloat64ToFloat32(input_node[i]));
+ }
+
+ m.Return(m.Int32Constant(magic));
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input[i] = 0.1 + i;
+ }
+
+ CHECK_EQ(magic, m.Call());
+
+ for (int i = 0; i < kNumInputs; i++) {
+ CHECK_EQ(result[i], DoubleToFloat32(input[i]));
+ }
+}
+
+
TEST(RunDeadChangeFloat64ToInt32) {
RawMachineAssemblerTester<int32_t> m;
const int magic = 0x88abcda4;
@@ -4242,4 +4443,249 @@ TEST(RunTruncateFloat64ToInt32P) {
}
}
+
+TEST(RunChangeFloat32ToFloat64) {
+ double actual = 0.0f;
+ float expected = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ m.StoreToPointer(
+ &actual, kMachFloat64,
+ m.ChangeFloat32ToFloat64(m.LoadFromPointer(&expected, kMachFloat32)));
+ m.Return(m.Int32Constant(0));
+ FOR_FLOAT32_INPUTS(i) {
+ expected = *i;
+ CHECK_EQ(0, m.Call());
+ CHECK_EQ(expected, actual);
+ }
+}
+
+
+TEST(RunChangeFloat32ToFloat64_spilled) {
+ RawMachineAssemblerTester<int32_t> m;
+ const int kNumInputs = 32;
+ int32_t magic = 0x786234;
+ float input[kNumInputs];
+ double result[kNumInputs];
+ Node* input_node[kNumInputs];
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input_node[i] =
+ m.Load(kMachFloat32, m.PointerConstant(&input), m.Int32Constant(i * 4));
+ }
+
+ for (int i = 0; i < kNumInputs; i++) {
+ m.Store(kMachFloat64, m.PointerConstant(&result), m.Int32Constant(i * 8),
+ m.ChangeFloat32ToFloat64(input_node[i]));
+ }
+
+ m.Return(m.Int32Constant(magic));
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input[i] = 100.9f + i;
+ }
+
+ CHECK_EQ(magic, m.Call());
+
+ for (int i = 0; i < kNumInputs; i++) {
+ CHECK_EQ(result[i], static_cast<double>(input[i]));
+ }
+}
+
+
+TEST(RunTruncateFloat64ToFloat32) {
+ float actual = 0.0f;
+ double input = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ m.StoreToPointer(
+ &actual, kMachFloat32,
+ m.TruncateFloat64ToFloat32(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ FOR_FLOAT64_INPUTS(i) {
+ input = *i;
+ volatile double expected = DoubleToFloat32(input);
+ CHECK_EQ(0, m.Call());
+ CHECK_EQ(expected, actual);
+ }
+}
+
+
+TEST(RunFloat32Constant) {
+ FOR_FLOAT32_INPUTS(i) {
+ float expected = *i;
+ float actual = *i;
+ RawMachineAssemblerTester<int32_t> m;
+ m.StoreToPointer(&actual, kMachFloat32, m.Float32Constant(expected));
+ m.Return(m.Int32Constant(0));
+ CHECK_EQ(0, m.Call());
+ CHECK_EQ(expected, actual);
+ }
+}
+
+
+static double two_30 = 1 << 30; // 2^30 is a smi boundary.
+static double two_52 = two_30 * (1 << 22); // 2^52 is a precision boundary.
+static double kValues[] = {0.1,
+ 0.2,
+ 0.49999999999999994,
+ 0.5,
+ 0.7,
+ 1.0 - std::numeric_limits<double>::epsilon(),
+ -0.1,
+ -0.49999999999999994,
+ -0.5,
+ -0.7,
+ 1.1,
+ 1.0 + std::numeric_limits<double>::epsilon(),
+ 1.5,
+ 1.7,
+ -1,
+ -1 + std::numeric_limits<double>::epsilon(),
+ -1 - std::numeric_limits<double>::epsilon(),
+ -1.1,
+ -1.5,
+ -1.7,
+ std::numeric_limits<double>::min(),
+ -std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ -std::numeric_limits<double>::max(),
+ std::numeric_limits<double>::infinity(),
+ -std::numeric_limits<double>::infinity(),
+ two_30,
+ two_30 + 0.1,
+ two_30 + 0.5,
+ two_30 + 0.7,
+ two_30 - 1,
+ two_30 - 1 + 0.1,
+ two_30 - 1 + 0.5,
+ two_30 - 1 + 0.7,
+ -two_30,
+ -two_30 + 0.1,
+ -two_30 + 0.5,
+ -two_30 + 0.7,
+ -two_30 + 1,
+ -two_30 + 1 + 0.1,
+ -two_30 + 1 + 0.5,
+ -two_30 + 1 + 0.7,
+ two_52,
+ two_52 + 0.1,
+ two_52 + 0.5,
+ two_52 + 0.5,
+ two_52 + 0.7,
+ two_52 + 0.7,
+ two_52 - 1,
+ two_52 - 1 + 0.1,
+ two_52 - 1 + 0.5,
+ two_52 - 1 + 0.7,
+ -two_52,
+ -two_52 + 0.1,
+ -two_52 + 0.5,
+ -two_52 + 0.7,
+ -two_52 + 1,
+ -two_52 + 1 + 0.1,
+ -two_52 + 1 + 0.5,
+ -two_52 + 1 + 0.7,
+ two_30,
+ two_30 - 0.1,
+ two_30 - 0.5,
+ two_30 - 0.7,
+ two_30 - 1,
+ two_30 - 1 - 0.1,
+ two_30 - 1 - 0.5,
+ two_30 - 1 - 0.7,
+ -two_30,
+ -two_30 - 0.1,
+ -two_30 - 0.5,
+ -two_30 - 0.7,
+ -two_30 + 1,
+ -two_30 + 1 - 0.1,
+ -two_30 + 1 - 0.5,
+ -two_30 + 1 - 0.7,
+ two_52,
+ two_52 - 0.1,
+ two_52 - 0.5,
+ two_52 - 0.5,
+ two_52 - 0.7,
+ two_52 - 0.7,
+ two_52 - 1,
+ two_52 - 1 - 0.1,
+ two_52 - 1 - 0.5,
+ two_52 - 1 - 0.7,
+ -two_52,
+ -two_52 - 0.1,
+ -two_52 - 0.5,
+ -two_52 - 0.7,
+ -two_52 + 1,
+ -two_52 + 1 - 0.1,
+ -two_52 + 1 - 0.5,
+ -two_52 + 1 - 0.7};
+
+
+TEST(RunFloat64Floor) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64Floor()) return;
+ m.StoreToPointer(&result, kMachFloat64,
+ m.Float64Floor(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = std::floor(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
+
+
+TEST(RunFloat64Ceil) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64Ceil()) return;
+ m.StoreToPointer(&result, kMachFloat64,
+ m.Float64Ceil(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = std::ceil(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
+
+
+TEST(RunFloat64RoundTruncate) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64Ceil()) return;
+ m.StoreToPointer(
+ &result, kMachFloat64,
+ m.Float64RoundTruncate(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = trunc(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
+
+
+TEST(RunFloat64RoundTiesAway) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64RoundTiesAway()) return;
+ m.StoreToPointer(
+ &result, kMachFloat64,
+ m.Float64RoundTiesAway(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = round(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-run-stackcheck.cc b/test/cctest/compiler/test-run-stackcheck.cc
new file mode 100644
index 00000000..8c1664bc
--- /dev/null
+++ b/test/cctest/compiler/test-run-stackcheck.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(TerminateAtMethodEntry) {
+ FunctionTester T("(function(a,b) { return 23; })");
+
+ T.CheckCall(T.Val(23));
+ T.isolate->stack_guard()->RequestTerminateExecution();
+ T.CheckThrows(T.undefined(), T.undefined());
+}
diff --git a/test/cctest/compiler/test-schedule.cc b/test/cctest/compiler/test-schedule.cc
index 6c05c059..1eb35471 100644
--- a/test/cctest/compiler/test-schedule.cc
+++ b/test/cctest/compiler/test-schedule.cc
@@ -5,7 +5,6 @@
#include "src/v8.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
@@ -16,26 +15,25 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 0, 0, 0);
TEST(TestScheduleAllocation) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
CHECK_NE(NULL, schedule.start());
- CHECK_EQ(schedule.start(), *(schedule.all_blocks().begin()));
+ CHECK_EQ(schedule.start(), schedule.GetBlockById(BasicBlock::Id::FromInt(0)));
}
TEST(TestScheduleAddNode) {
HandleAndZoneScope scope;
+ Schedule schedule(scope.main_zone());
Graph graph(scope.main_zone());
Node* n0 = graph.NewNode(&dummy_operator);
Node* n1 = graph.NewNode(&dummy_operator);
- Schedule schedule(scope.main_zone());
-
BasicBlock* entry = schedule.start();
schedule.AddNode(entry, n0);
schedule.AddNode(entry, n1);
@@ -51,50 +49,49 @@ TEST(TestScheduleAddNode) {
TEST(TestScheduleAddGoto) {
HandleAndZoneScope scope;
-
Schedule schedule(scope.main_zone());
+
BasicBlock* entry = schedule.start();
BasicBlock* next = schedule.NewBasicBlock();
schedule.AddGoto(entry, next);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(1, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(next, entry->SuccessorAt(0));
- CHECK_EQ(1, next->PredecessorCount());
+ CHECK_EQ(1, static_cast<int>(next->PredecessorCount()));
CHECK_EQ(entry, next->PredecessorAt(0));
- CHECK_EQ(0, next->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(next->SuccessorCount()));
}
TEST(TestScheduleAddBranch) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
-
- BasicBlock* entry = schedule.start();
- BasicBlock* tblock = schedule.NewBasicBlock();
- BasicBlock* fblock = schedule.NewBasicBlock();
-
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
Node* n0 = graph.NewNode(&dummy_operator);
Node* b = graph.NewNode(common.Branch(), n0);
+ BasicBlock* entry = schedule.start();
+ BasicBlock* tblock = schedule.NewBasicBlock();
+ BasicBlock* fblock = schedule.NewBasicBlock();
+
schedule.AddBranch(entry, b, tblock, fblock);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(2, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(2, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(tblock, entry->SuccessorAt(0));
CHECK_EQ(fblock, entry->SuccessorAt(1));
- CHECK_EQ(1, tblock->PredecessorCount());
+ CHECK_EQ(1, static_cast<int>(tblock->PredecessorCount()));
CHECK_EQ(entry, tblock->PredecessorAt(0));
- CHECK_EQ(0, tblock->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(tblock->SuccessorCount()));
- CHECK_EQ(1, fblock->PredecessorCount());
+ CHECK_EQ(1, static_cast<int>(fblock->PredecessorCount()));
CHECK_EQ(entry, fblock->PredecessorAt(0));
- CHECK_EQ(0, fblock->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(fblock->SuccessorCount()));
}
@@ -106,8 +103,8 @@ TEST(TestScheduleAddReturn) {
BasicBlock* entry = schedule.start();
schedule.AddReturn(entry, n0);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(1, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(schedule.end(), entry->SuccessorAt(0));
}
@@ -120,18 +117,53 @@ TEST(TestScheduleAddThrow) {
BasicBlock* entry = schedule.start();
schedule.AddThrow(entry, n0);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(1, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(schedule.end(), entry->SuccessorAt(0));
}
+TEST(TestScheduleInsertBranch) {
+ HandleAndZoneScope scope;
+ Schedule schedule(scope.main_zone());
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+ Node* n0 = graph.NewNode(&dummy_operator);
+ Node* n1 = graph.NewNode(&dummy_operator);
+ Node* b = graph.NewNode(common.Branch(), n1);
+
+ BasicBlock* entry = schedule.start();
+ BasicBlock* tblock = schedule.NewBasicBlock();
+ BasicBlock* fblock = schedule.NewBasicBlock();
+ BasicBlock* merge = schedule.NewBasicBlock();
+ schedule.AddReturn(entry, n0);
+ schedule.AddGoto(tblock, merge);
+ schedule.AddGoto(fblock, merge);
+
+ schedule.InsertBranch(entry, merge, b, tblock, fblock);
+
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(2, static_cast<int>(entry->SuccessorCount()));
+ CHECK_EQ(tblock, entry->SuccessorAt(0));
+ CHECK_EQ(fblock, entry->SuccessorAt(1));
+
+ CHECK_EQ(2, static_cast<int>(merge->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(merge->SuccessorCount()));
+ CHECK_EQ(schedule.end(), merge->SuccessorAt(0));
+
+ CHECK_EQ(1, static_cast<int>(schedule.end()->PredecessorCount()));
+ CHECK_EQ(0, static_cast<int>(schedule.end()->SuccessorCount()));
+ CHECK_EQ(merge, schedule.end()->PredecessorAt(0));
+}
+
+
TEST(BuildMulNodeGraph) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
- MachineOperatorBuilder machine;
+ // TODO(titzer): use test operators.
+ MachineOperatorBuilder machine(scope.main_zone());
Node* start = graph.NewNode(common.Start(0));
graph.SetStart(start);
diff --git a/test/cctest/compiler/test-scheduler.cc b/test/cctest/compiler/test-scheduler.cc
index cf331235..1b79ed54 100644
--- a/test/cctest/compiler/test-scheduler.cc
+++ b/test/cctest/compiler/test-scheduler.cc
@@ -3,31 +3,71 @@
// found in the LICENSE file.
#include "src/v8.h"
-#include "test/cctest/cctest.h"
+#include "src/compiler/access-builder.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node-inl.h"
-#include "src/compiler/generic-node.h"
#include "src/compiler/graph.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-operator.h"
-#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
+#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/compiler/schedule.h"
#include "src/compiler/scheduler.h"
+#include "src/compiler/simplified-operator.h"
#include "src/compiler/verifier.h"
+#include "test/cctest/cctest.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
+Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0, 0, 1,
+ 0, 0);
+
// TODO(titzer): pull RPO tests out to their own file.
+static void CheckRPONumbers(BasicBlockVector* order, size_t expected,
+ bool loops_allowed) {
+ CHECK(expected == order->size());
+ for (int i = 0; i < static_cast<int>(order->size()); i++) {
+ CHECK(order->at(i)->rpo_number() == i);
+ if (!loops_allowed) {
+ CHECK_EQ(NULL, order->at(i)->loop_end());
+ CHECK_EQ(NULL, order->at(i)->loop_header());
+ }
+ }
+}
+
+
+static void CheckLoop(BasicBlockVector* order, BasicBlock** blocks,
+ int body_size) {
+ BasicBlock* header = blocks[0];
+ BasicBlock* end = header->loop_end();
+ CHECK_NE(NULL, end);
+ CHECK_GT(end->rpo_number(), 0);
+ CHECK_EQ(body_size, end->rpo_number() - header->rpo_number());
+ for (int i = 0; i < body_size; i++) {
+ CHECK_GE(blocks[i]->rpo_number(), header->rpo_number());
+ CHECK_LT(blocks[i]->rpo_number(), end->rpo_number());
+ CHECK(header->LoopContains(blocks[i]));
+ CHECK(header->IsLoopHeader() || blocks[i]->loop_header() == header);
+ }
+ if (header->rpo_number() > 0) {
+ CHECK_NE(order->at(header->rpo_number() - 1)->loop_header(), header);
+ }
+ if (end->rpo_number() < static_cast<int>(order->size())) {
+ CHECK_NE(order->at(end->rpo_number())->loop_header(), header);
+ }
+}
+
+
struct TestLoop {
int count;
BasicBlock** nodes;
BasicBlock* header() { return nodes[0]; }
BasicBlock* last() { return nodes[count - 1]; }
~TestLoop() { delete[] nodes; }
+
+ void Check(BasicBlockVector* order) { CheckLoop(order, nodes, count); }
};
@@ -37,36 +77,15 @@ static TestLoop* CreateLoop(Schedule* schedule, int count) {
loop->nodes = new BasicBlock* [count];
for (int i = 0; i < count; i++) {
loop->nodes[i] = schedule->NewBasicBlock();
- if (i > 0) schedule->AddSuccessor(loop->nodes[i - 1], loop->nodes[i]);
+ if (i > 0) {
+ schedule->AddSuccessorForTesting(loop->nodes[i - 1], loop->nodes[i]);
+ }
}
- schedule->AddSuccessor(loop->nodes[count - 1], loop->nodes[0]);
+ schedule->AddSuccessorForTesting(loop->nodes[count - 1], loop->nodes[0]);
return loop;
}
-static void CheckRPONumbers(BasicBlockVector* order, int expected,
- bool loops_allowed) {
- CHECK_EQ(expected, static_cast<int>(order->size()));
- for (int i = 0; i < static_cast<int>(order->size()); i++) {
- CHECK(order->at(i)->rpo_number_ == i);
- if (!loops_allowed) CHECK_LT(order->at(i)->loop_end_, 0);
- }
-}
-
-
-static void CheckLoopContains(BasicBlock** blocks, int body_size) {
- BasicBlock* header = blocks[0];
- CHECK_GT(header->loop_end_, 0);
- CHECK_EQ(body_size, (header->loop_end_ - header->rpo_number_));
- for (int i = 0; i < body_size; i++) {
- int num = blocks[i]->rpo_number_;
- CHECK(num >= header->rpo_number_ && num < header->loop_end_);
- CHECK(header->LoopContains(blocks[i]));
- CHECK(header->IsLoopHeader() || blocks[i]->loop_header_ == header);
- }
-}
-
-
static int GetScheduledNodeCount(Schedule* schedule) {
int node_count = 0;
for (BasicBlockVectorIter i = schedule->rpo_order()->begin();
@@ -76,7 +95,7 @@ static int GetScheduledNodeCount(Schedule* schedule) {
++j) {
++node_count;
}
- BasicBlock::Control control = block->control_;
+ BasicBlock::Control control = block->control();
if (control != BasicBlock::kNone) {
++node_count;
}
@@ -91,11 +110,11 @@ static Schedule* ComputeAndVerifySchedule(int expected, Graph* graph) {
os << AsDOT(*graph);
}
- Schedule* schedule = Scheduler::ComputeSchedule(graph);
+ Schedule* schedule = Scheduler::ComputeSchedule(graph->zone(), graph);
if (FLAG_trace_turbo_scheduler) {
OFStream os(stdout);
- os << *schedule << endl;
+ os << *schedule << std::endl;
}
ScheduleVerifier::Run(schedule);
CHECK_EQ(expected, GetScheduledNodeCount(schedule));
@@ -107,7 +126,8 @@ TEST(RPODegenerate1) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 1, false);
CHECK_EQ(schedule.start(), order->at(0));
}
@@ -118,7 +138,8 @@ TEST(RPODegenerate2) {
Schedule schedule(scope.main_zone());
schedule.AddGoto(schedule.start(), schedule.end());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 2, false);
CHECK_EQ(schedule.start(), order->at(0));
CHECK_EQ(schedule.end(), order->at(1));
@@ -134,18 +155,18 @@ TEST(RPOLine) {
BasicBlock* last = schedule.start();
for (int j = 0; j < i; j++) {
BasicBlock* block = schedule.NewBasicBlock();
+ block->set_deferred(i & 1);
schedule.AddGoto(last, block);
last = block;
}
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 1 + i, false);
- Schedule::BasicBlocks blocks(schedule.all_blocks());
- for (Schedule::BasicBlocks::iterator iter = blocks.begin();
- iter != blocks.end(); ++iter) {
- BasicBlock* block = *iter;
- if (block->rpo_number_ >= 0 && block->SuccessorCount() == 1) {
- CHECK(block->rpo_number_ + 1 == block->SuccessorAt(0)->rpo_number_);
+ for (size_t i = 0; i < schedule.BasicBlockCount(); i++) {
+ BasicBlock* block = schedule.GetBlockById(BasicBlock::Id::FromSize(i));
+ if (block->rpo_number() >= 0 && block->SuccessorCount() == 1) {
+ CHECK(block->rpo_number() + 1 == block->SuccessorAt(0)->rpo_number());
}
}
}
@@ -155,23 +176,26 @@ TEST(RPOLine) {
TEST(RPOSelfLoop) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
- schedule.AddSuccessor(schedule.start(), schedule.start());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(schedule.start(), schedule.start());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 1, true);
BasicBlock* loop[] = {schedule.start()};
- CheckLoopContains(loop, 1);
+ CheckLoop(order, loop, 1);
}
TEST(RPOEntryLoop) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
- schedule.AddSuccessor(schedule.start(), schedule.end());
- schedule.AddSuccessor(schedule.end(), schedule.start());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlock* body = schedule.NewBasicBlock();
+ schedule.AddSuccessorForTesting(schedule.start(), body);
+ schedule.AddSuccessorForTesting(body, schedule.start());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 2, true);
- BasicBlock* loop[] = {schedule.start(), schedule.end()};
- CheckLoopContains(loop, 2);
+ BasicBlock* loop[] = {schedule.start(), body};
+ CheckLoop(order, loop, 2);
}
@@ -179,10 +203,11 @@ TEST(RPOEndLoop) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 2));
- schedule.AddSuccessor(schedule.start(), loop1->header());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(schedule.start(), loop1->header());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 3, true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
@@ -190,11 +215,12 @@ TEST(RPOEndLoopNested) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 2));
- schedule.AddSuccessor(schedule.start(), loop1->header());
- schedule.AddSuccessor(loop1->last(), schedule.start());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(schedule.start(), loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), schedule.start());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 3, true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
@@ -207,18 +233,19 @@ TEST(RPODiamond) {
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* D = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(A, C);
- schedule.AddSuccessor(B, D);
- schedule.AddSuccessor(C, D);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(A, C);
+ schedule.AddSuccessorForTesting(B, D);
+ schedule.AddSuccessorForTesting(C, D);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 4, false);
- CHECK_EQ(0, A->rpo_number_);
- CHECK((B->rpo_number_ == 1 && C->rpo_number_ == 2) ||
- (B->rpo_number_ == 2 && C->rpo_number_ == 1));
- CHECK_EQ(3, D->rpo_number_);
+ CHECK_EQ(0, A->rpo_number());
+ CHECK((B->rpo_number() == 1 && C->rpo_number() == 2) ||
+ (B->rpo_number() == 2 && C->rpo_number() == 1));
+ CHECK_EQ(3, D->rpo_number());
}
@@ -231,15 +258,16 @@ TEST(RPOLoop1) {
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* D = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, B);
- schedule.AddSuccessor(C, D);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, B);
+ schedule.AddSuccessorForTesting(C, D);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 4, true);
BasicBlock* loop[] = {B, C};
- CheckLoopContains(loop, 2);
+ CheckLoop(order, loop, 2);
}
@@ -252,15 +280,16 @@ TEST(RPOLoop2) {
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* D = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, B);
- schedule.AddSuccessor(B, D);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, B);
+ schedule.AddSuccessorForTesting(B, D);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 4, true);
BasicBlock* loop[] = {B, C};
- CheckLoopContains(loop, 2);
+ CheckLoop(order, loop, 2);
}
@@ -277,32 +306,33 @@ TEST(RPOLoopN) {
BasicBlock* F = schedule.NewBasicBlock();
BasicBlock* G = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, D);
- schedule.AddSuccessor(D, E);
- schedule.AddSuccessor(E, F);
- schedule.AddSuccessor(F, B);
- schedule.AddSuccessor(B, G);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, D);
+ schedule.AddSuccessorForTesting(D, E);
+ schedule.AddSuccessorForTesting(E, F);
+ schedule.AddSuccessorForTesting(F, B);
+ schedule.AddSuccessorForTesting(B, G);
// Throw in extra backedges from time to time.
- if (i == 1) schedule.AddSuccessor(B, B);
- if (i == 2) schedule.AddSuccessor(C, B);
- if (i == 3) schedule.AddSuccessor(D, B);
- if (i == 4) schedule.AddSuccessor(E, B);
- if (i == 5) schedule.AddSuccessor(F, B);
+ if (i == 1) schedule.AddSuccessorForTesting(B, B);
+ if (i == 2) schedule.AddSuccessorForTesting(C, B);
+ if (i == 3) schedule.AddSuccessorForTesting(D, B);
+ if (i == 4) schedule.AddSuccessorForTesting(E, B);
+ if (i == 5) schedule.AddSuccessorForTesting(F, B);
// Throw in extra loop exits from time to time.
- if (i == 6) schedule.AddSuccessor(B, G);
- if (i == 7) schedule.AddSuccessor(C, G);
- if (i == 8) schedule.AddSuccessor(D, G);
- if (i == 9) schedule.AddSuccessor(E, G);
- if (i == 10) schedule.AddSuccessor(F, G);
-
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ if (i == 6) schedule.AddSuccessorForTesting(B, G);
+ if (i == 7) schedule.AddSuccessorForTesting(C, G);
+ if (i == 8) schedule.AddSuccessorForTesting(D, G);
+ if (i == 9) schedule.AddSuccessorForTesting(E, G);
+ if (i == 10) schedule.AddSuccessorForTesting(F, G);
+
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 7, true);
BasicBlock* loop[] = {B, C, D, E, F};
- CheckLoopContains(loop, 5);
+ CheckLoop(order, loop, 5);
}
}
@@ -318,21 +348,22 @@ TEST(RPOLoopNest1) {
BasicBlock* E = schedule.NewBasicBlock();
BasicBlock* F = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, D);
- schedule.AddSuccessor(D, C);
- schedule.AddSuccessor(D, E);
- schedule.AddSuccessor(E, B);
- schedule.AddSuccessor(E, F);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, D);
+ schedule.AddSuccessorForTesting(D, C);
+ schedule.AddSuccessorForTesting(D, E);
+ schedule.AddSuccessorForTesting(E, B);
+ schedule.AddSuccessorForTesting(E, F);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 6, true);
BasicBlock* loop1[] = {B, C, D, E};
- CheckLoopContains(loop1, 4);
+ CheckLoop(order, loop1, 4);
BasicBlock* loop2[] = {C, D};
- CheckLoopContains(loop2, 2);
+ CheckLoop(order, loop2, 2);
}
@@ -349,28 +380,29 @@ TEST(RPOLoopNest2) {
BasicBlock* G = schedule.NewBasicBlock();
BasicBlock* H = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, D);
- schedule.AddSuccessor(D, E);
- schedule.AddSuccessor(E, F);
- schedule.AddSuccessor(F, G);
- schedule.AddSuccessor(G, H);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, D);
+ schedule.AddSuccessorForTesting(D, E);
+ schedule.AddSuccessorForTesting(E, F);
+ schedule.AddSuccessorForTesting(F, G);
+ schedule.AddSuccessorForTesting(G, H);
- schedule.AddSuccessor(E, D);
- schedule.AddSuccessor(F, C);
- schedule.AddSuccessor(G, B);
+ schedule.AddSuccessorForTesting(E, D);
+ schedule.AddSuccessorForTesting(F, C);
+ schedule.AddSuccessorForTesting(G, B);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 8, true);
BasicBlock* loop1[] = {B, C, D, E, F, G};
- CheckLoopContains(loop1, 6);
+ CheckLoop(order, loop1, 6);
BasicBlock* loop2[] = {C, D, E, F};
- CheckLoopContains(loop2, 4);
+ CheckLoop(order, loop2, 4);
BasicBlock* loop3[] = {D, E};
- CheckLoopContains(loop3, 2);
+ CheckLoop(order, loop3, 2);
}
@@ -384,17 +416,18 @@ TEST(RPOLoopFollow1) {
BasicBlock* A = schedule.start();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->header(), loop2->header());
- schedule.AddSuccessor(loop2->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->header(), loop2->header());
+ schedule.AddSuccessorForTesting(loop2->last(), E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ loop1->Check(order);
+ loop2->Check(order);
}
@@ -409,18 +442,18 @@ TEST(RPOLoopFollow2) {
BasicBlock* S = schedule.NewBasicBlock();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->header(), S);
- schedule.AddSuccessor(S, loop2->header());
- schedule.AddSuccessor(loop2->last(), E);
-
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->header(), S);
+ schedule.AddSuccessorForTesting(S, loop2->header());
+ schedule.AddSuccessorForTesting(loop2->last(), E);
- CheckLoopContains(loop1->nodes, loop1->count);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
+ loop1->Check(order);
+ loop2->Check(order);
}
@@ -435,15 +468,16 @@ TEST(RPOLoopFollowN) {
BasicBlock* A = schedule.start();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->nodes[exit], loop2->header());
- schedule.AddSuccessor(loop2->nodes[exit], E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->nodes[exit], loop2->header());
+ schedule.AddSuccessorForTesting(loop2->nodes[exit], E);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
+ loop1->Check(order);
+ loop2->Check(order);
}
}
}
@@ -461,23 +495,23 @@ TEST(RPONestedLoopFollow1) {
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, loop1->header());
- schedule.AddSuccessor(loop1->header(), loop2->header());
- schedule.AddSuccessor(loop2->last(), C);
- schedule.AddSuccessor(C, E);
- schedule.AddSuccessor(C, B);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->header(), loop2->header());
+ schedule.AddSuccessorForTesting(loop2->last(), C);
+ schedule.AddSuccessorForTesting(C, E);
+ schedule.AddSuccessorForTesting(C, B);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
-
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
+ loop1->Check(order);
+ loop2->Check(order);
BasicBlock* loop3[] = {B, loop1->nodes[0], loop2->nodes[0], C};
- CheckLoopContains(loop3, 4);
+ CheckLoop(order, loop3, 4);
}
@@ -492,15 +526,16 @@ TEST(RPOLoopBackedges1) {
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
- schedule.AddSuccessor(loop1->nodes[i], loop1->header());
- schedule.AddSuccessor(loop1->nodes[j], E);
+ schedule.AddSuccessorForTesting(loop1->nodes[i], loop1->header());
+ schedule.AddSuccessorForTesting(loop1->nodes[j], E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
}
}
@@ -518,16 +553,17 @@ TEST(RPOLoopOutedges1) {
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
- schedule.AddSuccessor(loop1->nodes[i], loop1->header());
- schedule.AddSuccessor(loop1->nodes[j], D);
- schedule.AddSuccessor(D, E);
+ schedule.AddSuccessorForTesting(loop1->nodes[i], loop1->header());
+ schedule.AddSuccessorForTesting(loop1->nodes[j], D);
+ schedule.AddSuccessorForTesting(D, E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
}
}
@@ -543,18 +579,19 @@ TEST(RPOLoopOutedges2) {
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
for (int j = 0; j < size; j++) {
BasicBlock* O = schedule.NewBasicBlock();
- schedule.AddSuccessor(loop1->nodes[j], O);
- schedule.AddSuccessor(O, E);
+ schedule.AddSuccessorForTesting(loop1->nodes[j], O);
+ schedule.AddSuccessorForTesting(O, E);
}
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
}
@@ -568,22 +605,23 @@ TEST(RPOLoopOutloops1) {
BasicBlock* A = schedule.start();
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
TestLoop** loopN = new TestLoop* [size];
for (int j = 0; j < size; j++) {
loopN[j] = CreateLoop(&schedule, 2);
- schedule.AddSuccessor(loop1->nodes[j], loopN[j]->header());
- schedule.AddSuccessor(loopN[j]->last(), E);
+ schedule.AddSuccessorForTesting(loop1->nodes[j], loopN[j]->header());
+ schedule.AddSuccessorForTesting(loopN[j]->last(), E);
}
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
for (int j = 0; j < size; j++) {
- CheckLoopContains(loopN[j]->nodes, loopN[j]->count);
+ loopN[j]->Check(order);
delete loopN[j];
}
delete[] loopN;
@@ -598,22 +636,23 @@ TEST(RPOLoopMultibackedge) {
BasicBlock* A = schedule.start();
BasicBlock* B = schedule.NewBasicBlock();
BasicBlock* C = schedule.NewBasicBlock();
- BasicBlock* D = schedule.end();
+ BasicBlock* D = schedule.NewBasicBlock();
BasicBlock* E = schedule.NewBasicBlock();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(B, D);
- schedule.AddSuccessor(B, E);
- schedule.AddSuccessor(C, B);
- schedule.AddSuccessor(D, B);
- schedule.AddSuccessor(E, B);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(B, D);
+ schedule.AddSuccessorForTesting(B, E);
+ schedule.AddSuccessorForTesting(C, B);
+ schedule.AddSuccessorForTesting(D, B);
+ schedule.AddSuccessorForTesting(E, B);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 5, true);
BasicBlock* loop1[] = {B, C, D, E};
- CheckLoopContains(loop1, 4);
+ CheckLoop(order, loop1, 4);
}
@@ -624,7 +663,7 @@ TEST(BuildScheduleEmpty) {
graph.SetStart(graph.NewNode(builder.Start(0)));
graph.SetEnd(graph.NewNode(builder.End(), graph.start()));
- USE(Scheduler::ComputeSchedule(&graph));
+ USE(Scheduler::ComputeSchedule(scope.main_zone(), &graph));
}
@@ -639,7 +678,7 @@ TEST(BuildScheduleOneParameter) {
graph.SetEnd(graph.NewNode(builder.End(), ret));
- USE(Scheduler::ComputeSchedule(&graph));
+ USE(Scheduler::ComputeSchedule(scope.main_zone(), &graph));
}
@@ -678,9 +717,10 @@ TEST(BuildScheduleIfSplitWithEffects) {
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c, y) {
@@ -823,9 +863,10 @@ TEST(BuildScheduleSimpleLoop) {
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b) {
@@ -935,9 +976,10 @@ TEST(BuildScheduleComplexLoops) {
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c) {
@@ -1182,9 +1224,10 @@ TEST(BuildScheduleBreakAndContinue) {
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c) {
@@ -1509,12 +1552,12 @@ TEST(BuildScheduleSimpleLoopWithCodeMotion) {
Graph graph(scope.main_zone());
CommonOperatorBuilder common_builder(scope.main_zone());
JSOperatorBuilder js_builder(scope.main_zone());
- MachineOperatorBuilder machine_builder;
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c) {
@@ -1544,7 +1587,7 @@ TEST(BuildScheduleSimpleLoopWithCodeMotion) {
Node* n20 = graph.NewNode(op, nil, nil, nil, nil, nil);
USE(n20);
n20->ReplaceInput(0, n9);
- op = machine_builder.Int32Add();
+ op = &kIntAdd;
Node* n19 = graph.NewNode(op, nil, nil);
USE(n19);
op = common_builder.Phi(kMachAnyTagged, 2);
@@ -1668,7 +1711,6 @@ TEST(FloatingDiamond2) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
- MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
@@ -1677,7 +1719,7 @@ TEST(FloatingDiamond2) {
Node* p1 = graph.NewNode(common.Parameter(1), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* d2 = CreateDiamond(&graph, &common, p1);
- Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
+ Node* add = graph.NewNode(&kIntAdd, d1, d2);
Node* ret = graph.NewNode(common.Return(), add, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
@@ -1691,7 +1733,6 @@ TEST(FloatingDiamond3) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
- MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
@@ -1700,7 +1741,7 @@ TEST(FloatingDiamond3) {
Node* p1 = graph.NewNode(common.Parameter(1), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* d2 = CreateDiamond(&graph, &common, p1);
- Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
+ Node* add = graph.NewNode(&kIntAdd, d1, d2);
Node* d3 = CreateDiamond(&graph, &common, add);
Node* ret = graph.NewNode(common.Return(), d3, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
@@ -1710,4 +1751,374 @@ TEST(FloatingDiamond3) {
ComputeAndVerifySchedule(33, &graph);
}
+
+TEST(NestedFloatingDiamonds) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+ SimplifiedOperatorBuilder simplified(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* fv = graph.NewNode(common.Int32Constant(7));
+ Node* br = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ Node* map = graph.NewNode(
+ simplified.LoadElement(AccessBuilder::ForFixedArrayElement()), p0, p0, p0,
+ start, f);
+ Node* br1 = graph.NewNode(common.Branch(), map, graph.start());
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+ Node* m1 = graph.NewNode(common.Merge(2), t1, f1);
+ Node* ttrue = graph.NewNode(common.Int32Constant(1));
+ Node* ffalse = graph.NewNode(common.Int32Constant(0));
+ Node* phi1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), ttrue, ffalse, m1);
+
+
+ Node* m = graph.NewNode(common.Merge(2), t, f);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), fv, phi1, m);
+ Node* ephi1 = graph.NewNode(common.EffectPhi(2), start, map, m);
+
+ Node* ret = graph.NewNode(common.Return(), phi, ephi1, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(23, &graph);
+}
+
+
+TEST(NestedFloatingDiamondWithChain) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+ Node* p1 = graph.NewNode(common.Parameter(1), start);
+ Node* c = graph.NewNode(common.Int32Constant(7));
+
+ Node* brA1 = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* tA1 = graph.NewNode(common.IfTrue(), brA1);
+ Node* fA1 = graph.NewNode(common.IfFalse(), brA1);
+ Node* mA1 = graph.NewNode(common.Merge(2), tA1, fA1);
+ Node* phiA1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p1, mA1);
+
+ Node* brB1 = graph.NewNode(common.Branch(), p1, graph.start());
+ Node* tB1 = graph.NewNode(common.IfTrue(), brB1);
+ Node* fB1 = graph.NewNode(common.IfFalse(), brB1);
+ Node* mB1 = graph.NewNode(common.Merge(2), tB1, fB1);
+ Node* phiB1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p1, mB1);
+
+ Node* brA2 = graph.NewNode(common.Branch(), phiB1, mA1);
+ Node* tA2 = graph.NewNode(common.IfTrue(), brA2);
+ Node* fA2 = graph.NewNode(common.IfFalse(), brA2);
+ Node* mA2 = graph.NewNode(common.Merge(2), tA2, fA2);
+ Node* phiA2 = graph.NewNode(common.Phi(kMachAnyTagged, 2), phiB1, c, mA2);
+
+ Node* brB2 = graph.NewNode(common.Branch(), phiA1, mB1);
+ Node* tB2 = graph.NewNode(common.IfTrue(), brB2);
+ Node* fB2 = graph.NewNode(common.IfFalse(), brB2);
+ Node* mB2 = graph.NewNode(common.Merge(2), tB2, fB2);
+ Node* phiB2 = graph.NewNode(common.Phi(kMachAnyTagged, 2), phiA1, c, mB2);
+
+ Node* add = graph.NewNode(&kIntAdd, phiA2, phiB2);
+ Node* ret = graph.NewNode(common.Return(), add, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(35, &graph);
+}
+
+
+TEST(NestedFloatingDiamondWithLoop) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* fv = graph.NewNode(common.Int32Constant(7));
+ Node* br = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ Node* loop = graph.NewNode(common.Loop(2), f, start);
+ Node* ind = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* add = graph.NewNode(&kIntAdd, ind, fv);
+ Node* br1 = graph.NewNode(common.Branch(), add, loop);
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+
+ loop->ReplaceInput(1, t1); // close loop.
+ ind->ReplaceInput(1, ind); // close induction variable.
+
+ Node* m = graph.NewNode(common.Merge(2), t, f1);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), fv, ind, m);
+
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(LoopedFloatingDiamond1) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* c = graph.NewNode(common.Int32Constant(7));
+ Node* loop = graph.NewNode(common.Loop(2), start, start);
+ Node* ind = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+ Node* add = graph.NewNode(&kIntAdd, ind, c);
+
+ Node* br = graph.NewNode(common.Branch(), add, loop);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ Node* br1 = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+ Node* m1 = graph.NewNode(common.Merge(2), t1, f1);
+ Node* phi1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), add, p0, m1);
+
+ loop->ReplaceInput(1, t); // close loop.
+ ind->ReplaceInput(1, phi1); // close induction variable.
+
+ Node* ret = graph.NewNode(common.Return(), ind, start, f);
+ Node* end = graph.NewNode(common.End(), ret, f);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(LoopedFloatingDiamond2) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* c = graph.NewNode(common.Int32Constant(7));
+ Node* loop = graph.NewNode(common.Loop(2), start, start);
+ Node* ind = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* br1 = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+ Node* m1 = graph.NewNode(common.Merge(2), t1, f1);
+ Node* phi1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), c, ind, m1);
+
+ Node* add = graph.NewNode(&kIntAdd, ind, phi1);
+
+ Node* br = graph.NewNode(common.Branch(), add, loop);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ loop->ReplaceInput(1, t); // close loop.
+ ind->ReplaceInput(1, add); // close induction variable.
+
+ Node* ret = graph.NewNode(common.Return(), ind, start, f);
+ Node* end = graph.NewNode(common.End(), ret, f);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(LoopedFloatingDiamond3) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* c = graph.NewNode(common.Int32Constant(7));
+ Node* loop = graph.NewNode(common.Loop(2), start, start);
+ Node* ind = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* br1 = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+
+ Node* loop1 = graph.NewNode(common.Loop(2), t1, start);
+ Node* ind1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* add1 = graph.NewNode(&kIntAdd, ind1, c);
+ Node* br2 = graph.NewNode(common.Branch(), add1, loop1);
+ Node* t2 = graph.NewNode(common.IfTrue(), br2);
+ Node* f2 = graph.NewNode(common.IfFalse(), br2);
+
+ loop1->ReplaceInput(1, t2); // close inner loop.
+ ind1->ReplaceInput(1, ind1); // close inner induction variable.
+
+ Node* m1 = graph.NewNode(common.Merge(2), f1, f2);
+ Node* phi1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), c, ind1, m1);
+
+ Node* add = graph.NewNode(&kIntAdd, ind, phi1);
+
+ Node* br = graph.NewNode(common.Branch(), add, loop);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ loop->ReplaceInput(1, t); // close loop.
+ ind->ReplaceInput(1, add); // close induction variable.
+
+ Node* ret = graph.NewNode(common.Return(), ind, start, f);
+ Node* end = graph.NewNode(common.End(), ret, f);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(28, &graph);
+}
+
+
+TEST(PhisPushedDownToDifferentBranches) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+ Node* p1 = graph.NewNode(common.Parameter(1), start);
+
+ Node* v1 = graph.NewNode(common.Int32Constant(1));
+ Node* v2 = graph.NewNode(common.Int32Constant(2));
+ Node* v3 = graph.NewNode(common.Int32Constant(3));
+ Node* v4 = graph.NewNode(common.Int32Constant(4));
+ Node* br = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+ Node* m = graph.NewNode(common.Merge(2), t, f);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), v1, v2, m);
+ Node* phi2 = graph.NewNode(common.Phi(kMachAnyTagged, 2), v3, v4, m);
+
+ Node* br2 = graph.NewNode(common.Branch(), p1, graph.start());
+ Node* t2 = graph.NewNode(common.IfTrue(), br2);
+ Node* f2 = graph.NewNode(common.IfFalse(), br2);
+ Node* m2 = graph.NewNode(common.Merge(2), t2, f2);
+ Node* phi3 = graph.NewNode(common.Phi(kMachAnyTagged, 2), phi, phi2, m2);
+
+ Node* ret = graph.NewNode(common.Return(), phi3, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(24, &graph);
+}
+
+
+TEST(BranchHintTrue) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(1));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+ Node* tv = graph.NewNode(common.Int32Constant(6));
+ Node* fv = graph.NewNode(common.Int32Constant(7));
+ Node* br = graph.NewNode(common.Branch(BranchHint::kTrue), p0, start);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+ Node* m = graph.NewNode(common.Merge(2), t, f);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), tv, fv, m);
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ Schedule* schedule = ComputeAndVerifySchedule(13, &graph);
+ // Make sure the false block is marked as deferred.
+ CHECK(!schedule->block(t)->deferred());
+ CHECK(schedule->block(f)->deferred());
+}
+
+
+TEST(BranchHintFalse) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(1));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+ Node* tv = graph.NewNode(common.Int32Constant(6));
+ Node* fv = graph.NewNode(common.Int32Constant(7));
+ Node* br = graph.NewNode(common.Branch(BranchHint::kFalse), p0, start);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+ Node* m = graph.NewNode(common.Merge(2), t, f);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), tv, fv, m);
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ Schedule* schedule = ComputeAndVerifySchedule(13, &graph);
+ // Make sure the true block is marked as deferred.
+ CHECK(schedule->block(t)->deferred());
+ CHECK(!schedule->block(f)->deferred());
+}
+
+
+TEST(ScheduleTerminate) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(1));
+ graph.SetStart(start);
+
+ Node* loop = graph.NewNode(common.Loop(2), start, start);
+ loop->ReplaceInput(1, loop); // self loop, NTL.
+
+ Node* effect = graph.NewNode(common.EffectPhi(1), start, loop);
+ effect->ReplaceInput(0, effect);
+
+ Node* terminate = graph.NewNode(common.Terminate(1), effect, loop);
+ Node* end = graph.NewNode(common.End(), terminate);
+
+ graph.SetEnd(end);
+
+ Schedule* schedule = ComputeAndVerifySchedule(6, &graph);
+ BasicBlock* block = schedule->block(loop);
+ CHECK_NE(NULL, loop);
+ CHECK_EQ(block, schedule->block(effect));
+ CHECK_GE(block->rpo_number(), 0);
+}
+
#endif
diff --git a/test/cctest/compiler/test-simplified-lowering.cc b/test/cctest/compiler/test-simplified-lowering.cc
index 96fb9650..147aa323 100644
--- a/test/cctest/compiler/test-simplified-lowering.cc
+++ b/test/cctest/compiler/test-simplified-lowering.cc
@@ -5,8 +5,9 @@
#include <limits>
#include "src/compiler/access-builder.h"
+#include "src/compiler/change-lowering.h"
#include "src/compiler/control-builders.h"
-#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph-reducer.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/pipeline.h"
@@ -35,11 +36,10 @@ class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> {
MachineType p3 = kMachNone,
MachineType p4 = kMachNone)
: GraphBuilderTester<ReturnType>(p0, p1, p2, p3, p4),
- typer(this->zone()),
+ typer(this->graph(), MaybeHandle<Context>()),
javascript(this->zone()),
- jsgraph(this->graph(), this->common(), &javascript, &typer,
- this->machine()),
- lowering(&jsgraph) {}
+ jsgraph(this->graph(), this->common(), &javascript, this->machine()),
+ lowering(&jsgraph, this->zone()) {}
Typer typer;
JSOperatorBuilder javascript;
@@ -48,16 +48,40 @@ class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> {
void LowerAllNodes() {
this->End();
+ typer.Run();
lowering.LowerAllNodes();
}
+ void LowerAllNodesAndLowerChanges() {
+ this->End();
+ typer.Run();
+ lowering.LowerAllNodes();
+
+ Zone* zone = this->zone();
+ CompilationInfo info(zone->isolate(), zone);
+ Linkage linkage(
+ zone, Linkage::GetSimplifiedCDescriptor(zone, this->machine_sig_));
+ ChangeLowering lowering(&jsgraph, &linkage);
+ GraphReducer reducer(this->graph(), this->zone());
+ reducer.AddReducer(&lowering);
+ reducer.ReduceGraph();
+ Verifier::Run(this->graph());
+ }
+
+ void CheckNumberCall(double expected, double input) {
+ // TODO(titzer): make calls to NewNumber work in cctests.
+ if (expected <= Smi::kMinValue) return;
+ if (expected >= Smi::kMaxValue) return;
+ Handle<Object> num = factory()->NewNumber(input);
+ Object* result = this->Call(*num);
+ CHECK(factory()->NewNumber(expected)->SameValue(result));
+ }
+
Factory* factory() { return this->isolate()->factory(); }
Heap* heap() { return this->isolate()->heap(); }
};
-#ifndef V8_TARGET_ARCH_ARM64
-// TODO(titzer): these result in a stub call that doesn't work on ARM64.
// TODO(titzer): factor these tests out to test-run-simplifiedops.cc.
// TODO(titzer): test tagged representation for input to NumberToInt32.
TEST(RunNumberToInt32_float64) {
@@ -68,6 +92,7 @@ TEST(RunNumberToInt32_float64) {
FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(),
kMachFloat64};
Node* loaded = t.LoadField(load, t.PointerConstant(&input));
+ NodeProperties::SetBounds(loaded, Bounds(Type::Number()));
Node* convert = t.NumberToInt32(loaded);
FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Signed32(),
kMachInt32};
@@ -96,6 +121,7 @@ TEST(RunNumberToUint32_float64) {
FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(),
kMachFloat64};
Node* loaded = t.LoadField(load, t.PointerConstant(&input));
+ NodeProperties::SetBounds(loaded, Bounds(Type::Number()));
Node* convert = t.NumberToUint32(loaded);
FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Unsigned32(),
kMachUint32};
@@ -113,7 +139,6 @@ TEST(RunNumberToUint32_float64) {
}
}
}
-#endif
// Create a simple JSObject with a unique map.
@@ -207,10 +232,8 @@ TEST(RunLoadStoreMap) {
TEST(RunLoadStoreFixedArrayIndex) {
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
ElementAccess access = AccessBuilder::ForFixedArrayElement();
- Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0),
- t.Int32Constant(2));
- t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), t.Int32Constant(2),
- load);
+ Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0));
+ t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), load);
t.Return(load);
t.LowerAllNodes();
@@ -235,14 +258,13 @@ TEST(RunLoadStoreArrayBuffer) {
const int index = 12;
const int array_length = 2 * index;
ElementAccess buffer_access =
- AccessBuilder::ForBackingStoreElement(kMachInt8);
+ AccessBuilder::ForTypedArrayElement(v8::kExternalInt8Array, true);
Node* backing_store = t.LoadField(
AccessBuilder::ForJSArrayBufferBackingStore(), t.Parameter(0));
Node* load =
- t.LoadElement(buffer_access, backing_store, t.Int32Constant(index),
- t.Int32Constant(array_length));
+ t.LoadElement(buffer_access, backing_store, t.Int32Constant(index));
t.StoreElement(buffer_access, backing_store, t.Int32Constant(index + 1),
- t.Int32Constant(array_length), load);
+ load);
t.Return(t.jsgraph.TrueConstant());
t.LowerAllNodes();
@@ -329,9 +351,8 @@ TEST(RunLoadElementFromUntaggedBase) {
kMachAnyTagged};
SimplifiedLoweringTester<Object*> t;
- Node* load = t.LoadElement(
- access, t.PointerConstant(smis), t.Int32Constant(static_cast<int>(j)),
- t.Int32Constant(static_cast<int>(arraysize(smis))));
+ Node* load = t.LoadElement(access, t.PointerConstant(smis),
+ t.Int32Constant(static_cast<int>(j)));
t.Return(load);
t.LowerAllNodes();
@@ -360,8 +381,7 @@ TEST(RunStoreElementFromUntaggedBase) {
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
Node* p0 = t.Parameter(0);
t.StoreElement(access, t.PointerConstant(smis),
- t.Int32Constant(static_cast<int>(j)),
- t.Int32Constant(static_cast<int>(arraysize(smis))), p0);
+ t.Int32Constant(static_cast<int>(j)), p0);
t.Return(p0);
t.LowerAllNodes();
@@ -427,10 +447,8 @@ class AccessTester : public HandleAndZoneScope {
SimplifiedLoweringTester<Object*> t;
Node* ptr = GetBaseNode(&t);
- Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index),
- t.Int32Constant(static_cast<int>(num_elements)));
- t.StoreElement(access, ptr, t.Int32Constant(to_index),
- t.Int32Constant(static_cast<int>(num_elements)), load);
+ Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index));
+ t.StoreElement(access, ptr, t.Int32Constant(to_index), load);
t.Return(t.jsgraph.TrueConstant());
t.LowerAllNodes();
t.GenerateCode();
@@ -648,9 +666,9 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
explicit TestingGraph(Type* p0_type, Type* p1_type = Type::None(),
Type* p2_type = Type::None())
: GraphAndBuilders(main_zone()),
- typer(main_zone()),
+ typer(graph(), MaybeHandle<Context>()),
javascript(main_zone()),
- jsgraph(graph(), common(), &javascript, &typer, machine()) {
+ jsgraph(graph(), common(), &javascript, machine()) {
start = graph()->NewNode(common()->Start(2));
graph()->SetStart(start);
ret =
@@ -660,6 +678,7 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
p0 = graph()->NewNode(common()->Parameter(0), start);
p1 = graph()->NewNode(common()->Parameter(1), start);
p2 = graph()->NewNode(common()->Parameter(2), start);
+ typer.Run();
NodeProperties::SetBounds(p0, Bounds(p0_type));
NodeProperties::SetBounds(p1, Bounds(p1_type));
NodeProperties::SetBounds(p2, Bounds(p2_type));
@@ -679,10 +698,7 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
CHECK_EQ(expected, node->opcode());
}
- void Lower() {
- SimplifiedLowering lowering(&jsgraph);
- lowering.LowerAllNodes();
- }
+ void Lower() { SimplifiedLowering(&jsgraph, jsgraph.zone()).LowerAllNodes(); }
// Inserts the node as the return value of the graph.
Node* Return(Node* node) {
@@ -718,6 +734,17 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
}
}
+ Node* ExampleWithTypeAndRep(Type* type, MachineType mach_type) {
+ FieldAccess access = {kUntaggedBase, 0, Handle<Name>::null(), type,
+ mach_type};
+ // TODO(titzer): using loads here just to force the representation is ugly.
+ Node* node = graph()->NewNode(simplified()->LoadField(access),
+ jsgraph.IntPtrConstant(0), graph()->start(),
+ graph()->start());
+ NodeProperties::SetBounds(node, Bounds(type));
+ return node;
+ }
+
Node* Use(Node* node, MachineType type) {
if (type & kTypeInt32) {
return graph()->NewNode(machine()->Int32LessThan(), node,
@@ -731,6 +758,9 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
} else if (type & kRepWord64) {
return graph()->NewNode(machine()->Int64LessThan(), node,
Int64Constant(1));
+ } else if (type & kRepWord32) {
+ return graph()->NewNode(machine()->Word32Equal(), node,
+ jsgraph.Int32Constant(1));
} else {
return graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), node,
jsgraph.TrueConstant());
@@ -757,6 +787,50 @@ class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
};
+TEST(LowerAnyToBoolean_bit_bit) {
+ // AnyToBoolean(x: kRepBit) used as kRepBit
+ HandleAndZoneScope scope;
+ Factory* f = scope.main_zone()->isolate()->factory();
+ Handle<Object> zero = f->NewNumber(0);
+ Handle<Object> one = f->NewNumber(1);
+ Type* singleton_zero = Type::Constant(zero, scope.main_zone());
+ Type* singleton_one = Type::Constant(one, scope.main_zone());
+ Type* zero_one_range = Type::Range(zero, one, scope.main_zone());
+ static Type* kTypes[] = {
+ singleton_zero, singleton_one, zero_one_range, Type::Boolean(),
+ Type::Union(Type::Boolean(), singleton_zero, scope.main_zone()),
+ Type::Union(Type::Boolean(), singleton_one, scope.main_zone()),
+ Type::Union(Type::Boolean(), zero_one_range, scope.main_zone())};
+ for (Type* type : kTypes) {
+ TestingGraph t(type);
+ Node* x = t.ExampleWithTypeAndRep(type, kRepBit);
+ Node* cnv = t.graph()->NewNode(t.simplified()->AnyToBoolean(), x);
+ Node* use = t.Branch(cnv);
+ t.Lower();
+ CHECK_EQ(x, use->InputAt(0));
+ }
+}
+
+
+#if V8_TURBOFAN_TARGET
+
+TEST(LowerAnyToBoolean_tagged_tagged) {
+ // AnyToBoolean(x: kRepTagged) used as kRepTagged
+ TestingGraph t(Type::Any());
+ Node* x = t.p0;
+ Node* cnv = t.graph()->NewNode(t.simplified()->AnyToBoolean(), x);
+ Node* use = t.Use(cnv, kRepTagged);
+ t.Return(use);
+ t.Lower();
+ CHECK_EQ(IrOpcode::kCall, cnv->opcode());
+ CHECK_EQ(IrOpcode::kHeapConstant, cnv->InputAt(0)->opcode());
+ CHECK_EQ(x, cnv->InputAt(1));
+ CHECK_EQ(t.jsgraph.NoContextConstant(), cnv->InputAt(2));
+}
+
+#endif
+
+
TEST(LowerBooleanNot_bit_bit) {
// BooleanNot(x: kRepBit) used as kRepBit
TestingGraph t(Type::Boolean());
@@ -765,7 +839,7 @@ TEST(LowerBooleanNot_bit_bit) {
Node* use = t.Branch(inv);
t.Lower();
Node* cmp = use->InputAt(0);
- CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+ CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
Node* f = t.jsgraph.Int32Constant(0);
CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
@@ -782,7 +856,7 @@ TEST(LowerBooleanNot_bit_tagged) {
t.Lower();
CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode());
Node* cmp = use->InputAt(0)->InputAt(0);
- CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+ CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
Node* f = t.jsgraph.Int32Constant(0);
CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
@@ -921,24 +995,50 @@ TEST(LowerNumberCmp_to_float64) {
TEST(LowerNumberAddSub_to_int32) {
- TestingGraph t(Type::Signed32(), Type::Signed32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
- t.simplified()->NumberAdd(),
- t.simplified()->NumberToInt32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
- t.simplified()->NumberSubtract(),
- t.simplified()->NumberToInt32());
+ HandleAndZoneScope scope;
+ Factory* f = scope.main_zone()->isolate()->factory();
+ Type* small_range =
+ Type::Range(f->NewNumber(1), f->NewNumber(10), scope.main_zone());
+ Type* large_range =
+ Type::Range(f->NewNumber(-1e+13), f->NewNumber(1e+14), scope.main_zone());
+ static Type* types[] = {Type::Signed32(), Type::Integral32(), small_range,
+ large_range};
+
+ for (size_t i = 0; i < arraysize(types); i++) {
+ for (size_t j = 0; j < arraysize(types); j++) {
+ TestingGraph t(types[i], types[j]);
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
+ t.simplified()->NumberAdd(),
+ t.simplified()->NumberToInt32());
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
+ t.simplified()->NumberSubtract(),
+ t.simplified()->NumberToInt32());
+ }
+ }
}
TEST(LowerNumberAddSub_to_uint32) {
- TestingGraph t(Type::Unsigned32(), Type::Unsigned32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
- t.simplified()->NumberAdd(),
- t.simplified()->NumberToUint32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
- t.simplified()->NumberSubtract(),
- t.simplified()->NumberToUint32());
+ HandleAndZoneScope scope;
+ Factory* f = scope.main_zone()->isolate()->factory();
+ Type* small_range =
+ Type::Range(f->NewNumber(1), f->NewNumber(10), scope.main_zone());
+ Type* large_range =
+ Type::Range(f->NewNumber(-1e+13), f->NewNumber(1e+14), scope.main_zone());
+ static Type* types[] = {Type::Signed32(), Type::Integral32(), small_range,
+ large_range};
+
+ for (size_t i = 0; i < arraysize(types); i++) {
+ for (size_t j = 0; j < arraysize(types); j++) {
+ TestingGraph t(types[i], types[j]);
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
+ t.simplified()->NumberAdd(),
+ t.simplified()->NumberToUint32());
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
+ t.simplified()->NumberSubtract(),
+ t.simplified()->NumberToUint32());
+ }
+ }
}
@@ -958,8 +1058,10 @@ TEST(LowerNumberDivMod_to_float64) {
TestingGraph t(test_types[i], test_types[i]);
t.CheckLoweringBinop(IrOpcode::kFloat64Div, t.simplified()->NumberDivide());
- t.CheckLoweringBinop(IrOpcode::kFloat64Mod,
- t.simplified()->NumberModulus());
+ if (!test_types[i]->Is(Type::Unsigned32())) {
+ t.CheckLoweringBinop(IrOpcode::kFloat64Mod,
+ t.simplified()->NumberModulus());
+ }
}
}
@@ -1006,7 +1108,7 @@ TEST(LowerNumberToInt32_to_ChangeTaggedToInt32) {
TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32) {
// NumberToInt32(x: kRepFloat64) used as kMachInt32
TestingGraph t(Type::Number());
- Node* p0 = t.ExampleWithOutput(kMachFloat64);
+ Node* p0 = t.ExampleWithTypeAndRep(Type::Number(), kMachFloat64);
Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), p0);
Node* use = t.Use(trunc, kMachInt32);
t.Return(use);
@@ -1030,17 +1132,6 @@ TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32_with_change) {
}
-TEST(LowerNumberToInt32_to_ChangeFloat64ToTagged) {
- // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepTagged
-}
-
-
-TEST(LowerNumberToInt32_to_ChangeFloat64ToInt32) {
- // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepWord32
- // | kTypeInt32
-}
-
-
TEST(LowerNumberToUint32_to_nop) {
// NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepTagged
TestingGraph t(Type::Unsigned32());
@@ -1078,6 +1169,8 @@ TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32) {
// NumberToUint32(x: kRepFloat64) used as kMachUint32
TestingGraph t(Type::Number());
Node* p0 = t.ExampleWithOutput(kMachFloat64);
+ // TODO(titzer): run the typer here, or attach machine type to param.
+ NodeProperties::SetBounds(p0, Bounds(Type::Number()));
Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), p0);
Node* use = t.Use(trunc, kMachUint32);
t.Return(use);
@@ -1101,20 +1194,67 @@ TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32_with_change) {
}
-TEST(LowerNumberToUint32_to_ChangeFloat64ToTagged) {
- // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as
- // kRepTagged
-}
-
-
-TEST(LowerNumberToUint32_to_ChangeFloat64ToUint32) {
- // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as
- // kRepWord32
+TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32_uint32) {
+ // NumberToUint32(x: kRepFloat64) used as kRepWord32
+ TestingGraph t(Type::Unsigned32());
+ Node* input = t.ExampleWithTypeAndRep(Type::Number(), kMachFloat64);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), input);
+ Node* use = t.Use(trunc, kRepWord32);
+ t.Return(use);
+ t.Lower();
+ CheckChangeOf(IrOpcode::kTruncateFloat64ToInt32, input, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToUI32_of_Float64_used_as_word32) {
+ // NumberTo(Int,Uint)32(x: kRepFloat64 | kType(Int,Uint)32) used as
+ // kType(Int,Uint)32 | kRepWord32
+ Type* types[] = {Type::Signed32(), Type::Unsigned32()};
+ MachineType mach[] = {kTypeInt32, kTypeUint32, kMachNone};
+
+ for (int i = 0; i < 2; i++) {
+ for (int u = 0; u < 3; u++) {
+ TestingGraph t(types[i]);
+ Node* input = t.ExampleWithTypeAndRep(
+ types[i], static_cast<MachineType>(kRepFloat64 | mach[i]));
+ const Operator* op = i == 0 ? t.simplified()->NumberToInt32()
+ : t.simplified()->NumberToUint32();
+ Node* trunc = t.graph()->NewNode(op, input);
+ Node* use = t.Use(trunc, static_cast<MachineType>(kRepWord32 | mach[u]));
+ t.Return(use);
+ t.Lower();
+ IrOpcode::Value opcode = i == 0 ? IrOpcode::kChangeFloat64ToInt32
+ : IrOpcode::kChangeFloat64ToUint32;
+ CheckChangeOf(opcode, input, use->InputAt(0));
+ }
+ }
}
-TEST(LowerNumberToUint32_to_TruncateFloat64ToUint32) {
- // TODO(titzer): NumberToUint32(x: kRepFloat64) used as kRepWord32
+TEST(LowerNumberToUI32_of_Float64_used_as_tagged) {
+ // NumberTo(Int,Uint)32(x: kRepFloat64 | kType(Int,Uint)32) used as
+ // kType(Int,Uint)32 | kRepTagged
+ Type* types[] = {Type::Signed32(), Type::Unsigned32(), Type::Any()};
+ MachineType mach[] = {kTypeInt32, kTypeUint32, kMachNone};
+
+ for (int i = 0; i < 2; i++) {
+ for (int u = 0; u < 3; u++) {
+ TestingGraph t(types[i]);
+ Node* input = t.ExampleWithTypeAndRep(
+ types[i], static_cast<MachineType>(kRepFloat64 | mach[i]));
+ const Operator* op = i == 0 ? t.simplified()->NumberToInt32()
+ : t.simplified()->NumberToUint32();
+ Node* trunc = t.graph()->NewNode(op, input);
+ // TODO(titzer): we use the store here to force the representation.
+ FieldAccess access = {kTaggedBase, 0, Handle<Name>(), types[u],
+ static_cast<MachineType>(mach[u] | kRepTagged)};
+ Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
+ trunc, t.start, t.start);
+ t.Effect(store);
+ t.Lower();
+ CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, input, store->InputAt(2));
+ }
+ }
}
@@ -1268,45 +1408,54 @@ TEST(InsertChangesAroundFloat64Cmp) {
}
+namespace {
+
void CheckFieldAccessArithmetic(FieldAccess access, Node* load_or_store) {
- Int32Matcher index = Int32Matcher(load_or_store->InputAt(1));
- CHECK(index.Is(access.offset - access.tag()));
+ IntPtrMatcher mindex(load_or_store->InputAt(1));
+ CHECK(mindex.Is(access.offset - access.tag()));
}
Node* CheckElementAccessArithmetic(ElementAccess access, Node* load_or_store) {
- Int32BinopMatcher index(load_or_store->InputAt(1));
- CHECK_EQ(IrOpcode::kInt32Add, index.node()->opcode());
- CHECK(index.right().Is(access.header_size - access.tag()));
+ Node* index = load_or_store->InputAt(1);
+ if (kPointerSize == 8) {
+ CHECK_EQ(IrOpcode::kChangeUint32ToUint64, index->opcode());
+ index = index->InputAt(0);
+ }
- int element_size = ElementSizeOf(access.machine_type);
+ Int32BinopMatcher mindex(index);
+ CHECK_EQ(IrOpcode::kInt32Add, mindex.node()->opcode());
+ CHECK(mindex.right().Is(access.header_size - access.tag()));
- if (element_size != 1) {
- Int32BinopMatcher mul(index.left().node());
- CHECK_EQ(IrOpcode::kInt32Mul, mul.node()->opcode());
- CHECK(mul.right().Is(element_size));
- return mul.left().node();
+ const int element_size_shift = ElementSizeLog2Of(access.machine_type);
+ if (element_size_shift) {
+ Int32BinopMatcher shl(mindex.left().node());
+ CHECK_EQ(IrOpcode::kWord32Shl, shl.node()->opcode());
+ CHECK(shl.right().Is(element_size_shift));
+ return shl.left().node();
} else {
- return index.left().node();
+ return mindex.left().node();
}
}
-static const MachineType machine_reps[] = {
- kRepBit, kMachInt8, kMachInt16, kMachInt32,
- kMachInt64, kMachFloat64, kMachAnyTagged};
+const MachineType kMachineReps[] = {kRepBit, kMachInt8, kMachInt16,
+ kMachInt32, kMachInt64, kMachFloat64,
+ kMachAnyTagged};
+
+} // namespace
TEST(LowerLoadField_to_load) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), machine_reps[i]};
+ Handle<Name>::null(), Type::Any(), kMachineReps[i]};
Node* load =
t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
- Node* use = t.Use(load, machine_reps[i]);
+ Node* use = t.Use(load, kMachineReps[i]);
t.Return(use);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1314,7 +1463,7 @@ TEST(LowerLoadField_to_load) {
CheckFieldAccessArithmetic(access, load);
MachineType rep = OpParameter<MachineType>(load);
- CHECK_EQ(machine_reps[i], rep);
+ CHECK_EQ(kMachineReps[i], rep);
}
}
@@ -1322,12 +1471,12 @@ TEST(LowerLoadField_to_load) {
TEST(LowerStoreField_to_store) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), machine_reps[i]};
+ Handle<Name>::null(), Type::Any(), kMachineReps[i]};
- Node* val = t.ExampleWithOutput(machine_reps[i]);
+ Node* val = t.ExampleWithOutput(kMachineReps[i]);
Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
val, t.start, t.start);
t.Effect(store);
@@ -1337,10 +1486,10 @@ TEST(LowerStoreField_to_store) {
CheckFieldAccessArithmetic(access, store);
StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
- if (machine_reps[i] & kRepTagged) {
+ if (kMachineReps[i] & kRepTagged) {
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
}
- CHECK_EQ(machine_reps[i], rep.machine_type());
+ CHECK_EQ(kMachineReps[i], rep.machine_type());
}
}
@@ -1348,14 +1497,13 @@ TEST(LowerStoreField_to_store) {
TEST(LowerLoadElement_to_load) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Type::Any(), machine_reps[i]};
+ Type::Any(), kMachineReps[i]};
- Node* load =
- t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, t.p1,
- t.jsgraph.Int32Constant(1024), t.start);
- Node* use = t.Use(load, machine_reps[i]);
+ Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
+ t.p1, t.start, t.start);
+ Node* use = t.Use(load, kMachineReps[i]);
t.Return(use);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1363,7 +1511,7 @@ TEST(LowerLoadElement_to_load) {
CheckElementAccessArithmetic(access, load);
MachineType rep = OpParameter<MachineType>(load);
- CHECK_EQ(machine_reps[i], rep);
+ CHECK_EQ(kMachineReps[i], rep);
}
}
@@ -1371,14 +1519,13 @@ TEST(LowerLoadElement_to_load) {
TEST(LowerStoreElement_to_store) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Type::Any(), machine_reps[i]};
+ Type::Any(), kMachineReps[i]};
- Node* val = t.ExampleWithOutput(machine_reps[i]);
+ Node* val = t.ExampleWithOutput(kMachineReps[i]);
Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
- t.p1, t.jsgraph.Int32Constant(1024), val,
- t.start, t.start);
+ t.p1, val, t.start, t.start);
t.Effect(store);
t.Lower();
CHECK_EQ(IrOpcode::kStore, store->opcode());
@@ -1386,10 +1533,10 @@ TEST(LowerStoreElement_to_store) {
CheckElementAccessArithmetic(access, store);
StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
- if (machine_reps[i] & kRepTagged) {
+ if (kMachineReps[i] & kRepTagged) {
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
}
- CHECK_EQ(machine_reps[i], rep.machine_type());
+ CHECK_EQ(kMachineReps[i], rep.machine_type());
}
}
@@ -1397,12 +1544,12 @@ TEST(LowerStoreElement_to_store) {
TEST(InsertChangeForLoadElementIndex) {
// LoadElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length) =>
// Load(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k))
- TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+ TestingGraph t(Type::Any(), Type::Signed32());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachAnyTagged};
Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.p2, t.start);
+ t.p1, t.start, t.start);
t.Return(load);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1416,12 +1563,12 @@ TEST(InsertChangeForLoadElementIndex) {
TEST(InsertChangeForStoreElementIndex) {
// StoreElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length, val) =>
// Store(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k), val)
- TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+ TestingGraph t(Type::Any(), Type::Signed32());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachAnyTagged};
Node* store =
- t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1, t.p2,
+ t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1,
t.jsgraph.TrueConstant(), t.start, t.start);
t.Effect(store);
t.Lower();
@@ -1440,7 +1587,7 @@ TEST(InsertChangeForLoadElement) {
kMachFloat64};
Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.p1, t.start);
+ t.p1, t.start, t.start);
t.Return(load);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1467,13 +1614,13 @@ TEST(InsertChangeForLoadField) {
TEST(InsertChangeForStoreElement) {
// TODO(titzer): test all load/store representation change insertions.
- TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+ TestingGraph t(Type::Any(), Type::Signed32());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachFloat64};
- Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
- t.jsgraph.Int32Constant(0), t.p2, t.p1,
- t.start, t.start);
+ Node* store =
+ t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
+ t.jsgraph.Int32Constant(0), t.p1, t.start, t.start);
t.Effect(store);
t.Lower();
@@ -1504,10 +1651,11 @@ TEST(UpdatePhi) {
TestingGraph t(Type::Any(), Type::Signed32());
static const MachineType kMachineTypes[] = {kMachInt32, kMachUint32,
kMachFloat64};
+ Type* kTypes[] = {Type::Signed32(), Type::Unsigned32(), Type::Number()};
for (size_t i = 0; i < arraysize(kMachineTypes); i++) {
FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), kMachineTypes[i]};
+ Handle<Name>::null(), kTypes[i], kMachineTypes[i]};
Node* load0 =
t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
@@ -1525,36 +1673,405 @@ TEST(UpdatePhi) {
}
-// TODO(titzer): this tests current behavior of assuming an implicit
-// representation change in loading float32s. Fix when float32 is fully
-// supported.
-TEST(ImplicitFloat32ToFloat64InLoads) {
- TestingGraph t(Type::Any());
+TEST(RunNumberDivide_minus_1_TruncatingToInt32) {
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(-1));
+ Node* trunc = t.NumberToInt32(div);
+ t.Return(trunc);
- FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), kMachFloat32};
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
- Node* load =
- t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
- t.Return(load);
- t.Lower();
- CHECK_EQ(IrOpcode::kLoad, load->opcode());
- CHECK_EQ(t.p0, load->InputAt(0));
- CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0));
+ FOR_INT32_INPUTS(i) {
+ int32_t x = 0 - *i;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
}
-TEST(ImplicitFloat64ToFloat32InStores) {
- TestingGraph t(Type::Any(), Type::Signed32());
- FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), kMachFloat32};
+TEST(NumberMultiply_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 0, 1, 100, 1000};
- Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
- t.p1, t.start, t.start);
- t.Effect(store);
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), mul);
+ t.Return(trunc);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Mul, mul->opcode());
+ }
+}
+
+
+TEST(RunNumberMultiply_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 0, 1, 100, 1000, 3000999};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ double k = static_cast<double>(constants[i]);
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* mul = t.NumberMultiply(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(mul);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ int32_t x = DoubleToInt32(static_cast<double>(*i) * k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(RunNumberMultiply_TruncatingToUint32) {
+ uint32_t constants[] = {0, 1, 2, 3, 4, 100, 1000, 1024, 2048, 3000999};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ double k = static_cast<double>(constants[i]);
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* mul = t.NumberMultiply(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToUint32(mul);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = DoubleToUint32(static_cast<double>(*i) * k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(RunNumberDivide_2_TruncatingToUint32) {
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(2));
+ Node* trunc = t.NumberToUint32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = DoubleToUint32(static_cast<double>(*i / 2.0));
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+}
+
+
+TEST(NumberMultiply_ConstantOutOfRange) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(1000000023);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), mul);
+ t.Return(trunc);
t.Lower();
- CHECK_EQ(IrOpcode::kStore, store->opcode());
- CHECK_EQ(t.p0, store->InputAt(0));
- CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2));
+ CHECK_EQ(IrOpcode::kFloat64Mul, mul->opcode());
+}
+
+
+TEST(NumberMultiply_NonTruncating) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(111);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ t.Return(mul);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kFloat64Mul, mul->opcode());
+}
+
+
+TEST(NumberDivide_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Div, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberDivide_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ if (*i == INT_MAX) continue; // exclude max int.
+ int32_t x = DoubleToInt32(static_cast<double>(*i) / k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberDivide_TruncatingToUint32) {
+ double constants[] = {1, 3, 100, 1000, 100998348};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachUint32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Div, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberDivide_TruncatingToUint32) {
+ uint32_t constants[] = {100, 10, 1, 1, 2, 4, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ uint32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(static_cast<double>(k)));
+ Node* trunc = t.NumberToUint32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = *i / k;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberDivide_BadConstants) {
+ {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(-1);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Sub, use->InputAt(0)->opcode());
+ }
+
+ {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(0);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Constant, use->InputAt(0)->opcode());
+ CHECK_EQ(0, OpParameter<int32_t>(use->InputAt(0)));
+ }
+
+ {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(0);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachUint32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Constant, use->InputAt(0)->opcode());
+ CHECK_EQ(0, OpParameter<int32_t>(use->InputAt(0)));
+ }
+}
+
+
+TEST(NumberModulus_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* use = t.Use(mod, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Mod, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberModulus_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* mod = t.NumberModulus(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(mod);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ if (*i == INT_MAX) continue; // exclude max int.
+ int32_t x = DoubleToInt32(std::fmod(static_cast<double>(*i), k));
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberModulus_TruncatingToUint32) {
+ double constants[] = {1, 3, 100, 1000, 100998348};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), mod);
+ Node* ret = t.Return(trunc);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Mod, ret->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberModulus_TruncatingToUint32) {
+ uint32_t constants[] = {1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ uint32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* mod =
+ t.NumberModulus(num, t.jsgraph.Constant(static_cast<double>(k)));
+ Node* trunc = t.NumberToUint32(mod);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = *i % k;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberModulus_Int32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ t.Return(mod);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kFloat64Mod, mod->opcode()); // Pesky -0 behavior.
+ }
+}
+
+
+TEST(NumberModulus_Uint32) {
+ const double kConstants[] = {2, 100, 1000, 1024, 2048};
+ const MachineType kTypes[] = {kMachInt32, kMachUint32};
+
+ for (auto const type : kTypes) {
+ for (auto const c : kConstants) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(c);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* use = t.Use(mod, type);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Mod, use->InputAt(0)->opcode());
+ }
+ }
+}
+
+
+TEST(PhiRepresentation) {
+ HandleAndZoneScope scope;
+ Zone* z = scope.main_zone();
+
+ struct TestData {
+ Type* arg1;
+ Type* arg2;
+ MachineType use;
+ MachineTypeUnion expected;
+ };
+
+ TestData test_data[] = {
+ {Type::Signed32(), Type::Unsigned32(), kMachInt32,
+ kRepWord32 | kTypeNumber},
+ {Type::Signed32(), Type::Unsigned32(), kMachUint32,
+ kRepWord32 | kTypeNumber},
+ {Type::Signed32(), Type::Signed32(), kMachInt32, kMachInt32},
+ {Type::Unsigned32(), Type::Unsigned32(), kMachInt32, kMachUint32},
+ {Type::Number(), Type::Signed32(), kMachInt32, kMachFloat64},
+ {Type::Signed32(), Type::String(), kMachInt32, kMachAnyTagged}};
+
+ for (auto const d : test_data) {
+ TestingGraph t(d.arg1, d.arg2, Type::Boolean());
+
+ Node* br = t.graph()->NewNode(t.common()->Branch(), t.p2, t.start);
+ Node* tb = t.graph()->NewNode(t.common()->IfTrue(), br);
+ Node* fb = t.graph()->NewNode(t.common()->IfFalse(), br);
+ Node* m = t.graph()->NewNode(t.common()->Merge(2), tb, fb);
+
+ Node* phi =
+ t.graph()->NewNode(t.common()->Phi(kMachAnyTagged, 2), t.p0, t.p1, m);
+
+ Bounds phi_bounds = Bounds::Either(Bounds(d.arg1), Bounds(d.arg2), z);
+ NodeProperties::SetBounds(phi, phi_bounds);
+
+ Node* use = t.Use(phi, d.use);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(d.expected, OpParameter<MachineType>(phi));
+ }
}
diff --git a/test/cctest/compiler/test-typer.cc b/test/cctest/compiler/test-typer.cc
new file mode 100644
index 00000000..5f7f55af
--- /dev/null
+++ b/test/cctest/compiler/test-typer.cc
@@ -0,0 +1,380 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <functional>
+
+#include "src/codegen.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/typer.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+#include "test/cctest/types-fuzz.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+
+// TODO(titzer): generate a large set of deterministic inputs for these tests.
+class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
+ public:
+ TyperTester()
+ : GraphAndBuilders(main_zone()),
+ types_(main_zone(), isolate()),
+ typer_(graph(), MaybeHandle<Context>()),
+ javascript_(main_zone()) {
+ Node* s = graph()->NewNode(common()->Start(3));
+ graph()->SetStart(s);
+ context_node_ = graph()->NewNode(common()->Parameter(2), graph()->start());
+ rng_ = isolate()->random_number_generator();
+
+ integers.push_back(0);
+ integers.push_back(0);
+ integers.push_back(-1);
+ integers.push_back(+1);
+ integers.push_back(-V8_INFINITY);
+ integers.push_back(+V8_INFINITY);
+ for (int i = 0; i < 5; ++i) {
+ double x = rng_->NextInt();
+ integers.push_back(x);
+ x *= rng_->NextInt();
+ if (!IsMinusZero(x)) integers.push_back(x);
+ }
+
+ int32s.push_back(0);
+ int32s.push_back(0);
+ int32s.push_back(-1);
+ int32s.push_back(+1);
+ int32s.push_back(kMinInt);
+ int32s.push_back(kMaxInt);
+ for (int i = 0; i < 10; ++i) {
+ int32s.push_back(rng_->NextInt());
+ }
+ }
+
+ Types<Type, Type*, Zone> types_;
+ Typer typer_;
+ JSOperatorBuilder javascript_;
+ Node* context_node_;
+ v8::base::RandomNumberGenerator* rng_;
+ std::vector<double> integers;
+ std::vector<double> int32s;
+
+ Isolate* isolate() { return main_isolate(); }
+ Graph* graph() { return main_graph_; }
+ CommonOperatorBuilder* common() { return &main_common_; }
+
+ Node* Parameter(int index = 0) {
+ return graph()->NewNode(common()->Parameter(index), graph()->start());
+ }
+
+ Type* TypeBinaryOp(const Operator* op, Type* lhs, Type* rhs) {
+ Node* p0 = Parameter(0);
+ Node* p1 = Parameter(1);
+ NodeProperties::SetBounds(p0, Bounds(lhs));
+ NodeProperties::SetBounds(p1, Bounds(rhs));
+ Node* n = graph()->NewNode(
+ op, p0, p1, context_node_, graph()->start(), graph()->start());
+ return NodeProperties::GetBounds(n).upper;
+ }
+
+ Type* RandomRange(bool int32 = false) {
+ std::vector<double>& numbers = int32 ? int32s : integers;
+ double i = numbers[rng_->NextInt(static_cast<int>(numbers.size()))];
+ double j = numbers[rng_->NextInt(static_cast<int>(numbers.size()))];
+ return NewRange(i, j);
+ }
+
+ Type* NewRange(double i, double j) {
+ Factory* f = isolate()->factory();
+ i::Handle<i::Object> min = f->NewNumber(i);
+ i::Handle<i::Object> max = f->NewNumber(j);
+ if (min->Number() > max->Number()) std::swap(min, max);
+ return Type::Range(min, max, main_zone());
+ }
+
+ double RandomInt(double min, double max) {
+ switch (rng_->NextInt(4)) {
+ case 0: return min;
+ case 1: return max;
+ default: break;
+ }
+ if (min == +V8_INFINITY) return +V8_INFINITY;
+ if (max == -V8_INFINITY) return -V8_INFINITY;
+ if (min == -V8_INFINITY && max == +V8_INFINITY) {
+ return rng_->NextInt() * static_cast<double>(rng_->NextInt());
+ }
+ double result = nearbyint(min + (max - min) * rng_->NextDouble());
+ if (IsMinusZero(result)) return 0;
+ if (std::isnan(result)) return rng_->NextInt(2) ? min : max;
+ DCHECK(min <= result && result <= max);
+ return result;
+ }
+
+ double RandomInt(Type::RangeType* range) {
+ return RandomInt(range->Min()->Number(), range->Max()->Number());
+ }
+
+ // Careful, this function runs O(max_width^5) trials.
+ template <class BinaryFunction>
+ void TestBinaryArithOpCloseToZero(const Operator* op, BinaryFunction opfun,
+ int max_width) {
+ const int min_min = -2 - max_width / 2;
+ const int max_min = 2 + max_width / 2;
+ for (int width = 0; width < max_width; width++) {
+ for (int lmin = min_min; lmin <= max_min; lmin++) {
+ for (int rmin = min_min; rmin <= max_min; rmin++) {
+ Type* r1 = NewRange(lmin, lmin + width);
+ Type* r2 = NewRange(rmin, rmin + width);
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+
+ for (int x1 = lmin; x1 < lmin + width; x1++) {
+ for (int x2 = rmin; x2 < rmin + width; x2++) {
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryArithOp(const Operator* op, BinaryFunction opfun) {
+ TestBinaryArithOpCloseToZero(op, opfun, 8);
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange()->AsRange();
+ Type::RangeType* r2 = RandomRange()->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ for (int i = 0; i < 10; i++) {
+ double x1 = RandomInt(r1);
+ double x2 = RandomInt(r2);
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryCompareOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange()->AsRange();
+ Type::RangeType* r2 = RandomRange()->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ for (int i = 0; i < 10; i++) {
+ double x1 = RandomInt(r1);
+ double x2 = RandomInt(r2);
+ bool result_value = opfun(x1, x2);
+ Type* result_type =
+ Type::Constant(result_value ? isolate()->factory()->true_value()
+ : isolate()->factory()->false_value(),
+ main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryBitOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange(true)->AsRange();
+ Type::RangeType* r2 = RandomRange(true)->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ for (int i = 0; i < 10; i++) {
+ int32_t x1 = static_cast<int32_t>(RandomInt(r1));
+ int32_t x2 = static_cast<int32_t>(RandomInt(r2));
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+
+ Type* RandomSubtype(Type* type) {
+ Type* subtype;
+ do {
+ subtype = types_.Fuzz();
+ } while (!subtype->Is(type));
+ return subtype;
+ }
+
+ void TestBinaryMonotonicity(const Operator* op) {
+ for (int i = 0; i < 50; ++i) {
+ Type* type1 = types_.Fuzz();
+ Type* type2 = types_.Fuzz();
+ Type* type = TypeBinaryOp(op, type1, type2);
+ Type* subtype1 = RandomSubtype(type1);;
+ Type* subtype2 = RandomSubtype(type2);;
+ Type* subtype = TypeBinaryOp(op, subtype1, subtype2);
+ CHECK(subtype->Is(type));
+ }
+ }
+};
+
+
+static int32_t shift_left(int32_t x, int32_t y) { return x << y; }
+static int32_t shift_right(int32_t x, int32_t y) { return x >> y; }
+static int32_t bit_or(int32_t x, int32_t y) { return x | y; }
+static int32_t bit_and(int32_t x, int32_t y) { return x & y; }
+static int32_t bit_xor(int32_t x, int32_t y) { return x ^ y; }
+
+
+//------------------------------------------------------------------------------
+// Soundness
+// For simplicity, we currently only test soundness on expression operators
+// that have a direct equivalent in C++. Also, testing is currently limited
+// to ranges as input types.
+
+
+TEST(TypeJSAdd) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Add(), std::plus<double>());
+}
+
+
+TEST(TypeJSSubtract) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Subtract(), std::minus<double>());
+}
+
+
+TEST(TypeJSMultiply) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Multiply(), std::multiplies<double>());
+}
+
+
+TEST(TypeJSDivide) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Divide(), std::divides<double>());
+}
+
+
+TEST(TypeJSModulus) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Modulus(), modulo);
+}
+
+
+TEST(TypeJSBitwiseOr) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseOr(), bit_or);
+}
+
+
+TEST(TypeJSBitwiseAnd) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseAnd(), bit_and);
+}
+
+
+TEST(TypeJSBitwiseXor) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseXor(), bit_xor);
+}
+
+
+TEST(TypeJSShiftLeft) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.ShiftLeft(), shift_left);
+}
+
+
+TEST(TypeJSShiftRight) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.ShiftRight(), shift_right);
+}
+
+
+TEST(TypeJSLessThan) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.LessThan(), std::less<double>());
+}
+
+
+TEST(TypeJSLessThanOrEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.LessThanOrEqual(), std::less_equal<double>());
+}
+
+
+TEST(TypeJSGreaterThan) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.GreaterThan(), std::greater<double>());
+}
+
+
+TEST(TypeJSGreaterThanOrEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.GreaterThanOrEqual(), std::greater_equal<double>());
+}
+
+
+TEST(TypeJSEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.Equal(), std::equal_to<double>());
+}
+
+
+TEST(TypeJSNotEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.NotEqual(), std::not_equal_to<double>());
+}
+
+
+// For numbers there's no difference between strict and non-strict equality.
+TEST(TypeJSStrictEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.StrictEqual(), std::equal_to<double>());
+}
+
+
+TEST(TypeJSStrictNotEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.StrictNotEqual(), std::not_equal_to<double>());
+}
+
+
+//------------------------------------------------------------------------------
+// Monotonicity
+
+
+// List should be in sync with JS_SIMPLE_BINOP_LIST.
+#define JSBINOP_LIST(V) \
+ V(Equal) \
+ V(NotEqual) \
+ V(StrictEqual) \
+ V(StrictNotEqual) \
+ V(LessThan) \
+ V(GreaterThan) \
+ V(LessThanOrEqual) \
+ V(GreaterThanOrEqual) \
+ V(BitwiseOr) \
+ V(BitwiseXor) \
+ V(BitwiseAnd) \
+ V(ShiftLeft) \
+ V(ShiftRight) \
+ V(ShiftRightLogical) \
+ V(Add) \
+ V(Subtract) \
+ V(Multiply) \
+ V(Divide) \
+ V(Modulus)
+
+
+#define TEST_FUNC(name) \
+ TEST(Monotonicity_##name) { \
+ TyperTester t; \
+ t.TestBinaryMonotonicity(t.javascript_.name()); \
+ }
+JSBINOP_LIST(TEST_FUNC)
+#undef TEST_FUNC
diff --git a/test/cctest/compiler/value-helper.h b/test/cctest/compiler/value-helper.h
index b5da982e..218a773f 100644
--- a/test/cctest/compiler/value-helper.h
+++ b/test/cctest/compiler/value-helper.h
@@ -60,6 +60,45 @@ class ValueHelper {
CheckHeapConstant(isolate_->heap()->false_value(), node);
}
+ static std::vector<float> float32_vector() {
+ static const float kValues[] = {
+ -std::numeric_limits<float>::infinity(), -2.70497e+38f, -1.4698e+37f,
+ -1.22813e+35f, -1.20555e+35f, -1.34584e+34f,
+ -1.0079e+32f, -6.49364e+26f, -3.06077e+25f,
+ -1.46821e+25f, -1.17658e+23f, -1.9617e+22f,
+ -2.7357e+20f, -1.48708e+13f, -1.89633e+12f,
+ -4.66622e+11f, -2.22581e+11f, -1.45381e+10f,
+ -1.3956e+09f, -1.32951e+09f, -1.30721e+09f,
+ -1.19756e+09f, -9.26822e+08f, -6.35647e+08f,
+ -4.00037e+08f, -1.81227e+08f, -5.09256e+07f,
+ -964300.0f, -192446.0f, -28455.0f,
+ -27194.0f, -26401.0f, -20575.0f,
+ -17069.0f, -9167.0f, -960.178f,
+ -113.0f, -62.0f, -15.0f,
+ -7.0f, -0.0256635f, -4.60374e-07f,
+ -3.63759e-10f, -4.30175e-14f, -5.27385e-15f,
+ -1.48084e-15f, -1.05755e-19f, -3.2995e-21f,
+ -1.67354e-23f, -1.11885e-23f, -1.78506e-30f,
+ -5.07594e-31f, -3.65799e-31f, -1.43718e-34f,
+ -1.27126e-38f, -0.0f, 0.0f,
+ 1.17549e-38f, 1.56657e-37f, 4.08512e-29f,
+ 3.31357e-28f, 6.25073e-22f, 4.1723e-13f,
+ 1.44343e-09f, 5.27004e-08f, 9.48298e-08f,
+ 5.57888e-07f, 4.89988e-05f, 0.244326f,
+ 12.4895f, 19.0f, 47.0f,
+ 106.0f, 538.324f, 564.536f,
+ 819.124f, 7048.0f, 12611.0f,
+ 19878.0f, 20309.0f, 797056.0f,
+ 1.77219e+09f, 1.51116e+11f, 4.18193e+13f,
+ 3.59167e+16f, 3.38211e+19f, 2.67488e+20f,
+ 1.78831e+21f, 9.20914e+21f, 8.35654e+23f,
+ 1.4495e+24f, 5.94015e+25f, 4.43608e+30f,
+ 2.44502e+33f, 2.61152e+33f, 1.38178e+37f,
+ 1.71306e+37f, 3.31899e+38f, 3.40282e+38f,
+ std::numeric_limits<float>::infinity()};
+ return std::vector<float>(&kValues[0], &kValues[arraysize(kValues)]);
+ }
+
static std::vector<double> float64_vector() {
static const double nan = v8::base::OS::nan_value();
static const double values[] = {
@@ -82,6 +121,8 @@ class ValueHelper {
static const std::vector<uint32_t> uint32_vector() {
static const uint32_t kValues[] = {
0x00000000, 0x00000001, 0xffffffff, 0x1b09788b, 0x04c5fce8, 0xcc0de5bf,
+ // This row is useful for testing lea optimizations on intel.
+ 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000008, 0x00000009,
0x273a798e, 0x187937a3, 0xece3af83, 0x5495a16b, 0x0b668ecc, 0x11223344,
0x0000009e, 0x00000043, 0x0000af73, 0x0000116b, 0x00658ecc, 0x002b3b4c,
0x88776655, 0x70000000, 0x07200000, 0x7fffffff, 0x56123761, 0x7fffff00,
@@ -117,6 +158,7 @@ class ValueHelper {
#define FOR_INT32_INPUTS(var) FOR_INPUTS(int32_t, int32, var)
#define FOR_UINT32_INPUTS(var) FOR_INPUTS(uint32_t, uint32, var)
+#define FOR_FLOAT32_INPUTS(var) FOR_INPUTS(float, float32, var)
#define FOR_FLOAT64_INPUTS(var) FOR_INPUTS(double, float64, var)
#define FOR_INT32_SHIFTS(var) for (int32_t var = 0; var < 32; var++)
diff --git a/test/cctest/test-accessors.cc b/test/cctest/test-accessors.cc
index 5bf61c8f..5f452ead 100644
--- a/test/cctest/test-accessors.cc
+++ b/test/cctest/test-accessors.cc
@@ -38,6 +38,7 @@ using ::v8::ObjectTemplate;
using ::v8::Value;
using ::v8::Context;
using ::v8::Local;
+using ::v8::Name;
using ::v8::String;
using ::v8::Script;
using ::v8::Function;
@@ -513,7 +514,7 @@ void JSONStringifyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) {
}
-void JSONStringifyGetter(Local<String> name,
+void JSONStringifyGetter(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_str("crbug-161028"));
}
@@ -525,8 +526,8 @@ THREADED_TEST(JSONStringifyNamedInterceptorObject) {
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
- obj->SetNamedPropertyHandler(
- JSONStringifyGetter, NULL, NULL, NULL, JSONStringifyEnumerator);
+ obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ JSONStringifyGetter, NULL, NULL, NULL, JSONStringifyEnumerator));
env->Global()->Set(v8_str("obj"), obj->NewInstance());
v8::Handle<v8::String> expected = v8_str("{\"regress\":\"crbug-161028\"}");
CHECK(CompileRun("JSON.stringify(obj)")->Equals(expected));
@@ -577,3 +578,30 @@ THREADED_TEST(GlobalObjectAccessor) {
CHECK(v8::Utils::OpenHandle(*CompileRun("getter()"))->IsJSGlobalProxy());
CHECK(v8::Utils::OpenHandle(*CompileRun("set_value"))->IsJSGlobalProxy());
}
+
+
+static void EmptyGetter(Local<Name> name,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ ApiTestFuzzer::Fuzz();
+}
+
+
+static void OneProperty(Local<String> name,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ ApiTestFuzzer::Fuzz();
+ info.GetReturnValue().Set(v8_num(1));
+}
+
+
+THREADED_TEST(Regress433458) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
+ obj->SetHandler(v8::NamedPropertyHandlerConfiguration(EmptyGetter));
+ obj->SetNativeDataProperty(v8_str("prop"), OneProperty);
+ env->Global()->Set(v8_str("obj"), obj->NewInstance());
+ CompileRun(
+ "Object.defineProperty(obj, 'prop', { writable: false });"
+ "Object.defineProperty(obj, 'prop', { writable: true });");
+}
diff --git a/test/cctest/test-alloc.cc b/test/cctest/test-alloc.cc
index d647a312..2e071acc 100644
--- a/test/cctest/test-alloc.cc
+++ b/test/cctest/test-alloc.cc
@@ -86,7 +86,7 @@ static AllocationResult AllocateAfterFailures() {
Builtins::kIllegal)).ToObjectChecked();
// Return success.
- return Smi::FromInt(42);
+ return heap->true_value();
}
@@ -100,7 +100,7 @@ TEST(StressHandles) {
v8::Handle<v8::Context> env = v8::Context::New(CcTest::isolate());
env->Enter();
Handle<Object> o = Test();
- CHECK(o->IsSmi() && Smi::cast(*o)->value() == 42);
+ CHECK(o->IsTrue());
env->Exit();
}
@@ -162,7 +162,7 @@ TEST(StressJS) {
// Call the accessor through JavaScript.
v8::Handle<v8::Value> result = v8::Script::Compile(
v8::String::NewFromUtf8(CcTest::isolate(), "(new Foo).get"))->Run();
- CHECK_EQ(42, result->Int32Value());
+ CHECK_EQ(true, result->BooleanValue());
env->Exit();
}
@@ -198,7 +198,8 @@ TEST(CodeRange) {
const size_t code_range_size = 32*MB;
CcTest::InitializeVM();
CodeRange code_range(reinterpret_cast<Isolate*>(CcTest::isolate()));
- code_range.SetUp(code_range_size);
+ code_range.SetUp(code_range_size +
+ kReservedCodeRangePages * v8::base::OS::CommitPageSize());
size_t current_allocated = 0;
size_t total_allocated = 0;
List< ::Block> blocks(1000);
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 0e803841..06cf5532 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -185,7 +185,8 @@ THREADED_TEST(IsolateOfContext) {
}
-static void TestSignature(const char* loop_js, Local<Value> receiver) {
+static void TestSignature(const char* loop_js, Local<Value> receiver,
+ v8::Isolate* isolate) {
i::ScopedVector<char> source(200);
i::SNPrintF(source,
"for (var i = 0; i < 10; i++) {"
@@ -202,7 +203,7 @@ static void TestSignature(const char* loop_js, Local<Value> receiver) {
CHECK_EQ(10, signature_callback_count);
} else {
CHECK_EQ(v8_str("TypeError: Illegal invocation"),
- try_catch.Exception()->ToString());
+ try_catch.Exception()->ToString(isolate));
}
}
@@ -267,17 +268,17 @@ THREADED_TEST(ReceiverSignature) {
i::SNPrintF(
source, "var test_object = %s; test_object", test_objects[i]);
Local<Value> test_object = CompileRun(source.start());
- TestSignature("test_object.prop();", test_object);
- TestSignature("test_object.accessor;", test_object);
- TestSignature("test_object[accessor_key];", test_object);
- TestSignature("test_object.accessor = 1;", test_object);
- TestSignature("test_object[accessor_key] = 1;", test_object);
+ TestSignature("test_object.prop();", test_object, isolate);
+ TestSignature("test_object.accessor;", test_object, isolate);
+ TestSignature("test_object[accessor_key];", test_object, isolate);
+ TestSignature("test_object.accessor = 1;", test_object, isolate);
+ TestSignature("test_object[accessor_key] = 1;", test_object, isolate);
if (i >= bad_signature_start_offset) test_object = Local<Value>();
- TestSignature("test_object.prop_sig();", test_object);
- TestSignature("test_object.accessor_sig;", test_object);
- TestSignature("test_object[accessor_sig_key];", test_object);
- TestSignature("test_object.accessor_sig = 1;", test_object);
- TestSignature("test_object[accessor_sig_key] = 1;", test_object);
+ TestSignature("test_object.prop_sig();", test_object, isolate);
+ TestSignature("test_object.accessor_sig;", test_object, isolate);
+ TestSignature("test_object[accessor_sig_key];", test_object, isolate);
+ TestSignature("test_object.accessor_sig = 1;", test_object, isolate);
+ TestSignature("test_object[accessor_sig_key] = 1;", test_object, isolate);
}
}
@@ -356,7 +357,7 @@ THREADED_TEST(HulIgennem) {
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::Primitive> undef = v8::Undefined(isolate);
- Local<String> undef_str = undef->ToString();
+ Local<String> undef_str = undef->ToString(isolate);
char* value = i::NewArray<char>(undef_str->Utf8Length() + 1);
undef_str->WriteUtf8(value);
CHECK_EQ(0, strcmp(value, "undefined"));
@@ -742,6 +743,58 @@ THREADED_TEST(UsingExternalOneByteString) {
}
+class RandomLengthResource : public v8::String::ExternalStringResource {
+ public:
+ explicit RandomLengthResource(int length) : length_(length) {}
+ virtual const uint16_t* data() const { return string_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ uint16_t string_[10];
+ int length_;
+};
+
+
+class RandomLengthOneByteResource
+ : public v8::String::ExternalOneByteStringResource {
+ public:
+ explicit RandomLengthOneByteResource(int length) : length_(length) {}
+ virtual const char* data() const { return string_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ char string_[10];
+ int length_;
+};
+
+
+THREADED_TEST(NewExternalForVeryLongString) {
+ {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::TryCatch try_catch;
+ RandomLengthOneByteResource r(1 << 30);
+ v8::Local<v8::String> str = v8::String::NewExternal(CcTest::isolate(), &r);
+ CHECK(str.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ String::Utf8Value exception_value(try_catch.Exception());
+ CHECK_EQ("RangeError: Invalid string length", *exception_value);
+ }
+
+ {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::TryCatch try_catch;
+ RandomLengthResource r(1 << 30);
+ v8::Local<v8::String> str = v8::String::NewExternal(CcTest::isolate(), &r);
+ CHECK(str.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ String::Utf8Value exception_value(try_catch.Exception());
+ CHECK_EQ("RangeError: Invalid string length", *exception_value);
+ }
+}
+
+
THREADED_TEST(ScavengeExternalString) {
i::FLAG_stress_compaction = false;
i::FLAG_gc_global = false;
@@ -934,7 +987,7 @@ static void CheckReturnValue(const T& t, i::Address callback) {
// If CPU profiler is active check that when API callback is invoked
// VMState is set to EXTERNAL.
if (isolate->cpu_profiler()->is_profiling()) {
- CHECK_EQ(i::EXTERNAL, isolate->current_vm_state());
+ CHECK_EQ(v8::EXTERNAL, isolate->current_vm_state());
CHECK(isolate->external_callback_scope());
CHECK_EQ(callback, isolate->external_callback_scope()->callback());
}
@@ -1181,7 +1234,8 @@ Handle<Value> TestFastReturnValues() {
THREADED_PROFILED_TEST(FastReturnValues) {
LocalContext env;
- v8::HandleScope scope(CcTest::isolate());
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
v8::Handle<v8::Value> value;
// check int32_t and uint32_t
int32_t int_values[] = {
@@ -1206,13 +1260,13 @@ THREADED_PROFILED_TEST(FastReturnValues) {
// check double
value = TestFastReturnValues<double>();
CHECK(value->IsNumber());
- CHECK_EQ(kFastReturnValueDouble, value->ToNumber()->Value());
+ CHECK_EQ(kFastReturnValueDouble, value->ToNumber(isolate)->Value());
// check bool values
for (int i = 0; i < 2; i++) {
fast_return_value_bool = i == 0;
value = TestFastReturnValues<bool>();
CHECK(value->IsBoolean());
- CHECK_EQ(fast_return_value_bool, value->ToBoolean()->Value());
+ CHECK_EQ(fast_return_value_bool, value->ToBoolean(isolate)->Value());
}
// check oddballs
ReturnValueOddball oddballs[] = {
@@ -1542,6 +1596,34 @@ THREADED_TEST(IsNativeError) {
}
+THREADED_TEST(IsGeneratorFunctionOrObject) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ CompileRun("function *gen() { yield 1; }\nfunction func() {}");
+ v8::Handle<Value> gen = CompileRun("gen");
+ v8::Handle<Value> genObj = CompileRun("gen()");
+ v8::Handle<Value> object = CompileRun("{a:42}");
+ v8::Handle<Value> func = CompileRun("func");
+
+ CHECK(gen->IsGeneratorFunction());
+ CHECK(gen->IsFunction());
+ CHECK(!gen->IsGeneratorObject());
+
+ CHECK(!genObj->IsGeneratorFunction());
+ CHECK(!genObj->IsFunction());
+ CHECK(genObj->IsGeneratorObject());
+
+ CHECK(!object->IsGeneratorFunction());
+ CHECK(!object->IsFunction());
+ CHECK(!object->IsGeneratorObject());
+
+ CHECK(!func->IsGeneratorFunction());
+ CHECK(func->IsFunction());
+ CHECK(!func->IsGeneratorObject());
+}
+
+
THREADED_TEST(ArgumentsObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
@@ -1904,7 +1986,7 @@ THREADED_TEST(DescriptorInheritance) {
int echo_named_call_count;
-static void EchoNamedProperty(Local<String> name,
+static void EchoNamedProperty(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK_EQ(v8_str("data"), info.Data());
@@ -1946,18 +2028,41 @@ void SymbolAccessorSetter(Local<Name> name, Local<Value> value,
SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info);
}
-void EmptyInterceptorGetter(Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+void SymbolAccessorGetterReturnsDefault(
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+ CHECK(name->IsSymbol());
+ Local<Symbol> sym = Local<Symbol>::Cast(name);
+ if (sym->Name()->IsUndefined()) return;
+ info.GetReturnValue().Set(info.Data());
}
-void EmptyInterceptorSetter(Local<String> name,
- Local<Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+static void ThrowingSymbolAccessorGetter(
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+ info.GetReturnValue().Set(info.GetIsolate()->ThrowException(name));
}
-void InterceptorGetter(Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- // Intercept names that start with 'interceptor_'.
+
+void EmptyInterceptorGetter(Local<Name> name,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {}
+
+
+void EmptyInterceptorSetter(Local<Name> name, Local<Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {}
+
+
+void EmptyGenericInterceptorGetter(
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {}
+
+
+void EmptyGenericInterceptorSetter(
+ Local<Name> name, Local<Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {}
+
+
+void StringInterceptorGetter(
+ Local<String> name,
+ const v8::PropertyCallbackInfo<v8::Value>&
+ info) { // Intercept names that start with 'interceptor_'.
String::Utf8Value utf8(name);
char* name_str = *utf8;
char prefix[] = "interceptor_";
@@ -1969,9 +2074,9 @@ void InterceptorGetter(Local<String> name,
info.GetReturnValue().Set(self->GetHiddenValue(v8_str(name_str + i)));
}
-void InterceptorSetter(Local<String> name,
- Local<Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+
+void StringInterceptorSetter(Local<String> name, Local<Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
// Intercept accesses that set certain integer values, for which the name does
// not start with 'accessor_'.
String::Utf8Value utf8(name);
@@ -1990,6 +2095,57 @@ void InterceptorSetter(Local<String> name,
}
}
+void InterceptorGetter(Local<Name> generic_name,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ if (generic_name->IsSymbol()) return;
+ StringInterceptorGetter(Local<String>::Cast(generic_name), info);
+}
+
+void InterceptorSetter(Local<Name> generic_name, Local<Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ if (generic_name->IsSymbol()) return;
+ StringInterceptorSetter(Local<String>::Cast(generic_name), value, info);
+}
+
+void GenericInterceptorGetter(Local<Name> generic_name,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<String> str;
+ if (generic_name->IsSymbol()) {
+ Local<Value> name = Local<Symbol>::Cast(generic_name)->Name();
+ if (name->IsUndefined()) return;
+ str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name));
+ } else {
+ Local<String> name = Local<String>::Cast(generic_name);
+ String::Utf8Value utf8(name);
+ char* name_str = *utf8;
+ if (*name_str == '_') return;
+ str = String::Concat(v8_str("_str_"), name);
+ }
+
+ Handle<Object> self = Handle<Object>::Cast(info.This());
+ info.GetReturnValue().Set(self->Get(str));
+}
+
+void GenericInterceptorSetter(Local<Name> generic_name, Local<Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<String> str;
+ if (generic_name->IsSymbol()) {
+ Local<Value> name = Local<Symbol>::Cast(generic_name)->Name();
+ if (name->IsUndefined()) return;
+ str = String::Concat(v8_str("_sym_"), Local<String>::Cast(name));
+ } else {
+ Local<String> name = Local<String>::Cast(generic_name);
+ String::Utf8Value utf8(name);
+ char* name_str = *utf8;
+ if (*name_str == '_') return;
+ str = String::Concat(v8_str("_str_"), name);
+ }
+
+ Handle<Object> self = Handle<Object>::Cast(info.This());
+ self->Set(str, value);
+ info.GetReturnValue().Set(value);
+}
+
void AddAccessor(Handle<FunctionTemplate> templ,
Handle<String> name,
v8::AccessorGetterCallback getter,
@@ -2011,6 +2167,13 @@ void AddAccessor(Handle<FunctionTemplate> templ,
templ->PrototypeTemplate()->SetAccessor(name, getter, setter);
}
+void AddInterceptor(Handle<FunctionTemplate> templ,
+ v8::GenericNamedPropertyGetterCallback getter,
+ v8::GenericNamedPropertySetterCallback setter) {
+ templ->InstanceTemplate()->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(getter, setter));
+}
+
THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
v8::HandleScope scope(CcTest::isolate());
@@ -2030,6 +2193,62 @@ THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) {
}
+THREADED_TEST(LegacyInterceptorDoesNotSeeSymbols) {
+ LocalContext env;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
+ Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
+ v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
+
+ child->Inherit(parent);
+ AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
+ AddInterceptor(child, StringInterceptorGetter, StringInterceptorSetter);
+
+ env->Global()->Set(v8_str("Child"), child->GetFunction());
+ env->Global()->Set(v8_str("age"), age);
+ CompileRun(
+ "var child = new Child;"
+ "child[age] = 10;");
+ ExpectInt32("child[age]", 10);
+ ExpectBoolean("child.hasOwnProperty('age')", false);
+ ExpectBoolean("child.hasOwnProperty('accessor_age')", true);
+}
+
+
+THREADED_TEST(GenericInterceptorDoesSeeSymbols) {
+ LocalContext env;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate);
+ Handle<FunctionTemplate> child = FunctionTemplate::New(isolate);
+ v8::Local<v8::Symbol> age = v8::Symbol::New(isolate, v8_str("age"));
+ v8::Local<v8::Symbol> anon = v8::Symbol::New(isolate);
+
+ child->Inherit(parent);
+ AddAccessor(parent, age, SymbolAccessorGetter, SymbolAccessorSetter);
+ AddInterceptor(child, GenericInterceptorGetter, GenericInterceptorSetter);
+
+ env->Global()->Set(v8_str("Child"), child->GetFunction());
+ env->Global()->Set(v8_str("age"), age);
+ env->Global()->Set(v8_str("anon"), anon);
+ CompileRun(
+ "var child = new Child;"
+ "child[age] = 10;");
+ ExpectInt32("child[age]", 10);
+ ExpectInt32("child._sym_age", 10);
+
+ // Check that it also sees strings.
+ CompileRun("child.foo = 47");
+ ExpectInt32("child.foo", 47);
+ ExpectInt32("child._str_foo", 47);
+
+ // Check that the interceptor can punt (in this case, on anonymous symbols).
+ CompileRun("child[anon] = 31337");
+ ExpectInt32("child[anon]", 31337);
+}
+
+
THREADED_TEST(ExecutableAccessorIsPreservedOnAttributeChange) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
@@ -2275,9 +2494,8 @@ THREADED_TEST(NamedPropertyHandlerGetter) {
v8::HandleScope scope(CcTest::isolate());
v8::Handle<v8::FunctionTemplate> templ =
v8::FunctionTemplate::New(CcTest::isolate());
- templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty,
- 0, 0, 0, 0,
- v8_str("data"));
+ templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ EchoNamedProperty, 0, 0, 0, 0, v8_str("data")));
LocalContext env;
env->Global()->Set(v8_str("obj"),
templ->GetFunction()->NewInstance());
@@ -2312,9 +2530,8 @@ THREADED_TEST(IndexedPropertyHandlerGetter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
- templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty,
- 0, 0, 0, 0,
- v8_num(637));
+ templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ EchoIndexedProperty, 0, 0, 0, 0, v8_num(637)));
LocalContext env;
env->Global()->Set(v8_str("obj"),
templ->GetFunction()->NewInstance());
@@ -2334,8 +2551,7 @@ static void CheckThisIndexedPropertyHandler(
}
static void CheckThisNamedPropertyHandler(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
@@ -2352,8 +2568,7 @@ void CheckThisIndexedPropertySetter(
void CheckThisNamedPropertySetter(
- Local<String> property,
- Local<Value> value,
+ Local<Name> property, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter));
ApiTestFuzzer::Fuzz();
@@ -2370,8 +2585,7 @@ void CheckThisIndexedPropertyQuery(
void CheckThisNamedPropertyQuery(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Integer>& info) {
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
@@ -2388,8 +2602,7 @@ void CheckThisIndexedPropertyDeleter(
void CheckThisNamedPropertyDeleter(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter));
ApiTestFuzzer::Fuzz();
CHECK(info.This()->Equals(bottom));
@@ -2419,19 +2632,15 @@ THREADED_PROFILED_TEST(PropertyHandlerInPrototype) {
// Set up a prototype chain with three interceptors.
v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
- templ->InstanceTemplate()->SetIndexedPropertyHandler(
- CheckThisIndexedPropertyHandler,
- CheckThisIndexedPropertySetter,
- CheckThisIndexedPropertyQuery,
- CheckThisIndexedPropertyDeleter,
- CheckThisIndexedPropertyEnumerator);
-
- templ->InstanceTemplate()->SetNamedPropertyHandler(
- CheckThisNamedPropertyHandler,
- CheckThisNamedPropertySetter,
- CheckThisNamedPropertyQuery,
- CheckThisNamedPropertyDeleter,
- CheckThisNamedPropertyEnumerator);
+ templ->InstanceTemplate()->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ CheckThisIndexedPropertyHandler, CheckThisIndexedPropertySetter,
+ CheckThisIndexedPropertyQuery, CheckThisIndexedPropertyDeleter,
+ CheckThisIndexedPropertyEnumerator));
+
+ templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ CheckThisNamedPropertyHandler, CheckThisNamedPropertySetter,
+ CheckThisNamedPropertyQuery, CheckThisNamedPropertyDeleter,
+ CheckThisNamedPropertyEnumerator));
bottom = templ->GetFunction()->NewInstance();
Local<v8::Object> top = templ->GetFunction()->NewInstance();
@@ -2463,8 +2672,7 @@ THREADED_PROFILED_TEST(PropertyHandlerInPrototype) {
static void PrePropertyHandlerGet(
- Local<String> key,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("pre")->Equals(key)) {
info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre"));
@@ -2473,8 +2681,7 @@ static void PrePropertyHandlerGet(
static void PrePropertyHandlerQuery(
- Local<String> key,
- const v8::PropertyCallbackInfo<v8::Integer>& info) {
+ Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
if (v8_str("pre")->Equals(key)) {
info.GetReturnValue().Set(static_cast<int32_t>(v8::None));
}
@@ -2485,9 +2692,8 @@ THREADED_TEST(PrePropertyHandler) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
- desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet,
- 0,
- PrePropertyHandlerQuery);
+ desc->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ PrePropertyHandlerGet, 0, PrePropertyHandlerQuery));
LocalContext env(NULL, desc->InstanceTemplate());
CompileRun("var pre = 'Object: pre'; var on = 'Object: on';");
v8::Handle<Value> result_pre = CompileRun("pre");
@@ -2560,16 +2766,17 @@ THREADED_TEST(DeepCrossLanguageRecursion) {
static void ThrowingPropertyHandlerGet(
- Local<String> key,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
+ // Since this interceptor is used on "with" objects, the runtime will look up
+ // @@unscopables. Punt.
+ if (key->IsSymbol()) return;
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(info.GetIsolate()->ThrowException(key));
}
static void ThrowingPropertyHandlerSet(
- Local<String> key,
- Local<Value>,
+ Local<Name> key, Local<Value>,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetIsolate()->ThrowException(key);
info.GetReturnValue().SetUndefined(); // not the same as empty handle
@@ -2580,8 +2787,8 @@ THREADED_TEST(CallbackExceptionRegression) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
- obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet,
- ThrowingPropertyHandlerSet);
+ obj->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ ThrowingPropertyHandlerGet, ThrowingPropertyHandlerSet));
LocalContext env;
env->Global()->Set(v8_str("obj"), obj->NewInstance());
v8::Handle<Value> otto = CompileRun(
@@ -2749,6 +2956,16 @@ THREADED_TEST(EmbedderData) {
}
+THREADED_TEST(GetIsolate) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ Local<v8::Object> obj = v8::Object::New(isolate);
+ CHECK_EQ(isolate, obj->GetIsolate());
+ CHECK_EQ(isolate, CcTest::global()->GetIsolate());
+}
+
+
THREADED_TEST(IdentityHash) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
@@ -2813,6 +3030,53 @@ THREADED_TEST(GlobalProxyIdentityHash) {
}
+TEST(SymbolIdentityHash) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+
+ {
+ Local<v8::Symbol> symbol = v8::Symbol::New(isolate);
+ int hash = symbol->GetIdentityHash();
+ int hash1 = symbol->GetIdentityHash();
+ CHECK_EQ(hash, hash1);
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+ int hash3 = symbol->GetIdentityHash();
+ CHECK_EQ(hash, hash3);
+ }
+
+ {
+ v8::Handle<v8::Symbol> js_symbol =
+ CompileRun("Symbol('foo')").As<v8::Symbol>();
+ int hash = js_symbol->GetIdentityHash();
+ int hash1 = js_symbol->GetIdentityHash();
+ CHECK_EQ(hash, hash1);
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+ int hash3 = js_symbol->GetIdentityHash();
+ CHECK_EQ(hash, hash3);
+ }
+}
+
+
+TEST(StringIdentityHash) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+
+ Local<v8::String> str = v8::String::NewFromUtf8(isolate, "str1");
+ int hash = str->GetIdentityHash();
+ int hash1 = str->GetIdentityHash();
+ CHECK_EQ(hash, hash1);
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+ int hash3 = str->GetIdentityHash();
+ CHECK_EQ(hash, hash3);
+
+ Local<v8::String> str2 = v8::String::NewFromUtf8(isolate, "str1");
+ int hash4 = str2->GetIdentityHash();
+ CHECK_EQ(hash, hash4);
+}
+
+
THREADED_TEST(SymbolProperties) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
@@ -3195,6 +3459,24 @@ THREADED_TEST(ArrayBuffer_External) {
}
+THREADED_TEST(ArrayBuffer_DisableNeuter) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope handle_scope(isolate);
+
+ i::ScopedVector<uint8_t> my_data(100);
+ memset(my_data.start(), 0, 100);
+ Local<v8::ArrayBuffer> ab =
+ v8::ArrayBuffer::New(isolate, my_data.start(), 100);
+ CHECK(ab->IsNeuterable());
+
+ i::Handle<i::JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
+ buf->set_is_neuterable(false);
+
+ CHECK(!ab->IsNeuterable());
+}
+
+
static void CheckDataViewIsNeutered(v8::Handle<v8::DataView> dv) {
CHECK_EQ(0, static_cast<int>(dv->ByteLength()));
CHECK_EQ(0, static_cast<int>(dv->ByteOffset()));
@@ -3411,7 +3693,7 @@ THREADED_TEST(Regress97784) {
static bool interceptor_for_hidden_properties_called;
static void InterceptorForHiddenProperties(
- Local<String> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
interceptor_for_hidden_properties_called = true;
}
@@ -3428,7 +3710,8 @@ THREADED_TEST(HiddenPropertiesWithInterceptors) {
// Associate an interceptor with an object and start setting hidden values.
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
- instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties);
+ instance_templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorForHiddenProperties));
Local<v8::Function> function = fun_templ->GetFunction();
Local<v8::Object> obj = function->NewInstance();
CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2302)));
@@ -4144,6 +4427,45 @@ THREADED_TEST(ApiObjectGroupsCycle) {
}
+THREADED_TEST(WeakRootsSurviveTwoRoundsOfGC) {
+ LocalContext env;
+ v8::Isolate* iso = env->GetIsolate();
+ HandleScope scope(iso);
+
+ WeakCallCounter counter(1234);
+
+ WeakCallCounterAndPersistent<Value> weak_obj(&counter);
+
+ // Create a weak object that references a internalized string.
+ {
+ HandleScope scope(iso);
+ weak_obj.handle.Reset(iso, Object::New(iso));
+ weak_obj.handle.SetWeak(&weak_obj, &WeakPointerCallback);
+ CHECK(weak_obj.handle.IsWeak());
+ Local<Object>::New(iso, weak_obj.handle.As<Object>())->Set(
+ v8_str("x"),
+ String::NewFromUtf8(iso, "magic cookie", String::kInternalizedString));
+ }
+ // Do a single full GC
+ i::Isolate* i_iso = reinterpret_cast<v8::internal::Isolate*>(iso);
+ i::Heap* heap = i_iso->heap();
+ heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
+
+ // We should have received the weak callback.
+ CHECK_EQ(1, counter.NumberOfWeakCalls());
+
+ // Check that the string is still alive.
+ {
+ HandleScope scope(iso);
+ i::MaybeHandle<i::String> magic_string =
+ i::StringTable::LookupStringIfExists(
+ i_iso,
+ v8::Utils::OpenHandle(*String::NewFromUtf8(iso, "magic cookie")));
+ magic_string.Check();
+ }
+}
+
+
// TODO(mstarzinger): This should be a THREADED_TEST but causes failures
// on the buildbots, so was made non-threaded for the time being.
TEST(ApiObjectGroupsCycleForScavenger) {
@@ -4266,13 +4588,14 @@ THREADED_TEST(ScriptException) {
TEST(TryCatchCustomException) {
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
v8::TryCatch try_catch;
CompileRun("function CustomError() { this.a = 'b'; }"
"(function f() { throw new CustomError(); })();");
CHECK(try_catch.HasCaught());
- CHECK(try_catch.Exception()->ToObject()->
- Get(v8_str("a"))->Equals(v8_str("b")));
+ CHECK(try_catch.Exception()->ToObject(isolate)->Get(v8_str("a"))->Equals(
+ v8_str("b")));
}
@@ -4766,49 +5089,51 @@ static void CheckUncle(v8::TryCatch* try_catch) {
THREADED_TEST(ConversionNumber) {
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
// Very large number.
CompileRun("var obj = Math.pow(2,32) * 1237;");
Local<Value> obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(5312874545152.0, obj->ToNumber()->Value());
- CHECK_EQ(0, obj->ToInt32()->Value());
- CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned.
+ CHECK_EQ(5312874545152.0, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(0, obj->ToInt32(isolate)->Value());
+ CHECK(0u ==
+ obj->ToUint32(isolate)->Value()); // NOLINT - no CHECK_EQ for unsigned.
// Large number.
CompileRun("var obj = -1234567890123;");
obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value());
- CHECK_EQ(-1912276171, obj->ToInt32()->Value());
- CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT
+ CHECK_EQ(-1234567890123.0, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(-1912276171, obj->ToInt32(isolate)->Value());
+ CHECK(2382691125u == obj->ToUint32(isolate)->Value()); // NOLINT
// Small positive integer.
CompileRun("var obj = 42;");
obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(42.0, obj->ToNumber()->Value());
- CHECK_EQ(42, obj->ToInt32()->Value());
- CHECK(42u == obj->ToUint32()->Value()); // NOLINT
+ CHECK_EQ(42.0, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(42, obj->ToInt32(isolate)->Value());
+ CHECK(42u == obj->ToUint32(isolate)->Value()); // NOLINT
// Negative integer.
CompileRun("var obj = -37;");
obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(-37.0, obj->ToNumber()->Value());
- CHECK_EQ(-37, obj->ToInt32()->Value());
- CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT
+ CHECK_EQ(-37.0, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(-37, obj->ToInt32(isolate)->Value());
+ CHECK(4294967259u == obj->ToUint32(isolate)->Value()); // NOLINT
// Positive non-int32 integer.
CompileRun("var obj = 0x81234567;");
obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(2166572391.0, obj->ToNumber()->Value());
- CHECK_EQ(-2128394905, obj->ToInt32()->Value());
- CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT
+ CHECK_EQ(2166572391.0, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(-2128394905, obj->ToInt32(isolate)->Value());
+ CHECK(2166572391u == obj->ToUint32(isolate)->Value()); // NOLINT
// Fraction.
CompileRun("var obj = 42.3;");
obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(42.3, obj->ToNumber()->Value());
- CHECK_EQ(42, obj->ToInt32()->Value());
- CHECK(42u == obj->ToUint32()->Value()); // NOLINT
+ CHECK_EQ(42.3, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(42, obj->ToInt32(isolate)->Value());
+ CHECK(42u == obj->ToUint32(isolate)->Value()); // NOLINT
// Large negative fraction.
CompileRun("var obj = -5726623061.75;");
obj = env->Global()->Get(v8_str("obj"));
- CHECK_EQ(-5726623061.75, obj->ToNumber()->Value());
- CHECK_EQ(-1431655765, obj->ToInt32()->Value());
- CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT
+ CHECK_EQ(-5726623061.75, obj->ToNumber(isolate)->Value());
+ CHECK_EQ(-1431655765, obj->ToInt32(isolate)->Value());
+ CHECK(2863311531u == obj->ToUint32(isolate)->Value()); // NOLINT
}
@@ -4873,29 +5198,29 @@ THREADED_TEST(ConversionException) {
"var obj = new TestClass();");
Local<Value> obj = env->Global()->Get(v8_str("obj"));
- v8::TryCatch try_catch;
+ v8::TryCatch try_catch(isolate);
- Local<Value> to_string_result = obj->ToString();
+ Local<Value> to_string_result = obj->ToString(isolate);
CHECK(to_string_result.IsEmpty());
CheckUncle(&try_catch);
- Local<Value> to_number_result = obj->ToNumber();
+ Local<Value> to_number_result = obj->ToNumber(isolate);
CHECK(to_number_result.IsEmpty());
CheckUncle(&try_catch);
- Local<Value> to_integer_result = obj->ToInteger();
+ Local<Value> to_integer_result = obj->ToInteger(isolate);
CHECK(to_integer_result.IsEmpty());
CheckUncle(&try_catch);
- Local<Value> to_uint32_result = obj->ToUint32();
+ Local<Value> to_uint32_result = obj->ToUint32(isolate);
CHECK(to_uint32_result.IsEmpty());
CheckUncle(&try_catch);
- Local<Value> to_int32_result = obj->ToInt32();
+ Local<Value> to_int32_result = obj->ToInt32(isolate);
CHECK(to_int32_result.IsEmpty());
CheckUncle(&try_catch);
- Local<Value> to_object_result = v8::Undefined(isolate)->ToObject();
+ Local<Value> to_object_result = v8::Undefined(isolate)->ToObject(isolate);
CHECK(to_object_result.IsEmpty());
CHECK(try_catch.HasCaught());
try_catch.Reset();
@@ -4931,7 +5256,7 @@ void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
v8::HandleScope scope(args.GetIsolate());
v8::TryCatch try_catch;
- Local<Value> result = CompileRun(args[0]->ToString());
+ Local<Value> result = CompileRun(args[0]->ToString(args.GetIsolate()));
CHECK(!try_catch.HasCaught() || result.IsEmpty());
args.GetReturnValue().Set(try_catch.HasCaught());
}
@@ -5980,7 +6305,7 @@ THREADED_TEST(NoAccessors) {
}
-static void XPropertyGetter(Local<String> property,
+static void XPropertyGetter(Local<Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(info.Data()->IsUndefined());
@@ -5992,7 +6317,7 @@ THREADED_TEST(NamedInterceptorPropertyRead) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(XPropertyGetter);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> script = v8_compile("obj.x");
@@ -6007,7 +6332,7 @@ THREADED_TEST(NamedInterceptorDictionaryIC) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(XPropertyGetter);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
LocalContext context;
// Create an object with a named interceptor.
context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
@@ -6041,7 +6366,7 @@ THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
context1->Enter();
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(XPropertyGetter);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(XPropertyGetter));
// Create an object with a named interceptor.
v8::Local<v8::Object> object = templ->NewInstance();
context1->Global()->Set(v8_str("interceptor_obj"), object);
@@ -6076,8 +6401,7 @@ THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) {
static void SetXOnPrototypeGetter(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
// Set x on the prototype object and do not handle the get request.
v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
proto.As<v8::Object>()->Set(v8_str("x"),
@@ -6094,7 +6418,8 @@ THREADED_TEST(NamedInterceptorMapTransitionRead) {
v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_template
= function_template->InstanceTemplate();
- instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
+ instance_template->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(SetXOnPrototypeGetter));
LocalContext context;
context->Global()->Set(v8_str("F"), function_template->GetFunction());
// Create an instance of F and introduce a map transition for x.
@@ -6130,8 +6455,8 @@ THREADED_TEST(IndexedInterceptorWithIndexedAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IndexedPropertyGetter,
- IndexedPropertySetter);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ IndexedPropertyGetter, IndexedPropertySetter));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> getter_script = v8_compile(
@@ -6196,11 +6521,9 @@ THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(UnboxedDoubleIndexedPropertyGetter,
- UnboxedDoubleIndexedPropertySetter,
- 0,
- 0,
- UnboxedDoubleIndexedPropertyEnumerator);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ UnboxedDoubleIndexedPropertyGetter, UnboxedDoubleIndexedPropertySetter, 0,
+ 0, UnboxedDoubleIndexedPropertyEnumerator));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
// When obj is created, force it to be Stored in a FastDoubleArray.
@@ -6210,7 +6533,7 @@ THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) {
"for (x in obj) {key_count++;};"
"obj;");
Local<Value> result = create_unboxed_double_script->Run();
- CHECK(result->ToObject()->HasRealIndexedProperty(2000));
+ CHECK(result->ToObject(isolate)->HasRealIndexedProperty(2000));
Local<Script> key_count_check = v8_compile("key_count;");
result = key_count_check->Run();
CHECK_EQ(v8_num(40013), result);
@@ -6252,11 +6575,9 @@ THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(SloppyIndexedPropertyGetter,
- 0,
- 0,
- 0,
- SloppyArgsIndexedPropertyEnumerator);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ SloppyIndexedPropertyGetter, 0, 0, 0,
+ SloppyArgsIndexedPropertyEnumerator));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
Local<Script> create_args_script = v8_compile(
@@ -6278,7 +6599,8 @@ THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
@@ -6300,7 +6622,8 @@ THREADED_TEST(IndexedInterceptorWithNoSetter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
@@ -6324,7 +6647,8 @@ THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6352,7 +6676,8 @@ THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6390,7 +6715,8 @@ THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6414,7 +6740,8 @@ THREADED_TEST(IndexedInterceptorWithNegativeIndices) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6454,7 +6781,8 @@ THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6484,7 +6812,8 @@ THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6515,7 +6844,8 @@ THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -6546,7 +6876,8 @@ THREADED_TEST(IndexedInterceptorOnProto) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+ templ->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(IdentityIndexedPropertyGetter));
LocalContext context;
Local<v8::Object> obj = templ->NewInstance();
@@ -7371,14 +7702,12 @@ struct FlagAndPersistent {
};
-static void DisposeAndSetFlag(
- const v8::WeakCallbackData<v8::Object, FlagAndPersistent>& data) {
- data.GetParameter()->handle.Reset();
+static void SetFlag(const v8::PhantomCallbackData<FlagAndPersistent>& data) {
data.GetParameter()->flag = true;
}
-THREADED_TEST(IndependentWeakHandle) {
+static void IndependentWeakHandle(bool global_gc, bool interlinked) {
v8::Isolate* iso = CcTest::isolate();
v8::HandleScope scope(iso);
v8::Handle<Context> context = Context::New(iso);
@@ -7386,26 +7715,209 @@ THREADED_TEST(IndependentWeakHandle) {
FlagAndPersistent object_a, object_b;
+ intptr_t big_heap_size;
+
{
v8::HandleScope handle_scope(iso);
- object_a.handle.Reset(iso, v8::Object::New(iso));
- object_b.handle.Reset(iso, v8::Object::New(iso));
+ Local<Object> a(v8::Object::New(iso));
+ Local<Object> b(v8::Object::New(iso));
+ object_a.handle.Reset(iso, a);
+ object_b.handle.Reset(iso, b);
+ if (interlinked) {
+ a->Set(v8_str("x"), b);
+ b->Set(v8_str("x"), a);
+ }
+ if (global_gc) {
+ CcTest::heap()->CollectAllGarbage(TestHeap::Heap::kNoGCFlags);
+ } else {
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE);
+ }
+ // We are relying on this creating a big flag array and reserving the space
+ // up front.
+ v8::Handle<Value> big_array = CompileRun("new Array(50000)");
+ a->Set(v8_str("y"), big_array);
+ big_heap_size = CcTest::heap()->SizeOfObjects();
}
object_a.flag = false;
object_b.flag = false;
- object_a.handle.SetWeak(&object_a, &DisposeAndSetFlag);
- object_b.handle.SetWeak(&object_b, &DisposeAndSetFlag);
+ object_a.handle.SetPhantom(&object_a, &SetFlag);
+ object_b.handle.SetPhantom(&object_b, &SetFlag);
CHECK(!object_b.handle.IsIndependent());
object_a.handle.MarkIndependent();
object_b.handle.MarkIndependent();
CHECK(object_b.handle.IsIndependent());
- CcTest::heap()->CollectGarbage(i::NEW_SPACE);
+ if (global_gc) {
+ CcTest::heap()->CollectAllGarbage(TestHeap::Heap::kNoGCFlags);
+ } else {
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE);
+ }
+ // A single GC should be enough to reclaim the memory, since we are using
+ // phantom handles.
+ CHECK_LT(CcTest::heap()->SizeOfObjects(), big_heap_size - 200000);
CHECK(object_a.flag);
CHECK(object_b.flag);
}
+THREADED_TEST(IndependentWeakHandle) {
+ IndependentWeakHandle(false, false);
+ IndependentWeakHandle(false, true);
+ IndependentWeakHandle(true, false);
+ IndependentWeakHandle(true, true);
+}
+
+
+class Trivial {
+ public:
+ explicit Trivial(int x) : x_(x) {}
+
+ int x() { return x_; }
+ void set_x(int x) { x_ = x; }
+
+ private:
+ int x_;
+};
+
+
+class Trivial2 {
+ public:
+ Trivial2(int x, int y) : y_(y), x_(x) {}
+
+ int x() { return x_; }
+ void set_x(int x) { x_ = x; }
+
+ int y() { return y_; }
+ void set_y(int y) { y_ = y; }
+
+ private:
+ int y_;
+ int x_;
+};
+
+
+void CheckInternalFields(
+ const v8::InternalFieldsCallbackData<Trivial, Trivial2>& data) {
+ Trivial* t1 = data.GetInternalField1();
+ Trivial2* t2 = data.GetInternalField2();
+ CHECK_EQ(42, t1->x());
+ CHECK_EQ(103, t2->x());
+ t1->set_x(1729);
+ t2->set_x(33550336);
+}
+
+
+void InternalFieldCallback(bool global_gc) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+
+ Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
+ Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate();
+ Trivial* t1;
+ Trivial2* t2;
+ instance_templ->SetInternalFieldCount(2);
+ {
+ v8::HandleScope scope(isolate);
+ Local<v8::Object> obj = templ->GetFunction()->NewInstance();
+ v8::Persistent<v8::Object> handle(isolate, obj);
+ CHECK_EQ(2, obj->InternalFieldCount());
+ CHECK(obj->GetInternalField(0)->IsUndefined());
+ t1 = new Trivial(42);
+ t2 = new Trivial2(103, 9);
+
+ obj->SetAlignedPointerInInternalField(0, t1);
+ t1 = reinterpret_cast<Trivial*>(obj->GetAlignedPointerFromInternalField(0));
+ CHECK_EQ(42, t1->x());
+
+ obj->SetAlignedPointerInInternalField(1, t2);
+ t2 =
+ reinterpret_cast<Trivial2*>(obj->GetAlignedPointerFromInternalField(1));
+ CHECK_EQ(103, t2->x());
+
+ handle.SetPhantom(CheckInternalFields, 0, 1);
+ if (!global_gc) {
+ handle.MarkIndependent();
+ }
+ }
+ if (global_gc) {
+ CcTest::heap()->CollectAllGarbage(TestHeap::Heap::kNoGCFlags);
+ } else {
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE);
+ }
+
+ CHECK_EQ(1729, t1->x());
+ CHECK_EQ(33550336, t2->x());
+
+ delete t1;
+ delete t2;
+}
+
+
+THREADED_TEST(InternalFieldCallback) {
+ InternalFieldCallback(false);
+ InternalFieldCallback(true);
+}
+
+
+static void ResetUseValueAndSetFlag(
+ const v8::WeakCallbackData<v8::Object, FlagAndPersistent>& data) {
+ // Blink will reset the handle, and then use the other handle, so they
+ // can't use the same backing slot.
+ data.GetParameter()->handle.Reset();
+ data.GetValue()->IsBoolean(); // Make sure the handle still works.
+ data.GetParameter()->flag = true;
+}
+
+
+static void ResetWeakHandle(bool global_gc) {
+ v8::Isolate* iso = CcTest::isolate();
+ v8::HandleScope scope(iso);
+ v8::Handle<Context> context = Context::New(iso);
+ Context::Scope context_scope(context);
+
+ FlagAndPersistent object_a, object_b;
+
+ {
+ v8::HandleScope handle_scope(iso);
+ Local<Object> a(v8::Object::New(iso));
+ Local<Object> b(v8::Object::New(iso));
+ object_a.handle.Reset(iso, a);
+ object_b.handle.Reset(iso, b);
+ if (global_gc) {
+ CcTest::heap()->CollectAllGarbage(
+ TestHeap::Heap::kAbortIncrementalMarkingMask);
+ } else {
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE);
+ }
+ }
+
+ object_a.flag = false;
+ object_b.flag = false;
+ object_a.handle.SetWeak(&object_a, &ResetUseValueAndSetFlag);
+ object_b.handle.SetWeak(&object_b, &ResetUseValueAndSetFlag);
+ if (!global_gc) {
+ object_a.handle.MarkIndependent();
+ object_b.handle.MarkIndependent();
+ CHECK(object_b.handle.IsIndependent());
+ }
+ if (global_gc) {
+ CcTest::heap()->CollectAllGarbage(
+ TestHeap::Heap::kAbortIncrementalMarkingMask);
+ } else {
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE);
+ }
+ CHECK(object_a.flag);
+ CHECK(object_b.flag);
+}
+
+
+THREADED_TEST(ResetWeakHandle) {
+ ResetWeakHandle(false);
+ ResetWeakHandle(true);
+}
+
+
static void InvokeScavenge() {
CcTest::heap()->CollectGarbage(i::NEW_SPACE);
}
@@ -7533,9 +8045,8 @@ THREADED_TEST(Arguments) {
}
-static void NoBlockGetterX(Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>&) {
-}
+static void NoBlockGetterX(Local<Name> name,
+ const v8::PropertyCallbackInfo<v8::Value>&) {}
static void NoBlockGetterI(uint32_t index,
@@ -7543,7 +8054,7 @@ static void NoBlockGetterI(uint32_t index,
}
-static void PDeleter(Local<String> name,
+static void PDeleter(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
if (!name->Equals(v8_str("foo"))) {
return; // not intercepted
@@ -7567,8 +8078,10 @@ THREADED_TEST(Deleter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
- obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL);
- obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL);
+ obj->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX, NULL,
+ NULL, PDeleter, NULL));
+ obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ NoBlockGetterI, NULL, NULL, IDeleter, NULL));
LocalContext context;
context->Global()->Set(v8_str("k"), obj->NewInstance());
CompileRun(
@@ -7590,7 +8103,7 @@ THREADED_TEST(Deleter) {
}
-static void GetK(Local<String> name,
+static void GetK(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (name->Equals(v8_str("foo")) ||
@@ -7631,8 +8144,10 @@ THREADED_TEST(Enumerators) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
- obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
- obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
+ obj->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(GetK, NULL, NULL, NULL, NamedEnum));
+ obj->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ IndexedGetK, NULL, NULL, NULL, IndexedEnum));
LocalContext context;
context->Global()->Set(v8_str("k"), obj->NewInstance());
v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
@@ -7724,7 +8239,7 @@ static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) {
}
-static void PGetter2(Local<String> name,
+static void PGetter2(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
p_getter_count2++;
@@ -7761,7 +8276,7 @@ THREADED_TEST(PreInterceptorHolders) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate);
- obj->SetNamedPropertyHandler(PGetter2);
+ obj->SetHandler(v8::NamedPropertyHandlerConfiguration(PGetter2));
p_getter_count2 = 0;
RunHolderTest(obj);
CHECK_EQ(40, p_getter_count2);
@@ -8419,6 +8934,85 @@ THREADED_TEST(ErrorConstruction) {
}
+static void ThrowV8Exception(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ ApiTestFuzzer::Fuzz();
+ v8::Handle<String> foo = v8_str("foo");
+ v8::Handle<String> message = v8_str("message");
+ v8::Handle<Value> error = v8::Exception::Error(foo);
+ CHECK(error->IsObject());
+ CHECK(error.As<v8::Object>()->Get(message)->Equals(foo));
+ info.GetIsolate()->ThrowException(error);
+ info.GetReturnValue().SetUndefined();
+}
+
+
+THREADED_TEST(ExceptionCreateMessage) {
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ v8::Handle<String> foo_str = v8_str("foo");
+ v8::Handle<String> message_str = v8_str("message");
+
+ v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
+
+ Local<v8::FunctionTemplate> fun =
+ v8::FunctionTemplate::New(context->GetIsolate(), ThrowV8Exception);
+ v8::Local<v8::Object> global = context->Global();
+ global->Set(v8_str("throwV8Exception"), fun->GetFunction());
+
+ TryCatch try_catch;
+ CompileRun(
+ "function f1() {\n"
+ " throwV8Exception();\n"
+ "};\n"
+ "f1();");
+ CHECK(try_catch.HasCaught());
+
+ v8::Handle<v8::Value> error = try_catch.Exception();
+ CHECK(error->IsObject());
+ CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str));
+
+ v8::Handle<v8::Message> message = v8::Exception::CreateMessage(error);
+ CHECK(!message.IsEmpty());
+ CHECK_EQ(2, message->GetLineNumber());
+ CHECK_EQ(2, message->GetStartColumn());
+
+ v8::Handle<v8::StackTrace> stackTrace = message->GetStackTrace();
+ CHECK(!stackTrace.IsEmpty());
+ CHECK_EQ(2, stackTrace->GetFrameCount());
+
+ stackTrace = v8::Exception::GetStackTrace(error);
+ CHECK(!stackTrace.IsEmpty());
+ CHECK_EQ(2, stackTrace->GetFrameCount());
+
+ v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
+
+ // Now check message location when SetCaptureStackTraceForUncaughtExceptions
+ // is false.
+ try_catch.Reset();
+
+ CompileRun(
+ "function f2() {\n"
+ " return throwV8Exception();\n"
+ "};\n"
+ "f2();");
+ CHECK(try_catch.HasCaught());
+
+ error = try_catch.Exception();
+ CHECK(error->IsObject());
+ CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str));
+
+ message = v8::Exception::CreateMessage(error);
+ CHECK(!message.IsEmpty());
+ CHECK_EQ(2, message->GetLineNumber());
+ CHECK_EQ(9, message->GetStartColumn());
+
+ // Should be empty stack trace.
+ stackTrace = message->GetStackTrace();
+ CHECK(stackTrace.IsEmpty());
+ CHECK(v8::Exception::GetStackTrace(error).IsEmpty());
+}
+
+
static void YGetter(Local<String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
@@ -8532,6 +9126,33 @@ TEST(ApiUncaughtException) {
v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
}
+
+TEST(ApiUncaughtExceptionInObjectObserve) {
+ v8::internal::FLAG_stack_size = 150;
+ report_count = 0;
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener);
+ CompileRun(
+ "var obj = {};"
+ "var observe_count = 0;"
+ "function observer1() { ++observe_count; };"
+ "function observer2() { ++observe_count; };"
+ "function observer_throws() { throw new Error(); };"
+ "function stack_overflow() { return (function f(x) { f(x+1); })(0); };"
+ "Object.observe(obj, observer_throws.bind());"
+ "Object.observe(obj, observer1);"
+ "Object.observe(obj, stack_overflow);"
+ "Object.observe(obj, observer2);"
+ "Object.observe(obj, observer_throws.bind());"
+ "obj.foo = 'bar';");
+ CHECK_EQ(3, report_count);
+ ExpectInt32("observe_count", 2);
+ v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener);
+}
+
+
static const char* script_resource_name = "ExceptionInNativeScript.js";
static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message,
v8::Handle<Value>) {
@@ -8603,7 +9224,7 @@ TEST(TryCatchFinallyUsingTryCatchHandler) {
void CEvaluate(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
- CompileRun(args[0]->ToString());
+ CompileRun(args[0]->ToString(args.GetIsolate()));
}
@@ -8877,11 +9498,11 @@ TEST(SecurityTestGCAllowed) {
CHECK(indexed_security_check_with_gc_called);
named_security_check_with_gc_called = false;
- CHECK(CompileRun("obj.foo")->ToString()->Equals(v8_str("1001")));
+ CHECK(CompileRun("obj.foo")->ToString(isolate)->Equals(v8_str("1001")));
CHECK(named_security_check_with_gc_called);
indexed_security_check_with_gc_called = false;
- CHECK(CompileRun("obj[0]")->ToString()->Equals(v8_str("1002")));
+ CHECK(CompileRun("obj[0]")->ToString(isolate)->Equals(v8_str("1002")));
CHECK(indexed_security_check_with_gc_called);
}
@@ -9609,20 +10230,52 @@ TEST(SuperAccessControl) {
LocalContext env;
env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance());
- v8::TryCatch try_catch;
- CompileRun(
- "function f() { return super.hasOwnProperty; };"
- "var m = f.toMethod(prohibited);"
- "m();");
- CHECK(try_catch.HasCaught());
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "function f() { return super.hasOwnProperty; };"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "function f() { return super[42]; };"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "function f() { super.hasOwnProperty = function () {}; };"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
+
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "Object.defineProperty(Object.prototype, 'x', { set : function(){}});"
+ "function f() { "
+ " 'use strict';"
+ " super.x = function () {}; "
+ "};"
+ "var m = f.toMethod(prohibited);"
+ "m();");
+ CHECK(try_catch.HasCaught());
+ }
}
static void IndexedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) {
- v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
+ v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 1);
result->Set(0, v8::Integer::New(info.GetIsolate(), 7));
- result->Set(1, v8::Object::New(info.GetIsolate()));
info.GetReturnValue().Set(result);
}
@@ -9631,7 +10284,7 @@ static void NamedPropertyEnumerator(
const v8::PropertyCallbackInfo<v8::Array>& info) {
v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2);
result->Set(0, v8_str("x"));
- result->Set(1, v8::Object::New(info.GetIsolate()));
+ result->Set(1, v8::Symbol::GetIterator(info.GetIsolate()));
info.GetReturnValue().Set(result);
}
@@ -9644,10 +10297,10 @@ THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
obj_template->Set(v8_str("7"), v8::Integer::New(CcTest::isolate(), 7));
obj_template->Set(v8_str("x"), v8::Integer::New(CcTest::isolate(), 42));
- obj_template->SetIndexedPropertyHandler(NULL, NULL, NULL, NULL,
- IndexedPropertyEnumerator);
- obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL,
- NamedPropertyEnumerator);
+ obj_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ NULL, NULL, NULL, NULL, IndexedPropertyEnumerator));
+ obj_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ NULL, NULL, NULL, NULL, NamedPropertyEnumerator));
LocalContext context;
v8::Handle<v8::Object> global = context->Global();
@@ -9657,13 +10310,26 @@ THREADED_TEST(GetOwnPropertyNamesWithInterceptor) {
CompileRun("Object.getOwnPropertyNames(object)");
CHECK(result->IsArray());
v8::Handle<v8::Array> result_array = v8::Handle<v8::Array>::Cast(result);
- CHECK_EQ(3, result_array->Length());
+ CHECK_EQ(2, result_array->Length());
+ CHECK(result_array->Get(0)->IsString());
+ CHECK(result_array->Get(1)->IsString());
+ CHECK_EQ(v8_str("7"), result_array->Get(0));
+ CHECK_EQ(v8_str("x"), result_array->Get(1));
+
+ result = CompileRun("var ret = []; for (var k in object) ret.push(k); ret");
+ CHECK(result->IsArray());
+ result_array = v8::Handle<v8::Array>::Cast(result);
+ CHECK_EQ(2, result_array->Length());
CHECK(result_array->Get(0)->IsString());
CHECK(result_array->Get(1)->IsString());
- CHECK(result_array->Get(2)->IsString());
CHECK_EQ(v8_str("7"), result_array->Get(0));
- CHECK_EQ(v8_str("[object Object]"), result_array->Get(1));
- CHECK_EQ(v8_str("x"), result_array->Get(2));
+ CHECK_EQ(v8_str("x"), result_array->Get(1));
+
+ result = CompileRun("Object.getOwnPropertySymbols(object)");
+ CHECK(result->IsArray());
+ result_array = v8::Handle<v8::Array>::Cast(result);
+ CHECK_EQ(1, result_array->Length());
+ CHECK_EQ(result_array->Get(0), v8::Symbol::GetIterator(isolate));
}
@@ -9937,15 +10603,13 @@ THREADED_TEST(AccessControlFlatten) {
static void AccessControlNamedGetter(
- Local<String>,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name>, const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(42);
}
static void AccessControlNamedSetter(
- Local<String>,
- Local<Value> value,
+ Local<Name>, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(value);
}
@@ -9984,10 +10648,10 @@ THREADED_TEST(AccessControlInterceptorIC) {
v8::ObjectTemplate::New(isolate);
object_template->SetAccessCheckCallbacks(NamedAccessCounter,
IndexedAccessCounter);
- object_template->SetNamedPropertyHandler(AccessControlNamedGetter,
- AccessControlNamedSetter);
- object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter,
- AccessControlIndexedSetter);
+ object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ AccessControlNamedGetter, AccessControlNamedSetter));
+ object_template->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ AccessControlIndexedGetter, AccessControlIndexedSetter));
Local<v8::Object> object = object_template->NewInstance();
v8::HandleScope scope1(isolate);
@@ -10069,8 +10733,7 @@ THREADED_TEST(InstanceProperties) {
static void GlobalObjectInstancePropertiesGet(
- Local<String> key,
- const v8::PropertyCallbackInfo<v8::Value>&) {
+ Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>&) {
ApiTestFuzzer::Fuzz();
}
@@ -10082,8 +10745,8 @@ THREADED_TEST(GlobalObjectInstanceProperties) {
Local<Value> global_object;
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
- t->InstanceTemplate()->SetNamedPropertyHandler(
- GlobalObjectInstancePropertiesGet);
+ t->InstanceTemplate()->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(GlobalObjectInstancePropertiesGet));
Local<ObjectTemplate> instance_template = t->InstanceTemplate();
instance_template->Set(v8_str("x"), v8_num(42));
instance_template->Set(v8_str("f"),
@@ -10207,9 +10870,8 @@ static void ShadowIndexedGet(uint32_t index,
}
-static void ShadowNamedGet(Local<String> key,
- const v8::PropertyCallbackInfo<v8::Value>&) {
-}
+static void ShadowNamedGet(Local<Name> key,
+ const v8::PropertyCallbackInfo<v8::Value>&) {}
THREADED_TEST(ShadowObject) {
@@ -10221,8 +10883,10 @@ THREADED_TEST(ShadowObject) {
LocalContext context(NULL, global_template);
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
- t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet);
- t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet);
+ t->InstanceTemplate()->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(ShadowNamedGet));
+ t->InstanceTemplate()->SetHandler(
+ v8::IndexedPropertyHandlerConfiguration(ShadowIndexedGet));
Local<ObjectTemplate> proto = t->PrototypeTemplate();
Local<ObjectTemplate> instance = t->InstanceTemplate();
@@ -10709,7 +11373,7 @@ THREADED_TEST(ConstructorForObject) {
"(function() { var o = new obj('tipli'); return o.a; })()");
CHECK(!try_catch.HasCaught());
CHECK(value->IsString());
- String::Utf8Value string_value1(value->ToString());
+ String::Utf8Value string_value1(value->ToString(isolate));
CHECK_EQ("tipli", *string_value1);
Local<Value> args2[] = { v8_str("tipli") };
@@ -10719,7 +11383,7 @@ THREADED_TEST(ConstructorForObject) {
value = object2->Get(v8_str("a"));
CHECK(!try_catch.HasCaught());
CHECK(value->IsString());
- String::Utf8Value string_value2(value->ToString());
+ String::Utf8Value string_value2(value->ToString(isolate));
CHECK_EQ("tipli", *string_value2);
// Call the Object's constructor with a Boolean.
@@ -10982,8 +11646,7 @@ THREADED_TEST(CrossEval) {
// Test that calling eval in a context which has been detached from
-// its global throws an exception. This behavior is consistent with
-// other JavaScript implementations.
+// its global proxy works.
THREADED_TEST(EvalInDetachedGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
@@ -11011,8 +11674,7 @@ THREADED_TEST(EvalInDetachedGlobal) {
context0->DetachGlobal();
v8::TryCatch catcher;
x_value = CompileRun("fun('x')");
- CHECK(x_value.IsEmpty());
- CHECK(catcher.HasCaught());
+ CHECK_EQ(42, x_value->Int32Value());
context1->Exit();
}
@@ -11330,8 +11992,7 @@ THREADED_TEST(HandleIteration) {
static void InterceptorHasOwnPropertyGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
}
@@ -11342,7 +12003,8 @@ THREADED_TEST(InterceptorHasOwnProperty) {
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
- instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter);
+ instance_templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetter));
Local<Function> function = fun_templ->GetFunction();
context->Global()->Set(v8_str("constructor"), function);
v8::Handle<Value> value = CompileRun(
@@ -11361,8 +12023,7 @@ THREADED_TEST(InterceptorHasOwnProperty) {
static void InterceptorHasOwnPropertyGetterGC(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
}
@@ -11374,7 +12035,8 @@ THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
v8::HandleScope scope(isolate);
Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate);
Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate();
- instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC);
+ instance_templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorHasOwnPropertyGetterGC));
Local<Function> function = fun_templ->GetFunction();
context->Global()->Set(v8_str("constructor"), function);
// Let's first make some stuff so we can be sure to get a good GC.
@@ -11398,18 +12060,14 @@ THREADED_TEST(InterceptorHasOwnPropertyCausingGC) {
}
-typedef void (*NamedPropertyGetter)(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Value>& info);
-
-
-static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
- const char* source,
- int expected) {
+static void CheckInterceptorLoadIC(
+ v8::GenericNamedPropertyGetterCallback getter, const char* source,
+ int expected) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data"));
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(getter, 0, 0, 0, 0,
+ v8_str("data")));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(source);
@@ -11418,8 +12076,7 @@ static void CheckInterceptorLoadIC(NamedPropertyGetter getter,
static void InterceptorLoadICGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
v8::Isolate* isolate = CcTest::isolate();
CHECK_EQ(isolate, info.GetIsolate());
@@ -11445,8 +12102,7 @@ THREADED_TEST(InterceptorLoadIC) {
// (those cases are special cased to get better performance).
static void InterceptorLoadXICGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
info.GetReturnValue().Set(
v8_str("x")->Equals(name) ?
@@ -11561,8 +12217,7 @@ THREADED_TEST(InterceptorLoadICInvalidatedField) {
static int interceptor_load_not_handled_calls = 0;
static void InterceptorLoadNotHandled(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
++interceptor_load_not_handled_calls;
}
@@ -11623,7 +12278,8 @@ THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
templ->SetAccessor(v8_str("y"), Return239Callback);
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
@@ -11653,7 +12309,8 @@ THREADED_TEST(InterceptorLoadICWithCallbackOnProto) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ_o->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
templ_p->SetAccessor(v8_str("y"), Return239Callback);
@@ -11687,7 +12344,8 @@ THREADED_TEST(InterceptorLoadICForCallbackWithOverride) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
templ->SetAccessor(v8_str("y"), Return239Callback);
LocalContext context;
@@ -11716,7 +12374,8 @@ THREADED_TEST(InterceptorLoadICCallbackNotNeeded) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ_o->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
templ_p->SetAccessor(v8_str("y"), Return239Callback);
@@ -11745,7 +12404,8 @@ THREADED_TEST(InterceptorLoadICInvalidatedCallback) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ_o->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
@@ -11778,7 +12438,8 @@ THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ_o->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(isolate);
templ_p->SetAccessor(v8_str("y"), Return239Callback, SetOnThis);
@@ -11804,8 +12465,7 @@ THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) {
static void InterceptorLoadICGetter0(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(v8_str("x")->Equals(name));
info.GetReturnValue().Set(v8::Integer::New(info.GetIsolate(), 0));
@@ -11820,8 +12480,7 @@ THREADED_TEST(InterceptorReturningZero) {
static void InterceptorStoreICSetter(
- Local<String> key,
- Local<Value> value,
+ Local<Name> key, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(v8_str("x")->Equals(key));
CHECK_EQ(42, value->Int32Value());
@@ -11834,9 +12493,9 @@ THREADED_TEST(InterceptorStoreIC) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorLoadICGetter,
- InterceptorStoreICSetter,
- 0, 0, 0, v8_str("data"));
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ InterceptorLoadICGetter, InterceptorStoreICSetter, 0, 0, 0,
+ v8_str("data")));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
CompileRun(
@@ -11850,7 +12509,8 @@ THREADED_TEST(InterceptorStoreICWithNoSetter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(
@@ -11869,8 +12529,7 @@ v8::Handle<Value> call_ic_function2;
v8::Handle<Value> call_ic_function3;
static void InterceptorCallICGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(v8_str("x")->Equals(name));
info.GetReturnValue().Set(call_ic_function);
@@ -11882,7 +12541,8 @@ THREADED_TEST(InterceptorCallIC) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorCallICGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
call_ic_function =
@@ -11902,7 +12562,7 @@ THREADED_TEST(InterceptorCallICSeesOthers) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(
@@ -11917,8 +12577,7 @@ THREADED_TEST(InterceptorCallICSeesOthers) {
static v8::Handle<Value> call_ic_function4;
static void InterceptorCallICGetter4(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CHECK(v8_str("x")->Equals(name));
info.GetReturnValue().Set(call_ic_function4);
@@ -11932,7 +12591,8 @@ THREADED_TEST(InterceptorCallICCacheableNotNeeded) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorCallICGetter4);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter4));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
call_ic_function4 =
@@ -11953,7 +12613,7 @@ THREADED_TEST(InterceptorCallICInvalidatedCacheable) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(
@@ -11981,7 +12641,7 @@ THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(
@@ -11998,8 +12658,7 @@ THREADED_TEST(InterceptorCallICConstantFunctionUsed) {
static v8::Handle<Value> call_ic_function5;
static void InterceptorCallICGetter5(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("x")->Equals(name))
info.GetReturnValue().Set(call_ic_function5);
@@ -12013,7 +12672,8 @@ THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorCallICGetter5);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter5));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
call_ic_function5 =
@@ -12032,8 +12692,7 @@ THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) {
static v8::Handle<Value> call_ic_function6;
static void InterceptorCallICGetter6(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("x")->Equals(name))
info.GetReturnValue().Set(call_ic_function6);
@@ -12047,7 +12706,8 @@ THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorCallICGetter6);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorCallICGetter6));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
call_ic_function6 =
@@ -12078,7 +12738,7 @@ THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(
@@ -12109,7 +12769,7 @@ THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
v8::Handle<Value> value = CompileRun(
@@ -12135,7 +12795,7 @@ THREADED_TEST(InterceptorCallICCachedFromGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+ templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
@@ -12160,8 +12820,7 @@ THREADED_TEST(InterceptorCallICCachedFromGlobal) {
}
static void InterceptorCallICFastApi(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
CheckReturnValue(info, FUNCTION_ADDR(InterceptorCallICFastApi));
int* call_count =
@@ -12350,9 +13009,9 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_TrivialSignature) {
v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
proto_templ->Set(v8_str("method"), method_templ);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
- v8::External::New(isolate, &interceptor_call_count));
+ v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -12380,9 +13039,9 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature) {
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
- v8::External::New(isolate, &interceptor_call_count));
+ v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -12413,9 +13072,9 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
- v8::External::New(isolate, &interceptor_call_count));
+ v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -12452,9 +13111,9 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
- v8::External::New(isolate, &interceptor_call_count));
+ v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -12491,9 +13150,9 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
- v8::External::New(isolate, &interceptor_call_count));
+ v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -12515,7 +13174,7 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) {
CHECK(try_catch.HasCaught());
// TODO(verwaest): Adjust message.
CHECK_EQ(v8_str("TypeError: undefined is not a function"),
- try_catch.Exception()->ToString());
+ try_catch.Exception()->ToString(isolate));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
}
@@ -12534,9 +13193,9 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
proto_templ->Set(v8_str("method"), method_templ);
fun_templ->SetHiddenPrototype(true);
v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
- templ->SetNamedPropertyHandler(
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
InterceptorCallICFastApi, NULL, NULL, NULL, NULL,
- v8::External::New(isolate, &interceptor_call_count));
+ v8::External::New(isolate, &interceptor_call_count)));
LocalContext context;
v8::Handle<v8::Function> fun = fun_templ->GetFunction();
GenerateSomeGarbage();
@@ -12557,7 +13216,7 @@ THREADED_PROFILED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
"}");
CHECK(try_catch.HasCaught());
CHECK_EQ(v8_str("TypeError: Illegal invocation"),
- try_catch.Exception()->ToString());
+ try_catch.Exception()->ToString(isolate));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
CHECK_GE(interceptor_call_count, 50);
}
@@ -12690,7 +13349,7 @@ THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_Miss2) {
CHECK(try_catch.HasCaught());
// TODO(verwaest): Adjust message.
CHECK_EQ(v8_str("TypeError: undefined is not a function"),
- try_catch.Exception()->ToString());
+ try_catch.Exception()->ToString(isolate));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
@@ -12728,7 +13387,7 @@ THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_TypeError) {
"}");
CHECK(try_catch.HasCaught());
CHECK_EQ(v8_str("TypeError: Illegal invocation"),
- try_catch.Exception()->ToString());
+ try_catch.Exception()->ToString(isolate));
CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
}
@@ -12736,8 +13395,7 @@ THREADED_PROFILED_TEST(CallICFastApi_SimpleSignature_TypeError) {
v8::Handle<Value> keyed_call_ic_function;
static void InterceptorKeyedCallICGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("x")->Equals(name)) {
info.GetReturnValue().Set(keyed_call_ic_function);
@@ -12751,7 +13409,7 @@ THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
CompileRun(
@@ -12776,7 +13434,8 @@ THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorKeyedCallICGetter));
LocalContext context;
context->Global()->Set(v8_str("proto1"), templ->NewInstance());
keyed_call_ic_function =
@@ -12804,7 +13463,7 @@ THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NoBlockGetterX);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ->NewInstance());
CompileRun(
@@ -12830,7 +13489,7 @@ THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+ templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
@@ -12856,7 +13515,7 @@ THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+ templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
@@ -12879,7 +13538,7 @@ THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(isolate);
- templ_o->SetNamedPropertyHandler(NoBlockGetterX);
+ templ_o->SetHandler(v8::NamedPropertyHandlerConfiguration(NoBlockGetterX));
LocalContext context;
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
@@ -12900,8 +13559,7 @@ THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
static int interceptor_call_count = 0;
static void InterceptorICRefErrorGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) {
info.GetReturnValue().Set(call_ic_function2);
@@ -12916,7 +13574,8 @@ THREADED_TEST(InterceptorICReferenceErrors) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorICRefErrorGetter));
LocalContext context(0, templ, v8::Handle<Value>());
call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run();
v8::Handle<Value> value = CompileRun(
@@ -12944,8 +13603,7 @@ THREADED_TEST(InterceptorICReferenceErrors) {
static int interceptor_ic_exception_get_count = 0;
static void InterceptorICExceptionGetter(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) {
info.GetReturnValue().Set(call_ic_function3);
@@ -12964,7 +13622,8 @@ THREADED_TEST(InterceptorICGetterExceptions) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(InterceptorICExceptionGetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorICExceptionGetter));
LocalContext context(0, templ, v8::Handle<Value>());
call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run();
v8::Handle<Value> value = CompileRun(
@@ -12992,9 +13651,8 @@ THREADED_TEST(InterceptorICGetterExceptions) {
static int interceptor_ic_exception_set_count = 0;
static void InterceptorICExceptionSetter(
- Local<String> key,
- Local<Value> value,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> key, Local<Value> value,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
ApiTestFuzzer::Fuzz();
if (++interceptor_ic_exception_set_count > 20) {
info.GetIsolate()->ThrowException(v8_num(42));
@@ -13009,7 +13667,8 @@ THREADED_TEST(InterceptorICSetterExceptions) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(0, InterceptorICExceptionSetter));
LocalContext context(0, templ, v8::Handle<Value>());
v8::Handle<Value> value = CompileRun(
"function f() {"
@@ -13028,8 +13687,8 @@ THREADED_TEST(NullNamedInterceptor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(
- static_cast<v8::NamedPropertyGetterCallback>(0));
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ static_cast<v8::GenericNamedPropertyGetterCallback>(0)));
LocalContext context;
templ->Set(CcTest::isolate(), "x", v8_num(42));
v8::Handle<v8::Object> obj = templ->NewInstance();
@@ -13045,8 +13704,8 @@ THREADED_TEST(NullIndexedInterceptor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(
- static_cast<v8::IndexedPropertyGetterCallback>(0));
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ static_cast<v8::IndexedPropertyGetterCallback>(0)));
LocalContext context;
templ->Set(CcTest::isolate(), "42", v8_num(42));
v8::Handle<v8::Object> obj = templ->NewInstance();
@@ -13061,7 +13720,8 @@ THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
- templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter);
+ templ->InstanceTemplate()->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(InterceptorLoadXICGetter));
LocalContext env;
env->Global()->Set(v8_str("obj"),
templ->GetFunction()->NewInstance());
@@ -13315,7 +13975,7 @@ THREADED_TEST(ObjectProtoToString) {
// Normal ToString call should call replaced Object.prototype.toString
Local<v8::Object> instance = templ->GetFunction()->NewInstance();
- Local<String> value = instance->ToString();
+ Local<String> value = instance->ToString(isolate);
CHECK(value->IsString() && value->Equals(customized_tostring));
// ObjectProtoToString should not call replace toString function.
@@ -13333,9 +13993,127 @@ THREADED_TEST(ObjectProtoToString) {
}
+TEST(ObjectProtoToStringES6) {
+ // TODO(dslomov, caitp): merge into ObjectProtoToString test once shipped.
+ i::FLAG_harmony_tostring = true;
+ LocalContext context;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope scope(isolate);
+ Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
+ templ->SetClassName(v8_str("MyClass"));
+
+ Local<String> customized_tostring = v8_str("customized toString");
+
+ // Replace Object.prototype.toString
+ CompileRun(
+ "Object.prototype.toString = function() {"
+ " return 'customized toString';"
+ "}");
+
+ // Normal ToString call should call replaced Object.prototype.toString
+ Local<v8::Object> instance = templ->GetFunction()->NewInstance();
+ Local<String> value = instance->ToString(isolate);
+ CHECK(value->IsString() && value->Equals(customized_tostring));
+
+ // ObjectProtoToString should not call replace toString function.
+ value = instance->ObjectProtoToString();
+ CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]")));
+
+ // Check global
+ value = context->Global()->ObjectProtoToString();
+ CHECK(value->IsString() && value->Equals(v8_str("[object global]")));
+
+ // Check ordinary object
+ Local<Value> object = CompileRun("new Object()");
+ value = object.As<v8::Object>()->ObjectProtoToString();
+ CHECK(value->IsString() && value->Equals(v8_str("[object Object]")));
+
+ // Check that ES6 semantics using @@toStringTag work
+ Local<v8::Symbol> toStringTag = v8::Symbol::GetToStringTag(isolate);
+
+#define TEST_TOSTRINGTAG(type, tag, expected) \
+ do { \
+ object = CompileRun("new " #type "()"); \
+ object.As<v8::Object>()->Set(toStringTag, v8_str(#tag)); \
+ value = object.As<v8::Object>()->ObjectProtoToString(); \
+ CHECK(value->IsString() && \
+ value->Equals(v8_str("[object " #expected "]"))); \
+ } while (0)
+
+ TEST_TOSTRINGTAG(Array, Object, Object);
+ TEST_TOSTRINGTAG(Object, Arguments, ~Arguments);
+ TEST_TOSTRINGTAG(Object, Array, ~Array);
+ TEST_TOSTRINGTAG(Object, Boolean, ~Boolean);
+ TEST_TOSTRINGTAG(Object, Date, ~Date);
+ TEST_TOSTRINGTAG(Object, Error, ~Error);
+ TEST_TOSTRINGTAG(Object, Function, ~Function);
+ TEST_TOSTRINGTAG(Object, Number, ~Number);
+ TEST_TOSTRINGTAG(Object, RegExp, ~RegExp);
+ TEST_TOSTRINGTAG(Object, String, ~String);
+ TEST_TOSTRINGTAG(Object, Foo, Foo);
+
+#undef TEST_TOSTRINGTAG
+
+ // @@toStringTag getter throws
+ Local<Value> obj = v8::Object::New(isolate);
+ obj.As<v8::Object>()->SetAccessor(toStringTag, ThrowingSymbolAccessorGetter);
+ {
+ TryCatch try_catch;
+ value = obj.As<v8::Object>()->ObjectProtoToString();
+ CHECK(value.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ }
+
+ // @@toStringTag getter does not throw
+ obj = v8::Object::New(isolate);
+ obj.As<v8::Object>()->SetAccessor(
+ toStringTag, SymbolAccessorGetterReturnsDefault, 0, v8_str("Test"));
+ {
+ TryCatch try_catch;
+ value = obj.As<v8::Object>()->ObjectProtoToString();
+ CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
+ CHECK(!try_catch.HasCaught());
+ }
+
+ // JS @@toStringTag value
+ obj = CompileRun("obj = {}; obj[Symbol.toStringTag] = 'Test'; obj");
+ {
+ TryCatch try_catch;
+ value = obj.As<v8::Object>()->ObjectProtoToString();
+ CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
+ CHECK(!try_catch.HasCaught());
+ }
+
+ // JS @@toStringTag getter throws
+ obj = CompileRun(
+ "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {"
+ " get: function() { throw 'Test'; }"
+ "}); obj");
+ {
+ TryCatch try_catch;
+ value = obj.As<v8::Object>()->ObjectProtoToString();
+ CHECK(value.IsEmpty());
+ CHECK(try_catch.HasCaught());
+ }
+
+ // JS @@toStringTag getter does not throw
+ obj = CompileRun(
+ "obj = {}; Object.defineProperty(obj, Symbol.toStringTag, {"
+ " get: function() { return 'Test'; }"
+ "}); obj");
+ {
+ TryCatch try_catch;
+ value = obj.As<v8::Object>()->ObjectProtoToString();
+ CHECK(value->IsString() && value->Equals(v8_str("[object Test]")));
+ CHECK(!try_catch.HasCaught());
+ }
+}
+
+
THREADED_TEST(ObjectGetConstructorName) {
+ v8::Isolate* isolate = CcTest::isolate();
LocalContext context;
- v8::HandleScope scope(context->GetIsolate());
+ v8::HandleScope scope(isolate);
v8_compile("function Parent() {};"
"function Child() {};"
"Child.prototype = new Parent();"
@@ -13345,16 +14123,17 @@ THREADED_TEST(ObjectGetConstructorName) {
"var x = new outer.inner();")->Run();
Local<v8::Value> p = context->Global()->Get(v8_str("p"));
- CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals(
- v8_str("Parent")));
+ CHECK(p->IsObject() &&
+ p->ToObject(isolate)->GetConstructorName()->Equals(v8_str("Parent")));
Local<v8::Value> c = context->Global()->Get(v8_str("c"));
- CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals(
- v8_str("Child")));
+ CHECK(c->IsObject() &&
+ c->ToObject(isolate)->GetConstructorName()->Equals(v8_str("Child")));
Local<v8::Value> x = context->Global()->Get(v8_str("x"));
- CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals(
- v8_str("outer.inner")));
+ CHECK(x->IsObject() &&
+ x->ToObject(isolate)->GetConstructorName()->Equals(
+ v8_str("outer.inner")));
}
@@ -13902,7 +14681,7 @@ THREADED_TEST(NestedHandleScopeAndContexts) {
v8::Local<Context> env = Context::New(isolate);
env->Enter();
v8::Handle<Value> value = NestedScope(env);
- v8::Handle<String> str(value->ToString());
+ v8::Handle<String> str(value->ToString(isolate));
CHECK(!str.IsEmpty());
env->Exit();
}
@@ -14744,7 +15523,7 @@ THREADED_TEST(PropertyEnumeration2) {
v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames();
CHECK_EQ(0, props->Length());
for (uint32_t i = 0; i < props->Length(); i++) {
- printf("p[%d]\n", i);
+ printf("p[%u]\n", i);
}
}
@@ -15291,7 +16070,7 @@ TEST(CompileExternalTwoByteSource) {
#ifndef V8_INTERPRETED_REGEXP
struct RegExpInterruptionData {
- int loop_count;
+ v8::base::Atomic32 loop_count;
UC16VectorResource* string_resource;
v8::Persistent<v8::String> string;
} regexp_interruption_data;
@@ -15303,9 +16082,10 @@ class RegExpInterruptionThread : public v8::base::Thread {
: Thread(Options("TimeoutThread")), isolate_(isolate) {}
virtual void Run() {
- for (regexp_interruption_data.loop_count = 0;
- regexp_interruption_data.loop_count < 7;
- regexp_interruption_data.loop_count++) {
+ for (v8::base::NoBarrier_Store(&regexp_interruption_data.loop_count, 0);
+ v8::base::NoBarrier_Load(&regexp_interruption_data.loop_count) < 7;
+ v8::base::NoBarrier_AtomicIncrement(
+ &regexp_interruption_data.loop_count, 1)) {
v8::base::OS::Sleep(50); // Wait a bit before requesting GC.
reinterpret_cast<i::Isolate*>(isolate_)->stack_guard()->RequestGC();
}
@@ -15319,7 +16099,9 @@ class RegExpInterruptionThread : public v8::base::Thread {
void RunBeforeGC(v8::GCType type, v8::GCCallbackFlags flags) {
- if (regexp_interruption_data.loop_count != 2) return;
+ if (v8::base::NoBarrier_Load(&regexp_interruption_data.loop_count) != 2) {
+ return;
+ }
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::String> string = v8::Local<v8::String>::New(
CcTest::isolate(), regexp_interruption_data.string);
@@ -15407,9 +16189,14 @@ static void ForceSetSetter(v8::Local<v8::String> name,
force_set_set_count++;
}
+static void ForceSetInterceptGetter(
+ v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
+ CHECK(name->IsString());
+ ForceSetGetter(Local<String>::Cast(name), info);
+}
+
static void ForceSetInterceptSetter(
- v8::Local<v8::String> name,
- v8::Local<v8::Value> value,
+ v8::Local<v8::Name> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
force_set_set_count++;
info.GetReturnValue().SetUndefined();
@@ -15469,7 +16256,8 @@ TEST(ForceSetWithInterceptor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ ForceSetInterceptGetter, ForceSetInterceptSetter));
LocalContext context(NULL, templ);
v8::Handle<v8::Object> global = context->Global();
@@ -15537,7 +16325,7 @@ static bool pass_on_delete = false;
static void ForceDeleteDeleter(
- v8::Local<v8::String> name,
+ v8::Local<v8::Name> name,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
force_delete_interceptor_count++;
if (pass_on_delete) return;
@@ -15552,7 +16340,8 @@ THREADED_TEST(ForceDeleteWithInterceptor) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter);
+ templ->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(0, 0, 0, ForceDeleteDeleter));
LocalContext context(NULL, templ);
v8::Handle<v8::Object> global = context->Global();
@@ -16195,8 +16984,8 @@ THREADED_TEST(PixelArrayWithInterceptor) {
}
v8::Handle<v8::ObjectTemplate> templ =
v8::ObjectTemplate::New(context->GetIsolate());
- templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter,
- NotHandledIndexedPropertySetter);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ NotHandledIndexedPropertyGetter, NotHandledIndexedPropertySetter));
v8::Handle<v8::Object> obj = templ->NewInstance();
obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
context->Global()->Set(v8_str("pixels"), obj);
@@ -17335,6 +18124,7 @@ TEST(CaptureStackTrace) {
static void StackTraceForUncaughtExceptionListener(
v8::Handle<v8::Message> message,
v8::Handle<Value>) {
+ report_count++;
v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace();
CHECK_EQ(2, stack_trace->GetFrameCount());
checkStackFrame("origin", "foo", 2, 3, false, false,
@@ -17365,6 +18155,38 @@ TEST(CaptureStackTraceForUncaughtException) {
Function::Cast(*trouble)->Call(global, 0, NULL);
v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
+ CHECK_EQ(1, report_count);
+}
+
+
+TEST(GetStackTraceForUncaughtExceptionFromSimpleStackTrace) {
+ report_count = 0;
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+
+ // Create an Error object first.
+ CompileRunWithOrigin(
+ "function foo() {\n"
+ "e=new Error('err');\n"
+ "};\n"
+ "function bar() {\n"
+ " foo();\n"
+ "};\n"
+ "var e;",
+ "origin");
+ v8::Local<v8::Object> global = env->Global();
+ Local<Value> trouble = global->Get(v8_str("bar"));
+ CHECK(trouble->IsFunction());
+ Function::Cast(*trouble)->Call(global, 0, NULL);
+
+ // Enable capturing detailed stack trace late, and throw the exception.
+ // The detailed stack trace should be extracted from the simple stack.
+ v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener);
+ v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
+ CompileRunWithOrigin("throw e", "origin");
+ v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
+ v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener);
+ CHECK_EQ(1, report_count);
}
@@ -17519,6 +18341,343 @@ TEST(RethrowBogusErrorStackTrace) {
}
+v8::PromiseRejectEvent reject_event = v8::kPromiseRejectWithNoHandler;
+int promise_reject_counter = 0;
+int promise_revoke_counter = 0;
+int promise_reject_line_number = -1;
+int promise_reject_frame_count = -1;
+
+void PromiseRejectCallback(v8::PromiseRejectMessage message) {
+ if (message.GetEvent() == v8::kPromiseRejectWithNoHandler) {
+ promise_reject_counter++;
+ CcTest::global()->Set(v8_str("rejected"), message.GetPromise());
+ CcTest::global()->Set(v8_str("value"), message.GetValue());
+ v8::Handle<v8::StackTrace> stack_trace =
+ v8::Exception::CreateMessage(message.GetValue())->GetStackTrace();
+ if (!stack_trace.IsEmpty()) {
+ promise_reject_frame_count = stack_trace->GetFrameCount();
+ if (promise_reject_frame_count > 0) {
+ CHECK(stack_trace->GetFrame(0)->GetScriptName()->Equals(v8_str("pro")));
+ promise_reject_line_number = stack_trace->GetFrame(0)->GetLineNumber();
+ } else {
+ promise_reject_line_number = -1;
+ }
+ }
+ } else {
+ promise_revoke_counter++;
+ CcTest::global()->Set(v8_str("revoked"), message.GetPromise());
+ CHECK(message.GetValue().IsEmpty());
+ }
+}
+
+
+v8::Handle<v8::Promise> GetPromise(const char* name) {
+ return v8::Handle<v8::Promise>::Cast(CcTest::global()->Get(v8_str(name)));
+}
+
+
+v8::Handle<v8::Value> RejectValue() {
+ return CcTest::global()->Get(v8_str("value"));
+}
+
+
+void ResetPromiseStates() {
+ promise_reject_counter = 0;
+ promise_revoke_counter = 0;
+ promise_reject_line_number = -1;
+ promise_reject_frame_count = -1;
+ CcTest::global()->Set(v8_str("rejected"), v8_str(""));
+ CcTest::global()->Set(v8_str("value"), v8_str(""));
+ CcTest::global()->Set(v8_str("revoked"), v8_str(""));
+}
+
+
+TEST(PromiseRejectCallback) {
+ LocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+
+ isolate->SetPromiseRejectCallback(PromiseRejectCallback);
+
+ ResetPromiseStates();
+
+ // Create promise p0.
+ CompileRun(
+ "var reject; \n"
+ "var p0 = new Promise( \n"
+ " function(res, rej) { \n"
+ " reject = rej; \n"
+ " } \n"
+ "); \n");
+ CHECK(!GetPromise("p0")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Add resolve handler (and default reject handler) to p0.
+ CompileRun("var p1 = p0.then(function(){});");
+ CHECK(GetPromise("p0")->HasHandler());
+ CHECK(!GetPromise("p1")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Reject p0.
+ CompileRun("reject('ppp');");
+ CHECK(GetPromise("p0")->HasHandler());
+ CHECK(!GetPromise("p1")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK_EQ(v8::kPromiseRejectWithNoHandler, reject_event);
+ CHECK(GetPromise("rejected")->Equals(GetPromise("p1")));
+ CHECK(RejectValue()->Equals(v8_str("ppp")));
+
+ // Reject p0 again. Callback is not triggered again.
+ CompileRun("reject();");
+ CHECK(GetPromise("p0")->HasHandler());
+ CHECK(!GetPromise("p1")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Add resolve handler to p1.
+ CompileRun("var p2 = p1.then(function(){});");
+ CHECK(GetPromise("p0")->HasHandler());
+ CHECK(GetPromise("p1")->HasHandler());
+ CHECK(!GetPromise("p2")->HasHandler());
+ CHECK_EQ(2, promise_reject_counter);
+ CHECK_EQ(1, promise_revoke_counter);
+ CHECK(GetPromise("rejected")->Equals(GetPromise("p2")));
+ CHECK(RejectValue()->Equals(v8_str("ppp")));
+ CHECK(GetPromise("revoked")->Equals(GetPromise("p1")));
+
+ ResetPromiseStates();
+
+ // Create promise q0.
+ CompileRun(
+ "var q0 = new Promise( \n"
+ " function(res, rej) { \n"
+ " reject = rej; \n"
+ " } \n"
+ "); \n");
+ CHECK(!GetPromise("q0")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Add reject handler to q0.
+ CompileRun("var q1 = q0.catch(function() {});");
+ CHECK(GetPromise("q0")->HasHandler());
+ CHECK(!GetPromise("q1")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Reject q0.
+ CompileRun("reject('qq')");
+ CHECK(GetPromise("q0")->HasHandler());
+ CHECK(!GetPromise("q1")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Add a new reject handler, which rejects by returning Promise.reject().
+ // The returned promise q_ triggers a reject callback at first, only to
+ // revoke it when returning it causes q2 to be rejected.
+ CompileRun(
+ "var q_;"
+ "var q2 = q0.catch( \n"
+ " function() { \n"
+ " q_ = Promise.reject('qqq'); \n"
+ " return q_; \n"
+ " } \n"
+ "); \n");
+ CHECK(GetPromise("q0")->HasHandler());
+ CHECK(!GetPromise("q1")->HasHandler());
+ CHECK(!GetPromise("q2")->HasHandler());
+ CHECK(GetPromise("q_")->HasHandler());
+ CHECK_EQ(2, promise_reject_counter);
+ CHECK_EQ(1, promise_revoke_counter);
+ CHECK(GetPromise("rejected")->Equals(GetPromise("q2")));
+ CHECK(GetPromise("revoked")->Equals(GetPromise("q_")));
+ CHECK(RejectValue()->Equals(v8_str("qqq")));
+
+ // Add a reject handler to the resolved q1, which rejects by throwing.
+ CompileRun(
+ "var q3 = q1.then( \n"
+ " function() { \n"
+ " throw 'qqqq'; \n"
+ " } \n"
+ "); \n");
+ CHECK(GetPromise("q0")->HasHandler());
+ CHECK(GetPromise("q1")->HasHandler());
+ CHECK(!GetPromise("q2")->HasHandler());
+ CHECK(!GetPromise("q3")->HasHandler());
+ CHECK_EQ(3, promise_reject_counter);
+ CHECK_EQ(1, promise_revoke_counter);
+ CHECK(GetPromise("rejected")->Equals(GetPromise("q3")));
+ CHECK(RejectValue()->Equals(v8_str("qqqq")));
+
+ ResetPromiseStates();
+
+ // Create promise r0, which has three handlers, two of which handle rejects.
+ CompileRun(
+ "var r0 = new Promise( \n"
+ " function(res, rej) { \n"
+ " reject = rej; \n"
+ " } \n"
+ "); \n"
+ "var r1 = r0.catch(function() {}); \n"
+ "var r2 = r0.then(function() {}); \n"
+ "var r3 = r0.then(function() {}, \n"
+ " function() {}); \n");
+ CHECK(GetPromise("r0")->HasHandler());
+ CHECK(!GetPromise("r1")->HasHandler());
+ CHECK(!GetPromise("r2")->HasHandler());
+ CHECK(!GetPromise("r3")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Reject r0.
+ CompileRun("reject('rrr')");
+ CHECK(GetPromise("r0")->HasHandler());
+ CHECK(!GetPromise("r1")->HasHandler());
+ CHECK(!GetPromise("r2")->HasHandler());
+ CHECK(!GetPromise("r3")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK(GetPromise("rejected")->Equals(GetPromise("r2")));
+ CHECK(RejectValue()->Equals(v8_str("rrr")));
+
+ // Add reject handler to r2.
+ CompileRun("var r4 = r2.catch(function() {});");
+ CHECK(GetPromise("r0")->HasHandler());
+ CHECK(!GetPromise("r1")->HasHandler());
+ CHECK(GetPromise("r2")->HasHandler());
+ CHECK(!GetPromise("r3")->HasHandler());
+ CHECK(!GetPromise("r4")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(1, promise_revoke_counter);
+ CHECK(GetPromise("revoked")->Equals(GetPromise("r2")));
+ CHECK(RejectValue()->Equals(v8_str("rrr")));
+
+ // Add reject handlers to r4.
+ CompileRun("var r5 = r4.then(function() {}, function() {});");
+ CHECK(GetPromise("r0")->HasHandler());
+ CHECK(!GetPromise("r1")->HasHandler());
+ CHECK(GetPromise("r2")->HasHandler());
+ CHECK(!GetPromise("r3")->HasHandler());
+ CHECK(GetPromise("r4")->HasHandler());
+ CHECK(!GetPromise("r5")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(1, promise_revoke_counter);
+
+ ResetPromiseStates();
+
+ // Create promise s0, which has three handlers, none of which handle rejects.
+ CompileRun(
+ "var s0 = new Promise( \n"
+ " function(res, rej) { \n"
+ " reject = rej; \n"
+ " } \n"
+ "); \n"
+ "var s1 = s0.then(function() {}); \n"
+ "var s2 = s0.then(function() {}); \n"
+ "var s3 = s0.then(function() {}); \n");
+ CHECK(GetPromise("s0")->HasHandler());
+ CHECK(!GetPromise("s1")->HasHandler());
+ CHECK(!GetPromise("s2")->HasHandler());
+ CHECK(!GetPromise("s3")->HasHandler());
+ CHECK_EQ(0, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+
+ // Reject s0.
+ CompileRun("reject('sss')");
+ CHECK(GetPromise("s0")->HasHandler());
+ CHECK(!GetPromise("s1")->HasHandler());
+ CHECK(!GetPromise("s2")->HasHandler());
+ CHECK(!GetPromise("s3")->HasHandler());
+ CHECK_EQ(3, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK(RejectValue()->Equals(v8_str("sss")));
+
+ // Test stack frames.
+ V8::SetCaptureStackTraceForUncaughtExceptions(true);
+
+ ResetPromiseStates();
+
+ // Create promise t0, which is rejected in the constructor with an error.
+ CompileRunWithOrigin(
+ "var t0 = new Promise( \n"
+ " function(res, rej) { \n"
+ " reference_error; \n"
+ " } \n"
+ "); \n",
+ "pro", 0, 0);
+ CHECK(!GetPromise("t0")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK_EQ(2, promise_reject_frame_count);
+ CHECK_EQ(3, promise_reject_line_number);
+
+ ResetPromiseStates();
+
+ // Create promise u0 and chain u1 to it, which is rejected via throw.
+ CompileRunWithOrigin(
+ "var u0 = Promise.resolve(); \n"
+ "var u1 = u0.then( \n"
+ " function() { \n"
+ " (function() { \n"
+ " throw new Error(); \n"
+ " })(); \n"
+ " } \n"
+ " ); \n",
+ "pro", 0, 0);
+ CHECK(GetPromise("u0")->HasHandler());
+ CHECK(!GetPromise("u1")->HasHandler());
+ CHECK_EQ(1, promise_reject_counter);
+ CHECK_EQ(0, promise_revoke_counter);
+ CHECK_EQ(2, promise_reject_frame_count);
+ CHECK_EQ(5, promise_reject_line_number);
+
+ // Throw in u3, which handles u1's rejection.
+ CompileRunWithOrigin(
+ "function f() { \n"
+ " return (function() { \n"
+ " return new Error(); \n"
+ " })(); \n"
+ "} \n"
+ "var u2 = Promise.reject(f()); \n"
+ "var u3 = u1.catch( \n"
+ " function() { \n"
+ " return u2; \n"
+ " } \n"
+ " ); \n",
+ "pro", 0, 0);
+ CHECK(GetPromise("u0")->HasHandler());
+ CHECK(GetPromise("u1")->HasHandler());
+ CHECK(GetPromise("u2")->HasHandler());
+ CHECK(!GetPromise("u3")->HasHandler());
+ CHECK_EQ(3, promise_reject_counter);
+ CHECK_EQ(2, promise_revoke_counter);
+ CHECK_EQ(3, promise_reject_frame_count);
+ CHECK_EQ(3, promise_reject_line_number);
+
+ ResetPromiseStates();
+
+ // Create promise rejected promise v0, which is incorrectly handled by v1
+ // via chaining cycle.
+ CompileRunWithOrigin(
+ "var v0 = Promise.reject(); \n"
+ "var v1 = v0.catch( \n"
+ " function() { \n"
+ " return v1; \n"
+ " } \n"
+ " ); \n",
+ "pro", 0, 0);
+ CHECK(GetPromise("v0")->HasHandler());
+ CHECK(!GetPromise("v1")->HasHandler());
+ CHECK_EQ(2, promise_reject_counter);
+ CHECK_EQ(1, promise_revoke_counter);
+ CHECK_EQ(0, promise_reject_frame_count);
+ CHECK_EQ(-1, promise_reject_line_number);
+}
+
+
void AnalyzeStackOfEvalWithSourceURL(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
@@ -17835,40 +18994,6 @@ TEST(IdleNotificationWithLargeHint) {
}
-TEST(Regress2107) {
- const intptr_t MB = 1024 * 1024;
- const int kIdlePauseInMs = 1000;
- LocalContext env;
- v8::Isolate* isolate = env->GetIsolate();
- v8::HandleScope scope(env->GetIsolate());
- intptr_t initial_size = CcTest::heap()->SizeOfObjects();
- // Send idle notification to start a round of incremental GCs.
- env->GetIsolate()->IdleNotification(kIdlePauseInMs);
- // Emulate 7 page reloads.
- for (int i = 0; i < 7; i++) {
- {
- v8::HandleScope inner_scope(env->GetIsolate());
- v8::Local<v8::Context> ctx = v8::Context::New(isolate);
- ctx->Enter();
- CreateGarbageInOldSpace();
- ctx->Exit();
- }
- env->GetIsolate()->ContextDisposedNotification();
- env->GetIsolate()->IdleNotification(kIdlePauseInMs);
- }
- // Create garbage and check that idle notification still collects it.
- CreateGarbageInOldSpace();
- intptr_t size_with_garbage = CcTest::heap()->SizeOfObjects();
- CHECK_GT(size_with_garbage, initial_size + MB);
- bool finished = false;
- for (int i = 0; i < 200 && !finished; i++) {
- finished = env->GetIsolate()->IdleNotification(kIdlePauseInMs);
- }
- intptr_t final_size = CcTest::heap()->SizeOfObjects();
- CHECK_LT(final_size, initial_size + 1);
-}
-
-
TEST(Regress2333) {
LocalContext env;
for (int i = 0; i < 3; i++) {
@@ -17998,10 +19123,11 @@ class VisitorImpl : public v8::ExternalResourceVisitor {
TEST(ExternalizeOldSpaceTwoByteCons) {
+ v8::Isolate* isolate = CcTest::isolate();
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
+ v8::HandleScope scope(isolate);
v8::Local<v8::String> cons =
- CompileRun("'Romeo Montague ' + 'Juliet Capulet'")->ToString();
+ CompileRun("'Romeo Montague ' + 'Juliet Capulet'")->ToString(isolate);
CHECK(v8::Utils::OpenHandle(*cons)->IsConsString());
CcTest::heap()->CollectAllAvailableGarbage();
CHECK(CcTest::heap()->old_pointer_space()->Contains(
@@ -18020,10 +19146,11 @@ TEST(ExternalizeOldSpaceTwoByteCons) {
TEST(ExternalizeOldSpaceOneByteCons) {
+ v8::Isolate* isolate = CcTest::isolate();
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
+ v8::HandleScope scope(isolate);
v8::Local<v8::String> cons =
- CompileRun("'Romeo Montague ' + 'Juliet Capulet'")->ToString();
+ CompileRun("'Romeo Montague ' + 'Juliet Capulet'")->ToString(isolate);
CHECK(v8::Utils::OpenHandle(*cons)->IsConsString());
CcTest::heap()->CollectAllAvailableGarbage();
CHECK(CcTest::heap()->old_pointer_space()->Contains(
@@ -18042,8 +19169,9 @@ TEST(ExternalizeOldSpaceOneByteCons) {
TEST(VisitExternalStrings) {
+ v8::Isolate* isolate = CcTest::isolate();
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
+ v8::HandleScope scope(isolate);
const char* string = "Some string";
uint16_t* two_byte_string = AsciiToTwoByteString(string);
TestResource* resource[4];
@@ -18113,7 +19241,7 @@ TEST(ExternalInternalizedStringCollectedAtTearDown) {
const char* s = "One string to test them all";
TestOneByteResource* inscription =
new TestOneByteResource(i::StrDup(s), &destroyed);
- v8::Local<v8::String> ring = CompileRun("ring")->ToString();
+ v8::Local<v8::String> ring = CompileRun("ring")->ToString(isolate);
CHECK(v8::Utils::OpenHandle(*ring)->IsInternalizedString());
ring->MakeExternal(inscription);
// Ring is still alive. Orcs are roaming freely across our lands.
@@ -18128,6 +19256,9 @@ TEST(ExternalInternalizedStringCollectedAtTearDown) {
TEST(ExternalInternalizedStringCollectedAtGC) {
+ // TODO(mvstanton): vector ics need weak support.
+ if (i::FLAG_vector_ics) return;
+
int destroyed = 0;
{ LocalContext env;
v8::HandleScope handle_scope(env->GetIsolate());
@@ -18135,7 +19266,7 @@ TEST(ExternalInternalizedStringCollectedAtGC) {
const char* s = "One string to test them all";
TestOneByteResource* inscription =
new TestOneByteResource(i::StrDup(s), &destroyed);
- v8::Local<v8::String> ring = CompileRun("ring")->ToString();
+ v8::Local<v8::String> ring = CompileRun("ring").As<v8::String>();
CHECK(v8::Utils::OpenHandle(*ring)->IsInternalizedString());
ring->MakeExternal(inscription);
// Ring is still alive. Orcs are roaming freely across our lands.
@@ -18274,7 +19405,7 @@ static void SpaghettiIncident(
const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
v8::TryCatch tc;
- v8::Handle<v8::String> str(args[0]->ToString());
+ v8::Handle<v8::String> str(args[0]->ToString(args.GetIsolate()));
USE(str);
if (tc.HasCaught())
tc.ReThrow();
@@ -18625,7 +19756,7 @@ static void SetterWhichSetsYOnThisTo23(
}
-void FooGetInterceptor(Local<String> name,
+void FooGetInterceptor(Local<Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject());
CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject());
@@ -18634,8 +19765,7 @@ void FooGetInterceptor(Local<String> name,
}
-void FooSetInterceptor(Local<String> name,
- Local<Value> value,
+void FooSetInterceptor(Local<Name> name, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
CHECK(v8::Utils::OpenHandle(*info.This())->IsJSObject());
CHECK(v8::Utils::OpenHandle(*info.Holder())->IsJSObject());
@@ -18681,15 +19811,13 @@ script = v8_compile("new C2();");
static void NamedPropertyGetterWhichReturns42(
- Local<String> name,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_num(42));
}
static void NamedPropertySetterWhichSetsYOnThisTo23(
- Local<String> name,
- Local<Value> value,
+ Local<Name> name, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
if (name->Equals(v8_str("x"))) {
Local<Object>::Cast(info.This())->Set(v8_str("y"), v8_num(23));
@@ -18701,8 +19829,9 @@ THREADED_TEST(InterceptorOnConstructorPrototype) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
- NamedPropertySetterWhichSetsYOnThisTo23);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ NamedPropertyGetterWhichReturns42,
+ NamedPropertySetterWhichSetsYOnThisTo23));
LocalContext context;
context->Global()->Set(v8_str("P"), templ->NewInstance());
CompileRun("function C1() {"
@@ -19860,7 +20989,7 @@ TEST(PersistentHandleVisitor) {
CHECK_EQ(42, object.WrapperClassId());
Visitor42 visitor(&object);
- v8::V8::VisitHandlesWithClassIds(&visitor);
+ v8::V8::VisitHandlesWithClassIds(isolate, &visitor);
CHECK_EQ(1, visitor.counter_);
object.Reset();
@@ -19994,8 +21123,8 @@ THREADED_TEST(Equals) {
}
-static void Getter(v8::Local<v8::String> property,
- const v8::PropertyCallbackInfo<v8::Value>& info ) {
+static void Getter(v8::Local<v8::Name> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8_str("42!"));
}
@@ -20014,7 +21143,8 @@ TEST(NamedEnumeratorAndForIn) {
v8::Context::Scope context_scope(context.local());
v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate);
- tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator);
+ tmpl->SetHandler(v8::NamedPropertyHandlerConfiguration(Getter, NULL, NULL,
+ NULL, Enumerator));
context->Global()->Set(v8_str("o"), tmpl->NewInstance());
v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
"var result = []; for (var k in o) result.push(k); result"));
@@ -20159,8 +21289,7 @@ void HasOwnPropertyIndexedPropertyGetter(
void HasOwnPropertyNamedPropertyGetter(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
if (property->Equals(v8_str("foo"))) info.GetReturnValue().Set(v8_str("yes"));
}
@@ -20172,15 +21301,13 @@ void HasOwnPropertyIndexedPropertyQuery(
void HasOwnPropertyNamedPropertyQuery(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Integer>& info) {
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
if (property->Equals(v8_str("foo"))) info.GetReturnValue().Set(1);
}
void HasOwnPropertyNamedPropertyQuery2(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Integer>& info) {
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Integer>& info) {
if (property->Equals(v8_str("bar"))) info.GetReturnValue().Set(1);
}
@@ -20209,7 +21336,7 @@ TEST(HasOwnProperty) {
"Bar.prototype = new Foo();"
"new Bar();");
CHECK(value->IsObject());
- Handle<Object> object = value->ToObject();
+ Handle<Object> object = value->ToObject(isolate);
CHECK(object->Has(v8_str("foo")));
CHECK(!object->HasOwnProperty(v8_str("foo")));
CHECK(object->HasOwnProperty(v8_str("bar")));
@@ -20219,7 +21346,8 @@ TEST(HasOwnProperty) {
}
{ // Check named getter interceptors.
Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ HasOwnPropertyNamedPropertyGetter));
Handle<Object> instance = templ->NewInstance();
CHECK(!instance->HasOwnProperty(v8_str("42")));
CHECK(instance->HasOwnProperty(v8_str("foo")));
@@ -20227,7 +21355,8 @@ TEST(HasOwnProperty) {
}
{ // Check indexed getter interceptors.
Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(HasOwnPropertyIndexedPropertyGetter);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ HasOwnPropertyIndexedPropertyGetter));
Handle<Object> instance = templ->NewInstance();
CHECK(instance->HasOwnProperty(v8_str("42")));
CHECK(!instance->HasOwnProperty(v8_str("43")));
@@ -20235,14 +21364,16 @@ TEST(HasOwnProperty) {
}
{ // Check named query interceptors.
Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(0, 0, HasOwnPropertyNamedPropertyQuery);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ 0, 0, HasOwnPropertyNamedPropertyQuery));
Handle<Object> instance = templ->NewInstance();
CHECK(instance->HasOwnProperty(v8_str("foo")));
CHECK(!instance->HasOwnProperty(v8_str("bar")));
}
{ // Check indexed query interceptors.
Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(0, 0, HasOwnPropertyIndexedPropertyQuery);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ 0, 0, HasOwnPropertyIndexedPropertyQuery));
Handle<Object> instance = templ->NewInstance();
CHECK(instance->HasOwnProperty(v8_str("42")));
CHECK(!instance->HasOwnProperty(v8_str("41")));
@@ -20256,9 +21387,9 @@ TEST(HasOwnProperty) {
}
{ // Check that query wins on disagreement.
Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter,
- 0,
- HasOwnPropertyNamedPropertyQuery2);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ HasOwnPropertyNamedPropertyGetter, 0,
+ HasOwnPropertyNamedPropertyQuery2));
Handle<Object> instance = templ->NewInstance();
CHECK(!instance->HasOwnProperty(v8_str("foo")));
CHECK(instance->HasOwnProperty(v8_str("bar")));
@@ -20270,9 +21401,8 @@ TEST(IndexedInterceptorWithStringProto) {
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate);
- templ->SetIndexedPropertyHandler(NULL,
- NULL,
- HasOwnPropertyIndexedPropertyQuery);
+ templ->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ NULL, NULL, HasOwnPropertyIndexedPropertyQuery));
LocalContext context;
context->Global()->Set(v8_str("obj"), templ->NewInstance());
CompileRun("var s = new String('foobar'); obj.__proto__ = s;");
@@ -20417,29 +21547,40 @@ THREADED_TEST(ReadOnlyIndexedProperties) {
}
+static int CountLiveMapsInMapCache(i::Context* context) {
+ i::FixedArray* map_cache = i::FixedArray::cast(context->map_cache());
+ int length = map_cache->length();
+ int count = 0;
+ for (int i = 0; i < length; i++) {
+ i::Object* value = map_cache->get(i);
+ if (value->IsWeakCell() && !i::WeakCell::cast(value)->cleared()) count++;
+ }
+ return count;
+}
+
+
THREADED_TEST(Regress1516) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
+ // Object with 20 properties is not a common case, so it should be removed
+ // from the cache after GC.
{ v8::HandleScope temp_scope(context->GetIsolate());
- CompileRun("({'a': 0})");
+ CompileRun(
+ "({"
+ "'a00': 0, 'a01': 0, 'a02': 0, 'a03': 0, 'a04': 0, "
+ "'a05': 0, 'a06': 0, 'a07': 0, 'a08': 0, 'a09': 0, "
+ "'a10': 0, 'a11': 0, 'a12': 0, 'a13': 0, 'a14': 0, "
+ "'a15': 0, 'a16': 0, 'a17': 0, 'a18': 0, 'a19': 0, "
+ "})");
}
- int elements;
- { i::MapCache* map_cache =
- i::MapCache::cast(CcTest::i_isolate()->context()->map_cache());
- elements = map_cache->NumberOfElements();
- CHECK_LE(1, elements);
- }
+ int elements = CountLiveMapsInMapCache(CcTest::i_isolate()->context());
+ CHECK_LE(1, elements);
- CcTest::heap()->CollectAllGarbage(
- i::Heap::kAbortIncrementalMarkingMask);
- { i::Object* raw_map_cache = CcTest::i_isolate()->context()->map_cache();
- if (raw_map_cache != CcTest::heap()->undefined_value()) {
- i::MapCache* map_cache = i::MapCache::cast(raw_map_cache);
- CHECK_GT(elements, map_cache->NumberOfElements());
- }
- }
+ CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
+
+ CHECK_GT(elements, CountLiveMapsInMapCache(CcTest::i_isolate()->context()));
}
@@ -20448,12 +21589,11 @@ static bool BlockProtoNamedSecurityTestCallback(Local<v8::Object> global,
v8::AccessType type,
Local<Value> data) {
// Only block read access to __proto__.
- if (type == v8::ACCESS_GET &&
- name->IsString() &&
- name->ToString()->Length() == 9 &&
- name->ToString()->Utf8Length() == 9) {
+ if (type == v8::ACCESS_GET && name->IsString() &&
+ name.As<v8::String>()->Length() == 9 &&
+ name.As<v8::String>()->Utf8Length() == 9) {
char buffer[10];
- CHECK_EQ(10, name->ToString()->WriteUtf8(buffer));
+ CHECK_EQ(10, name.As<v8::String>()->WriteUtf8(buffer));
return strncmp(buffer, "__proto__", 9) != 0;
}
@@ -20500,8 +21640,7 @@ THREADED_TEST(Regress93759) {
context->Global();
// Global object, the prototype of proxy_object. No security checks.
- Local<Object> global_object =
- proxy_object->GetPrototype()->ToObject();
+ Local<Object> global_object = proxy_object->GetPrototype()->ToObject(isolate);
// Hidden prototype without security check.
Local<Object> hidden_prototype =
@@ -20545,7 +21684,7 @@ THREADED_TEST(Regress93759) {
Local<Value> result5 = CompileRun("Object.getPrototypeOf(hidden)");
CHECK(result5->Equals(
- object_with_hidden->GetPrototype()->ToObject()->GetPrototype()));
+ object_with_hidden->GetPrototype()->ToObject(isolate)->GetPrototype()));
Local<Value> result6 = CompileRun("Object.getPrototypeOf(phidden)");
CHECK(result6.IsEmpty());
@@ -20581,8 +21720,8 @@ static void TestReceiver(Local<Value> expected_result,
const char* code) {
Local<Value> result = CompileRun(code);
CHECK(result->IsObject());
- CHECK(expected_receiver->Equals(result->ToObject()->Get(1)));
- CHECK(expected_result->Equals(result->ToObject()->Get(0)));
+ CHECK(expected_receiver->Equals(result.As<v8::Object>()->Get(1)));
+ CHECK(expected_result->Equals(result.As<v8::Object>()->Get(0)));
}
@@ -20970,8 +22109,8 @@ static void DebugEventInObserver(const v8::Debug::EventDetails& event_details) {
Handle<Object> exec_state = event_details.GetExecutionState();
Handle<Value> break_id = exec_state->Get(v8_str("break_id"));
CompileRun("function f(id) { new FrameDetails(id, 0); }");
- Handle<Function> fun = Handle<Function>::Cast(
- CcTest::global()->Get(v8_str("f"))->ToObject());
+ Handle<Function> fun =
+ Handle<Function>::Cast(CcTest::global()->Get(v8_str("f")));
fun->Call(CcTest::global(), 1, &break_id);
}
@@ -20995,7 +22134,7 @@ TEST(Regress385349) {
}
-#ifdef DEBUG
+#ifdef ENABLE_DISASSEMBLER
static int probes_counter = 0;
static int misses_counter = 0;
static int updates_counter = 0;
@@ -21029,7 +22168,7 @@ static const char* kMegamorphicTestProgram =
static void StubCacheHelper(bool primary) {
-#ifdef DEBUG
+#ifdef ENABLE_DISASSEMBLER
i::FLAG_native_code_counters = true;
if (primary) {
i::FLAG_test_primary_stub_cache = true;
@@ -21376,7 +22515,8 @@ static void Helper137002(bool do_store,
LocalContext context;
Local<ObjectTemplate> templ = ObjectTemplate::New(context->GetIsolate());
if (interceptor) {
- templ->SetNamedPropertyHandler(FooGetInterceptor, FooSetInterceptor);
+ templ->SetHandler(v8::NamedPropertyHandlerConfiguration(FooGetInterceptor,
+ FooSetInterceptor));
} else {
templ->SetAccessor(v8_str("foo"),
GetterWhichReturns42,
@@ -21782,7 +22922,8 @@ void CatcherCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
void HasOwnPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
- args[0]->ToObject()->HasOwnProperty(args[1]->ToString());
+ args[0]->ToObject(args.GetIsolate())->HasOwnProperty(
+ args[1]->ToString(args.GetIsolate()));
}
@@ -21998,8 +23139,6 @@ class RequestInterruptTestBase {
TestBody();
- isolate_->ClearInterrupt();
-
// Verify we arrived here because interruptor was called
// not due to a bug causing us to exit the loop too early.
CHECK(!should_continue());
@@ -22149,7 +23288,8 @@ class RequestInterruptTestWithMethodCallAndInterceptor
proto->Set(v8_str("shouldContinue"), Function::New(
isolate_, ShouldContinueCallback, v8::External::New(isolate_, this)));
v8::Local<v8::ObjectTemplate> instance_template = t->InstanceTemplate();
- instance_template->SetNamedPropertyHandler(EmptyInterceptor);
+ instance_template->SetHandler(
+ v8::NamedPropertyHandlerConfiguration(EmptyInterceptor));
env_->Global()->Set(v8_str("Klass"), t->GetFunction());
@@ -22158,9 +23298,7 @@ class RequestInterruptTestWithMethodCallAndInterceptor
private:
static void EmptyInterceptor(
- Local<String> property,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
- }
+ Local<Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {}
};
@@ -22249,10 +23387,9 @@ TEST(RequestInterruptTestWithMathAbs) {
}
-class ClearInterruptFromAnotherThread
- : public RequestInterruptTestBase {
+class RequestMultipleInterrupts : public RequestInterruptTestBase {
public:
- ClearInterruptFromAnotherThread() : i_thread(this), sem2_(0) { }
+ RequestMultipleInterrupts() : i_thread(this), counter_(0) {}
virtual void StartInterruptThread() {
i_thread.Start();
@@ -22269,39 +23406,33 @@ class ClearInterruptFromAnotherThread
private:
class InterruptThread : public v8::base::Thread {
public:
- explicit InterruptThread(ClearInterruptFromAnotherThread* test)
+ enum { NUM_INTERRUPTS = 10 };
+ explicit InterruptThread(RequestMultipleInterrupts* test)
: Thread(Options("RequestInterruptTest")), test_(test) {}
virtual void Run() {
test_->sem_.Wait();
- test_->isolate_->RequestInterrupt(&OnInterrupt, test_);
- test_->sem_.Wait();
- test_->isolate_->ClearInterrupt();
- test_->sem2_.Signal();
+ for (int i = 0; i < NUM_INTERRUPTS; i++) {
+ test_->isolate_->RequestInterrupt(&OnInterrupt, test_);
+ }
}
static void OnInterrupt(v8::Isolate* isolate, void* data) {
- ClearInterruptFromAnotherThread* test =
- reinterpret_cast<ClearInterruptFromAnotherThread*>(data);
- test->sem_.Signal();
- bool success = test->sem2_.WaitFor(v8::base::TimeDelta::FromSeconds(2));
- // Crash instead of timeout to make this failure more prominent.
- CHECK(success);
- test->should_continue_ = false;
+ RequestMultipleInterrupts* test =
+ reinterpret_cast<RequestMultipleInterrupts*>(data);
+ test->should_continue_ = ++test->counter_ < NUM_INTERRUPTS;
}
private:
- ClearInterruptFromAnotherThread* test_;
+ RequestMultipleInterrupts* test_;
};
InterruptThread i_thread;
- v8::base::Semaphore sem2_;
+ int counter_;
};
-TEST(ClearInterruptFromAnotherThread) {
- ClearInterruptFromAnotherThread().RunTest();
-}
+TEST(RequestMultipleInterrupts) { RequestMultipleInterrupts().RunTest(); }
static Local<Value> function_new_expected_env;
@@ -22606,6 +23737,7 @@ TEST(Promises) {
Handle<v8::Promise::Resolver> rr = v8::Promise::Resolver::New(isolate);
Handle<v8::Promise> p = pr->GetPromise();
Handle<v8::Promise> r = rr->GetPromise();
+ CHECK_EQ(isolate, p->GetIsolate());
// IsPromise predicate.
CHECK(p->IsPromise());
@@ -22867,10 +23999,8 @@ TEST(ScriptNameAndLineNumber) {
CHECK_EQ(13, line_number);
}
-
-void SourceURLHelper(const char* source, const char* expected_source_url,
- const char* expected_source_mapping_url) {
- Local<Script> script = v8_compile(source);
+void CheckMagicComments(Handle<Script> script, const char* expected_source_url,
+ const char* expected_source_mapping_url) {
if (expected_source_url != NULL) {
v8::String::Utf8Value url(script->GetUnboundScript()->GetSourceURL());
CHECK_EQ(expected_source_url, *url);
@@ -22886,6 +24016,12 @@ void SourceURLHelper(const char* source, const char* expected_source_url,
}
}
+void SourceURLHelper(const char* source, const char* expected_source_url,
+ const char* expected_source_mapping_url) {
+ Local<Script> script = v8_compile(source);
+ CheckMagicComments(script, expected_source_url, expected_source_mapping_url);
+}
+
TEST(ScriptSourceURLAndSourceMappingURL) {
LocalContext env;
@@ -23077,7 +24213,9 @@ class TestSourceStream : public v8::ScriptCompiler::ExternalSourceStream {
void RunStreamingTest(const char** chunks,
v8::ScriptCompiler::StreamedSource::Encoding encoding =
v8::ScriptCompiler::StreamedSource::ONE_BYTE,
- bool expected_success = true) {
+ bool expected_success = true,
+ const char* expected_source_url = NULL,
+ const char* expected_source_mapping_url = NULL) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
@@ -23106,6 +24244,8 @@ void RunStreamingTest(const char** chunks,
v8::Handle<Value> result(script->Run());
// All scripts are supposed to return the fixed value 13 when ran.
CHECK_EQ(13, result->Int32Value());
+ CheckMagicComments(script, expected_source_url,
+ expected_source_mapping_url);
} else {
CHECK(script.IsEmpty());
CHECK(try_catch.HasCaught());
@@ -23170,14 +24310,14 @@ TEST(StreamingScriptWithParseError) {
TEST(StreamingUtf8Script) {
- // We'd want to write \uc481 instead of \xeb\x91\x80, but Windows compilers
+ // We'd want to write \uc481 instead of \xec\x92\x81, but Windows compilers
// don't like it.
const char* chunk1 =
"function foo() {\n"
" // This function will contain an UTF-8 character which is not in\n"
" // ASCII.\n"
- " var foob\xeb\x91\x80r = 13;\n"
- " return foob\xeb\x91\x80r;\n"
+ " var foob\xec\x92\x81r = 13;\n"
+ " return foob\xec\x92\x81r;\n"
"}\n";
const char* chunks[] = {chunk1, "foo(); ", NULL};
RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8);
@@ -23188,7 +24328,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) {
// A sanity check to prove that the approach of splitting UTF-8
// characters is correct. Here is an UTF-8 character which will take three
// bytes.
- const char* reference = "\xeb\x91\x80";
+ const char* reference = "\xec\x92\x81";
CHECK(3u == strlen(reference)); // NOLINT - no CHECK_EQ for unsigned.
char chunk1[] =
@@ -23198,7 +24338,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) {
" var foob";
char chunk2[] =
"XXXr = 13;\n"
- " return foob\xeb\x91\x80r;\n"
+ " return foob\xec\x92\x81r;\n"
"}\n";
for (int i = 0; i < 3; ++i) {
chunk2[i] = reference[i];
@@ -23211,7 +24351,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersSanityCheck) {
TEST(StreamingUtf8ScriptWithSplitCharacters) {
// Stream data where a multi-byte UTF-8 character is split between two data
// chunks.
- const char* reference = "\xeb\x91\x80";
+ const char* reference = "\xec\x92\x81";
char chunk1[] =
"function foo() {\n"
" // This function will contain an UTF-8 character which is not in\n"
@@ -23219,7 +24359,7 @@ TEST(StreamingUtf8ScriptWithSplitCharacters) {
" var foobX";
char chunk2[] =
"XXr = 13;\n"
- " return foob\xeb\x91\x80r;\n"
+ " return foob\xec\x92\x81r;\n"
"}\n";
chunk1[strlen(chunk1) - 1] = reference[0];
chunk2[0] = reference[1];
@@ -23235,7 +24375,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) {
// Case 1: a chunk contains only bytes for a split character (and no other
// data). This kind of a chunk would be exceptionally small, but we should
// still decode it correctly.
- const char* reference = "\xeb\x91\x80";
+ const char* reference = "\xec\x92\x81";
// The small chunk is at the beginning of the split character
{
char chunk1[] =
@@ -23246,7 +24386,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) {
char chunk2[] = "XX";
char chunk3[] =
"Xr = 13;\n"
- " return foob\xeb\x91\x80r;\n"
+ " return foob\xec\x92\x81r;\n"
"}\n";
chunk2[0] = reference[0];
chunk2[1] = reference[1];
@@ -23264,7 +24404,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) {
char chunk2[] = "XX";
char chunk3[] =
"r = 13;\n"
- " return foob\xeb\x91\x80r;\n"
+ " return foob\xec\x92\x81r;\n"
"}\n";
chunk1[strlen(chunk1) - 1] = reference[0];
chunk2[0] = reference[1];
@@ -23276,8 +24416,8 @@ TEST(StreamingUtf8ScriptWithSplitCharactersValidEdgeCases) {
// decoded correctly and not just ignored.
{
char chunk1[] =
- "var foob\xeb\x91\x80 = 13;\n"
- "foob\xeb\x91\x80";
+ "var foob\xec\x92\x81 = 13;\n"
+ "foob\xec\x92\x81";
const char* chunks[] = {chunk1, NULL};
RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8);
}
@@ -23288,7 +24428,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersInvalidEdgeCases) {
// Test cases where a UTF-8 character is split over several chunks. Those
// cases are not supported (the embedder should give the data in big enough
// chunks), but we shouldn't crash, just produce a parse error.
- const char* reference = "\xeb\x91\x80";
+ const char* reference = "\xec\x92\x81";
char chunk1[] =
"function foo() {\n"
" // This function will contain an UTF-8 character which is not in\n"
@@ -23297,7 +24437,7 @@ TEST(StreamingUtf8ScriptWithSplitCharactersInvalidEdgeCases) {
char chunk2[] = "X";
char chunk3[] =
"Xr = 13;\n"
- " return foob\xeb\x91\x80r;\n"
+ " return foob\xec\x92\x81r;\n"
"}\n";
chunk1[strlen(chunk1) - 1] = reference[0];
chunk2[0] = reference[1];
@@ -23332,6 +24472,7 @@ TEST(StreamingProducesParserCache) {
const v8::ScriptCompiler::CachedData* cached_data = source.GetCachedData();
CHECK(cached_data != NULL);
CHECK(cached_data->data != NULL);
+ CHECK(!cached_data->rejected);
CHECK_GT(cached_data->length, 0);
}
@@ -23339,7 +24480,7 @@ TEST(StreamingProducesParserCache) {
TEST(StreamingScriptWithInvalidUtf8) {
// Regression test for a crash: test that invalid UTF-8 bytes in the end of a
// chunk don't produce a crash.
- const char* reference = "\xeb\x91\x80\x80\x80";
+ const char* reference = "\xec\x92\x81\x80\x80";
char chunk1[] =
"function foo() {\n"
" // This function will contain an UTF-8 character which is not in\n"
@@ -23347,10 +24488,274 @@ TEST(StreamingScriptWithInvalidUtf8) {
" var foobXXXXX"; // Too many bytes which look like incomplete chars!
char chunk2[] =
"r = 13;\n"
- " return foob\xeb\x91\x80\x80\x80r;\n"
+ " return foob\xec\x92\x81\x80\x80r;\n"
"}\n";
for (int i = 0; i < 5; ++i) chunk1[strlen(chunk1) - 5 + i] = reference[i];
const char* chunks[] = {chunk1, chunk2, "foo();", NULL};
RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, false);
}
+
+
+TEST(StreamingUtf8ScriptWithMultipleMultibyteCharactersSomeSplit) {
+ // Regression test: Stream data where there are several multi-byte UTF-8
+ // characters in a sequence and one of them is split between two data chunks.
+ const char* reference = "\xec\x92\x81";
+ char chunk1[] =
+ "function foo() {\n"
+ " // This function will contain an UTF-8 character which is not in\n"
+ " // ASCII.\n"
+ " var foob\xec\x92\x81X";
+ char chunk2[] =
+ "XXr = 13;\n"
+ " return foob\xec\x92\x81\xec\x92\x81r;\n"
+ "}\n";
+ chunk1[strlen(chunk1) - 1] = reference[0];
+ chunk2[0] = reference[1];
+ chunk2[1] = reference[2];
+ const char* chunks[] = {chunk1, chunk2, "foo();", NULL};
+ RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8);
+}
+
+
+TEST(StreamingUtf8ScriptWithMultipleMultibyteCharactersSomeSplit2) {
+ // Another regression test, similar to the previous one. The difference is
+ // that the split character is not the last one in the sequence.
+ const char* reference = "\xec\x92\x81";
+ char chunk1[] =
+ "function foo() {\n"
+ " // This function will contain an UTF-8 character which is not in\n"
+ " // ASCII.\n"
+ " var foobX";
+ char chunk2[] =
+ "XX\xec\x92\x81r = 13;\n"
+ " return foob\xec\x92\x81\xec\x92\x81r;\n"
+ "}\n";
+ chunk1[strlen(chunk1) - 1] = reference[0];
+ chunk2[0] = reference[1];
+ chunk2[1] = reference[2];
+ const char* chunks[] = {chunk1, chunk2, "foo();", NULL};
+ RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8);
+}
+
+
+void TestInvalidCacheData(v8::ScriptCompiler::CompileOptions option) {
+ const char* garbage = "garbage garbage garbage garbage garbage garbage";
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(garbage);
+ int length = 16;
+ v8::ScriptCompiler::CachedData* cached_data =
+ new v8::ScriptCompiler::CachedData(data, length);
+ DCHECK(!cached_data->rejected);
+ v8::ScriptOrigin origin(v8_str("origin"));
+ v8::ScriptCompiler::Source source(v8_str("42"), origin, cached_data);
+ v8::Handle<v8::Script> script =
+ v8::ScriptCompiler::Compile(CcTest::isolate(), &source, option);
+ CHECK(cached_data->rejected);
+ CHECK_EQ(42, script->Run()->Int32Value());
+}
+
+
+TEST(InvalidCacheData) {
+ v8::V8::Initialize();
+ v8::HandleScope scope(CcTest::isolate());
+ LocalContext context;
+ TestInvalidCacheData(v8::ScriptCompiler::kConsumeParserCache);
+ TestInvalidCacheData(v8::ScriptCompiler::kConsumeCodeCache);
+}
+
+
+TEST(ParserCacheRejectedGracefully) {
+ i::FLAG_min_preparse_length = 0;
+ v8::V8::Initialize();
+ v8::HandleScope scope(CcTest::isolate());
+ LocalContext context;
+ // Produce valid cached data.
+ v8::ScriptOrigin origin(v8_str("origin"));
+ v8::Local<v8::String> source_str = v8_str("function foo() {}");
+ v8::ScriptCompiler::Source source(source_str, origin);
+ v8::Handle<v8::Script> script = v8::ScriptCompiler::Compile(
+ CcTest::isolate(), &source, v8::ScriptCompiler::kProduceParserCache);
+ CHECK(!script.IsEmpty());
+ const v8::ScriptCompiler::CachedData* original_cached_data =
+ source.GetCachedData();
+ CHECK(original_cached_data != NULL);
+ CHECK(original_cached_data->data != NULL);
+ CHECK(!original_cached_data->rejected);
+ CHECK_GT(original_cached_data->length, 0);
+ // Recompiling the same script with it won't reject the data.
+ {
+ v8::ScriptCompiler::Source source_with_cached_data(
+ source_str, origin,
+ new v8::ScriptCompiler::CachedData(original_cached_data->data,
+ original_cached_data->length));
+ v8::Handle<v8::Script> script =
+ v8::ScriptCompiler::Compile(CcTest::isolate(), &source_with_cached_data,
+ v8::ScriptCompiler::kConsumeParserCache);
+ CHECK(!script.IsEmpty());
+ const v8::ScriptCompiler::CachedData* new_cached_data =
+ source_with_cached_data.GetCachedData();
+ CHECK(new_cached_data != NULL);
+ CHECK(!new_cached_data->rejected);
+ }
+ // Compile an incompatible script with the cached data. The new script doesn't
+ // have the same starting position for the function as the old one, so the old
+ // cached data will be incompatible with it and will be rejected.
+ {
+ v8::Local<v8::String> incompatible_source_str =
+ v8_str(" function foo() {}");
+ v8::ScriptCompiler::Source source_with_cached_data(
+ incompatible_source_str, origin,
+ new v8::ScriptCompiler::CachedData(original_cached_data->data,
+ original_cached_data->length));
+ v8::Handle<v8::Script> script =
+ v8::ScriptCompiler::Compile(CcTest::isolate(), &source_with_cached_data,
+ v8::ScriptCompiler::kConsumeParserCache);
+ CHECK(!script.IsEmpty());
+ const v8::ScriptCompiler::CachedData* new_cached_data =
+ source_with_cached_data.GetCachedData();
+ CHECK(new_cached_data != NULL);
+ CHECK(new_cached_data->rejected);
+ }
+}
+
+
+TEST(StringConcatOverflow) {
+ v8::V8::Initialize();
+ v8::HandleScope scope(CcTest::isolate());
+ RandomLengthOneByteResource* r =
+ new RandomLengthOneByteResource(i::String::kMaxLength);
+ v8::Local<v8::String> str = v8::String::NewExternal(CcTest::isolate(), r);
+ CHECK(!str.IsEmpty());
+ v8::TryCatch try_catch;
+ v8::Local<v8::String> result = v8::String::Concat(str, str);
+ CHECK(result.IsEmpty());
+ CHECK(!try_catch.HasCaught());
+}
+
+
+TEST(TurboAsmDisablesNeuter) {
+ v8::V8::Initialize();
+ v8::HandleScope scope(CcTest::isolate());
+ LocalContext context;
+#if V8_TURBOFAN_TARGET
+ bool should_be_neuterable = !i::FLAG_turbo_asm;
+#else
+ bool should_be_neuterable = true;
+#endif
+ const char* load =
+ "function Module(stdlib, foreign, heap) {"
+ " 'use asm';"
+ " var MEM32 = new stdlib.Int32Array(heap);"
+ " function load() { return MEM32[0]; }"
+ " return { load: load };"
+ "}"
+ "var buffer = new ArrayBuffer(4);"
+ "Module(this, {}, buffer).load();"
+ "buffer";
+
+ v8::Local<v8::ArrayBuffer> result = CompileRun(load).As<v8::ArrayBuffer>();
+ CHECK_EQ(should_be_neuterable, result->IsNeuterable());
+
+ const char* store =
+ "function Module(stdlib, foreign, heap) {"
+ " 'use asm';"
+ " var MEM32 = new stdlib.Int32Array(heap);"
+ " function store() { MEM32[0] = 0; }"
+ " return { store: store };"
+ "}"
+ "var buffer = new ArrayBuffer(4);"
+ "Module(this, {}, buffer).store();"
+ "buffer";
+
+ result = CompileRun(store).As<v8::ArrayBuffer>();
+ CHECK_EQ(should_be_neuterable, result->IsNeuterable());
+}
+
+
+TEST(GetPrototypeAccessControl) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handle_scope(isolate);
+ LocalContext env;
+
+ v8::Handle<v8::ObjectTemplate> obj_template =
+ v8::ObjectTemplate::New(isolate);
+ obj_template->SetAccessCheckCallbacks(BlockEverythingNamed,
+ BlockEverythingIndexed);
+
+ env->Global()->Set(v8_str("prohibited"), obj_template->NewInstance());
+
+ {
+ v8::TryCatch try_catch;
+ CompileRun(
+ "function f() { %_GetPrototype(prohibited); }"
+ "%OptimizeFunctionOnNextCall(f);"
+ "f();");
+ CHECK(try_catch.HasCaught());
+ }
+}
+
+
+TEST(GetPrototypeHidden) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handle_scope(isolate);
+ LocalContext env;
+
+ Handle<FunctionTemplate> t = FunctionTemplate::New(isolate);
+ t->SetHiddenPrototype(true);
+ Handle<Object> proto = t->GetFunction()->NewInstance();
+ Handle<Object> object = Object::New(isolate);
+ Handle<Object> proto2 = Object::New(isolate);
+ object->SetPrototype(proto);
+ proto->SetPrototype(proto2);
+
+ env->Global()->Set(v8_str("object"), object);
+ env->Global()->Set(v8_str("proto"), proto);
+ env->Global()->Set(v8_str("proto2"), proto2);
+
+ v8::Handle<v8::Value> result = CompileRun("%_GetPrototype(object)");
+ CHECK(result->Equals(proto2));
+
+ result = CompileRun(
+ "function f() { return %_GetPrototype(object); }"
+ "%OptimizeFunctionOnNextCall(f);"
+ "f()");
+ CHECK(result->Equals(proto2));
+}
+
+
+TEST(ClassPrototypeCreationContext) {
+ i::FLAG_harmony_classes = true;
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::HandleScope handle_scope(isolate);
+ LocalContext env;
+
+ Handle<Object> result = Handle<Object>::Cast(
+ CompileRun("'use strict'; class Example { }; Example.prototype"));
+ CHECK(env.local() == result->CreationContext());
+}
+
+
+TEST(SimpleStreamingScriptWithSourceURL) {
+ const char* chunks[] = {"function foo() { ret", "urn 13; } f", "oo();\n",
+ "//# sourceURL=bar2.js\n", NULL};
+ RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, true,
+ "bar2.js");
+}
+
+
+TEST(StreamingScriptWithSplitSourceURL) {
+ const char* chunks[] = {"function foo() { ret", "urn 13; } f",
+ "oo();\n//# sourceURL=b", "ar2.js\n", NULL};
+ RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, true,
+ "bar2.js");
+}
+
+
+TEST(StreamingScriptWithSourceMappingURLInTheMiddle) {
+ const char* chunks[] = {"function foo() { ret", "urn 13; }\n//#",
+ " sourceMappingURL=bar2.js\n", "foo();", NULL};
+ RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8, true, NULL,
+ "bar2.js");
+}
diff --git a/test/cctest/test-assembler-arm.cc b/test/cctest/test-assembler-arm.cc
index ed9563d0..2bcf0224 100644
--- a/test/cctest/test-assembler-arm.cc
+++ b/test/cctest/test-assembler-arm.cc
@@ -25,6 +25,8 @@
// (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 <iostream> // NOLINT(readability/streams)
+
#include "src/v8.h"
#include "test/cctest/cctest.h"
@@ -34,6 +36,7 @@
#include "src/factory.h"
#include "src/ostreams.h"
+using namespace v8::base;
using namespace v8::internal;
@@ -1372,14 +1375,14 @@ TEST(16) {
__ pkhtb(r2, r0, Operand(r1, ASR, 8));
__ str(r2, MemOperand(r4, OFFSET_OF(T, dst1)));
- __ uxtb16(r2, Operand(r0, ROR, 8));
+ __ uxtb16(r2, r0, 8);
__ str(r2, MemOperand(r4, OFFSET_OF(T, dst2)));
- __ uxtb(r2, Operand(r0, ROR, 8));
+ __ uxtb(r2, r0, 8);
__ str(r2, MemOperand(r4, OFFSET_OF(T, dst3)));
__ ldr(r0, MemOperand(r4, OFFSET_OF(T, src2)));
- __ uxtab(r2, r0, Operand(r1, ROR, 8));
+ __ uxtab(r2, r0, r1, 8);
__ str(r2, MemOperand(r4, OFFSET_OF(T, dst4)));
__ ldm(ia_w, sp, r4.bit() | pc.bit());
@@ -1439,21 +1442,19 @@ TEST(17) {
CHECK_EQ(expected_, t.result);
-TEST(18) {
+TEST(sdiv) {
// Test the sdiv.
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
-
- typedef struct {
- uint32_t dividend;
- uint32_t divisor;
- uint32_t result;
- } T;
- T t;
-
Assembler assm(isolate, NULL, 0);
+ struct T {
+ int32_t dividend;
+ int32_t divisor;
+ int32_t result;
+ } t;
+
if (CpuFeatures::IsSupported(SUDIV)) {
CpuFeatureScope scope(&assm, SUDIV);
@@ -1477,6 +1478,8 @@ TEST(18) {
#endif
F3 f = FUNCTION_CAST<F3>(code->entry());
Object* dummy;
+ TEST_SDIV(0, kMinInt, 0);
+ TEST_SDIV(0, 1024, 0);
TEST_SDIV(1073741824, kMinInt, -2);
TEST_SDIV(kMinInt, kMinInt, -1);
TEST_SDIV(5, 10, 2);
@@ -1495,6 +1498,322 @@ TEST(18) {
#undef TEST_SDIV
+#define TEST_UDIV(expected_, dividend_, divisor_) \
+ t.dividend = dividend_; \
+ t.divisor = divisor_; \
+ t.result = 0; \
+ dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0); \
+ CHECK_EQ(expected_, t.result);
+
+
+TEST(udiv) {
+ // Test the udiv.
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ Assembler assm(isolate, NULL, 0);
+
+ struct T {
+ uint32_t dividend;
+ uint32_t divisor;
+ uint32_t result;
+ } t;
+
+ if (CpuFeatures::IsSupported(SUDIV)) {
+ CpuFeatureScope scope(&assm, SUDIV);
+
+ __ mov(r3, Operand(r0));
+
+ __ ldr(r0, MemOperand(r3, OFFSET_OF(T, dividend)));
+ __ ldr(r1, MemOperand(r3, OFFSET_OF(T, divisor)));
+
+ __ sdiv(r2, r0, r1);
+ __ str(r2, MemOperand(r3, OFFSET_OF(T, result)));
+
+ __ bx(lr);
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef DEBUG
+ OFStream os(stdout);
+ code->Print(os);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ Object* dummy;
+ TEST_UDIV(0, 0, 0);
+ TEST_UDIV(0, 1024, 0);
+ TEST_UDIV(5, 10, 2);
+ TEST_UDIV(3, 10, 3);
+ USE(dummy);
+ }
+}
+
+
+#undef TEST_UDIV
+
+
+TEST(smmla) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ smmla(r1, r1, r2, r3);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt(), z = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, z, 0);
+ CHECK_EQ(bits::SignedMulHighAndAdd32(x, y, z), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(smmul) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ smmul(r1, r1, r2);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0);
+ CHECK_EQ(bits::SignedMulHigh32(x, y), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(sxtb) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ sxtb(r1, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<int8_t>(x)), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(sxtab) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ sxtab(r1, r2, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<int8_t>(x)) + y, r);
+ USE(dummy);
+ }
+}
+
+
+TEST(sxth) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ sxth(r1, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<int16_t>(x)), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(sxtah) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ sxtah(r1, r2, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<int16_t>(x)) + y, r);
+ USE(dummy);
+ }
+}
+
+
+TEST(uxtb) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ uxtb(r1, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<uint8_t>(x)), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(uxtab) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ uxtab(r1, r2, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<uint8_t>(x)) + y, r);
+ USE(dummy);
+ }
+}
+
+
+TEST(uxth) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ uxth(r1, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<uint16_t>(x)), r);
+ USE(dummy);
+ }
+}
+
+
+TEST(uxtah) {
+ CcTest::InitializeVM();
+ Isolate* const isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+ RandomNumberGenerator* const rng = isolate->random_number_generator();
+ Assembler assm(isolate, nullptr, 0);
+ __ uxtah(r1, r2, r1);
+ __ str(r1, MemOperand(r0));
+ __ bx(lr);
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ code->Print(std::cout);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+ for (size_t i = 0; i < 128; ++i) {
+ int32_t r, x = rng->NextInt(), y = rng->NextInt();
+ Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0);
+ CHECK_EQ(static_cast<int32_t>(static_cast<uint16_t>(x)) + y, r);
+ USE(dummy);
+ }
+}
+
+
TEST(code_relative_offset) {
// Test extracting the offset of a label from the beginning of the code
// in a register.
@@ -1565,4 +1884,100 @@ TEST(code_relative_offset) {
CHECK_EQ(42, res);
}
+
+TEST(ARMv8_vrintX) {
+ // Test the vrintX floating point instructions.
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+
+ typedef struct {
+ double input;
+ double ar;
+ double nr;
+ double mr;
+ double pr;
+ double zr;
+ } T;
+ T t;
+
+ // Create a function that accepts &t, and loads, manipulates, and stores
+ // the doubles and floats.
+ Assembler assm(isolate, NULL, 0);
+ Label L, C;
+
+
+ if (CpuFeatures::IsSupported(ARMv8)) {
+ CpuFeatureScope scope(&assm, ARMv8);
+
+ __ mov(ip, Operand(sp));
+ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
+
+ __ mov(r4, Operand(r0));
+
+ // Test vrinta
+ __ vldr(d6, r4, OFFSET_OF(T, input));
+ __ vrinta(d5, d6);
+ __ vstr(d5, r4, OFFSET_OF(T, ar));
+
+ // Test vrintn
+ __ vldr(d6, r4, OFFSET_OF(T, input));
+ __ vrintn(d5, d6);
+ __ vstr(d5, r4, OFFSET_OF(T, nr));
+
+ // Test vrintp
+ __ vldr(d6, r4, OFFSET_OF(T, input));
+ __ vrintp(d5, d6);
+ __ vstr(d5, r4, OFFSET_OF(T, pr));
+
+ // Test vrintm
+ __ vldr(d6, r4, OFFSET_OF(T, input));
+ __ vrintm(d5, d6);
+ __ vstr(d5, r4, OFFSET_OF(T, mr));
+
+ // Test vrintz
+ __ vldr(d6, r4, OFFSET_OF(T, input));
+ __ vrintz(d5, d6);
+ __ vstr(d5, r4, OFFSET_OF(T, zr));
+
+ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef DEBUG
+ OFStream os(stdout);
+ code->Print(os);
+#endif
+ F3 f = FUNCTION_CAST<F3>(code->entry());
+
+ Object* dummy = nullptr;
+ USE(dummy);
+
+#define CHECK_VRINT(input_val, ares, nres, mres, pres, zres) \
+ t.input = input_val; \
+ dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0); \
+ CHECK_EQ(ares, t.ar); \
+ CHECK_EQ(nres, t.nr); \
+ CHECK_EQ(mres, t.mr); \
+ CHECK_EQ(pres, t.pr); \
+ CHECK_EQ(zres, t.zr);
+
+ CHECK_VRINT(-0.5, -1.0, -0.0, -1.0, -0.0, -0.0)
+ CHECK_VRINT(-0.6, -1.0, -1.0, -1.0, -0.0, -0.0)
+ CHECK_VRINT(-1.1, -1.0, -1.0, -2.0, -1.0, -1.0)
+ CHECK_VRINT(0.5, 1.0, 0.0, 0.0, 1.0, 0.0)
+ CHECK_VRINT(0.6, 1.0, 1.0, 0.0, 1.0, 0.0)
+ CHECK_VRINT(1.1, 1.0, 1.0, 1.0, 2.0, 1.0)
+ double inf = std::numeric_limits<double>::infinity();
+ CHECK_VRINT(inf, inf, inf, inf, inf, inf)
+ CHECK_VRINT(-inf, -inf, -inf, -inf, -inf, -inf)
+ CHECK_VRINT(-0.0, -0.0, -0.0, -0.0, -0.0, -0.0)
+ double nan = std::numeric_limits<double>::quiet_NaN();
+ CHECK_VRINT(nan, nan, nan, nan, nan, nan)
+
+#undef CHECK_VRINT
+ }
+}
#undef __
diff --git a/test/cctest/test-assembler-arm64.cc b/test/cctest/test-assembler-arm64.cc
index 587a4ce9..108152ef 100644
--- a/test/cctest/test-assembler-arm64.cc
+++ b/test/cctest/test-assembler-arm64.cc
@@ -6554,6 +6554,95 @@ TEST(frintn) {
}
+TEST(frintp) {
+ INIT_V8();
+ SETUP();
+
+ START();
+ __ Fmov(s16, 1.0);
+ __ Fmov(s17, 1.1);
+ __ Fmov(s18, 1.5);
+ __ Fmov(s19, 1.9);
+ __ Fmov(s20, 2.5);
+ __ Fmov(s21, -1.5);
+ __ Fmov(s22, -2.5);
+ __ Fmov(s23, kFP32PositiveInfinity);
+ __ Fmov(s24, kFP32NegativeInfinity);
+ __ Fmov(s25, 0.0);
+ __ Fmov(s26, -0.0);
+ __ Fmov(s27, -0.2);
+
+ __ Frintp(s0, s16);
+ __ Frintp(s1, s17);
+ __ Frintp(s2, s18);
+ __ Frintp(s3, s19);
+ __ Frintp(s4, s20);
+ __ Frintp(s5, s21);
+ __ Frintp(s6, s22);
+ __ Frintp(s7, s23);
+ __ Frintp(s8, s24);
+ __ Frintp(s9, s25);
+ __ Frintp(s10, s26);
+ __ Frintp(s11, s27);
+
+ __ Fmov(d16, -0.5);
+ __ Fmov(d17, -0.8);
+ __ Fmov(d18, 1.5);
+ __ Fmov(d19, 1.9);
+ __ Fmov(d20, 2.5);
+ __ Fmov(d21, -1.5);
+ __ Fmov(d22, -2.5);
+ __ Fmov(d23, kFP32PositiveInfinity);
+ __ Fmov(d24, kFP32NegativeInfinity);
+ __ Fmov(d25, 0.0);
+ __ Fmov(d26, -0.0);
+ __ Fmov(d27, -0.2);
+
+ __ Frintp(d12, d16);
+ __ Frintp(d13, d17);
+ __ Frintp(d14, d18);
+ __ Frintp(d15, d19);
+ __ Frintp(d16, d20);
+ __ Frintp(d17, d21);
+ __ Frintp(d18, d22);
+ __ Frintp(d19, d23);
+ __ Frintp(d20, d24);
+ __ Frintp(d21, d25);
+ __ Frintp(d22, d26);
+ __ Frintp(d23, d27);
+ END();
+
+ RUN();
+
+ CHECK_EQUAL_FP32(1.0, s0);
+ CHECK_EQUAL_FP32(2.0, s1);
+ CHECK_EQUAL_FP32(2.0, s2);
+ CHECK_EQUAL_FP32(2.0, s3);
+ CHECK_EQUAL_FP32(3.0, s4);
+ CHECK_EQUAL_FP32(-1.0, s5);
+ CHECK_EQUAL_FP32(-2.0, s6);
+ CHECK_EQUAL_FP32(kFP32PositiveInfinity, s7);
+ CHECK_EQUAL_FP32(kFP32NegativeInfinity, s8);
+ CHECK_EQUAL_FP32(0.0, s9);
+ CHECK_EQUAL_FP32(-0.0, s10);
+ CHECK_EQUAL_FP32(-0.0, s11);
+ CHECK_EQUAL_FP64(-0.0, d12);
+ CHECK_EQUAL_FP64(-0.0, d13);
+ CHECK_EQUAL_FP64(2.0, d14);
+ CHECK_EQUAL_FP64(2.0, d15);
+ CHECK_EQUAL_FP64(3.0, d16);
+ CHECK_EQUAL_FP64(-1.0, d17);
+ CHECK_EQUAL_FP64(-2.0, d18);
+ CHECK_EQUAL_FP64(kFP64PositiveInfinity, d19);
+ CHECK_EQUAL_FP64(kFP64NegativeInfinity, d20);
+ CHECK_EQUAL_FP64(0.0, d21);
+ CHECK_EQUAL_FP64(-0.0, d22);
+ CHECK_EQUAL_FP64(-0.0, d23);
+
+ TEARDOWN();
+}
+
+
TEST(frintz) {
INIT_V8();
SETUP();
diff --git a/test/cctest/test-assembler-ia32.cc b/test/cctest/test-assembler-ia32.cc
index e8c7f951..f59c3c4a 100644
--- a/test/cctest/test-assembler-ia32.cc
+++ b/test/cctest/test-assembler-ia32.cc
@@ -170,11 +170,10 @@ TEST(AssemblerIa323) {
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
- // don't print the code - our disassembler can't handle cvttss2si
- // instead print bytes
- Disassembler::Dump(stdout,
- code->instruction_start(),
- code->instruction_start() + code->instruction_size());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
+#endif
F3 f = FUNCTION_CAST<F3>(code->entry());
int res = f(static_cast<float>(-3.1415));
::printf("f() = %d\n", res);
@@ -200,11 +199,10 @@ TEST(AssemblerIa324) {
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
- // don't print the code - our disassembler can't handle cvttsd2si
- // instead print bytes
- Disassembler::Dump(stdout,
- code->instruction_start(),
- code->instruction_start() + code->instruction_size());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
+#endif
F4 f = FUNCTION_CAST<F4>(code->entry());
int res = f(2.718281828);
::printf("f() = %d\n", res);
@@ -261,13 +259,9 @@ TEST(AssemblerIa326) {
assm.GetCode(&desc);
Handle<Code> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
-#ifdef DEBUG
- ::printf("\n---\n");
- // don't print the code - our disassembler can't handle SSE instructions
- // instead print bytes
- Disassembler::Dump(stdout,
- code->instruction_start(),
- code->instruction_start() + code->instruction_size());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
#endif
F5 f = FUNCTION_CAST<F5>(code->entry());
double res = f(2.2, 1.1);
@@ -595,4 +589,458 @@ TEST(AssemblerIa32SSE) {
}
+typedef int (*F9)(double x, double y, double z);
+TEST(AssemblerX64FMA_sd) {
+ CcTest::InitializeVM();
+ if (!CpuFeatures::IsSupported(FMA3)) return;
+
+ Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
+ HandleScope scope(isolate);
+ v8::internal::byte buffer[1024];
+ MacroAssembler assm(isolate, buffer, sizeof buffer);
+ {
+ CpuFeatureScope fscope(&assm, FMA3);
+ Label exit;
+ __ movsd(xmm0, Operand(esp, 1 * kPointerSize));
+ __ movsd(xmm1, Operand(esp, 3 * kPointerSize));
+ __ movsd(xmm2, Operand(esp, 5 * kPointerSize));
+ // argument in xmm0, xmm1 and xmm2
+ // xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ addsd(xmm3, xmm2); // Expected result in xmm3
+
+ __ sub(esp, Immediate(kDoubleSize)); // For memory operand
+ // vfmadd132sd
+ __ mov(eax, Immediate(1)); // Test number
+ __ movaps(xmm4, xmm0);
+ __ vfmadd132sd(xmm4, xmm2, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfmadd213sd(xmm4, xmm0, xmm2);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfmadd231sd(xmm4, xmm0, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmadd132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfmadd132sd(xmm4, xmm2, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movsd(Operand(esp, 0), xmm2);
+ __ vfmadd213sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfmadd231sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ subsd(xmm3, xmm2); // Expected result in xmm3
+
+ // vfmsub132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ vfmsub132sd(xmm4, xmm2, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfmsub213sd(xmm4, xmm0, xmm2);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfmsub231sd(xmm4, xmm0, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmsub132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfmsub132sd(xmm4, xmm2, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movsd(Operand(esp, 0), xmm2);
+ __ vfmsub213sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfmsub231sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ Move(xmm4, (uint64_t)1 << 63);
+ __ xorpd(xmm3, xmm4);
+ __ addsd(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmadd132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ vfnmadd132sd(xmm4, xmm2, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfnmadd213sd(xmm4, xmm0, xmm2);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfnmadd231sd(xmm4, xmm0, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmadd132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfnmadd132sd(xmm4, xmm2, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movsd(Operand(esp, 0), xmm2);
+ __ vfnmadd213sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfnmadd231sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ Move(xmm4, (uint64_t)1 << 63);
+ __ xorpd(xmm3, xmm4);
+ __ subsd(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmsub132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ vfnmsub132sd(xmm4, xmm2, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfnmsub213sd(xmm4, xmm0, xmm2);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfnmsub231sd(xmm4, xmm0, xmm1);
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmsub132sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfnmsub132sd(xmm4, xmm2, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub213sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movsd(Operand(esp, 0), xmm2);
+ __ vfnmsub213sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231sd
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movsd(Operand(esp, 0), xmm1);
+ __ vfnmsub231sd(xmm4, xmm0, Operand(esp, 0));
+ __ ucomisd(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+
+ __ xor_(eax, eax);
+ __ bind(&exit);
+ __ add(esp, Immediate(kDoubleSize));
+ __ ret(0);
+ }
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
+#endif
+
+ F9 f = FUNCTION_CAST<F9>(code->entry());
+ CHECK_EQ(0, f(0.000092662107262076, -2.460774966188315, -1.0958787393627414));
+}
+
+
+typedef int (*F10)(float x, float y, float z);
+TEST(AssemblerX64FMA_ss) {
+ CcTest::InitializeVM();
+ if (!CpuFeatures::IsSupported(FMA3)) return;
+
+ Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
+ HandleScope scope(isolate);
+ v8::internal::byte buffer[1024];
+ MacroAssembler assm(isolate, buffer, sizeof buffer);
+ {
+ CpuFeatureScope fscope(&assm, FMA3);
+ Label exit;
+ __ movss(xmm0, Operand(esp, 1 * kPointerSize));
+ __ movss(xmm1, Operand(esp, 2 * kPointerSize));
+ __ movss(xmm2, Operand(esp, 3 * kPointerSize));
+ // arguments in xmm0, xmm1 and xmm2
+ // xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ addss(xmm3, xmm2); // Expected result in xmm3
+
+ __ sub(esp, Immediate(kDoubleSize)); // For memory operand
+ // vfmadd132ss
+ __ mov(eax, Immediate(1)); // Test number
+ __ movaps(xmm4, xmm0);
+ __ vfmadd132ss(xmm4, xmm2, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfmadd213ss(xmm4, xmm0, xmm2);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfmadd231ss(xmm4, xmm0, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmadd132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfmadd132ss(xmm4, xmm2, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movss(Operand(esp, 0), xmm2);
+ __ vfmadd213ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfmadd231ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ subss(xmm3, xmm2); // Expected result in xmm3
+
+ // vfmsub132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ vfmsub132ss(xmm4, xmm2, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfmsub213ss(xmm4, xmm0, xmm2);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfmsub231ss(xmm4, xmm0, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmsub132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfmsub132ss(xmm4, xmm2, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movss(Operand(esp, 0), xmm2);
+ __ vfmsub213ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfmsub231ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ Move(xmm4, (uint32_t)1 << 31);
+ __ xorps(xmm3, xmm4);
+ __ addss(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmadd132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ vfnmadd132ss(xmm4, xmm2, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfnmadd213ss(xmm4, xmm0, xmm2);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfnmadd231ss(xmm4, xmm0, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmadd132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfnmadd132ss(xmm4, xmm2, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movss(Operand(esp, 0), xmm2);
+ __ vfnmadd213ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfnmadd231ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ Move(xmm4, (uint32_t)1 << 31);
+ __ xorps(xmm3, xmm4);
+ __ subss(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmsub132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ vfnmsub132ss(xmm4, xmm2, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ vfnmsub213ss(xmm4, xmm0, xmm2);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ vfnmsub231ss(xmm4, xmm0, xmm1);
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmsub132ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm0);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfnmsub132ss(xmm4, xmm2, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub213ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm1);
+ __ movss(Operand(esp, 0), xmm2);
+ __ vfnmsub213ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231ss
+ __ inc(eax);
+ __ movaps(xmm4, xmm2);
+ __ movss(Operand(esp, 0), xmm1);
+ __ vfnmsub231ss(xmm4, xmm0, Operand(esp, 0));
+ __ ucomiss(xmm4, xmm3);
+ __ j(not_equal, &exit);
+
+
+ __ xor_(eax, eax);
+ __ bind(&exit);
+ __ add(esp, Immediate(kDoubleSize));
+ __ ret(0);
+ }
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
+#endif
+
+ F10 f = FUNCTION_CAST<F10>(code->entry());
+ CHECK_EQ(0, f(9.26621069e-05f, -2.4607749f, -1.09587872f));
+}
#undef __
diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc
index 3d305b65..23d0be64 100644
--- a/test/cctest/test-assembler-x64.cc
+++ b/test/cctest/test-assembler-x64.cc
@@ -736,4 +736,454 @@ TEST(AssemblerX64SSE) {
F6 f = FUNCTION_CAST<F6>(code->entry());
CHECK_EQ(2, f(1.0, 2.0));
}
+
+
+typedef int (*F7)(double x, double y, double z);
+TEST(AssemblerX64FMA_sd) {
+ CcTest::InitializeVM();
+ if (!CpuFeatures::IsSupported(FMA3)) return;
+
+ Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
+ HandleScope scope(isolate);
+ v8::internal::byte buffer[1024];
+ MacroAssembler assm(isolate, buffer, sizeof buffer);
+ {
+ CpuFeatureScope fscope(&assm, FMA3);
+ Label exit;
+ // argument in xmm0, xmm1 and xmm2
+ // xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ addsd(xmm3, xmm2); // Expected result in xmm3
+
+ __ subq(rsp, Immediate(kDoubleSize)); // For memory operand
+ // vfmadd132sd
+ __ movl(rax, Immediate(1)); // Test number
+ __ movaps(xmm8, xmm0);
+ __ vfmadd132sd(xmm8, xmm2, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfmadd213sd(xmm8, xmm0, xmm2);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfmadd231sd(xmm8, xmm0, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmadd132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfmadd132sd(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movsd(Operand(rsp, 0), xmm2);
+ __ vfmadd213sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfmadd231sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ subsd(xmm3, xmm2); // Expected result in xmm3
+
+ // vfmsub132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ vfmsub132sd(xmm8, xmm2, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfmsub213sd(xmm8, xmm0, xmm2);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfmsub231sd(xmm8, xmm0, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmsub132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfmsub132sd(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movsd(Operand(rsp, 0), xmm2);
+ __ vfmsub213sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfmsub231sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ Move(xmm4, (uint64_t)1 << 63);
+ __ xorpd(xmm3, xmm4);
+ __ addsd(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmadd132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ vfnmadd132sd(xmm8, xmm2, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfnmadd213sd(xmm8, xmm0, xmm2);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfnmadd231sd(xmm8, xmm0, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmadd132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfnmadd132sd(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movsd(Operand(rsp, 0), xmm2);
+ __ vfnmadd213sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfnmadd231sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulsd(xmm3, xmm1);
+ __ Move(xmm4, (uint64_t)1 << 63);
+ __ xorpd(xmm3, xmm4);
+ __ subsd(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmsub132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ vfnmsub132sd(xmm8, xmm2, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfnmsub213sd(xmm8, xmm0, xmm2);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfnmsub231sd(xmm8, xmm0, xmm1);
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmsub132sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfnmsub132sd(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub213sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movsd(Operand(rsp, 0), xmm2);
+ __ vfnmsub213sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231sd
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movsd(Operand(rsp, 0), xmm1);
+ __ vfnmsub231sd(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomisd(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+
+ __ xorl(rax, rax);
+ __ bind(&exit);
+ __ addq(rsp, Immediate(kDoubleSize));
+ __ ret(0);
+ }
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
+#endif
+
+ F7 f = FUNCTION_CAST<F7>(code->entry());
+ CHECK_EQ(0, f(0.000092662107262076, -2.460774966188315, -1.0958787393627414));
+}
+
+
+typedef int (*F8)(float x, float y, float z);
+TEST(AssemblerX64FMA_ss) {
+ CcTest::InitializeVM();
+ if (!CpuFeatures::IsSupported(FMA3)) return;
+
+ Isolate* isolate = reinterpret_cast<Isolate*>(CcTest::isolate());
+ HandleScope scope(isolate);
+ v8::internal::byte buffer[1024];
+ MacroAssembler assm(isolate, buffer, sizeof buffer);
+ {
+ CpuFeatureScope fscope(&assm, FMA3);
+ Label exit;
+ // arguments in xmm0, xmm1 and xmm2
+ // xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ addss(xmm3, xmm2); // Expected result in xmm3
+
+ __ subq(rsp, Immediate(kDoubleSize)); // For memory operand
+ // vfmadd132ss
+ __ movl(rax, Immediate(1)); // Test number
+ __ movaps(xmm8, xmm0);
+ __ vfmadd132ss(xmm8, xmm2, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfmadd213ss(xmm8, xmm0, xmm2);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfmadd231ss(xmm8, xmm0, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmadd132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfmadd132ss(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movss(Operand(rsp, 0), xmm2);
+ __ vfmadd213ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfmadd231ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ subss(xmm3, xmm2); // Expected result in xmm3
+
+ // vfmsub132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ vfmsub132ss(xmm8, xmm2, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfmsub213ss(xmm8, xmm0, xmm2);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfmsub231ss(xmm8, xmm0, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfmsub132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfmsub132ss(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movss(Operand(rsp, 0), xmm2);
+ __ vfmsub213ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfmsub231ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 + xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ Move(xmm4, (uint32_t)1 << 31);
+ __ xorps(xmm3, xmm4);
+ __ addss(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmadd132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ vfnmadd132ss(xmm8, xmm2, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmadd213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfnmadd213ss(xmm8, xmm0, xmm2);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfnmadd231ss(xmm8, xmm0, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmadd132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfnmadd132ss(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movss(Operand(rsp, 0), xmm2);
+ __ vfnmadd213ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmadd231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfnmadd231ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+
+ // - xmm0 * xmm1 - xmm2
+ __ movaps(xmm3, xmm0);
+ __ mulss(xmm3, xmm1);
+ __ Move(xmm4, (uint32_t)1 << 31);
+ __ xorps(xmm3, xmm4);
+ __ subss(xmm3, xmm2); // Expected result in xmm3
+
+ // vfnmsub132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ vfnmsub132ss(xmm8, xmm2, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfmsub213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ vfnmsub213ss(xmm8, xmm0, xmm2);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ vfnmsub231ss(xmm8, xmm0, xmm1);
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+ // vfnmsub132ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm0);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfnmsub132ss(xmm8, xmm2, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub213ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm1);
+ __ movss(Operand(rsp, 0), xmm2);
+ __ vfnmsub213ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+ // vfnmsub231ss
+ __ incq(rax);
+ __ movaps(xmm8, xmm2);
+ __ movss(Operand(rsp, 0), xmm1);
+ __ vfnmsub231ss(xmm8, xmm0, Operand(rsp, 0));
+ __ ucomiss(xmm8, xmm3);
+ __ j(not_equal, &exit);
+
+
+ __ xorl(rax, rax);
+ __ bind(&exit);
+ __ addq(rsp, Immediate(kDoubleSize));
+ __ ret(0);
+ }
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Handle<Code> code = isolate->factory()->NewCode(
+ desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
+#ifdef OBJECT_PRINT
+ OFStream os(stdout);
+ code->Print(os);
+#endif
+
+ F8 f = FUNCTION_CAST<F8>(code->entry());
+ CHECK_EQ(0, f(9.26621069e-05f, -2.4607749f, -1.09587872f));
+}
#undef __
diff --git a/test/cctest/test-ast.cc b/test/cctest/test-ast.cc
index 24819dfc..096d5c78 100644
--- a/test/cctest/test-ast.cc
+++ b/test/cctest/test-ast.cc
@@ -40,8 +40,8 @@ TEST(List) {
Isolate* isolate = CcTest::i_isolate();
Zone zone(isolate);
- AstNode::IdGen id_gen;
- AstNodeFactory<AstNullVisitor> factory(&zone, NULL, &id_gen);
+ AstValueFactory value_factory(&zone, 0);
+ AstNodeFactory factory(&value_factory);
AstNode* node = factory.NewEmptyStatement(RelocInfo::kNoPosition);
list->Add(node);
CHECK_EQ(1, list->length());
diff --git a/test/cctest/test-dataflow.cc b/test/cctest/test-bit-vector.cc
index 43d950d8..ac00fabb 100644
--- a/test/cctest/test-dataflow.cc
+++ b/test/cctest/test-bit-vector.cc
@@ -29,7 +29,7 @@
#include "src/v8.h"
-#include "src/data-flow.h"
+#include "src/bit-vector.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
@@ -83,7 +83,7 @@ TEST(BitVector) {
BitVector v(15, &zone);
v.Add(0);
BitVector w(15, &zone);
- w = v;
+ w.CopyFrom(v);
CHECK(w.Contains(0));
w.Add(1);
BitVector u(w, &zone);
diff --git a/test/cctest/test-code-stubs-mips64.cc b/test/cctest/test-code-stubs-mips64.cc
index 025a8ba0..1f7df380 100644
--- a/test/cctest/test-code-stubs-mips64.cc
+++ b/test/cctest/test-code-stubs-mips64.cc
@@ -104,7 +104,7 @@ ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate,
for (--reg_num; reg_num >= 2; --reg_num) {
Register reg = Register::from_code(reg_num);
if (!reg.is(destination_reg)) {
- __ lw(at, MemOperand(sp, 0));
+ __ ld(at, MemOperand(sp, 0));
__ Assert(eq, kRegisterWasClobbered, reg, Operand(at));
__ Daddu(sp, sp, Operand(kPointerSize));
}
diff --git a/test/cctest/test-compiler.cc b/test/cctest/test-compiler.cc
index 4d6e005a..a05231e5 100644
--- a/test/cctest/test-compiler.cc
+++ b/test/cctest/test-compiler.cc
@@ -306,12 +306,15 @@ TEST(FeedbackVectorPreservedAcrossRecompiles) {
// We shouldn't have deoptimization support. We want to recompile and
// verify that our feedback vector preserves information.
CHECK(!f->shared()->has_deoptimization_support());
- Handle<FixedArray> feedback_vector(f->shared()->feedback_vector());
+ Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
// Verify that we gathered feedback.
- int expected_count = FLAG_vector_ics ? 2 : 1;
- CHECK_EQ(expected_count, feedback_vector->length());
- CHECK(feedback_vector->get(expected_count - 1)->IsJSFunction());
+ int expected_slots = 0;
+ int expected_ic_slots = 1;
+ CHECK_EQ(expected_slots, feedback_vector->Slots());
+ CHECK_EQ(expected_ic_slots, feedback_vector->ICSlots());
+ FeedbackVectorICSlot slot_for_a(0);
+ CHECK(feedback_vector->Get(slot_for_a)->IsJSFunction());
CompileRun("%OptimizeFunctionOnNextCall(f); f(fun1);");
@@ -319,8 +322,7 @@ TEST(FeedbackVectorPreservedAcrossRecompiles) {
// of the full code.
CHECK(f->IsOptimized());
CHECK(f->shared()->has_deoptimization_support());
- CHECK(f->shared()->feedback_vector()->
- get(expected_count - 1)->IsJSFunction());
+ CHECK(f->shared()->feedback_vector()->Get(slot_for_a)->IsJSFunction());
}
@@ -346,16 +348,19 @@ TEST(FeedbackVectorUnaffectedByScopeChanges) {
*v8::Handle<v8::Function>::Cast(
CcTest::global()->Get(v8_str("morphing_call"))));
- int expected_count = FLAG_vector_ics ? 2 : 1;
- CHECK_EQ(expected_count, f->shared()->feedback_vector()->length());
- // And yet it's not compiled.
+ // Not compiled, and so no feedback vector allocated yet.
CHECK(!f->shared()->is_compiled());
+ CHECK_EQ(0, f->shared()->feedback_vector()->Slots());
+ CHECK_EQ(0, f->shared()->feedback_vector()->ICSlots());
CompileRun("morphing_call();");
- // The vector should have the same size despite the new scoping.
- CHECK_EQ(expected_count, f->shared()->feedback_vector()->length());
+ // Now a feedback vector is allocated.
CHECK(f->shared()->is_compiled());
+ int expected_slots = 0;
+ int expected_ic_slots = FLAG_vector_ics ? 2 : 1;
+ CHECK_EQ(expected_slots, f->shared()->feedback_vector()->Slots());
+ CHECK_EQ(expected_ic_slots, f->shared()->feedback_vector()->ICSlots());
}
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index 8d429d2e..0e2dd912 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -1064,6 +1064,104 @@ TEST(BoundFunctionCall) {
}
+// This tests checks distribution of the samples through the source lines.
+TEST(TickLines) {
+ CcTest::InitializeVM();
+ LocalContext env;
+ i::FLAG_turbo_source_positions = true;
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+ i::HandleScope scope(isolate);
+
+ i::EmbeddedVector<char, 512> script;
+
+ const char* func_name = "func";
+ i::SNPrintF(script,
+ "function %s() {\n"
+ " var n = 0;\n"
+ " var m = 100*100;\n"
+ " while (m > 1) {\n"
+ " m--;\n"
+ " n += m * m * m;\n"
+ " }\n"
+ "}\n"
+ "%s();\n",
+ func_name, func_name);
+
+ CompileRun(script.start());
+
+ i::Handle<i::JSFunction> func = v8::Utils::OpenHandle(
+ *v8::Local<v8::Function>::Cast((*env)->Global()->Get(v8_str(func_name))));
+ CHECK_NE(NULL, func->shared());
+ CHECK_NE(NULL, func->shared()->code());
+ i::Code* code = NULL;
+ if (func->code()->is_optimized_code()) {
+ code = func->code();
+ } else {
+ CHECK(func->shared()->code() == func->code() || !i::FLAG_crankshaft);
+ code = func->shared()->code();
+ }
+ CHECK_NE(NULL, code);
+ i::Address code_address = code->instruction_start();
+ CHECK_NE(NULL, code_address);
+
+ CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate->heap());
+ profiles->StartProfiling("", false);
+ ProfileGenerator generator(profiles);
+ ProfilerEventsProcessor* processor = new ProfilerEventsProcessor(
+ &generator, NULL, v8::base::TimeDelta::FromMicroseconds(100));
+ processor->Start();
+ CpuProfiler profiler(isolate, profiles, &generator, processor);
+
+ // Enqueue code creation events.
+ i::Handle<i::String> str = factory->NewStringFromAsciiChecked(func_name);
+ int line = 1;
+ int column = 1;
+ profiler.CodeCreateEvent(i::Logger::FUNCTION_TAG, code, func->shared(), NULL,
+ *str, line, column);
+
+ // Enqueue a tick event to enable code events processing.
+ EnqueueTickSampleEvent(processor, code_address);
+
+ processor->StopSynchronously();
+
+ CpuProfile* profile = profiles->StopProfiling("");
+ CHECK_NE(NULL, profile);
+
+ // Check the state of profile generator.
+ CodeEntry* func_entry = generator.code_map()->FindEntry(code_address);
+ CHECK_NE(NULL, func_entry);
+ CHECK_EQ(func_name, func_entry->name());
+ const i::JITLineInfoTable* line_info = func_entry->line_info();
+ CHECK_NE(NULL, line_info);
+ CHECK(!line_info->empty());
+
+ // Check the hit source lines using V8 Public APIs.
+ const i::ProfileTree* tree = profile->top_down();
+ ProfileNode* root = tree->root();
+ CHECK_NE(NULL, root);
+ ProfileNode* func_node = root->FindChild(func_entry);
+ CHECK_NE(NULL, func_node);
+
+ // Add 10 faked ticks to source line #5.
+ int hit_line = 5;
+ int hit_count = 10;
+ for (int i = 0; i < hit_count; i++) func_node->IncrementLineTicks(hit_line);
+
+ unsigned int line_count = func_node->GetHitLineCount();
+ CHECK_EQ(2, line_count); // Expect two hit source lines - #1 and #5.
+ ScopedVector<v8::CpuProfileNode::LineTick> entries(line_count);
+ CHECK(func_node->GetLineTicks(&entries[0], line_count));
+ int value = 0;
+ for (int i = 0; i < entries.length(); i++)
+ if (entries[i].line == hit_line) {
+ value = entries[i].hit_count;
+ break;
+ }
+ CHECK_EQ(hit_count, value);
+}
+
+
static const char* call_function_test_source = "function bar(iterations) {\n"
"}\n"
"function start(duration) {\n"
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 2f0674a3..c3c65fd2 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -621,7 +621,7 @@ static void DebugEventBreakPointHitCount(
last_function_hit[0] = '\0';
} else {
CHECK(result->IsString());
- v8::Handle<v8::String> function_name(result->ToString());
+ v8::Handle<v8::String> function_name(result.As<v8::String>());
function_name->WriteUtf8(last_function_hit);
}
}
@@ -656,7 +656,7 @@ static void DebugEventBreakPointHitCount(
last_script_name_hit[0] = '\0';
} else {
CHECK(result->IsString());
- v8::Handle<v8::String> script_name(result->ToString());
+ v8::Handle<v8::String> script_name(result.As<v8::String>());
script_name->WriteUtf8(last_script_name_hit);
}
}
@@ -775,7 +775,7 @@ static void DebugEventEvaluate(
v8::Handle<v8::Value> result =
evaluate_check_function->Call(exec_state, argc, argv);
if (!result->IsTrue()) {
- v8::String::Utf8Value utf8(checks[i].expected->ToString());
+ v8::String::Utf8Value utf8(checks[i].expected);
V8_Fatal(__FILE__, __LINE__, "%s != %s", checks[i].expr, *utf8);
}
}
@@ -849,7 +849,7 @@ static void DebugEventStepSequence(
v8::Handle<v8::Value> result = frame_function_name->Call(exec_state,
argc, argv);
CHECK(result->IsString());
- v8::String::Utf8Value function_name(result->ToString());
+ v8::String::Utf8Value function_name(result->ToString(CcTest::isolate()));
CHECK_EQ(1, StrLength(*function_name));
CHECK_EQ((*function_name)[0],
expected_step_sequence[break_point_hit_count]);
@@ -2860,7 +2860,7 @@ TEST(DebugStepKeyedLoadLoop) {
foo->Call(env->Global(), kArgc, args);
// With stepping all break locations are hit.
- CHECK_EQ(35, break_point_hit_count);
+ CHECK_EQ(45, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2908,7 +2908,7 @@ TEST(DebugStepKeyedStoreLoop) {
foo->Call(env->Global(), kArgc, args);
// With stepping all break locations are hit.
- CHECK_EQ(34, break_point_hit_count);
+ CHECK_EQ(44, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2952,7 +2952,7 @@ TEST(DebugStepNamedLoadLoop) {
foo->Call(env->Global(), 0, NULL);
// With stepping all break locations are hit.
- CHECK_EQ(55, break_point_hit_count);
+ CHECK_EQ(65, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2995,9 +2995,7 @@ static void DoDebugStepNamedStoreLoop(int expected) {
// Test of the stepping mechanism for named load in a loop.
-TEST(DebugStepNamedStoreLoop) {
- DoDebugStepNamedStoreLoop(24);
-}
+TEST(DebugStepNamedStoreLoop) { DoDebugStepNamedStoreLoop(34); }
// Test the stepping mechanism with different ICs.
@@ -3330,14 +3328,14 @@ TEST(DebugStepFor) {
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(isolate, 10) };
foo->Call(env->Global(), argc, argv_10);
- CHECK_EQ(23, break_point_hit_count);
+ CHECK_EQ(45, break_point_hit_count);
// Looping 100 times.
step_action = StepIn;
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(isolate, 100) };
foo->Call(env->Global(), argc, argv_100);
- CHECK_EQ(203, break_point_hit_count);
+ CHECK_EQ(405, break_point_hit_count);
// Get rid of the debug event listener.
v8::Debug::SetDebugEventListener(NULL);
@@ -3381,7 +3379,7 @@ TEST(DebugStepForContinue) {
v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(isolate, 10) };
result = foo->Call(env->Global(), argc, argv_10);
CHECK_EQ(5, result->Int32Value());
- CHECK_EQ(52, break_point_hit_count);
+ CHECK_EQ(62, break_point_hit_count);
// Looping 100 times.
step_action = StepIn;
@@ -3389,7 +3387,7 @@ TEST(DebugStepForContinue) {
v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(isolate, 100) };
result = foo->Call(env->Global(), argc, argv_100);
CHECK_EQ(50, result->Int32Value());
- CHECK_EQ(457, break_point_hit_count);
+ CHECK_EQ(557, break_point_hit_count);
// Get rid of the debug event listener.
v8::Debug::SetDebugEventListener(NULL);
@@ -3434,7 +3432,7 @@ TEST(DebugStepForBreak) {
v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(isolate, 10) };
result = foo->Call(env->Global(), argc, argv_10);
CHECK_EQ(9, result->Int32Value());
- CHECK_EQ(55, break_point_hit_count);
+ CHECK_EQ(64, break_point_hit_count);
// Looping 100 times.
step_action = StepIn;
@@ -3442,7 +3440,7 @@ TEST(DebugStepForBreak) {
v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(isolate, 100) };
result = foo->Call(env->Global(), argc, argv_100);
CHECK_EQ(99, result->Int32Value());
- CHECK_EQ(505, break_point_hit_count);
+ CHECK_EQ(604, break_point_hit_count);
// Get rid of the debug event listener.
v8::Debug::SetDebugEventListener(NULL);
@@ -3473,7 +3471,7 @@ TEST(DebugStepForIn) {
step_action = StepIn;
break_point_hit_count = 0;
foo->Call(env->Global(), 0, NULL);
- CHECK_EQ(6, break_point_hit_count);
+ CHECK_EQ(8, break_point_hit_count);
// Create a function for testing stepping. Run it to allow it to get
// optimized.
@@ -3490,7 +3488,7 @@ TEST(DebugStepForIn) {
step_action = StepIn;
break_point_hit_count = 0;
foo->Call(env->Global(), 0, NULL);
- CHECK_EQ(8, break_point_hit_count);
+ CHECK_EQ(10, break_point_hit_count);
// Get rid of the debug event listener.
v8::Debug::SetDebugEventListener(NULL);
@@ -4352,9 +4350,10 @@ static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) {
}
-static void NamedGetter(v8::Local<v8::String> name,
+static void NamedGetter(v8::Local<v8::Name> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
- v8::String::Utf8Value n(name);
+ if (name->IsSymbol()) return;
+ v8::String::Utf8Value n(v8::Local<v8::String>::Cast(name));
if (strcmp(*n, "a") == 0) {
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), "AA"));
return;
@@ -4387,26 +4386,26 @@ TEST(InterceptorPropertyMirror) {
// Create object with named interceptor.
v8::Handle<v8::ObjectTemplate> named = v8::ObjectTemplate::New(isolate);
- named->SetNamedPropertyHandler(NamedGetter, NULL, NULL, NULL, NamedEnum);
+ named->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ NamedGetter, NULL, NULL, NULL, NamedEnum));
env->Global()->Set(
v8::String::NewFromUtf8(isolate, "intercepted_named"),
named->NewInstance());
// Create object with indexed interceptor.
v8::Handle<v8::ObjectTemplate> indexed = v8::ObjectTemplate::New(isolate);
- indexed->SetIndexedPropertyHandler(IndexedGetter,
- NULL,
- NULL,
- NULL,
- IndexedEnum);
+ indexed->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ IndexedGetter, NULL, NULL, NULL, IndexedEnum));
env->Global()->Set(
v8::String::NewFromUtf8(isolate, "intercepted_indexed"),
indexed->NewInstance());
// Create object with both named and indexed interceptor.
v8::Handle<v8::ObjectTemplate> both = v8::ObjectTemplate::New(isolate);
- both->SetNamedPropertyHandler(NamedGetter, NULL, NULL, NULL, NamedEnum);
- both->SetIndexedPropertyHandler(IndexedGetter, NULL, NULL, NULL, IndexedEnum);
+ both->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ NamedGetter, NULL, NULL, NULL, NamedEnum));
+ both->SetHandler(v8::IndexedPropertyHandlerConfiguration(
+ IndexedGetter, NULL, NULL, NULL, IndexedEnum));
env->Global()->Set(
v8::String::NewFromUtf8(isolate, "intercepted_both"),
both->NewInstance());
@@ -5264,6 +5263,7 @@ void V8Thread::Run() {
CompileRun(source);
}
+ threaded_debugging_barriers.barrier_4.Wait();
isolate_->Dispose();
}
@@ -5285,6 +5285,7 @@ void DebuggerThread::Run() {
threaded_debugging_barriers.barrier_2.Wait();
v8::Debug::SendCommand(isolate_, buffer, AsciiToUtf16(command_1, buffer));
v8::Debug::SendCommand(isolate_, buffer, AsciiToUtf16(command_2, buffer));
+ threaded_debugging_barriers.barrier_4.Wait();
}
@@ -5388,6 +5389,7 @@ void BreakpointsV8Thread::Run() {
breakpoints_barriers->barrier_2.Wait();
CompileRun(source_2);
}
+ breakpoints_barriers->barrier_4.Wait();
isolate_->Dispose();
}
@@ -5503,6 +5505,7 @@ void BreakpointsDebuggerThread::Run() {
CHECK_EQ(116, evaluate_int_result);
// 9: Continue evaluation of source2, reach end.
v8::Debug::SendCommand(isolate_, buffer, AsciiToUtf16(command_8, buffer));
+ breakpoints_barriers->barrier_4.Wait();
}
@@ -6157,7 +6160,8 @@ static void DebugEventDebugBreak(
last_function_hit[0] = '\0';
} else {
CHECK(result->IsString());
- v8::Handle<v8::String> function_name(result->ToString());
+ v8::Handle<v8::String> function_name(
+ result->ToString(CcTest::isolate()));
function_name->WriteUtf8(last_function_hit);
}
}
@@ -7040,7 +7044,8 @@ static void DebugEventBreakDeoptimize(
if (!result->IsUndefined()) {
char fn[80];
CHECK(result->IsString());
- v8::Handle<v8::String> function_name(result->ToString());
+ v8::Handle<v8::String> function_name(
+ result->ToString(CcTest::isolate()));
function_name->WriteUtf8(fn);
if (strcmp(fn, "bar") == 0) {
i::Deoptimizer::DeoptimizeAll(CcTest::i_isolate());
@@ -7105,12 +7110,12 @@ static void DebugEventBreakWithOptimizedStack(
v8::Handle<v8::Value> result =
frame_function_name->Call(exec_state, argc, argv);
CHECK(result->IsString());
- v8::Handle<v8::String> function_name(result->ToString());
+ v8::Handle<v8::String> function_name(result->ToString(isolate));
CHECK(function_name->Equals(v8::String::NewFromUtf8(isolate, "loop")));
// Get the name of the first argument in frame i.
result = frame_argument_name->Call(exec_state, argc, argv);
CHECK(result->IsString());
- v8::Handle<v8::String> argument_name(result->ToString());
+ v8::Handle<v8::String> argument_name(result->ToString(isolate));
CHECK(argument_name->Equals(v8::String::NewFromUtf8(isolate, "count")));
// Get the value of the first argument in frame i. If the
// funtion is optimized the value will be undefined, otherwise
@@ -7123,7 +7128,7 @@ static void DebugEventBreakWithOptimizedStack(
// Get the name of the first local variable.
result = frame_local_name->Call(exec_state, argc, argv);
CHECK(result->IsString());
- v8::Handle<v8::String> local_name(result->ToString());
+ v8::Handle<v8::String> local_name(result->ToString(isolate));
CHECK(local_name->Equals(v8::String::NewFromUtf8(isolate, "local")));
// Get the value of the first local variable. If the function
// is optimized the value will be undefined, otherwise it will
@@ -7518,3 +7523,159 @@ TEST(DebugBreakOffThreadTerminate) {
CompileRun("while (true);");
CHECK(try_catch.HasTerminated());
}
+
+
+static void DebugEventExpectNoException(
+ const v8::Debug::EventDetails& event_details) {
+ v8::DebugEvent event = event_details.GetEvent();
+ CHECK_NE(v8::Exception, event);
+}
+
+
+static void TryCatchWrappedThrowCallback(
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
+ v8::TryCatch try_catch;
+ CompileRun("throw 'rejection';");
+ CHECK(try_catch.HasCaught());
+}
+
+
+TEST(DebugPromiseInterceptedByTryCatch) {
+ DebugLocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::Debug::SetDebugEventListener(&DebugEventExpectNoException);
+ ChangeBreakOnException(false, true);
+
+ v8::Handle<v8::FunctionTemplate> fun =
+ v8::FunctionTemplate::New(isolate, TryCatchWrappedThrowCallback);
+ env->Global()->Set(v8_str("fun"), fun->GetFunction());
+
+ CompileRun("var p = new Promise(function(res, rej) { fun(); res(); });");
+ CompileRun(
+ "var r;"
+ "p.chain(function() { r = 'resolved'; },"
+ " function() { r = 'rejected'; });");
+ CHECK(CompileRun("r")->Equals(v8_str("resolved")));
+}
+
+
+static int exception_event_counter = 0;
+
+
+static void DebugEventCountException(
+ const v8::Debug::EventDetails& event_details) {
+ v8::DebugEvent event = event_details.GetEvent();
+ if (event == v8::Exception) exception_event_counter++;
+}
+
+
+static void ThrowCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ CompileRun("throw 'rejection';");
+}
+
+
+TEST(DebugPromiseRejectedByCallback) {
+ DebugLocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::Debug::SetDebugEventListener(&DebugEventCountException);
+ ChangeBreakOnException(false, true);
+ exception_event_counter = 0;
+
+ v8::Handle<v8::FunctionTemplate> fun =
+ v8::FunctionTemplate::New(isolate, ThrowCallback);
+ env->Global()->Set(v8_str("fun"), fun->GetFunction());
+
+ CompileRun("var p = new Promise(function(res, rej) { fun(); res(); });");
+ CompileRun(
+ "var r;"
+ "p.chain(function() { r = 'resolved'; },"
+ " function(e) { r = 'rejected' + e; });");
+ CHECK(CompileRun("r")->Equals(v8_str("rejectedrejection")));
+ CHECK_EQ(1, exception_event_counter);
+}
+
+
+TEST(DebugBreakOnExceptionInObserveCallback) {
+ DebugLocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::Debug::SetDebugEventListener(&DebugEventCountException);
+ // Break on uncaught exception
+ ChangeBreakOnException(false, true);
+ exception_event_counter = 0;
+
+ v8::Handle<v8::FunctionTemplate> fun =
+ v8::FunctionTemplate::New(isolate, ThrowCallback);
+ env->Global()->Set(v8_str("fun"), fun->GetFunction());
+
+ CompileRun(
+ "var obj = {};"
+ "var callbackRan = false;"
+ "Object.observe(obj, function() {"
+ " callbackRan = true;"
+ " throw Error('foo');"
+ "});"
+ "obj.prop = 1");
+ CHECK(CompileRun("callbackRan")->BooleanValue());
+ CHECK_EQ(1, exception_event_counter);
+}
+
+
+static void DebugHarmonyScopingListener(
+ const v8::Debug::EventDetails& event_details) {
+ v8::DebugEvent event = event_details.GetEvent();
+ if (event != v8::Break) return;
+
+ int break_id = CcTest::i_isolate()->debug()->break_id();
+
+ char script[128];
+ i::Vector<char> script_vector(script, sizeof(script));
+ SNPrintF(script_vector, "%%GetFrameCount(%d)", break_id);
+ ExpectInt32(script, 1);
+
+ SNPrintF(script_vector, "var frame = new FrameMirror(%d, 0);", break_id);
+ CompileRun(script);
+ ExpectInt32("frame.evaluate('x').value_", 1);
+ ExpectInt32("frame.evaluate('y').value_", 2);
+
+ CompileRun("var allScopes = frame.allScopes()");
+ ExpectInt32("allScopes.length", 2);
+
+ ExpectBoolean("allScopes[0].scopeType() === ScopeType.Script", true);
+
+ ExpectInt32("allScopes[0].scopeObject().value_.x", 1);
+
+ ExpectInt32("allScopes[0].scopeObject().value_.y", 2);
+
+ CompileRun("allScopes[0].setVariableValue('x', 5);");
+ CompileRun("allScopes[0].setVariableValue('y', 6);");
+ ExpectInt32("frame.evaluate('x + y').value_", 11);
+}
+
+
+TEST(DebugBreakInLexicalScopes) {
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_allow_natives_syntax = true;
+
+ DebugLocalContext env;
+ v8::Isolate* isolate = env->GetIsolate();
+ v8::HandleScope scope(isolate);
+ v8::Debug::SetDebugEventListener(DebugHarmonyScopingListener);
+
+ CompileRun(
+ "'use strict'; \n"
+ "let x = 1; \n");
+ ExpectInt32(
+ "'use strict'; \n"
+ "let y = 2; \n"
+ "debugger; \n"
+ "x * y",
+ 30);
+ ExpectInt32(
+ "x = 1; y = 2; \n"
+ "debugger;"
+ "x * y",
+ 30);
+}
diff --git a/test/cctest/test-decls.cc b/test/cctest/test-decls.cc
index 34f0b696..06afdd2b 100644
--- a/test/cctest/test-decls.cc
+++ b/test/cctest/test-decls.cc
@@ -70,9 +70,9 @@ class DeclarationContext {
int query_count() const { return query_count_; }
protected:
- virtual v8::Handle<Value> Get(Local<String> key);
- virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
- virtual v8::Handle<Integer> Query(Local<String> key);
+ virtual v8::Handle<Value> Get(Local<Name> key);
+ virtual v8::Handle<Value> Set(Local<Name> key, Local<Value> value);
+ virtual v8::Handle<Integer> Query(Local<Name> key);
void InitializeIfNeeded();
@@ -88,12 +88,11 @@ class DeclarationContext {
// The handlers are called as static functions that forward
// to the instance specific virtual methods.
- static void HandleGet(Local<String> key,
+ static void HandleGet(Local<Name> key,
const v8::PropertyCallbackInfo<v8::Value>& info);
- static void HandleSet(Local<String> key,
- Local<Value> value,
+ static void HandleSet(Local<Name> key, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info);
- static void HandleQuery(Local<String> key,
+ static void HandleQuery(Local<Name> key,
const v8::PropertyCallbackInfo<v8::Integer>& info);
v8::Isolate* isolate() const { return CcTest::isolate(); }
@@ -122,11 +121,8 @@ void DeclarationContext::InitializeIfNeeded() {
HandleScope scope(isolate);
Local<FunctionTemplate> function = FunctionTemplate::New(isolate);
Local<Value> data = External::New(CcTest::isolate(), this);
- GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
- &HandleSet,
- &HandleQuery,
- 0, 0,
- data);
+ GetHolder(function)->SetHandler(v8::NamedPropertyHandlerConfiguration(
+ &HandleGet, &HandleSet, &HandleQuery, 0, 0, data));
Local<Context> context = Context::New(isolate,
0,
function->InstanceTemplate(),
@@ -178,8 +174,7 @@ void DeclarationContext::Check(const char* source,
void DeclarationContext::HandleGet(
- Local<String> key,
- const v8::PropertyCallbackInfo<v8::Value>& info) {
+ Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
DeclarationContext* context = GetInstance(info.Data());
context->get_count_++;
info.GetReturnValue().Set(context->Get(key));
@@ -187,8 +182,7 @@ void DeclarationContext::HandleGet(
void DeclarationContext::HandleSet(
- Local<String> key,
- Local<Value> value,
+ Local<Name> key, Local<Value> value,
const v8::PropertyCallbackInfo<v8::Value>& info) {
DeclarationContext* context = GetInstance(info.Data());
context->set_count_++;
@@ -197,8 +191,7 @@ void DeclarationContext::HandleSet(
void DeclarationContext::HandleQuery(
- Local<String> key,
- const v8::PropertyCallbackInfo<v8::Integer>& info) {
+ Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) {
DeclarationContext* context = GetInstance(info.Data());
context->query_count_++;
info.GetReturnValue().Set(context->Query(key));
@@ -211,18 +204,17 @@ DeclarationContext* DeclarationContext::GetInstance(Local<Value> data) {
}
-v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
+v8::Handle<Value> DeclarationContext::Get(Local<Name> key) {
return v8::Handle<Value>();
}
-v8::Handle<Value> DeclarationContext::Set(Local<String> key,
- Local<Value> value) {
+v8::Handle<Value> DeclarationContext::Set(Local<Name> key, Local<Value> value) {
return v8::Handle<Value>();
}
-v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
+v8::Handle<Integer> DeclarationContext::Query(Local<Name> key) {
return v8::Handle<Integer>();
}
@@ -272,7 +264,7 @@ TEST(Unknown) {
class AbsentPropertyContext: public DeclarationContext {
protected:
- virtual v8::Handle<Integer> Query(Local<String> key) {
+ virtual v8::Handle<Integer> Query(Local<Name> key) {
return v8::Handle<Integer>();
}
};
@@ -336,7 +328,7 @@ class AppearingPropertyContext: public DeclarationContext {
AppearingPropertyContext() : state_(DECLARE) { }
protected:
- virtual v8::Handle<Integer> Query(Local<String> key) {
+ virtual v8::Handle<Integer> Query(Local<Name> key) {
switch (state_) {
case DECLARE:
// Force declaration by returning that the
@@ -405,7 +397,7 @@ class ExistsInPrototypeContext: public DeclarationContext {
public:
ExistsInPrototypeContext() { InitializeIfNeeded(); }
protected:
- virtual v8::Handle<Integer> Query(Local<String> key) {
+ virtual v8::Handle<Integer> Query(Local<Name> key) {
// Let it seem that the property exists in the prototype object.
return Integer::New(isolate(), v8::None);
}
@@ -464,7 +456,7 @@ TEST(ExistsInPrototype) {
class AbsentInPrototypeContext: public DeclarationContext {
protected:
- virtual v8::Handle<Integer> Query(Local<String> key) {
+ virtual v8::Handle<Integer> Query(Local<Name> key) {
// Let it seem that the property is absent in the prototype object.
return Handle<Integer>();
}
@@ -499,7 +491,7 @@ class ExistsInHiddenPrototypeContext: public DeclarationContext {
}
protected:
- virtual v8::Handle<Integer> Query(Local<String> key) {
+ virtual v8::Handle<Integer> Query(Local<Name> key) {
// Let it seem that the property exists in the hidden prototype object.
return Integer::New(isolate(), v8::None);
}
@@ -644,37 +636,215 @@ TEST(CrossScriptReferences) {
}
-TEST(CrossScriptReferencesHarmony) {
+TEST(CrossScriptReferences_Simple) {
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_use_strict = true;
+
+ v8::Isolate* isolate = CcTest::isolate();
+ HandleScope scope(isolate);
+
+ {
+ SimpleContext context;
+ context.Check("let x = 1; x", EXPECT_RESULT, Number::New(isolate, 1));
+ context.Check("let x = 5; x", EXPECT_EXCEPTION);
+ }
+}
+
+
+TEST(CrossScriptReferences_Simple2) {
+ i::FLAG_harmony_scoping = true;
i::FLAG_use_strict = true;
+
+ v8::Isolate* isolate = CcTest::isolate();
+ HandleScope scope(isolate);
+
+ for (int k = 0; k < 100; k++) {
+ SimpleContext context;
+ bool cond = (k % 2) == 0;
+ if (cond) {
+ context.Check("let x = 1; x", EXPECT_RESULT, Number::New(isolate, 1));
+ context.Check("let z = 4; z", EXPECT_RESULT, Number::New(isolate, 4));
+ } else {
+ context.Check("let z = 1; z", EXPECT_RESULT, Number::New(isolate, 1));
+ context.Check("let x = 4; x", EXPECT_RESULT, Number::New(isolate, 4));
+ }
+ context.Check("let y = 2; x", EXPECT_RESULT,
+ Number::New(isolate, cond ? 1 : 4));
+ }
+}
+
+
+TEST(CrossScriptReferencesHarmony) {
i::FLAG_harmony_scoping = true;
i::FLAG_harmony_modules = true;
v8::Isolate* isolate = CcTest::isolate();
HandleScope scope(isolate);
+ // Check that simple cross-script global scope access works.
const char* decs[] = {
- "var x = 1; x", "x", "this.x",
- "function x() { return 1 }; x()", "x()", "this.x()",
- "let x = 1; x", "x", "this.x",
- "const x = 1; x", "x", "this.x",
- "module x { export let a = 1 }; x.a", "x.a", "this.x.a",
+ "'use strict'; var x = 1; x", "x",
+ "'use strict'; function x() { return 1 }; x()", "x()",
+ "'use strict'; let x = 1; x", "x",
+ "'use strict'; const x = 1; x", "x",
+ "'use strict'; module x { export let a = 1 }; x.a", "x.a",
NULL
};
- for (int i = 0; decs[i] != NULL; i += 3) {
+ for (int i = 0; decs[i] != NULL; i += 2) {
SimpleContext context;
context.Check(decs[i], EXPECT_RESULT, Number::New(isolate, 1));
context.Check(decs[i+1], EXPECT_RESULT, Number::New(isolate, 1));
- // TODO(rossberg): The current ES6 draft spec does not reflect lexical
- // bindings on the global object. However, this will probably change, in
- // which case we reactivate the following test.
- if (i/3 < 2) {
- context.Check(decs[i+2], EXPECT_RESULT, Number::New(isolate, 1));
- }
+ }
+
+ // Check that cross-script global scope access works with late declarations.
+ {
+ SimpleContext context;
+ context.Check("function d0() { return x0 }", // dynamic lookup
+ EXPECT_RESULT, Undefined(isolate));
+ context.Check("this.x0 = -1;"
+ "d0()",
+ EXPECT_RESULT, Number::New(isolate, -1));
+ context.Check("'use strict';"
+ "function f0() { let y = 10; return x0 + y }"
+ "function g0() { let y = 10; return eval('x0 + y') }"
+ "function h0() { let y = 10; return (1,eval)('x0') + y }"
+ "x0 + f0() + g0() + h0()",
+ EXPECT_RESULT, Number::New(isolate, 26));
+
+ context.Check("'use strict';"
+ "let x1 = 1;"
+ "function f1() { let y = 10; return x1 + y }"
+ "function g1() { let y = 10; return eval('x1 + y') }"
+ "function h1() { let y = 10; return (1,eval)('x1') + y }"
+ "function i1() { "
+ " let y = 10; return (typeof x2 === 'undefined' ? 0 : 2) + y"
+ "}"
+ "function j1() { let y = 10; return eval('x2 + y') }"
+ "function k1() { let y = 10; return (1,eval)('x2') + y }"
+ "function cl() { "
+ " let y = 10; "
+ " return { "
+ " f: function(){ return x1 + y },"
+ " g: function(){ return eval('x1 + y') },"
+ " h: function(){ return (1,eval)('x1') + y },"
+ " i: function(){"
+ " return (typeof x2 == 'undefined' ? 0 : 2) + y"
+ " },"
+ " j: function(){ return eval('x2 + y') },"
+ " k: function(){ return (1,eval)('x2') + y },"
+ " }"
+ "}"
+ "let o = cl();"
+ "x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ context.Check("x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ context.Check("o.f() + o.g() + o.h();",
+ EXPECT_RESULT, Number::New(isolate, 33));
+ context.Check("i1() + o.i();",
+ EXPECT_RESULT, Number::New(isolate, 20));
+
+ context.Check("'use strict';"
+ "let x2 = 2;"
+ "function f2() { let y = 20; return x2 + y }"
+ "function g2() { let y = 20; return eval('x2 + y') }"
+ "function h2() { let y = 20; return (1,eval)('x2') + y }"
+ "function i2() { let y = 20; return x1 + y }"
+ "function j2() { let y = 20; return eval('x1 + y') }"
+ "function k2() { let y = 20; return (1,eval)('x1') + y }"
+ "x2 + eval('x2') + (1,eval)('x2') + f2() + g2() + h2();",
+ EXPECT_RESULT, Number::New(isolate, 72));
+ context.Check("x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ context.Check("i1() + j1() + k1();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ context.Check("i2() + j2() + k2();",
+ EXPECT_RESULT, Number::New(isolate, 63));
+ context.Check("o.f() + o.g() + o.h();",
+ EXPECT_RESULT, Number::New(isolate, 33));
+ context.Check("o.i() + o.j() + o.k();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ context.Check("i1() + o.i();",
+ EXPECT_RESULT, Number::New(isolate, 24));
+
+ context.Check("'use strict';"
+ "let x0 = 100;"
+ "x0 + eval('x0') + (1,eval)('x0') + "
+ " d0() + f0() + g0() + h0();",
+ EXPECT_RESULT, Number::New(isolate, 730));
+ context.Check("x0 + eval('x0') + (1,eval)('x0') + "
+ " d0() + f0() + g0() + h0();",
+ EXPECT_RESULT, Number::New(isolate, 730));
+ context.Check("delete this.x0;"
+ "x0 + eval('x0') + (1,eval)('x0') + "
+ " d0() + f0() + g0() + h0();",
+ EXPECT_RESULT, Number::New(isolate, 730));
+ context.Check("this.x1 = 666;"
+ "x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ context.Check("delete this.x1;"
+ "x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();",
+ EXPECT_RESULT, Number::New(isolate, 36));
+ }
+
+ // Check that caching does respect scopes.
+ {
+ SimpleContext context;
+ const char* script1 = "(function(){ return y1 })()";
+ const char* script2 = "(function(){ return y2 })()";
+
+ context.Check(script1, EXPECT_EXCEPTION);
+ context.Check("this.y1 = 1; this.y2 = 2; 0;",
+ EXPECT_RESULT, Number::New(isolate, 0));
+ context.Check(script1,
+ EXPECT_RESULT, Number::New(isolate, 1));
+ context.Check("'use strict'; let y1 = 3; 0;",
+ EXPECT_RESULT, Number::New(isolate, 0));
+ context.Check(script1,
+ EXPECT_RESULT, Number::New(isolate, 3));
+ context.Check("y1 = 4;",
+ EXPECT_RESULT, Number::New(isolate, 4));
+ context.Check(script1,
+ EXPECT_RESULT, Number::New(isolate, 4));
+
+ context.Check(script2,
+ EXPECT_RESULT, Number::New(isolate, 2));
+ context.Check("'use strict'; let y2 = 5; 0;",
+ EXPECT_RESULT, Number::New(isolate, 0));
+ context.Check(script1,
+ EXPECT_RESULT, Number::New(isolate, 4));
+ context.Check(script2,
+ EXPECT_RESULT, Number::New(isolate, 5));
}
}
+TEST(GlobalLexicalOSR) {
+ i::FLAG_use_strict = true;
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_harmony_modules = true;
+
+ v8::Isolate* isolate = CcTest::isolate();
+ HandleScope scope(isolate);
+ SimpleContext context;
+
+ context.Check("'use strict';"
+ "let x = 1; x;",
+ EXPECT_RESULT, Number::New(isolate, 1));
+ context.Check("'use strict';"
+ "let y = 2*x;"
+ "++x;"
+ "let z = 0;"
+ "const limit = 100000;"
+ "for (var i = 0; i < limit; ++i) {"
+ " z += x + y;"
+ "}"
+ "z;",
+ EXPECT_RESULT, Number::New(isolate, 400000));
+}
+
+
TEST(CrossScriptConflicts) {
i::FLAG_use_strict = true;
i::FLAG_harmony_scoping = true;
@@ -704,12 +874,280 @@ TEST(CrossScriptConflicts) {
SimpleContext context;
context.Check(firsts[i], EXPECT_RESULT,
Number::New(CcTest::isolate(), 1));
- // TODO(rossberg): All tests should actually be errors in Harmony,
- // but we currently do not detect the cases where the first declaration
- // is not lexical.
- context.Check(seconds[j],
- i < 2 ? EXPECT_RESULT : EXPECT_ERROR,
- Number::New(CcTest::isolate(), 2));
+ bool success_case = i < 2 && j < 2;
+ Local<Value> success_result;
+ if (success_case) success_result = Number::New(CcTest::isolate(), 2);
+
+ context.Check(seconds[j], success_case ? EXPECT_RESULT : EXPECT_EXCEPTION,
+ success_result);
}
}
}
+
+
+TEST(CrossScriptDynamicLookup) {
+ i::FLAG_harmony_scoping = true;
+
+ HandleScope handle_scope(CcTest::isolate());
+
+ {
+ SimpleContext context;
+ Local<String> undefined_string = String::NewFromUtf8(
+ CcTest::isolate(), "undefined", String::kInternalizedString);
+ Local<String> number_string = String::NewFromUtf8(
+ CcTest::isolate(), "number", String::kInternalizedString);
+
+ context.Check(
+ "function f(o) { with(o) { return x; } }"
+ "function g(o) { with(o) { x = 15; } }"
+ "function h(o) { with(o) { return typeof x; } }",
+ EXPECT_RESULT, Undefined(CcTest::isolate()));
+ context.Check("h({})", EXPECT_RESULT, undefined_string);
+ context.Check(
+ "'use strict';"
+ "let x = 1;"
+ "f({})",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
+ context.Check(
+ "'use strict';"
+ "g({});0",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
+ context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ context.Check("h({})", EXPECT_RESULT, number_string);
+ }
+}
+
+
+TEST(CrossScriptGlobal) {
+ i::FLAG_harmony_scoping = true;
+
+ HandleScope handle_scope(CcTest::isolate());
+ {
+ SimpleContext context;
+
+ context.Check(
+ "var global = this;"
+ "global.x = 255;"
+ "x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 255));
+ context.Check(
+ "'use strict';"
+ "let x = 1;"
+ "global.x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 255));
+ context.Check("global.x = 15; x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 1));
+ context.Check("x = 221; global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 15));
+ context.Check(
+ "z = 15;"
+ "function f() { return z; };"
+ "for (var k = 0; k < 3; k++) { f(); }"
+ "f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ context.Check(
+ "'use strict';"
+ "let z = 5; f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 5));
+ context.Check(
+ "function f() { konst = 10; return konst; };"
+ "f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 10));
+ context.Check(
+ "'use strict';"
+ "const konst = 255;"
+ "f()",
+ EXPECT_EXCEPTION);
+ }
+}
+
+
+TEST(CrossScriptStaticLookupUndeclared) {
+ i::FLAG_harmony_scoping = true;
+
+ HandleScope handle_scope(CcTest::isolate());
+
+ {
+ SimpleContext context;
+ Local<String> undefined_string = String::NewFromUtf8(
+ CcTest::isolate(), "undefined", String::kInternalizedString);
+ Local<String> number_string = String::NewFromUtf8(
+ CcTest::isolate(), "number", String::kInternalizedString);
+
+ context.Check(
+ "function f(o) { return x; }"
+ "function g(v) { x = v; }"
+ "function h(o) { return typeof x; }",
+ EXPECT_RESULT, Undefined(CcTest::isolate()));
+ context.Check("h({})", EXPECT_RESULT, undefined_string);
+ context.Check(
+ "'use strict';"
+ "let x = 1;"
+ "f({})",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
+ context.Check(
+ "'use strict';"
+ "g(15);x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ context.Check("h({})", EXPECT_RESULT, number_string);
+ context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ context.Check("h({})", EXPECT_RESULT, number_string);
+ }
+}
+
+
+TEST(CrossScriptLoadICs) {
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_allow_natives_syntax = true;
+
+ HandleScope handle_scope(CcTest::isolate());
+
+ {
+ SimpleContext context;
+ context.Check(
+ "x = 15;"
+ "function f() { return x; }"
+ "function g() { return x; }"
+ "f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ context.Check(
+ "'use strict';"
+ "let x = 5;"
+ "f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 5));
+ for (int k = 0; k < 3; k++) {
+ context.Check("g()", EXPECT_RESULT, Number::New(CcTest::isolate(), 5));
+ }
+ for (int k = 0; k < 3; k++) {
+ context.Check("f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 5));
+ }
+ context.Check("%OptimizeFunctionOnNextCall(g); g()", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 5));
+ context.Check("%OptimizeFunctionOnNextCall(f); f()", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 5));
+ }
+ {
+ SimpleContext context;
+ context.Check(
+ "x = 15;"
+ "function f() { return x; }"
+ "f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ for (int k = 0; k < 3; k++) {
+ context.Check("f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
+ }
+ context.Check("%OptimizeFunctionOnNextCall(f); f()", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 15));
+ context.Check(
+ "'use strict';"
+ "let x = 5;"
+ "f()",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 5));
+ for (int k = 0; k < 3; k++) {
+ context.Check("f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 5));
+ }
+ context.Check("%OptimizeFunctionOnNextCall(f); f()", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 5));
+ }
+}
+
+
+TEST(CrossScriptStoreICs) {
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_allow_natives_syntax = true;
+
+ HandleScope handle_scope(CcTest::isolate());
+
+ {
+ SimpleContext context;
+ context.Check(
+ "var global = this;"
+ "x = 15;"
+ "function f(v) { x = v; }"
+ "function g(v) { x = v; }"
+ "f(10); x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 10));
+ context.Check(
+ "'use strict';"
+ "let x = 5;"
+ "f(7); x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 7));
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 10));
+ for (int k = 0; k < 3; k++) {
+ context.Check("g(31); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 31));
+ }
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 10));
+ for (int k = 0; k < 3; k++) {
+ context.Check("f(32); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 32));
+ }
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 10));
+ context.Check("%OptimizeFunctionOnNextCall(g); g(18); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 18));
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 10));
+ context.Check("%OptimizeFunctionOnNextCall(f); f(33); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 33));
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 10));
+ }
+ {
+ SimpleContext context;
+ context.Check(
+ "var global = this;"
+ "x = 15;"
+ "function f(v) { x = v; }"
+ "f(10); x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 10));
+ for (int k = 0; k < 3; k++) {
+ context.Check("f(18); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 18));
+ }
+ context.Check("%OptimizeFunctionOnNextCall(f); f(20); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 20));
+ context.Check(
+ "'use strict';"
+ "let x = 5;"
+ "f(8); x",
+ EXPECT_RESULT, Number::New(CcTest::isolate(), 8));
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 20));
+ for (int k = 0; k < 3; k++) {
+ context.Check("f(13); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 13));
+ }
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 20));
+ context.Check("%OptimizeFunctionOnNextCall(f); f(41); x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 41));
+ context.Check("global.x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 20));
+ }
+}
+
+
+TEST(CrossScriptAssignmentToConst) {
+ i::FLAG_harmony_scoping = true;
+ i::FLAG_allow_natives_syntax = true;
+
+ HandleScope handle_scope(CcTest::isolate());
+
+ {
+ SimpleContext context;
+
+ context.Check("function f() { x = 27; }", EXPECT_RESULT,
+ Undefined(CcTest::isolate()));
+ context.Check("'use strict';const x = 1; x", EXPECT_RESULT,
+ Number::New(CcTest::isolate(), 1));
+ context.Check("f();", EXPECT_EXCEPTION);
+ context.Check("x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
+ context.Check("f();", EXPECT_EXCEPTION);
+ context.Check("x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
+ context.Check("%OptimizeFunctionOnNextCall(f);f();", EXPECT_EXCEPTION);
+ context.Check("x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
+ }
+}
diff --git a/test/cctest/test-disasm-arm.cc b/test/cctest/test-disasm-arm.cc
index c1f6ce26..095c6367 100644
--- a/test/cctest/test-disasm-arm.cc
+++ b/test/cctest/test-disasm-arm.cc
@@ -410,16 +410,40 @@ TEST(Type3) {
"e6843895 pkhbt r3, r4, r5, lsl #17");
COMPARE(pkhtb(r3, r4, Operand(r5, ASR, 17)),
"e68438d5 pkhtb r3, r4, r5, asr #17");
- COMPARE(uxtb(r9, Operand(r10, ROR, 0)),
- "e6ef907a uxtb r9, r10");
- COMPARE(uxtb(r3, Operand(r4, ROR, 8)),
- "e6ef3474 uxtb r3, r4, ror #8");
- COMPARE(uxtab(r3, r4, Operand(r5, ROR, 8)),
- "e6e43475 uxtab r3, r4, r5, ror #8");
- COMPARE(uxtb16(r3, Operand(r4, ROR, 8)),
- "e6cf3474 uxtb16 r3, r4, ror #8");
+
+ COMPARE(sxtb(r1, r7, 0, eq), "06af1077 sxtbeq r1, r7");
+ COMPARE(sxtb(r0, r0, 8, ne), "16af0470 sxtbne r0, r0, ror #8");
+ COMPARE(sxtb(r9, r10, 16), "e6af987a sxtb r9, r10, ror #16");
+ COMPARE(sxtb(r4, r3, 24), "e6af4c73 sxtb r4, r3, ror #24");
+
+ COMPARE(sxtab(r3, r4, r5), "e6a43075 sxtab r3, r4, r5");
+
+ COMPARE(sxth(r5, r0), "e6bf5070 sxth r5, r0");
+ COMPARE(sxth(r5, r9, 8), "e6bf5479 sxth r5, r9, ror #8");
+ COMPARE(sxth(r5, r9, 16, hi), "86bf5879 sxthhi r5, r9, ror #16");
+ COMPARE(sxth(r8, r9, 24, cc), "36bf8c79 sxthcc r8, r9, ror #24");
+
+ COMPARE(sxtah(r3, r4, r5, 16), "e6b43875 sxtah r3, r4, r5, ror #16");
+
+ COMPARE(uxtb(r9, r10), "e6ef907a uxtb r9, r10");
+ COMPARE(uxtb(r3, r4, 8), "e6ef3474 uxtb r3, r4, ror #8");
+
+ COMPARE(uxtab(r3, r4, r5, 8), "e6e43475 uxtab r3, r4, r5, ror #8");
+
+ COMPARE(uxtb16(r3, r4, 8), "e6cf3474 uxtb16 r3, r4, ror #8");
+
+ COMPARE(uxth(r9, r10), "e6ff907a uxth r9, r10");
+ COMPARE(uxth(r3, r4, 8), "e6ff3474 uxth r3, r4, ror #8");
+
+ COMPARE(uxtah(r3, r4, r5, 24), "e6f43c75 uxtah r3, r4, r5, ror #24");
}
+ COMPARE(smmla(r0, r1, r2, r3), "e7503211 smmla r0, r1, r2, r3");
+ COMPARE(smmla(r10, r9, r8, r7), "e75a7819 smmla r10, r9, r8, r7");
+
+ COMPARE(smmul(r0, r1, r2), "e750f211 smmul r0, r1, r2");
+ COMPARE(smmul(r8, r9, r10), "e758fa19 smmul r8, r9, r10");
+
VERIFY_RUN();
}
@@ -680,6 +704,30 @@ TEST(Vfp) {
}
+TEST(ARMv8_vrintX_disasm) {
+ SET_UP();
+
+ if (CpuFeatures::IsSupported(ARMv8)) {
+ COMPARE(vrinta(d0, d0), "feb80b40 vrinta.f64.f64 d0, d0");
+ COMPARE(vrinta(d2, d3), "feb82b43 vrinta.f64.f64 d2, d3");
+
+ COMPARE(vrintp(d0, d0), "feba0b40 vrintp.f64.f64 d0, d0");
+ COMPARE(vrintp(d2, d3), "feba2b43 vrintp.f64.f64 d2, d3");
+
+ COMPARE(vrintn(d0, d0), "feb90b40 vrintn.f64.f64 d0, d0");
+ COMPARE(vrintn(d2, d3), "feb92b43 vrintn.f64.f64 d2, d3");
+
+ COMPARE(vrintm(d0, d0), "febb0b40 vrintm.f64.f64 d0, d0");
+ COMPARE(vrintm(d2, d3), "febb2b43 vrintm.f64.f64 d2, d3");
+
+ COMPARE(vrintz(d0, d0), "eeb60bc0 vrintz.f64.f64 d0, d0");
+ COMPARE(vrintz(d2, d3, ne), "1eb62bc3 vrintzne.f64.f64 d2, d3");
+ }
+
+ VERIFY_RUN();
+}
+
+
TEST(Neon) {
SET_UP();
diff --git a/test/cctest/test-disasm-arm64.cc b/test/cctest/test-disasm-arm64.cc
index fb01347c..208f1f5a 100644
--- a/test/cctest/test-disasm-arm64.cc
+++ b/test/cctest/test-disasm-arm64.cc
@@ -1408,6 +1408,10 @@ TEST_(fp_dp1) {
COMPARE(frintn(s31, s30), "frintn s31, s30");
COMPARE(frintn(d12, d13), "frintn d12, d13");
COMPARE(frintn(d31, d30), "frintn d31, d30");
+ COMPARE(frintp(s10, s11), "frintp s10, s11");
+ COMPARE(frintp(s31, s30), "frintp s31, s30");
+ COMPARE(frintp(d12, d13), "frintp d12, d13");
+ COMPARE(frintp(d31, d30), "frintp d31, d30");
COMPARE(frintz(s10, s11), "frintz s10, s11");
COMPARE(frintz(s31, s30), "frintz s31, s30");
COMPARE(frintz(d12, d13), "frintz d12, d13");
diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc
index 49088f6e..a2eaa15e 100644
--- a/test/cctest/test-disasm-ia32.cc
+++ b/test/cctest/test-disasm-ia32.cc
@@ -51,7 +51,7 @@ TEST(DisasmIa320) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
- v8::internal::byte buffer[2048];
+ v8::internal::byte buffer[4096];
Assembler assm(isolate, buffer, sizeof buffer);
DummyStaticFunction(NULL); // just bloody use it (DELETE; debugging)
@@ -201,6 +201,12 @@ TEST(DisasmIa320) {
__ rcl(edx, 7);
__ rcr(edx, 1);
__ rcr(edx, 7);
+ __ ror(edx, 1);
+ __ ror(edx, 6);
+ __ ror_cl(edx);
+ __ ror(Operand(ebx, ecx, times_4, 10000), 1);
+ __ ror(Operand(ebx, ecx, times_4, 10000), 6);
+ __ ror_cl(Operand(ebx, ecx, times_4, 10000));
__ sar(edx, 1);
__ sar(edx, 6);
__ sar_cl(edx);
@@ -383,6 +389,8 @@ TEST(DisasmIa320) {
// Move operation
__ movaps(xmm0, xmm1);
__ shufps(xmm0, xmm0, 0x0);
+ __ cvtsd2ss(xmm0, xmm1);
+ __ cvtsd2ss(xmm0, Operand(ebx, ecx, times_4, 10000));
// logic operation
__ andps(xmm0, xmm1);
@@ -393,6 +401,14 @@ TEST(DisasmIa320) {
__ xorps(xmm0, Operand(ebx, ecx, times_4, 10000));
// Arithmetic operation
+ __ addss(xmm1, xmm0);
+ __ addss(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ mulss(xmm1, xmm0);
+ __ mulss(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ subss(xmm1, xmm0);
+ __ subss(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ divss(xmm1, xmm0);
+ __ divss(xmm1, Operand(ebx, ecx, times_4, 10000));
__ addps(xmm1, xmm0);
__ addps(xmm1, Operand(ebx, ecx, times_4, 10000));
__ subps(xmm1, xmm0);
@@ -401,10 +417,15 @@ TEST(DisasmIa320) {
__ mulps(xmm1, Operand(ebx, ecx, times_4, 10000));
__ divps(xmm1, xmm0);
__ divps(xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ ucomiss(xmm0, xmm1);
+ __ ucomiss(xmm0, Operand(ebx, ecx, times_4, 10000));
}
{
__ cvttss2si(edx, Operand(ebx, ecx, times_4, 10000));
__ cvtsi2sd(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ cvtss2sd(xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ cvtss2sd(xmm1, xmm0);
__ movsd(xmm1, Operand(ebx, ecx, times_4, 10000));
__ movsd(Operand(ebx, ecx, times_4, 10000), xmm1);
// 128 bit move instructions.
@@ -414,10 +435,13 @@ TEST(DisasmIa320) {
__ movdqu(Operand(ebx, ecx, times_4, 10000), xmm0);
__ addsd(xmm1, xmm0);
+ __ addsd(xmm1, Operand(ebx, ecx, times_4, 10000));
__ mulsd(xmm1, xmm0);
+ __ mulsd(xmm1, Operand(ebx, ecx, times_4, 10000));
__ subsd(xmm1, xmm0);
__ subsd(xmm1, Operand(ebx, ecx, times_4, 10000));
__ divsd(xmm1, xmm0);
+ __ divsd(xmm1, Operand(ebx, ecx, times_4, 10000));
__ ucomisd(xmm0, xmm1);
__ cmpltsd(xmm0, xmm1);
@@ -458,6 +482,83 @@ TEST(DisasmIa320) {
}
}
+ // AVX instruction
+ {
+ if (CpuFeatures::IsSupported(AVX)) {
+ CpuFeatureScope scope(&assm, AVX);
+ __ vaddsd(xmm0, xmm1, xmm2);
+ __ vaddsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vmulsd(xmm0, xmm1, xmm2);
+ __ vmulsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vsubsd(xmm0, xmm1, xmm2);
+ __ vsubsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vdivsd(xmm0, xmm1, xmm2);
+ __ vdivsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ }
+ }
+
+ // FMA3 instruction
+ {
+ if (CpuFeatures::IsSupported(FMA3)) {
+ CpuFeatureScope scope(&assm, FMA3);
+ __ vfmadd132sd(xmm0, xmm1, xmm2);
+ __ vfmadd132sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmadd213sd(xmm0, xmm1, xmm2);
+ __ vfmadd213sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmadd231sd(xmm0, xmm1, xmm2);
+ __ vfmadd231sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfmsub132sd(xmm0, xmm1, xmm2);
+ __ vfmsub132sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmsub213sd(xmm0, xmm1, xmm2);
+ __ vfmsub213sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmsub231sd(xmm0, xmm1, xmm2);
+ __ vfmsub231sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfnmadd132sd(xmm0, xmm1, xmm2);
+ __ vfnmadd132sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmadd213sd(xmm0, xmm1, xmm2);
+ __ vfnmadd213sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmadd231sd(xmm0, xmm1, xmm2);
+ __ vfnmadd231sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfnmsub132sd(xmm0, xmm1, xmm2);
+ __ vfnmsub132sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmsub213sd(xmm0, xmm1, xmm2);
+ __ vfnmsub213sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmsub231sd(xmm0, xmm1, xmm2);
+ __ vfnmsub231sd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfmadd132ss(xmm0, xmm1, xmm2);
+ __ vfmadd132ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmadd213ss(xmm0, xmm1, xmm2);
+ __ vfmadd213ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmadd231ss(xmm0, xmm1, xmm2);
+ __ vfmadd231ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfmsub132ss(xmm0, xmm1, xmm2);
+ __ vfmsub132ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmsub213ss(xmm0, xmm1, xmm2);
+ __ vfmsub213ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfmsub231ss(xmm0, xmm1, xmm2);
+ __ vfmsub231ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfnmadd132ss(xmm0, xmm1, xmm2);
+ __ vfnmadd132ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmadd213ss(xmm0, xmm1, xmm2);
+ __ vfnmadd213ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmadd231ss(xmm0, xmm1, xmm2);
+ __ vfnmadd231ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+
+ __ vfnmsub132ss(xmm0, xmm1, xmm2);
+ __ vfnmsub132ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmsub213ss(xmm0, xmm1, xmm2);
+ __ vfnmsub213ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ __ vfnmsub231ss(xmm0, xmm1, xmm2);
+ __ vfnmsub231ss(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+ }
+ }
+
// xchg.
{
__ xchg(eax, eax);
diff --git a/test/cctest/test-disasm-x64.cc b/test/cctest/test-disasm-x64.cc
index e756ce22..6cd58ec2 100644
--- a/test/cctest/test-disasm-x64.cc
+++ b/test/cctest/test-disasm-x64.cc
@@ -51,7 +51,7 @@ TEST(DisasmX64) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);
- v8::internal::byte buffer[2048];
+ v8::internal::byte buffer[4096];
Assembler assm(isolate, buffer, sizeof buffer);
DummyStaticFunction(NULL); // just bloody use it (DELETE; debugging)
@@ -117,6 +117,26 @@ TEST(DisasmX64) {
__ imulq(rdx, rcx);
__ shld(rdx, rcx);
__ shrd(rdx, rcx);
+ __ shlq(Operand(rdi, rax, times_4, 100), Immediate(1));
+ __ shlq(Operand(rdi, rax, times_4, 100), Immediate(6));
+ __ shlq(Operand(r15, 0), Immediate(1));
+ __ shlq(Operand(r15, 0), Immediate(6));
+ __ shlq_cl(Operand(r15, 0));
+ __ shlq_cl(Operand(r15, 0));
+ __ shlq_cl(Operand(rdi, rax, times_4, 100));
+ __ shlq_cl(Operand(rdi, rax, times_4, 100));
+ __ shlq(rdx, Immediate(1));
+ __ shlq(rdx, Immediate(6));
+ __ shll(Operand(rdi, rax, times_4, 100), Immediate(1));
+ __ shll(Operand(rdi, rax, times_4, 100), Immediate(6));
+ __ shll(Operand(r15, 0), Immediate(1));
+ __ shll(Operand(r15, 0), Immediate(6));
+ __ shll_cl(Operand(r15, 0));
+ __ shll_cl(Operand(r15, 0));
+ __ shll_cl(Operand(rdi, rax, times_4, 100));
+ __ shll_cl(Operand(rdi, rax, times_4, 100));
+ __ shll(rdx, Immediate(1));
+ __ shll(rdx, Immediate(6));
__ bts(Operand(rdx, 0), rcx);
__ bts(Operand(rbx, rcx, times_4, 0), rcx);
__ nop();
@@ -159,14 +179,22 @@ TEST(DisasmX64) {
__ nop();
__ idivq(rdx);
- __ mul(rdx);
+ __ mull(rdx);
+ __ mulq(rdx);
__ negq(rdx);
__ notq(rdx);
__ testq(Operand(rbx, rcx, times_4, 10000), rdx);
- __ imulq(rdx, Operand(rbx, rcx, times_4, 10000));
__ imulq(rdx, rcx, Immediate(12));
__ imulq(rdx, rcx, Immediate(1000));
+ __ imulq(rdx, Operand(rbx, rcx, times_4, 10000));
+ __ imulq(rdx, Operand(rbx, rcx, times_4, 10000), Immediate(12));
+ __ imulq(rdx, Operand(rbx, rcx, times_4, 10000), Immediate(1000));
+ __ imull(r15, rcx, Immediate(12));
+ __ imull(r15, rcx, Immediate(1000));
+ __ imull(r15, Operand(rbx, rcx, times_4, 10000));
+ __ imull(r15, Operand(rbx, rcx, times_4, 10000), Immediate(12));
+ __ imull(r15, Operand(rbx, rcx, times_4, 10000), Immediate(1000));
__ incq(rdx);
__ incq(Operand(rbx, rcx, times_4, 10000));
@@ -353,6 +381,8 @@ TEST(DisasmX64) {
// Move operation
__ cvttss2si(rdx, Operand(rbx, rcx, times_4, 10000));
__ cvttss2si(rdx, xmm1);
+ __ cvtsd2ss(xmm0, xmm1);
+ __ cvtsd2ss(xmm0, Operand(rbx, rcx, times_4, 10000));
__ movaps(xmm0, xmm1);
// logic operation
@@ -364,6 +394,14 @@ TEST(DisasmX64) {
__ xorps(xmm0, Operand(rbx, rcx, times_4, 10000));
// Arithmetic operation
+ __ addss(xmm1, xmm0);
+ __ addss(xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ mulss(xmm1, xmm0);
+ __ mulss(xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ subss(xmm1, xmm0);
+ __ subss(xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ divss(xmm1, xmm0);
+ __ divss(xmm1, Operand(rbx, rcx, times_4, 10000));
__ addps(xmm1, xmm0);
__ addps(xmm1, Operand(rbx, rcx, times_4, 10000));
__ subps(xmm1, xmm0);
@@ -372,6 +410,9 @@ TEST(DisasmX64) {
__ mulps(xmm1, Operand(rbx, rcx, times_4, 10000));
__ divps(xmm1, xmm0);
__ divps(xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ ucomiss(xmm0, xmm1);
+ __ ucomiss(xmm0, Operand(rbx, rcx, times_4, 10000));
}
// SSE 2 instructions
{
@@ -379,6 +420,8 @@ TEST(DisasmX64) {
__ cvttsd2si(rdx, xmm1);
__ cvttsd2siq(rdx, xmm1);
__ cvttsd2siq(rdx, Operand(rbx, rcx, times_4, 10000));
+ __ cvtqsi2sd(xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ cvtqsi2sd(xmm1, rdx);
__ movsd(xmm1, Operand(rbx, rcx, times_4, 10000));
__ movsd(Operand(rbx, rcx, times_4, 10000), xmm1);
// 128 bit move instructions.
@@ -386,12 +429,23 @@ TEST(DisasmX64) {
__ movdqa(Operand(rbx, rcx, times_4, 10000), xmm0);
__ addsd(xmm1, xmm0);
+ __ addsd(xmm1, Operand(rbx, rcx, times_4, 10000));
__ mulsd(xmm1, xmm0);
+ __ mulsd(xmm1, Operand(rbx, rcx, times_4, 10000));
__ subsd(xmm1, xmm0);
+ __ subsd(xmm1, Operand(rbx, rcx, times_4, 10000));
__ divsd(xmm1, xmm0);
+ __ divsd(xmm1, Operand(rbx, rcx, times_4, 10000));
__ ucomisd(xmm0, xmm1);
__ andpd(xmm0, xmm1);
+
+ __ pslld(xmm0, 6);
+ __ psrld(xmm0, 6);
+ __ psllq(xmm0, 6);
+ __ psrlq(xmm0, 6);
+
+ __ pcmpeqd(xmm1, xmm0);
}
// cmov.
@@ -421,6 +475,89 @@ TEST(DisasmX64) {
}
}
+ // AVX instruction
+ {
+ if (CpuFeatures::IsSupported(AVX)) {
+ CpuFeatureScope scope(&assm, AVX);
+ __ vaddsd(xmm0, xmm1, xmm2);
+ __ vaddsd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vmulsd(xmm0, xmm1, xmm2);
+ __ vmulsd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vsubsd(xmm0, xmm1, xmm2);
+ __ vsubsd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vdivsd(xmm0, xmm1, xmm2);
+ __ vdivsd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ }
+ }
+
+ // FMA3 instruction
+ {
+ if (CpuFeatures::IsSupported(FMA3)) {
+ CpuFeatureScope scope(&assm, FMA3);
+ __ vfmadd132sd(xmm0, xmm1, xmm2);
+ __ vfmadd132sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmadd213sd(xmm0, xmm1, xmm2);
+ __ vfmadd213sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmadd231sd(xmm0, xmm1, xmm2);
+ __ vfmadd231sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfmadd132sd(xmm9, xmm10, xmm11);
+ __ vfmadd132sd(xmm9, xmm10, Operand(r9, r11, times_4, 10000));
+ __ vfmadd213sd(xmm9, xmm10, xmm11);
+ __ vfmadd213sd(xmm9, xmm10, Operand(r9, r11, times_4, 10000));
+ __ vfmadd231sd(xmm9, xmm10, xmm11);
+ __ vfmadd231sd(xmm9, xmm10, Operand(r9, r11, times_4, 10000));
+
+ __ vfmsub132sd(xmm0, xmm1, xmm2);
+ __ vfmsub132sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmsub213sd(xmm0, xmm1, xmm2);
+ __ vfmsub213sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmsub231sd(xmm0, xmm1, xmm2);
+ __ vfmsub231sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfnmadd132sd(xmm0, xmm1, xmm2);
+ __ vfnmadd132sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmadd213sd(xmm0, xmm1, xmm2);
+ __ vfnmadd213sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmadd231sd(xmm0, xmm1, xmm2);
+ __ vfnmadd231sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfnmsub132sd(xmm0, xmm1, xmm2);
+ __ vfnmsub132sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmsub213sd(xmm0, xmm1, xmm2);
+ __ vfnmsub213sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmsub231sd(xmm0, xmm1, xmm2);
+ __ vfnmsub231sd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfmadd132ss(xmm0, xmm1, xmm2);
+ __ vfmadd132ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmadd213ss(xmm0, xmm1, xmm2);
+ __ vfmadd213ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmadd231ss(xmm0, xmm1, xmm2);
+ __ vfmadd231ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfmsub132ss(xmm0, xmm1, xmm2);
+ __ vfmsub132ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmsub213ss(xmm0, xmm1, xmm2);
+ __ vfmsub213ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfmsub231ss(xmm0, xmm1, xmm2);
+ __ vfmsub231ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfnmadd132ss(xmm0, xmm1, xmm2);
+ __ vfnmadd132ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmadd213ss(xmm0, xmm1, xmm2);
+ __ vfnmadd213ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmadd231ss(xmm0, xmm1, xmm2);
+ __ vfnmadd231ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+
+ __ vfnmsub132ss(xmm0, xmm1, xmm2);
+ __ vfnmsub132ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmsub213ss(xmm0, xmm1, xmm2);
+ __ vfnmsub213ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ __ vfnmsub231ss(xmm0, xmm1, xmm2);
+ __ vfnmsub231ss(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+ }
+ }
// xchg.
{
__ xchgq(rax, rax);
diff --git a/test/cctest/test-disasm-x87.cc b/test/cctest/test-disasm-x87.cc
index 6cd33e55..e9b0dc54 100644
--- a/test/cctest/test-disasm-x87.cc
+++ b/test/cctest/test-disasm-x87.cc
@@ -201,6 +201,12 @@ TEST(DisasmIa320) {
__ rcl(edx, 7);
__ rcr(edx, 1);
__ rcr(edx, 7);
+ __ ror(edx, 1);
+ __ ror(edx, 6);
+ __ ror_cl(edx);
+ __ ror(Operand(ebx, ecx, times_4, 10000), 1);
+ __ ror(Operand(ebx, ecx, times_4, 10000), 6);
+ __ ror_cl(Operand(ebx, ecx, times_4, 10000));
__ sar(edx, 1);
__ sar(edx, 6);
__ sar_cl(edx);
diff --git a/test/cctest/test-feedback-vector.cc b/test/cctest/test-feedback-vector.cc
new file mode 100644
index 00000000..fa2f195b
--- /dev/null
+++ b/test/cctest/test-feedback-vector.cc
@@ -0,0 +1,308 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/api.h"
+#include "src/debug.h"
+#include "src/execution.h"
+#include "src/factory.h"
+#include "src/global-handles.h"
+#include "src/macro-assembler.h"
+#include "src/objects.h"
+
+using namespace v8::internal;
+
+namespace {
+
+TEST(VectorStructure) {
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ // Empty vectors are the empty fixed array.
+ FeedbackVectorSpec empty;
+ Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(empty);
+ CHECK(Handle<FixedArray>::cast(vector)
+ .is_identical_to(factory->empty_fixed_array()));
+ // Which can nonetheless be queried.
+ CHECK_EQ(0, vector->ic_with_type_info_count());
+ CHECK_EQ(0, vector->ic_generic_count());
+ CHECK_EQ(0, vector->Slots());
+ CHECK_EQ(0, vector->ICSlots());
+
+ FeedbackVectorSpec one_slot(1, 0);
+ vector = factory->NewTypeFeedbackVector(one_slot);
+ CHECK_EQ(1, vector->Slots());
+ CHECK_EQ(0, vector->ICSlots());
+
+ FeedbackVectorSpec one_icslot(0, 1);
+ if (FLAG_vector_ics) {
+ one_icslot.SetKind(0, Code::CALL_IC);
+ }
+ vector = factory->NewTypeFeedbackVector(one_icslot);
+ CHECK_EQ(0, vector->Slots());
+ CHECK_EQ(1, vector->ICSlots());
+
+ FeedbackVectorSpec spec(3, 5);
+ if (FLAG_vector_ics) {
+ for (int i = 0; i < 5; i++) spec.SetKind(i, Code::CALL_IC);
+ }
+ vector = factory->NewTypeFeedbackVector(spec);
+ CHECK_EQ(3, vector->Slots());
+ CHECK_EQ(5, vector->ICSlots());
+
+ int metadata_length = vector->ic_metadata_length();
+ if (!FLAG_vector_ics) {
+ CHECK_EQ(0, metadata_length);
+ } else {
+ CHECK(metadata_length > 0);
+ }
+
+ int index = vector->GetIndex(FeedbackVectorSlot(0));
+
+ CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length, index);
+ CHECK(FeedbackVectorSlot(0) == vector->ToSlot(index));
+
+ index = vector->GetIndex(FeedbackVectorICSlot(0));
+ CHECK_EQ(index,
+ TypeFeedbackVector::kReservedIndexCount + metadata_length + 3);
+ CHECK(FeedbackVectorICSlot(0) == vector->ToICSlot(index));
+
+ CHECK_EQ(TypeFeedbackVector::kReservedIndexCount + metadata_length + 3 + 5,
+ vector->length());
+}
+
+
+// IC slots need an encoding to recognize what is in there.
+TEST(VectorICMetadata) {
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ if (!FLAG_vector_ics) {
+ // If FLAG_vector_ics is false, we only store CALL_ICs in the vector, so
+ // there is no need for metadata to describe the slots.
+ return;
+ }
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ FeedbackVectorSpec spec(10, 3 * 10);
+ // Set metadata.
+ for (int i = 0; i < 30; i++) {
+ Code::Kind kind;
+ if (i % 3 == 0) {
+ kind = Code::CALL_IC;
+ } else if (i % 3 == 1) {
+ kind = Code::LOAD_IC;
+ } else {
+ kind = Code::KEYED_LOAD_IC;
+ }
+ spec.SetKind(i, kind);
+ }
+
+ Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(spec);
+ CHECK_EQ(10, vector->Slots());
+ CHECK_EQ(3 * 10, vector->ICSlots());
+
+ // Meanwhile set some feedback values and type feedback values to
+ // verify the data structure remains intact.
+ vector->change_ic_with_type_info_count(100);
+ vector->change_ic_generic_count(3333);
+ vector->Set(FeedbackVectorSlot(0), *vector);
+
+ // Verify the metadata is correctly set up from the spec.
+ for (int i = 0; i < 30; i++) {
+ Code::Kind kind = vector->GetKind(FeedbackVectorICSlot(i));
+ if (i % 3 == 0) {
+ CHECK_EQ(Code::CALL_IC, kind);
+ } else if (i % 3 == 1) {
+ CHECK_EQ(Code::LOAD_IC, kind);
+ } else {
+ CHECK_EQ(Code::KEYED_LOAD_IC, kind);
+ }
+ }
+}
+
+
+TEST(VectorSlotClearing) {
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ // We only test clearing FeedbackVectorSlots, not FeedbackVectorICSlots.
+ // The reason is that FeedbackVectorICSlots need a full code environment
+ // to fully test (See VectorICProfilerStatistics test below).
+ FeedbackVectorSpec spec(5, 0);
+ Handle<TypeFeedbackVector> vector = factory->NewTypeFeedbackVector(spec);
+
+ // Fill with information
+ vector->Set(FeedbackVectorSlot(0), Smi::FromInt(1));
+ vector->Set(FeedbackVectorSlot(1), *factory->fixed_array_map());
+ Handle<AllocationSite> site = factory->NewAllocationSite();
+ vector->Set(FeedbackVectorSlot(2), *site);
+
+ vector->ClearSlots(NULL);
+
+ // The feedback vector slots are cleared. AllocationSites are granted
+ // an exemption from clearing, as are smis.
+ CHECK_EQ(Smi::FromInt(1), vector->Get(FeedbackVectorSlot(0)));
+ CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),
+ vector->Get(FeedbackVectorSlot(1)));
+ CHECK(vector->Get(FeedbackVectorSlot(2))->IsAllocationSite());
+}
+
+
+TEST(VectorICProfilerStatistics) {
+ if (i::FLAG_always_opt) return;
+ CcTest::InitializeVM();
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+
+ // Make sure function f has a call that uses a type feedback slot.
+ CompileRun(
+ "function fun() {};"
+ "function f(a) { a(); } f(fun);");
+ Handle<JSFunction> f = v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
+ // There should be one IC.
+ Code* code = f->shared()->code();
+ TypeFeedbackInfo* feedback_info =
+ TypeFeedbackInfo::cast(code->type_feedback_info());
+ CHECK_EQ(1, feedback_info->ic_total_count());
+ CHECK_EQ(0, feedback_info->ic_with_type_info_count());
+ CHECK_EQ(0, feedback_info->ic_generic_count());
+ TypeFeedbackVector* feedback_vector = f->shared()->feedback_vector();
+ CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
+ CHECK_EQ(0, feedback_vector->ic_generic_count());
+
+ // Now send the information generic.
+ CompileRun("f(Object);");
+ feedback_vector = f->shared()->feedback_vector();
+ CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
+ CHECK_EQ(1, feedback_vector->ic_generic_count());
+
+ // A collection will make the site uninitialized again.
+ heap->CollectAllGarbage(i::Heap::kNoGCFlags);
+ feedback_vector = f->shared()->feedback_vector();
+ CHECK_EQ(0, feedback_vector->ic_with_type_info_count());
+ CHECK_EQ(0, feedback_vector->ic_generic_count());
+
+ // The Array function is special. A call to array remains monomorphic
+ // and isn't cleared by gc because an AllocationSite is being held.
+ CompileRun("f(Array);");
+ feedback_vector = f->shared()->feedback_vector();
+ CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
+ CHECK_EQ(0, feedback_vector->ic_generic_count());
+
+ int ic_slot = 0;
+ CHECK(
+ feedback_vector->Get(FeedbackVectorICSlot(ic_slot))->IsAllocationSite());
+ heap->CollectAllGarbage(i::Heap::kNoGCFlags);
+ feedback_vector = f->shared()->feedback_vector();
+ CHECK_EQ(1, feedback_vector->ic_with_type_info_count());
+ CHECK_EQ(0, feedback_vector->ic_generic_count());
+ CHECK(
+ feedback_vector->Get(FeedbackVectorICSlot(ic_slot))->IsAllocationSite());
+}
+
+
+TEST(VectorCallICStates) {
+ if (i::FLAG_always_opt) return;
+ CcTest::InitializeVM();
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+
+ // Make sure function f has a call that uses a type feedback slot.
+ CompileRun(
+ "function foo() { return 17; }"
+ "function f(a) { a(); } f(foo);");
+ Handle<JSFunction> f = v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
+ // There should be one IC.
+ Handle<TypeFeedbackVector> feedback_vector =
+ Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
+ FeedbackVectorICSlot slot(0);
+ CallICNexus nexus(feedback_vector, slot);
+ CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
+ // CallIC doesn't return map feedback.
+ CHECK_EQ(NULL, nexus.FindFirstMap());
+
+ CompileRun("f(function() { return 16; })");
+ CHECK_EQ(GENERIC, nexus.StateFromFeedback());
+
+ // After a collection, state should be reset to UNINITIALIZED.
+ heap->CollectAllGarbage(i::Heap::kNoGCFlags);
+ CHECK_EQ(UNINITIALIZED, nexus.StateFromFeedback());
+
+ // Array is special. It will remain monomorphic across gcs and it contains an
+ // AllocationSite.
+ CompileRun("f(Array)");
+ CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
+ CHECK(feedback_vector->Get(FeedbackVectorICSlot(slot))->IsAllocationSite());
+
+ heap->CollectAllGarbage(i::Heap::kNoGCFlags);
+ CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
+}
+
+
+TEST(VectorLoadICStates) {
+ if (i::FLAG_always_opt || !i::FLAG_vector_ics) return;
+ CcTest::InitializeVM();
+ LocalContext context;
+ v8::HandleScope scope(context->GetIsolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+
+ // Make sure function f has a call that uses a type feedback slot.
+ CompileRun(
+ "var o = { foo: 3 };"
+ "function f(a) { return a.foo; } f(o);");
+ Handle<JSFunction> f = v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
+ // There should be one IC.
+ Handle<TypeFeedbackVector> feedback_vector =
+ Handle<TypeFeedbackVector>(f->shared()->feedback_vector(), isolate);
+ FeedbackVectorICSlot slot(0);
+ LoadICNexus nexus(feedback_vector, slot);
+ CHECK_EQ(PREMONOMORPHIC, nexus.StateFromFeedback());
+
+ CompileRun("f(o)");
+ CHECK_EQ(MONOMORPHIC, nexus.StateFromFeedback());
+ // Verify that the monomorphic map is the one we expect.
+ Handle<JSObject> o = v8::Utils::OpenHandle(
+ *v8::Handle<v8::Object>::Cast(CcTest::global()->Get(v8_str("o"))));
+ CHECK_EQ(o->map(), nexus.FindFirstMap());
+
+ // Now go polymorphic.
+ CompileRun("f({ blarg: 3, foo: 2 })");
+ CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
+
+ CompileRun(
+ "delete o.foo;"
+ "f(o)");
+ CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
+
+ CompileRun("f({ blarg: 3, torino: 10, foo: 2 })");
+ CHECK_EQ(POLYMORPHIC, nexus.StateFromFeedback());
+ MapHandleList maps;
+ nexus.FindAllMaps(&maps);
+ CHECK_EQ(4, maps.length());
+
+ // Finally driven megamorphic.
+ CompileRun("f({ blarg: 3, gran: 3, torino: 10, foo: 2 })");
+ CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
+ CHECK_EQ(NULL, nexus.FindFirstMap());
+
+ // After a collection, state should not be reset to PREMONOMORPHIC.
+ heap->CollectAllGarbage(i::Heap::kNoGCFlags);
+ CHECK_EQ(MEGAMORPHIC, nexus.StateFromFeedback());
+}
+}
diff --git a/test/cctest/test-func-name-inference.cc b/test/cctest/test-func-name-inference.cc
index bc503b58..7f3dafc0 100644
--- a/test/cctest/test-func-name-inference.cc
+++ b/test/cctest/test-func-name-inference.cc
@@ -30,7 +30,7 @@
#include "src/api.h"
#include "src/debug.h"
-#include "src/runtime.h"
+#include "src/string-search.h"
#include "test/cctest/cctest.h"
@@ -46,13 +46,13 @@ using ::v8::internal::Script;
using ::v8::internal::SmartArrayPointer;
using ::v8::internal::SharedFunctionInfo;
using ::v8::internal::String;
+using ::v8::internal::Vector;
static void CheckFunctionName(v8::Handle<v8::Script> script,
const char* func_pos_src,
const char* ref_inferred_name) {
Isolate* isolate = CcTest::i_isolate();
- Factory* factory = isolate->factory();
// Get script source.
Handle<Object> obj = v8::Utils::OpenHandle(*script);
@@ -69,12 +69,14 @@ static void CheckFunctionName(v8::Handle<v8::Script> script,
Handle<String> script_src(String::cast(i_script->source()));
// Find the position of a given func source substring in the source.
- Handle<String> func_pos_str =
- factory->NewStringFromAsciiChecked(func_pos_src);
- int func_pos = Runtime::StringMatch(isolate,
- script_src,
- func_pos_str,
- 0);
+ int func_pos;
+ {
+ i::DisallowHeapAllocation no_gc;
+ Vector<const uint8_t> func_pos_str = i::OneByteVector(func_pos_src);
+ String::FlatContent script_content = script_src->GetFlatContent();
+ func_pos = SearchString(isolate, script_content.ToOneByteVector(),
+ func_pos_str, 0);
+ }
CHECK_NE(0, func_pos);
// Obtain SharedFunctionInfo for the function.
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index 8f9b4843..94a5be47 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -890,9 +890,10 @@ class OneByteResource : public v8::String::ExternalOneByteStringResource {
} // namespace
TEST(HeapSnapshotJSONSerialization) {
+ v8::Isolate* isolate = CcTest::isolate();
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
- v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ v8::HandleScope scope(isolate);
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
#define STRING_LITERAL_FOR_TEST \
"\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
@@ -923,7 +924,7 @@ TEST(HeapSnapshotJSONSerialization) {
// Verify that snapshot object has required fields.
v8::Local<v8::Object> parsed_snapshot =
- env->Global()->Get(v8_str("parsed"))->ToObject();
+ env->Global()->Get(v8_str("parsed"))->ToObject(isolate);
CHECK(parsed_snapshot->Has(v8_str("snapshot")));
CHECK(parsed_snapshot->Has(v8_str("nodes")));
CHECK(parsed_snapshot->Has(v8_str("edges")));
@@ -979,17 +980,18 @@ TEST(HeapSnapshotJSONSerialization) {
" \"s\", property_type)");
CHECK(!string_obj_pos_val.IsEmpty());
int string_obj_pos =
- static_cast<int>(string_obj_pos_val->ToNumber()->Value());
+ static_cast<int>(string_obj_pos_val->ToNumber(isolate)->Value());
v8::Local<v8::Object> nodes_array =
- parsed_snapshot->Get(v8_str("nodes"))->ToObject();
+ parsed_snapshot->Get(v8_str("nodes"))->ToObject(isolate);
int string_index = static_cast<int>(
- nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
+ nodes_array->Get(string_obj_pos + 1)->ToNumber(isolate)->Value());
CHECK_GT(string_index, 0);
v8::Local<v8::Object> strings_array =
- parsed_snapshot->Get(v8_str("strings"))->ToObject();
- v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
+ parsed_snapshot->Get(v8_str("strings"))->ToObject(isolate);
+ v8::Local<v8::String> string =
+ strings_array->Get(string_index)->ToString(isolate);
v8::Local<v8::String> ref_string =
- CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
+ CompileRun(STRING_LITERAL_FOR_TEST)->ToString(isolate);
#undef STRING_LITERAL_FOR_TEST
CHECK_EQ(*v8::String::Utf8Value(ref_string),
*v8::String::Utf8Value(string));
@@ -1719,9 +1721,6 @@ TEST(GlobalObjectFields) {
const v8::HeapGraphNode* native_context =
GetProperty(global, v8::HeapGraphEdge::kInternal, "native_context");
CHECK_NE(NULL, native_context);
- const v8::HeapGraphNode* global_context =
- GetProperty(global, v8::HeapGraphEdge::kInternal, "global_context");
- CHECK_NE(NULL, global_context);
const v8::HeapGraphNode* global_proxy =
GetProperty(global, v8::HeapGraphEdge::kInternal, "global_proxy");
CHECK_NE(NULL, global_proxy);
@@ -1917,6 +1916,47 @@ TEST(FastCaseAccessors) {
}
+TEST(FastCaseRedefinedAccessors) {
+ LocalContext env;
+ v8::HandleScope scope(env->GetIsolate());
+ v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+
+ CompileRun(
+ "var obj1 = {};\n"
+ "Object.defineProperty(obj1, 'prop', { "
+ " get: function() { return 42; },\n"
+ " set: function(value) { return this.prop_ = value; },\n"
+ " configurable: true,\n"
+ " enumerable: true,\n"
+ "});\n"
+ "Object.defineProperty(obj1, 'prop', { "
+ " get: function() { return 153; },\n"
+ " set: function(value) { return this.prop_ = value; },\n"
+ " configurable: true,\n"
+ " enumerable: true,\n"
+ "});\n");
+ v8::Local<v8::Object> js_global =
+ env->Global()->GetPrototype().As<v8::Object>();
+ i::Handle<i::JSObject> js_obj1 =
+ v8::Utils::OpenHandle(*js_global->Get(v8_str("obj1")).As<v8::Object>());
+ USE(js_obj1);
+
+ const v8::HeapSnapshot* snapshot =
+ heap_profiler->TakeHeapSnapshot(v8_str("fastCaseAccessors"));
+ CHECK(ValidateSnapshot(snapshot));
+ const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
+ CHECK_NE(NULL, global);
+ const v8::HeapGraphNode* obj1 =
+ GetProperty(global, v8::HeapGraphEdge::kProperty, "obj1");
+ CHECK_NE(NULL, obj1);
+ const v8::HeapGraphNode* func;
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "get prop");
+ CHECK_NE(NULL, func);
+ func = GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set prop");
+ CHECK_NE(NULL, func);
+}
+
+
TEST(SlowCaseAccessors) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
@@ -1952,9 +1992,10 @@ TEST(SlowCaseAccessors) {
TEST(HiddenPropertiesFastCase) {
+ v8::Isolate* isolate = CcTest::isolate();
LocalContext env;
- v8::HandleScope scope(env->GetIsolate());
- v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
+ v8::HandleScope scope(isolate);
+ v8::HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
CompileRun(
"function C(x) { this.a = this; this.b = x; }\n"
@@ -1973,7 +2014,7 @@ TEST(HiddenPropertiesFastCase) {
v8::Handle<v8::Value> cHandle =
env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "c"));
CHECK(!cHandle.IsEmpty() && cHandle->IsObject());
- cHandle->ToObject()->SetHiddenValue(v8_str("key"), v8_str("val"));
+ cHandle->ToObject(isolate)->SetHiddenValue(v8_str("key"), v8_str("val"));
snapshot = heap_profiler->TakeHeapSnapshot(
v8_str("HiddenPropertiesFastCase2"));
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index e526761b..f8a7df20 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -1375,6 +1375,98 @@ TEST(TestCodeFlushingIncrementalAbort) {
}
+TEST(CompilationCacheCachingBehavior) {
+ // If we do not flush code, or have the compilation cache turned off, this
+ // test is invalid.
+ if (!FLAG_flush_code || !FLAG_flush_code_incrementally ||
+ !FLAG_compilation_cache) {
+ return;
+ }
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ Heap* heap = isolate->heap();
+ CompilationCache* compilation_cache = isolate->compilation_cache();
+
+ v8::HandleScope scope(CcTest::isolate());
+ const char* raw_source =
+ "function foo() {"
+ " var x = 42;"
+ " var y = 42;"
+ " var z = x + y;"
+ "};"
+ "foo()";
+ Handle<String> source = factory->InternalizeUtf8String(raw_source);
+ Handle<Context> native_context = isolate->native_context();
+
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun(raw_source);
+ }
+
+ // On first compilation, only a hash is inserted in the code cache. We can't
+ // find that value.
+ MaybeHandle<SharedFunctionInfo> info = compilation_cache->LookupScript(
+ source, Handle<Object>(), 0, 0, true, native_context);
+ CHECK(info.is_null());
+
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun(raw_source);
+ }
+
+ // On second compilation, the hash is replaced by a real cache entry mapping
+ // the source to the shared function info containing the code.
+ info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, true,
+ native_context);
+ CHECK(!info.is_null());
+
+ heap->CollectAllGarbage(Heap::kNoGCFlags);
+
+ // On second compilation, the hash is replaced by a real cache entry mapping
+ // the source to the shared function info containing the code.
+ info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, true,
+ native_context);
+ CHECK(!info.is_null());
+
+ while (!info.ToHandleChecked()->code()->IsOld()) {
+ info.ToHandleChecked()->code()->MakeOlder(NO_MARKING_PARITY);
+ }
+
+ heap->CollectAllGarbage(Heap::kNoGCFlags);
+ // Ensure code aging cleared the entry from the cache.
+ info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, true,
+ native_context);
+ CHECK(info.is_null());
+
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun(raw_source);
+ }
+
+ // On first compilation, only a hash is inserted in the code cache. We can't
+ // find that value.
+ info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, true,
+ native_context);
+ CHECK(info.is_null());
+
+ for (int i = 0; i < CompilationCacheTable::kHashGenerations; i++) {
+ compilation_cache->MarkCompactPrologue();
+ }
+
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun(raw_source);
+ }
+
+ // If we aged the cache before caching the script, ensure that we didn't cache
+ // on next compilation.
+ info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, true,
+ native_context);
+ CHECK(info.is_null());
+}
+
+
// Count the number of native contexts in the weak list of native contexts.
int CountNativeContexts() {
int count = 0;
@@ -1487,7 +1579,7 @@ TEST(TestInternalWeakLists) {
}
// Force compilation cache cleanup.
- CcTest::heap()->NotifyContextDisposed();
+ CcTest::heap()->NotifyContextDisposed(true);
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
// Dispose the native contexts one by one.
@@ -1602,6 +1694,64 @@ TEST(TestInternalWeakListsTraverseWithGC) {
}
+TEST(TestSizeOfRegExpCode) {
+ if (!FLAG_regexp_optimization) return;
+
+ v8::V8::Initialize();
+
+ Isolate* isolate = CcTest::i_isolate();
+ HandleScope scope(isolate);
+
+ LocalContext context;
+
+ // Adjust source below and this check to match
+ // RegExpImple::kRegExpTooLargeToOptimize.
+ DCHECK_EQ(i::RegExpImpl::kRegExpTooLargeToOptimize, 10 * KB);
+
+ // Compile a regexp that is much larger if we are using regexp optimizations.
+ CompileRun(
+ "var reg_exp_source = '(?:a|bc|def|ghij|klmno|pqrstu)';"
+ "var half_size_reg_exp;"
+ "while (reg_exp_source.length < 10 * 1024) {"
+ " half_size_reg_exp = reg_exp_source;"
+ " reg_exp_source = reg_exp_source + reg_exp_source;"
+ "}"
+ // Flatten string.
+ "reg_exp_source.match(/f/);");
+
+ // Get initial heap size after several full GCs, which will stabilize
+ // the heap size and return with sweeping finished completely.
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ MarkCompactCollector* collector = CcTest::heap()->mark_compact_collector();
+ if (collector->sweeping_in_progress()) {
+ collector->EnsureSweepingCompleted();
+ }
+ int initial_size = static_cast<int>(CcTest::heap()->SizeOfObjects());
+
+ CompileRun("'foo'.match(reg_exp_source);");
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ int size_with_regexp = static_cast<int>(CcTest::heap()->SizeOfObjects());
+
+ CompileRun("'foo'.match(half_size_reg_exp);");
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+ int size_with_optimized_regexp =
+ static_cast<int>(CcTest::heap()->SizeOfObjects());
+
+ int size_of_regexp_code = size_with_regexp - initial_size;
+
+ CHECK_LE(size_of_regexp_code, 1 * MB);
+
+ // Small regexp is half the size, but compiles to more than twice the code
+ // due to the optimization steps.
+ CHECK_GE(size_with_optimized_regexp,
+ size_with_regexp + size_of_regexp_code * 2);
+}
+
+
TEST(TestSizeOfObjects) {
v8::V8::Initialize();
@@ -2183,6 +2333,48 @@ TEST(ResetSharedFunctionInfoCountersDuringMarkSweep) {
}
+TEST(IdleNotificationFinishMarking) {
+ i::FLAG_allow_natives_syntax = true;
+ CcTest::InitializeVM();
+ SimulateFullSpace(CcTest::heap()->old_pointer_space());
+ IncrementalMarking* marking = CcTest::heap()->incremental_marking();
+ marking->Abort();
+ marking->Start();
+
+ CHECK_EQ(CcTest::heap()->gc_count(), 0);
+
+ // TODO(hpayer): We cannot write proper unit test right now for heap.
+ // The ideal test would call kMaxIdleMarkingDelayCounter to test the
+ // marking delay counter.
+
+ // Perform a huge incremental marking step but don't complete marking.
+ intptr_t bytes_processed = 0;
+ do {
+ bytes_processed =
+ marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD,
+ IncrementalMarking::FORCE_MARKING,
+ IncrementalMarking::DO_NOT_FORCE_COMPLETION);
+ CHECK(!marking->IsIdleMarkingDelayCounterLimitReached());
+ } while (bytes_processed);
+
+ // The next invocations of incremental marking are not going to complete
+ // marking
+ // since the completion threshold is not reached
+ for (size_t i = 0; i < IncrementalMarking::kMaxIdleMarkingDelayCounter - 2;
+ i++) {
+ marking->Step(1 * MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD,
+ IncrementalMarking::FORCE_MARKING,
+ IncrementalMarking::DO_NOT_FORCE_COMPLETION);
+ CHECK(!marking->IsIdleMarkingDelayCounterLimitReached());
+ }
+
+ // The next idle notification has to finish incremental marking.
+ const int kLongIdleTime = 1000000;
+ CcTest::isolate()->IdleNotification(kLongIdleTime);
+ CHECK_EQ(CcTest::heap()->gc_count(), 1);
+}
+
+
// Test that HAllocateObject will always return an object in new-space.
TEST(OptimizedAllocationAlwaysInNewSpace) {
i::FLAG_allow_natives_syntax = true;
@@ -2204,7 +2396,8 @@ TEST(OptimizedAllocationAlwaysInNewSpace) {
"f(1); f(2); f(3);"
"%OptimizeFunctionOnNextCall(f);"
"f(4);");
- CHECK_EQ(4, res->ToObject()->GetRealNamedProperty(v8_str("x"))->Int32Value());
+ CHECK_EQ(
+ 4, res.As<v8::Object>()->GetRealNamedProperty(v8_str("x"))->Int32Value());
Handle<JSObject> o =
v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(res));
@@ -2342,12 +2535,21 @@ TEST(OptimizedPretenuringMixedInObjectProperties) {
FieldIndex idx1 = FieldIndex::ForPropertyIndex(o->map(), 0);
FieldIndex idx2 = FieldIndex::ForPropertyIndex(o->map(), 1);
CHECK(CcTest::heap()->InOldPointerSpace(o->RawFastPropertyAt(idx1)));
- CHECK(CcTest::heap()->InOldDataSpace(o->RawFastPropertyAt(idx2)));
+ if (!o->IsUnboxedDoubleField(idx2)) {
+ CHECK(CcTest::heap()->InOldDataSpace(o->RawFastPropertyAt(idx2)));
+ } else {
+ CHECK_EQ(1.1, o->RawFastDoublePropertyAt(idx2));
+ }
JSObject* inner_object =
reinterpret_cast<JSObject*>(o->RawFastPropertyAt(idx1));
CHECK(CcTest::heap()->InOldPointerSpace(inner_object));
- CHECK(CcTest::heap()->InOldDataSpace(inner_object->RawFastPropertyAt(idx1)));
+ if (!inner_object->IsUnboxedDoubleField(idx1)) {
+ CHECK(
+ CcTest::heap()->InOldDataSpace(inner_object->RawFastPropertyAt(idx1)));
+ } else {
+ CHECK_EQ(2.2, inner_object->RawFastDoublePropertyAt(idx1));
+ }
CHECK(CcTest::heap()->InOldPointerSpace(
inner_object->RawFastPropertyAt(idx2)));
}
@@ -2806,6 +3008,7 @@ TEST(TransitionArrayShrinksDuringAllocToZero) {
"root = new F");
root = GetByName("root");
AddPropertyTo(2, root, "funny");
+ CcTest::heap()->CollectGarbage(NEW_SPACE);
// Count number of live transitions after marking. Note that one transition
// is left, because 'o' still holds an instance of one transition target.
@@ -2832,6 +3035,7 @@ TEST(TransitionArrayShrinksDuringAllocToOne) {
root = GetByName("root");
AddPropertyTo(2, root, "funny");
+ CcTest::heap()->CollectGarbage(NEW_SPACE);
// Count number of live transitions after marking. Note that one transition
// is left, because 'o' still holds an instance of one transition target.
@@ -3076,7 +3280,7 @@ TEST(PrintSharedFunctionInfo) {
OFStream os(stdout);
g->shared()->Print(os);
- os << endl;
+ os << std::endl;
}
#endif // OBJECT_PRINT
@@ -3147,22 +3351,20 @@ TEST(IncrementalMarkingClearsTypeFeedbackInfo) {
Handle<TypeFeedbackVector> feedback_vector(f->shared()->feedback_vector());
- int expected_length = FLAG_vector_ics ? 4 : 2;
- CHECK_EQ(expected_length, feedback_vector->length());
- for (int i = 0; i < expected_length; i++) {
- if ((i % 2) == 1) {
- CHECK(feedback_vector->get(i)->IsJSFunction());
- }
- }
+ int expected_slots = 2;
+ CHECK_EQ(expected_slots, feedback_vector->ICSlots());
+ int slot1 = 0;
+ int slot2 = 1;
+ CHECK(feedback_vector->Get(FeedbackVectorICSlot(slot1))->IsJSFunction());
+ CHECK(feedback_vector->Get(FeedbackVectorICSlot(slot2))->IsJSFunction());
SimulateIncrementalMarking(CcTest::heap());
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
- CHECK_EQ(expected_length, feedback_vector->length());
- for (int i = 0; i < expected_length; i++) {
- CHECK_EQ(feedback_vector->get(i),
- *TypeFeedbackVector::UninitializedSentinel(CcTest::i_isolate()));
- }
+ CHECK_EQ(feedback_vector->Get(FeedbackVectorICSlot(slot1)),
+ *TypeFeedbackVector::UninitializedSentinel(CcTest::i_isolate()));
+ CHECK_EQ(feedback_vector->Get(FeedbackVectorICSlot(slot2)),
+ *TypeFeedbackVector::UninitializedSentinel(CcTest::i_isolate()));
}
@@ -3181,6 +3383,25 @@ static Code* FindFirstIC(Code* code, Code::Kind kind) {
}
+static void CheckVectorIC(Handle<JSFunction> f, int ic_slot_index,
+ InlineCacheState desired_state) {
+ Handle<TypeFeedbackVector> vector =
+ Handle<TypeFeedbackVector>(f->shared()->feedback_vector());
+ FeedbackVectorICSlot slot(ic_slot_index);
+ LoadICNexus nexus(vector, slot);
+ CHECK(nexus.StateFromFeedback() == desired_state);
+}
+
+
+static void CheckVectorICCleared(Handle<JSFunction> f, int ic_slot_index) {
+ Handle<TypeFeedbackVector> vector =
+ Handle<TypeFeedbackVector>(f->shared()->feedback_vector());
+ FeedbackVectorICSlot slot(ic_slot_index);
+ LoadICNexus nexus(vector, slot);
+ CHECK(IC::IsCleared(&nexus));
+}
+
+
TEST(IncrementalMarkingPreservesMonomorphicIC) {
if (i::FLAG_always_opt) return;
CcTest::InitializeVM();
@@ -3196,13 +3417,23 @@ TEST(IncrementalMarkingPreservesMonomorphicIC) {
CcTest::global()->Get(v8_str("f"))));
Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
- CHECK(ic_before->ic_state() == MONOMORPHIC);
+ if (FLAG_vector_ics) {
+ CheckVectorIC(f, 0, MONOMORPHIC);
+ CHECK(ic_before->ic_state() == DEFAULT);
+ } else {
+ CHECK(ic_before->ic_state() == MONOMORPHIC);
+ }
SimulateIncrementalMarking(CcTest::heap());
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
- CHECK(ic_after->ic_state() == MONOMORPHIC);
+ if (FLAG_vector_ics) {
+ CheckVectorIC(f, 0, MONOMORPHIC);
+ CHECK(ic_after->ic_state() == DEFAULT);
+ } else {
+ CHECK(ic_after->ic_state() == MONOMORPHIC);
+ }
}
@@ -3228,7 +3459,12 @@ TEST(IncrementalMarkingClearsMonomorphicIC) {
CcTest::global()->Get(v8_str("f"))));
Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
- CHECK(ic_before->ic_state() == MONOMORPHIC);
+ if (FLAG_vector_ics) {
+ CheckVectorIC(f, 0, MONOMORPHIC);
+ CHECK(ic_before->ic_state() == DEFAULT);
+ } else {
+ CHECK(ic_before->ic_state() == MONOMORPHIC);
+ }
// Fire context dispose notification.
CcTest::isolate()->ContextDisposedNotification();
@@ -3236,7 +3472,60 @@ TEST(IncrementalMarkingClearsMonomorphicIC) {
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
- CHECK(IC::IsCleared(ic_after));
+ if (FLAG_vector_ics) {
+ CheckVectorICCleared(f, 0);
+ CHECK(ic_after->ic_state() == DEFAULT);
+ } else {
+ CHECK(IC::IsCleared(ic_after));
+ }
+}
+
+
+TEST(IncrementalMarkingPreservesPolymorphicIC) {
+ if (i::FLAG_always_opt) return;
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ v8::Local<v8::Value> obj1, obj2;
+
+ {
+ LocalContext env;
+ CompileRun("function fun() { this.x = 1; }; var obj = new fun();");
+ obj1 = env->Global()->Get(v8_str("obj"));
+ }
+
+ {
+ LocalContext env;
+ CompileRun("function fun() { this.x = 2; }; var obj = new fun();");
+ obj2 = env->Global()->Get(v8_str("obj"));
+ }
+
+ // Prepare function f that contains a polymorphic IC for objects
+ // originating from two different native contexts.
+ CcTest::global()->Set(v8_str("obj1"), obj1);
+ CcTest::global()->Set(v8_str("obj2"), obj2);
+ CompileRun("function f(o) { return o.x; } f(obj1); f(obj1); f(obj2);");
+ Handle<JSFunction> f = v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
+
+ Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
+ if (FLAG_vector_ics) {
+ CheckVectorIC(f, 0, POLYMORPHIC);
+ CHECK(ic_before->ic_state() == DEFAULT);
+ } else {
+ CHECK(ic_before->ic_state() == POLYMORPHIC);
+ }
+
+ // Fire context dispose notification.
+ SimulateIncrementalMarking(CcTest::heap());
+ CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
+
+ Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
+ if (FLAG_vector_ics) {
+ CheckVectorIC(f, 0, POLYMORPHIC);
+ CHECK(ic_after->ic_state() == DEFAULT);
+ } else {
+ CHECK(ic_after->ic_state() == POLYMORPHIC);
+ }
}
@@ -3269,7 +3558,12 @@ TEST(IncrementalMarkingClearsPolymorphicIC) {
CcTest::global()->Get(v8_str("f"))));
Code* ic_before = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
- CHECK(ic_before->ic_state() == POLYMORPHIC);
+ if (FLAG_vector_ics) {
+ CheckVectorIC(f, 0, POLYMORPHIC);
+ CHECK(ic_before->ic_state() == DEFAULT);
+ } else {
+ CHECK(ic_before->ic_state() == POLYMORPHIC);
+ }
// Fire context dispose notification.
CcTest::isolate()->ContextDisposedNotification();
@@ -3277,7 +3571,12 @@ TEST(IncrementalMarkingClearsPolymorphicIC) {
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
Code* ic_after = FindFirstIC(f->shared()->code(), Code::LOAD_IC);
- CHECK(IC::IsCleared(ic_after));
+ if (FLAG_vector_ics) {
+ CheckVectorICCleared(f, 0);
+ CHECK(ic_before->ic_state() == DEFAULT);
+ } else {
+ CHECK(IC::IsCleared(ic_after));
+ }
}
@@ -4021,8 +4320,9 @@ TEST(NoWeakHashTableLeakWithIncrementalMarking) {
"bar%d();"
"bar%d();"
"bar%d();"
- "%%OptimizeFunctionOnNextCall(bar%d);"
- "bar%d();", i, i, i, i, i, i, i, i);
+ "%%OptimizeFwunctionOnNextCall(bar%d);"
+ "bar%d();",
+ i, i, i, i, i, i, i, i);
CompileRun(source.start());
}
heap->CollectAllGarbage(i::Heap::kNoGCFlags);
@@ -4154,7 +4454,7 @@ void CheckWeakness(const char* source) {
v8::Persistent<v8::Object> garbage;
{
v8::HandleScope scope(isolate);
- garbage.Reset(isolate, CompileRun(source)->ToObject());
+ garbage.Reset(isolate, CompileRun(source)->ToObject(isolate));
}
weak_ic_cleared = false;
garbage.SetWeak(static_cast<void*>(&garbage), &ClearWeakIC);
@@ -4181,6 +4481,25 @@ TEST(WeakMapInMonomorphicLoadIC) {
}
+TEST(WeakMapInPolymorphicLoadIC) {
+ CheckWeakness(
+ "function loadIC(obj) {"
+ " return obj.name;"
+ "}"
+ " (function() {"
+ " var proto = {'name' : 'weak'};"
+ " var obj = Object.create(proto);"
+ " loadIC(obj);"
+ " loadIC(obj);"
+ " loadIC(obj);"
+ " var poly = Object.create(proto);"
+ " poly.x = true;"
+ " loadIC(poly);"
+ " return proto;"
+ " })();");
+}
+
+
TEST(WeakMapInMonomorphicKeyedLoadIC) {
CheckWeakness("function keyedLoadIC(obj, field) {"
" return obj[field];"
@@ -4196,6 +4515,25 @@ TEST(WeakMapInMonomorphicKeyedLoadIC) {
}
+TEST(WeakMapInPolymorphicKeyedLoadIC) {
+ CheckWeakness(
+ "function keyedLoadIC(obj, field) {"
+ " return obj[field];"
+ "}"
+ " (function() {"
+ " var proto = {'name' : 'weak'};"
+ " var obj = Object.create(proto);"
+ " keyedLoadIC(obj, 'name');"
+ " keyedLoadIC(obj, 'name');"
+ " keyedLoadIC(obj, 'name');"
+ " var poly = Object.create(proto);"
+ " poly.x = true;"
+ " keyedLoadIC(poly, 'name');"
+ " return proto;"
+ " })();");
+}
+
+
TEST(WeakMapInMonomorphicStoreIC) {
CheckWeakness("function storeIC(obj, value) {"
" obj.name = value;"
@@ -4211,6 +4549,25 @@ TEST(WeakMapInMonomorphicStoreIC) {
}
+TEST(WeakMapInPolymorphicStoreIC) {
+ CheckWeakness(
+ "function storeIC(obj, value) {"
+ " obj.name = value;"
+ "}"
+ " (function() {"
+ " var proto = {'name' : 'weak'};"
+ " var obj = Object.create(proto);"
+ " storeIC(obj, 'x');"
+ " storeIC(obj, 'x');"
+ " storeIC(obj, 'x');"
+ " var poly = Object.create(proto);"
+ " poly.x = true;"
+ " storeIC(poly, 'x');"
+ " return proto;"
+ " })();");
+}
+
+
TEST(WeakMapInMonomorphicKeyedStoreIC) {
CheckWeakness("function keyedStoreIC(obj, field, value) {"
" obj[field] = value;"
@@ -4226,6 +4583,25 @@ TEST(WeakMapInMonomorphicKeyedStoreIC) {
}
+TEST(WeakMapInPolymorphicKeyedStoreIC) {
+ CheckWeakness(
+ "function keyedStoreIC(obj, field, value) {"
+ " obj[field] = value;"
+ "}"
+ " (function() {"
+ " var proto = {'name' : 'weak'};"
+ " var obj = Object.create(proto);"
+ " keyedStoreIC(obj, 'x');"
+ " keyedStoreIC(obj, 'x');"
+ " keyedStoreIC(obj, 'x');"
+ " var poly = Object.create(proto);"
+ " poly.x = true;"
+ " keyedStoreIC(poly, 'x');"
+ " return proto;"
+ " })();");
+}
+
+
TEST(WeakMapInMonomorphicCompareNilIC) {
CheckWeakness("function compareNilIC(obj) {"
" return obj == null;"
@@ -4241,6 +4617,160 @@ TEST(WeakMapInMonomorphicCompareNilIC) {
}
+Handle<JSFunction> GetFunctionByName(Isolate* isolate, const char* name) {
+ Handle<String> str = isolate->factory()->InternalizeUtf8String(name);
+ Handle<Object> obj =
+ Object::GetProperty(isolate->global_object(), str).ToHandleChecked();
+ return Handle<JSFunction>::cast(obj);
+}
+
+
+void CheckIC(Code* code, Code::Kind kind, InlineCacheState state) {
+ Code* ic = FindFirstIC(code, kind);
+ CHECK(ic->is_inline_cache_stub());
+ CHECK(ic->ic_state() == state);
+}
+
+
+TEST(MonomorphicStaysMonomorphicAfterGC) {
+ if (FLAG_always_opt) return;
+ // TODO(mvstanton): vector ics need weak support!
+ if (FLAG_vector_ics) return;
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun(
+ "function loadIC(obj) {"
+ " return obj.name;"
+ "}"
+ "function testIC() {"
+ " var proto = {'name' : 'weak'};"
+ " var obj = Object.create(proto);"
+ " loadIC(obj);"
+ " loadIC(obj);"
+ " loadIC(obj);"
+ " return proto;"
+ "};");
+ Handle<JSFunction> loadIC = GetFunctionByName(isolate, "loadIC");
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun("(testIC())");
+ }
+ heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
+ CheckIC(loadIC->code(), Code::LOAD_IC, MONOMORPHIC);
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun("(testIC())");
+ }
+ CheckIC(loadIC->code(), Code::LOAD_IC, MONOMORPHIC);
+}
+
+
+TEST(PolymorphicStaysPolymorphicAfterGC) {
+ if (FLAG_always_opt) return;
+ // TODO(mvstanton): vector ics need weak support!
+ if (FLAG_vector_ics) return;
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun(
+ "function loadIC(obj) {"
+ " return obj.name;"
+ "}"
+ "function testIC() {"
+ " var proto = {'name' : 'weak'};"
+ " var obj = Object.create(proto);"
+ " loadIC(obj);"
+ " loadIC(obj);"
+ " loadIC(obj);"
+ " var poly = Object.create(proto);"
+ " poly.x = true;"
+ " loadIC(poly);"
+ " return proto;"
+ "};");
+ Handle<JSFunction> loadIC = GetFunctionByName(isolate, "loadIC");
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun("(testIC())");
+ }
+ heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
+ CheckIC(loadIC->code(), Code::LOAD_IC, POLYMORPHIC);
+ {
+ v8::HandleScope scope(CcTest::isolate());
+ CompileRun("(testIC())");
+ }
+ CheckIC(loadIC->code(), Code::LOAD_IC, POLYMORPHIC);
+}
+
+
+TEST(WeakCell) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::internal::Heap* heap = CcTest::heap();
+ v8::internal::Factory* factory = isolate->factory();
+
+ HandleScope outer_scope(isolate);
+ Handle<WeakCell> weak_cell1;
+ {
+ HandleScope inner_scope(isolate);
+ Handle<HeapObject> value = factory->NewFixedArray(1, NOT_TENURED);
+ weak_cell1 = inner_scope.CloseAndEscape(factory->NewWeakCell(value));
+ }
+
+ Handle<FixedArray> survivor = factory->NewFixedArray(1, NOT_TENURED);
+ Handle<WeakCell> weak_cell2;
+ {
+ HandleScope inner_scope(isolate);
+ weak_cell2 = inner_scope.CloseAndEscape(factory->NewWeakCell(survivor));
+ }
+ CHECK(weak_cell1->value()->IsFixedArray());
+ CHECK_EQ(*survivor, weak_cell2->value());
+ heap->CollectGarbage(NEW_SPACE);
+ CHECK(weak_cell1->value()->IsFixedArray());
+ CHECK_EQ(*survivor, weak_cell2->value());
+ heap->CollectGarbage(NEW_SPACE);
+ CHECK(weak_cell1->value()->IsFixedArray());
+ CHECK_EQ(*survivor, weak_cell2->value());
+ heap->CollectAllAvailableGarbage();
+ CHECK(weak_cell1->cleared());
+ CHECK_EQ(*survivor, weak_cell2->value());
+}
+
+
+TEST(WeakCellsWithIncrementalMarking) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::internal::Heap* heap = CcTest::heap();
+ v8::internal::Factory* factory = isolate->factory();
+
+ const int N = 16;
+ HandleScope outer_scope(isolate);
+ Handle<FixedArray> survivor = factory->NewFixedArray(1, NOT_TENURED);
+ Handle<WeakCell> weak_cells[N];
+
+ for (int i = 0; i < N; i++) {
+ HandleScope inner_scope(isolate);
+ Handle<HeapObject> value =
+ i == 0 ? survivor : factory->NewFixedArray(1, NOT_TENURED);
+ Handle<WeakCell> weak_cell = factory->NewWeakCell(value);
+ CHECK(weak_cell->value()->IsFixedArray());
+ IncrementalMarking* marking = heap->incremental_marking();
+ if (marking->IsStopped()) marking->Start();
+ marking->Step(128, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
+ heap->CollectGarbage(NEW_SPACE);
+ CHECK(weak_cell->value()->IsFixedArray());
+ weak_cells[i] = inner_scope.CloseAndEscape(weak_cell);
+ }
+ heap->CollectAllGarbage(Heap::kNoGCFlags);
+ CHECK_EQ(*survivor, weak_cells[0]->value());
+ for (int i = 1; i < N; i++) {
+ CHECK(weak_cells[i]->cleared());
+ }
+}
+
+
#ifdef DEBUG
TEST(AddInstructionChangesNewSpacePromotion) {
i::FLAG_allow_natives_syntax = true;
@@ -4320,7 +4850,7 @@ TEST(Regress357137) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope hscope(isolate);
- v8::Handle<v8::ObjectTemplate> global =v8::ObjectTemplate::New(isolate);
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
global->Set(v8::String::NewFromUtf8(isolate, "interrupt"),
v8::FunctionTemplate::New(isolate, RequestInterrupt));
v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
@@ -4333,7 +4863,7 @@ TEST(Regress357137) {
"eval('function f() {' + locals + 'return function() { return v0; }; }');"
"interrupt();" // This triggers a fake stack overflow in f.
"f()()");
- CHECK_EQ(42.0, result->ToNumber()->Value());
+ CHECK_EQ(42.0, result->ToNumber(isolate)->Value());
}
@@ -4502,6 +5032,63 @@ TEST(Regress388880) {
}
+TEST(Regress3631) {
+ i::FLAG_expose_gc = true;
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ IncrementalMarking* marking = CcTest::heap()->incremental_marking();
+ v8::Local<v8::Value> result = CompileRun(
+ "var weak_map = new WeakMap();"
+ "var future_keys = [];"
+ "for (var i = 0; i < 50; i++) {"
+ " var key = {'k' : i + 0.1};"
+ " weak_map.set(key, 1);"
+ " future_keys.push({'x' : i + 0.2});"
+ "}"
+ "weak_map");
+ if (marking->IsStopped()) {
+ marking->Start();
+ }
+ // Incrementally mark the backing store.
+ Handle<JSObject> obj =
+ v8::Utils::OpenHandle(*v8::Handle<v8::Object>::Cast(result));
+ Handle<JSWeakCollection> weak_map(reinterpret_cast<JSWeakCollection*>(*obj));
+ while (!Marking::IsBlack(
+ Marking::MarkBitFrom(HeapObject::cast(weak_map->table()))) &&
+ !marking->IsStopped()) {
+ marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD);
+ }
+ // Stash the backing store in a handle.
+ Handle<Object> save(weak_map->table(), isolate);
+ // The following line will update the backing store.
+ CompileRun(
+ "for (var i = 0; i < 50; i++) {"
+ " weak_map.set(future_keys[i], i);"
+ "}");
+ heap->incremental_marking()->set_should_hurry(true);
+ heap->CollectGarbage(OLD_POINTER_SPACE);
+}
+
+
+TEST(Regress442710) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ Factory* factory = isolate->factory();
+
+ HandleScope sc(isolate);
+ Handle<GlobalObject> global(CcTest::i_isolate()->context()->global_object());
+ Handle<JSArray> array = factory->NewJSArray(2);
+
+ Handle<String> name = factory->InternalizeUtf8String("testArray");
+ JSReceiver::SetProperty(global, name, array, SLOPPY).Check();
+ CompileRun("testArray[0] = 1; testArray[1] = 2; testArray.shift();");
+ heap->CollectGarbage(OLD_POINTER_SPACE);
+}
+
+
#ifdef DEBUG
TEST(PathTracer) {
CcTest::InitializeVM();
diff --git a/test/cctest/test-lockers.cc b/test/cctest/test-lockers.cc
index ed315cea..dc5404e9 100644
--- a/test/cctest/test-lockers.cc
+++ b/test/cctest/test-lockers.cc
@@ -141,6 +141,7 @@ class JoinableThread {
void Join() {
semaphore_.Wait();
+ thread_.Join();
}
virtual void Run() = 0;
diff --git a/test/cctest/test-log-stack-tracer.cc b/test/cctest/test-log-stack-tracer.cc
index 334a2010..714ad69c 100644
--- a/test/cctest/test-log-stack-tracer.cc
+++ b/test/cctest/test-log-stack-tracer.cc
@@ -235,9 +235,9 @@ TEST(PureJSStackTrace) {
static void CFuncDoTrace(byte dummy_parameter) {
Address fp;
-#ifdef __GNUC__
+#if V8_HAS_BUILTIN_FRAME_ADDRESS
fp = reinterpret_cast<Address>(__builtin_frame_address(0));
-#elif defined _MSC_VER
+#elif V8_CC_MSVC
// Approximate a frame pointer address. We compile without base pointers,
// so we can't trust ebp/rbp.
fp = &dummy_parameter - 2 * sizeof(void*); // NOLINT
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index 482f89f9..eee3e134 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -42,6 +42,7 @@
#include "src/natives.h"
#include "src/utils.h"
#include "src/v8threads.h"
+#include "src/version.h"
#include "src/vm-state-inl.h"
#include "test/cctest/cctest.h"
@@ -477,10 +478,9 @@ TEST(EquivalenceOfLoggingAndTraversal) {
isolate, log.start(), v8::String::kNormalString, log.length());
initialize_logger.env()->Global()->Set(v8_str("_log"), log_str);
- i::Vector<const unsigned char> source = TestSources::GetScriptsSource();
+ i::Vector<const char> source = TestSources::GetScriptsSource();
v8::Handle<v8::String> source_str = v8::String::NewFromUtf8(
- isolate, reinterpret_cast<const char*>(source.start()),
- v8::String::kNormalString, source.length());
+ isolate, source.start(), v8::String::kNormalString, source.length());
v8::TryCatch try_catch;
v8::Handle<v8::Script> script = CompileWithOrigin(source_str, "");
if (script.IsEmpty()) {
@@ -496,7 +496,7 @@ TEST(EquivalenceOfLoggingAndTraversal) {
}
// The result either be a "true" literal or problem description.
if (!result->IsTrue()) {
- v8::Local<v8::String> s = result->ToString();
+ v8::Local<v8::String> s = result->ToString(isolate);
i::ScopedVector<char> data(s->Utf8Length() + 1);
CHECK_NE(NULL, data.start());
s->WriteUtf8(data.start());
@@ -508,3 +508,23 @@ TEST(EquivalenceOfLoggingAndTraversal) {
}
isolate->Dispose();
}
+
+
+TEST(LogVersion) {
+ v8::Isolate* isolate;
+ {
+ ScopedLoggerInitializer initialize_logger;
+ isolate = initialize_logger.isolate();
+ bool exists = false;
+ i::Vector<const char> log(
+ i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true));
+ CHECK(exists);
+ i::EmbeddedVector<char, 100> ref_data;
+ i::SNPrintF(ref_data, "v8-version,%d,%d,%d,%d,%d", i::Version::GetMajor(),
+ i::Version::GetMinor(), i::Version::GetBuild(),
+ i::Version::GetPatch(), i::Version::IsCandidate());
+ CHECK_NE(NULL, StrNStr(log.start(), ref_data.start(), log.length()));
+ log.Dispose();
+ }
+ isolate->Dispose();
+}
diff --git a/test/cctest/test-mark-compact.cc b/test/cctest/test-mark-compact.cc
index c7d65310..64d995d9 100644
--- a/test/cctest/test-mark-compact.cc
+++ b/test/cctest/test-mark-compact.cc
@@ -446,7 +446,7 @@ static intptr_t MemoryInUse() {
bool write_permission = (buffer[position++] == 'w');
CHECK(buffer[position] == '-' || buffer[position] == 'x');
bool execute_permission = (buffer[position++] == 'x');
- CHECK(buffer[position] == '-' || buffer[position] == 'p');
+ CHECK(buffer[position] == 's' || buffer[position] == 'p');
bool private_mapping = (buffer[position++] == 'p');
CHECK_EQ(buffer[position++], ' ');
uintptr_t offset = ReadLong(buffer, &position, 16);
diff --git a/test/cctest/test-object-observe.cc b/test/cctest/test-object-observe.cc
index d208a269..2766a4f2 100644
--- a/test/cctest/test-object-observe.cc
+++ b/test/cctest/test-object-observe.cc
@@ -130,6 +130,59 @@ TEST(DeliveryOrdering) {
}
+TEST(DeliveryCallbackThrows) {
+ HandleScope scope(CcTest::isolate());
+ LocalContext context(CcTest::isolate());
+ CompileRun(
+ "var obj = {};"
+ "var ordering = [];"
+ "function observer1() { ordering.push(1); };"
+ "function observer2() { ordering.push(2); };"
+ "function observer_throws() {"
+ " ordering.push(0);"
+ " throw new Error();"
+ " ordering.push(-1);"
+ "};"
+ "Object.observe(obj, observer_throws.bind());"
+ "Object.observe(obj, observer1);"
+ "Object.observe(obj, observer_throws.bind());"
+ "Object.observe(obj, observer2);"
+ "Object.observe(obj, observer_throws.bind());"
+ "obj.foo = 'bar';");
+ CHECK_EQ(5, CompileRun("ordering.length")->Int32Value());
+ CHECK_EQ(0, CompileRun("ordering[0]")->Int32Value());
+ CHECK_EQ(1, CompileRun("ordering[1]")->Int32Value());
+ CHECK_EQ(0, CompileRun("ordering[2]")->Int32Value());
+ CHECK_EQ(2, CompileRun("ordering[3]")->Int32Value());
+ CHECK_EQ(0, CompileRun("ordering[4]")->Int32Value());
+}
+
+
+TEST(DeliveryChangesMutationInCallback) {
+ HandleScope scope(CcTest::isolate());
+ LocalContext context(CcTest::isolate());
+ CompileRun(
+ "var obj = {};"
+ "var ordering = [];"
+ "function observer1(records) {"
+ " ordering.push(100 + records.length);"
+ " records.push(11);"
+ " records.push(22);"
+ "};"
+ "function observer2(records) {"
+ " ordering.push(200 + records.length);"
+ " records.push(33);"
+ " records.push(44);"
+ "};"
+ "Object.observe(obj, observer1);"
+ "Object.observe(obj, observer2);"
+ "obj.foo = 'bar';");
+ CHECK_EQ(2, CompileRun("ordering.length")->Int32Value());
+ CHECK_EQ(101, CompileRun("ordering[0]")->Int32Value());
+ CHECK_EQ(201, CompileRun("ordering[1]")->Int32Value());
+}
+
+
TEST(DeliveryOrderingReentrant) {
HandleScope scope(CcTest::isolate());
LocalContext context(CcTest::isolate());
@@ -709,3 +762,76 @@ TEST(DontLeakContextOnNotifierPerformChange) {
CcTest::isolate()->ContextDisposedNotification();
CheckSurvivingGlobalObjectsCount(1);
}
+
+
+static void ObserverCallback(const FunctionCallbackInfo<Value>& args) {
+ *static_cast<int*>(Handle<External>::Cast(args.Data())->Value()) =
+ Handle<Array>::Cast(args[0])->Length();
+}
+
+
+TEST(ObjectObserveCallsCppFunction) {
+ Isolate* isolate = CcTest::isolate();
+ HandleScope scope(isolate);
+ LocalContext context(isolate);
+ int numRecordsSent = 0;
+ Handle<Function> observer =
+ Function::New(CcTest::isolate(), ObserverCallback,
+ External::New(isolate, &numRecordsSent));
+ context->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "observer"),
+ observer);
+ CompileRun(
+ "var obj = {};"
+ "Object.observe(obj, observer);"
+ "obj.foo = 1;"
+ "obj.bar = 2;");
+ CHECK_EQ(2, numRecordsSent);
+}
+
+
+TEST(ObjectObserveCallsFunctionTemplateInstance) {
+ Isolate* isolate = CcTest::isolate();
+ HandleScope scope(isolate);
+ LocalContext context(isolate);
+ int numRecordsSent = 0;
+ Handle<FunctionTemplate> tmpl = FunctionTemplate::New(
+ isolate, ObserverCallback, External::New(isolate, &numRecordsSent));
+ context->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "observer"),
+ tmpl->GetFunction());
+ CompileRun(
+ "var obj = {};"
+ "Object.observe(obj, observer);"
+ "obj.foo = 1;"
+ "obj.bar = 2;");
+ CHECK_EQ(2, numRecordsSent);
+}
+
+
+static void AccessorGetter(Local<String> property,
+ const PropertyCallbackInfo<Value>& info) {
+ info.GetReturnValue().Set(Integer::New(info.GetIsolate(), 42));
+}
+
+
+static void AccessorSetter(Local<String> property, Local<Value> value,
+ const PropertyCallbackInfo<void>& info) {
+ info.GetReturnValue().SetUndefined();
+}
+
+
+TEST(APIAccessorsShouldNotNotify) {
+ Isolate* isolate = CcTest::isolate();
+ HandleScope handle_scope(isolate);
+ LocalContext context(isolate);
+ Handle<Object> object = Object::New(isolate);
+ object->SetAccessor(String::NewFromUtf8(isolate, "accessor"), &AccessorGetter,
+ &AccessorSetter);
+ context->Global()->Set(String::NewFromUtf8(isolate, "obj"), object);
+ CompileRun(
+ "var records = null;"
+ "Object.observe(obj, function(r) { records = r });"
+ "obj.accessor = 43;");
+ CHECK(CompileRun("records")->IsNull());
+ CompileRun("Object.defineProperty(obj, 'accessor', { value: 44 });");
+ CHECK(CompileRun("records")->IsNull());
+}
diff --git a/test/cctest/test-ostreams.cc b/test/cctest/test-ostreams.cc
deleted file mode 100644
index c83f96d4..00000000
--- a/test/cctest/test-ostreams.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string.h>
-#include <limits>
-
-#include "include/v8stdint.h"
-#include "src/ostreams.h"
-#include "test/cctest/cctest.h"
-
-using namespace v8::internal;
-
-
-TEST(OStringStreamConstructor) {
- OStringStream oss;
- const size_t expected_size = 0;
- CHECK(expected_size == oss.size());
- CHECK_GT(oss.capacity(), 0);
- CHECK_NE(NULL, oss.data());
- CHECK_EQ("", oss.c_str());
-}
-
-
-#define TEST_STRING \
- "Ash nazg durbatuluk, " \
- "ash nazg gimbatul, " \
- "ash nazg thrakatuluk, " \
- "agh burzum-ishi krimpatul."
-
-TEST(OStringStreamGrow) {
- OStringStream oss;
- const int repeat = 30;
- size_t len = strlen(TEST_STRING);
- for (int i = 0; i < repeat; ++i) {
- oss.write(TEST_STRING, len);
- }
- const char* expected =
- TEST_STRING TEST_STRING TEST_STRING TEST_STRING TEST_STRING
- TEST_STRING TEST_STRING TEST_STRING TEST_STRING TEST_STRING
- TEST_STRING TEST_STRING TEST_STRING TEST_STRING TEST_STRING
- TEST_STRING TEST_STRING TEST_STRING TEST_STRING TEST_STRING
- TEST_STRING TEST_STRING TEST_STRING TEST_STRING TEST_STRING
- TEST_STRING TEST_STRING TEST_STRING TEST_STRING TEST_STRING;
- const size_t expected_len = len * repeat;
- CHECK(expected_len == oss.size());
- CHECK_GT(oss.capacity(), 0);
- CHECK_EQ(0, strncmp(expected, oss.data(), expected_len));
- CHECK_EQ(expected, oss.c_str());
-}
-
-
-template <class T>
-static void check(const char* expected, T value) {
- OStringStream oss;
- oss << value << " " << hex << value;
- CHECK_EQ(expected, oss.c_str());
-}
-
-
-TEST(NumericFormatting) {
- check<bool>("0 0", false);
- check<bool>("1 1", true);
-
- check<int16_t>("-12345 cfc7", -12345);
- check<int16_t>("-32768 8000", std::numeric_limits<int16_t>::min());
- check<int16_t>("32767 7fff", std::numeric_limits<int16_t>::max());
-
- check<uint16_t>("34567 8707", 34567);
- check<uint16_t>("0 0", std::numeric_limits<uint16_t>::min());
- check<uint16_t>("65535 ffff", std::numeric_limits<uint16_t>::max());
-
- check<int32_t>("-1234567 ffed2979", -1234567);
- check<int32_t>("-2147483648 80000000", std::numeric_limits<int32_t>::min());
- check<int32_t>("2147483647 7fffffff", std::numeric_limits<int32_t>::max());
-
- check<uint32_t>("3456789 34bf15", 3456789);
- check<uint32_t>("0 0", std::numeric_limits<uint32_t>::min());
- check<uint32_t>("4294967295 ffffffff", std::numeric_limits<uint32_t>::max());
-
- check<int64_t>("-1234567 ffffffffffed2979", -1234567);
- check<int64_t>("-9223372036854775808 8000000000000000",
- std::numeric_limits<int64_t>::min());
- check<int64_t>("9223372036854775807 7fffffffffffffff",
- std::numeric_limits<int64_t>::max());
-
- check<uint64_t>("3456789 34bf15", 3456789);
- check<uint64_t>("0 0", std::numeric_limits<uint64_t>::min());
- check<uint64_t>("18446744073709551615 ffffffffffffffff",
- std::numeric_limits<uint64_t>::max());
-
- check<float>("0 0", 0.0f);
- check<float>("123 123", 123.0f);
- check<float>("-0.5 -0.5", -0.5f);
- check<float>("1.25 1.25", 1.25f);
- check<float>("0.0625 0.0625", 6.25e-2f);
-
- check<double>("0 0", 0.0);
- check<double>("123 123", 123.0);
- check<double>("-0.5 -0.5", -0.5);
- check<double>("1.25 1.25", 1.25);
- check<double>("0.0625 0.0625", 6.25e-2);
-}
-
-
-TEST(CharacterOutput) {
- check<char>("a a", 'a');
- check<signed char>("B B", 'B');
- check<unsigned char>("9 9", '9');
- check<const char*>("bye bye", "bye");
-
- OStringStream os;
- os.put('H').write("ello", 4);
- CHECK_EQ("Hello", os.c_str());
-}
-
-
-TEST(Manipulators) {
- OStringStream os;
- os << 123 << hex << 123 << endl << 123 << dec << 123 << 123;
- CHECK_EQ("1237b\n7b123123", os.c_str());
-}
-
-
-class MiscStuff {
- public:
- MiscStuff(int i, double d, const char* s) : i_(i), d_(d), s_(s) { }
-
- private:
- friend OStream& operator<<(OStream& os, const MiscStuff& m);
-
- int i_;
- double d_;
- const char* s_;
-};
-
-
-OStream& operator<<(OStream& os, const MiscStuff& m) {
- return os << "{i:" << m.i_ << ", d:" << m.d_ << ", s:'" << m.s_ << "'}";
-}
-
-
-TEST(CustomOutput) {
- OStringStream os;
- MiscStuff m(123, 4.5, "Hurz!");
- os << m;
- CHECK_EQ("{i:123, d:4.5, s:'Hurz!'}", os.c_str());
-}
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index 72f22980..08caeab5 100644
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -31,6 +31,8 @@
#include "src/v8.h"
+#include "src/ast.h"
+#include "src/ast-numbering.h"
#include "src/ast-value-factory.h"
#include "src/compiler.h"
#include "src/execution.h"
@@ -317,8 +319,8 @@ TEST(StandAlonePreParser) {
i::PreParser preparser(&scanner, &log, stack_limit);
preparser.set_allow_lazy(true);
- preparser.set_allow_natives_syntax(true);
- preparser.set_allow_arrow_functions(true);
+ preparser.set_allow_natives(true);
+ preparser.set_allow_harmony_arrow_functions(true);
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
CHECK(!log.HasError());
@@ -452,14 +454,14 @@ TEST(Regress928) {
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData* sd = log.GetScriptData();
- i::ParseData pd(sd);
- pd.Initialize();
+ i::ParseData* pd = i::ParseData::FromCachedData(sd);
+ pd->Initialize();
int first_function =
static_cast<int>(strstr(program, "function") - program);
int first_lbrace = first_function + i::StrLength("function () ");
CHECK_EQ('{', program[first_lbrace]);
- i::FunctionEntry entry1 = pd.GetFunctionEntry(first_lbrace);
+ i::FunctionEntry entry1 = pd->GetFunctionEntry(first_lbrace);
CHECK(!entry1.is_valid());
int second_function =
@@ -467,10 +469,11 @@ TEST(Regress928) {
int second_lbrace =
second_function + i::StrLength("function () ");
CHECK_EQ('{', program[second_lbrace]);
- i::FunctionEntry entry2 = pd.GetFunctionEntry(second_lbrace);
+ i::FunctionEntry entry2 = pd->GetFunctionEntry(second_lbrace);
CHECK(entry2.is_valid());
CHECK_EQ('}', program[entry2.end_pos() - 1]);
delete sd;
+ delete pd;
}
@@ -496,7 +499,7 @@ TEST(PreParseOverflow) {
i::PreParser preparser(&scanner, &log, stack_limit);
preparser.set_allow_lazy(true);
- preparser.set_allow_arrow_functions(true);
+ preparser.set_allow_harmony_arrow_functions(true);
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseStackOverflow, result);
}
@@ -917,6 +920,142 @@ static int Utf8LengthHelper(const char* s) {
}
+TEST(ScopeUsesArgumentsSuperThis) {
+ static const struct {
+ const char* prefix;
+ const char* suffix;
+ } surroundings[] = {
+ { "function f() {", "}" },
+ { "var f = () => {", "}" },
+ };
+
+ enum Expected {
+ NONE = 0,
+ ARGUMENTS = 1,
+ SUPER_PROPERTY = 2,
+ SUPER_CONSTRUCTOR_CALL = 4,
+ THIS = 8,
+ INNER_ARGUMENTS = 16,
+ INNER_SUPER_PROPERTY = 32,
+ INNER_SUPER_CONSTRUCTOR_CALL = 64,
+ INNER_THIS = 128
+ };
+
+ static const struct {
+ const char* body;
+ int expected;
+ } source_data[] = {
+ {"", NONE},
+ {"return this", THIS},
+ {"return arguments", ARGUMENTS},
+ {"return super()", SUPER_CONSTRUCTOR_CALL},
+ {"return super.x", SUPER_PROPERTY},
+ {"return arguments[0]", ARGUMENTS},
+ {"return this + arguments[0]", ARGUMENTS | THIS},
+ {"return this + arguments[0] + super.x",
+ ARGUMENTS | SUPER_PROPERTY | THIS},
+ {"return x => this + x", INNER_THIS},
+ {"return x => super() + x", INNER_SUPER_CONSTRUCTOR_CALL},
+ {"this.foo = 42;", THIS},
+ {"this.foo();", THIS},
+ {"if (foo()) { this.f() }", THIS},
+ {"if (foo()) { super.f() }", SUPER_PROPERTY},
+ {"if (arguments.length) { this.f() }", ARGUMENTS | THIS},
+ {"while (true) { this.f() }", THIS},
+ {"while (true) { super.f() }", SUPER_PROPERTY},
+ {"if (true) { while (true) this.foo(arguments) }", ARGUMENTS | THIS},
+ // Multiple nesting levels must work as well.
+ {"while (true) { while (true) { while (true) return this } }", THIS},
+ {"while (true) { while (true) { while (true) return super() } }",
+ SUPER_CONSTRUCTOR_CALL},
+ {"if (1) { return () => { while (true) new this() } }", INNER_THIS},
+ {"if (1) { return () => { while (true) new super() } }", NONE},
+ {"if (1) { return () => { while (true) new new super() } }", NONE},
+ // Note that propagation of the inner_uses_this() value does not
+ // cross boundaries of normal functions onto parent scopes.
+ {"return function (x) { return this + x }", NONE},
+ {"return function (x) { return super() + x }", NONE},
+ {"var x = function () { this.foo = 42 };", NONE},
+ {"var x = function () { super.foo = 42 };", NONE},
+ {"if (1) { return function () { while (true) new this() } }", NONE},
+ {"if (1) { return function () { while (true) new super() } }", NONE},
+ {"return function (x) { return () => this }", NONE},
+ {"return function (x) { return () => super() }", NONE},
+ // Flags must be correctly set when using block scoping.
+ {"\"use strict\"; while (true) { let x; this, arguments; }",
+ INNER_ARGUMENTS | INNER_THIS},
+ {"\"use strict\"; while (true) { let x; this, super(), arguments; }",
+ INNER_ARGUMENTS | INNER_SUPER_CONSTRUCTOR_CALL | INNER_THIS},
+ {"\"use strict\"; if (foo()) { let x; this.f() }", INNER_THIS},
+ {"\"use strict\"; if (foo()) { let x; super.f() }",
+ INNER_SUPER_PROPERTY},
+ {"\"use strict\"; if (1) {"
+ " let x; return function () { return this + super() + arguments }"
+ "}",
+ NONE},
+ };
+
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+
+ v8::HandleScope handles(CcTest::isolate());
+ v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
+ v8::Context::Scope context_scope(context);
+
+ isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
+ 128 * 1024);
+
+ for (unsigned j = 0; j < arraysize(surroundings); ++j) {
+ for (unsigned i = 0; i < arraysize(source_data); ++i) {
+ int kProgramByteSize = i::StrLength(surroundings[j].prefix) +
+ i::StrLength(surroundings[j].suffix) +
+ i::StrLength(source_data[i].body);
+ i::ScopedVector<char> program(kProgramByteSize + 1);
+ i::SNPrintF(program, "%s%s%s", surroundings[j].prefix,
+ source_data[i].body, surroundings[j].suffix);
+ i::Handle<i::String> source =
+ factory->NewStringFromUtf8(i::CStrVector(program.start()))
+ .ToHandleChecked();
+ i::Handle<i::Script> script = factory->NewScript(source);
+ i::CompilationInfoWithZone info(script);
+ i::Parser::ParseInfo parse_info = {isolate->stack_guard()->real_climit(),
+ isolate->heap()->HashSeed(),
+ isolate->unicode_cache()};
+ i::Parser parser(&info, &parse_info);
+ parser.set_allow_harmony_arrow_functions(true);
+ parser.set_allow_harmony_classes(true);
+ parser.set_allow_harmony_scoping(true);
+ info.MarkAsGlobal();
+ parser.Parse();
+ CHECK(i::Rewriter::Rewrite(&info));
+ CHECK(i::Scope::Analyze(&info));
+ CHECK(info.function() != NULL);
+
+ i::Scope* script_scope = info.function()->scope();
+ CHECK(script_scope->is_script_scope());
+ CHECK_EQ(1, script_scope->inner_scopes()->length());
+
+ i::Scope* scope = script_scope->inner_scopes()->at(0);
+ CHECK_EQ((source_data[i].expected & ARGUMENTS) != 0,
+ scope->uses_arguments());
+ CHECK_EQ((source_data[i].expected & SUPER_PROPERTY) != 0,
+ scope->uses_super_property());
+ CHECK_EQ((source_data[i].expected & SUPER_CONSTRUCTOR_CALL) != 0,
+ scope->uses_super_constructor_call());
+ CHECK_EQ((source_data[i].expected & THIS) != 0, scope->uses_this());
+ CHECK_EQ((source_data[i].expected & INNER_ARGUMENTS) != 0,
+ scope->inner_uses_arguments());
+ CHECK_EQ((source_data[i].expected & INNER_SUPER_PROPERTY) != 0,
+ scope->inner_uses_super_property());
+ CHECK_EQ((source_data[i].expected & INNER_SUPER_CONSTRUCTOR_CALL) != 0,
+ scope->inner_uses_super_constructor_call());
+ CHECK_EQ((source_data[i].expected & INNER_THIS) != 0,
+ scope->inner_uses_this());
+ }
+ }
+}
+
+
TEST(ScopePositions) {
v8::internal::FLAG_harmony_scoping = true;
@@ -974,11 +1113,10 @@ TEST(ScopePositions) {
" infunction;\n"
" }", "\n"
" more;", i::FUNCTION_SCOPE, i::SLOPPY },
- // TODO(aperez): Change to use i::ARROW_SCOPE when implemented
{ " start;\n", "(a,b) => a + b", "; more;",
- i::FUNCTION_SCOPE, i::SLOPPY },
+ i::ARROW_SCOPE, i::SLOPPY },
{ " start;\n", "(a,b) => { return a+b; }", "\nmore;",
- i::FUNCTION_SCOPE, i::SLOPPY },
+ i::ARROW_SCOPE, i::SLOPPY },
{ " start;\n"
" (function fun", "(a,b) { infunction; }", ")();",
i::FUNCTION_SCOPE, i::SLOPPY },
@@ -1137,7 +1275,7 @@ TEST(ScopePositions) {
i::Parser parser(&info, &parse_info);
parser.set_allow_lazy(true);
parser.set_allow_harmony_scoping(true);
- parser.set_allow_arrow_functions(true);
+ parser.set_allow_harmony_arrow_functions(true);
info.MarkAsGlobal();
info.SetStrictMode(source_data[i].strict_mode);
parser.Parse();
@@ -1145,7 +1283,7 @@ TEST(ScopePositions) {
// Check scope types and positions.
i::Scope* scope = info.function()->scope();
- CHECK(scope->is_global_scope());
+ CHECK(scope->is_script_scope());
CHECK_EQ(scope->start_position(), 0);
CHECK_EQ(scope->end_position(), kProgramSize);
CHECK_EQ(scope->inner_scopes()->length(), 1);
@@ -1211,13 +1349,16 @@ i::Handle<i::String> FormatMessage(i::Vector<unsigned> data) {
enum ParserFlag {
kAllowLazy,
- kAllowNativesSyntax,
+ kAllowNatives,
kAllowHarmonyScoping,
- kAllowModules,
+ kAllowHarmonyModules,
kAllowHarmonyNumericLiterals,
- kAllowArrowFunctions,
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyArrowFunctions,
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonyTemplates,
+ kAllowHarmonySloppy,
+ kAllowHarmonyUnicode
};
@@ -1231,15 +1372,19 @@ template <typename Traits>
void SetParserFlags(i::ParserBase<Traits>* parser,
i::EnumSet<ParserFlag> flags) {
parser->set_allow_lazy(flags.Contains(kAllowLazy));
- parser->set_allow_natives_syntax(flags.Contains(kAllowNativesSyntax));
+ parser->set_allow_natives(flags.Contains(kAllowNatives));
parser->set_allow_harmony_scoping(flags.Contains(kAllowHarmonyScoping));
- parser->set_allow_modules(flags.Contains(kAllowModules));
+ parser->set_allow_harmony_modules(flags.Contains(kAllowHarmonyModules));
parser->set_allow_harmony_numeric_literals(
flags.Contains(kAllowHarmonyNumericLiterals));
parser->set_allow_harmony_object_literals(
flags.Contains(kAllowHarmonyObjectLiterals));
- parser->set_allow_arrow_functions(flags.Contains(kAllowArrowFunctions));
- parser->set_allow_classes(flags.Contains(kAllowClasses));
+ parser->set_allow_harmony_arrow_functions(
+ flags.Contains(kAllowHarmonyArrowFunctions));
+ parser->set_allow_harmony_classes(flags.Contains(kAllowHarmonyClasses));
+ parser->set_allow_harmony_templates(flags.Contains(kAllowHarmonyTemplates));
+ parser->set_allow_harmony_sloppy(flags.Contains(kAllowHarmonySloppy));
+ parser->set_allow_harmony_unicode(flags.Contains(kAllowHarmonyUnicode));
}
@@ -1250,6 +1395,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
i::Factory* factory = isolate->factory();
uintptr_t stack_limit = isolate->stack_guard()->real_climit();
+ int preparser_materialized_literals = -1;
+ int parser_materialized_literals = -2;
// Preparse the data.
i::CompleteParserRecorder log;
@@ -1259,7 +1406,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
i::PreParser preparser(&scanner, &log, stack_limit);
SetParserFlags(&preparser, flags);
scanner.Initialize(&stream);
- i::PreParser::PreParseResult result = preparser.PreParseProgram();
+ i::PreParser::PreParseResult result = preparser.PreParseProgram(
+ &preparser_materialized_literals);
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
}
@@ -1278,6 +1426,9 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
info.MarkAsGlobal();
parser.Parse();
function = info.function();
+ if (function) {
+ parser_materialized_literals = function->materialized_literal_count();
+ }
}
// Check that preparsing fails iff parsing fails.
@@ -1343,6 +1494,15 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
"However, parser and preparser succeeded",
source->ToCString().get());
CHECK(false);
+ } else if (preparser_materialized_literals != parser_materialized_literals) {
+ v8::base::OS::Print(
+ "Preparser materialized literals (%d) differ from Parser materialized "
+ "literals (%d) on:\n"
+ "\t%s\n"
+ "However, parser and preparser succeeded",
+ preparser_materialized_literals, parser_materialized_literals,
+ source->ToCString().get());
+ CHECK(false);
}
}
@@ -1352,7 +1512,9 @@ void TestParserSync(const char* source,
size_t varying_flags_length,
ParserSyncTestResult result = kSuccessOrError,
const ParserFlag* always_true_flags = NULL,
- size_t always_true_flags_length = 0) {
+ size_t always_true_flags_length = 0,
+ const ParserFlag* always_false_flags = NULL,
+ size_t always_false_flags_length = 0) {
i::Handle<i::String> str =
CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(source);
for (int bits = 0; bits < (1 << varying_flags_length); bits++) {
@@ -1365,6 +1527,10 @@ void TestParserSync(const char* source,
++flag_index) {
flags.Add(always_true_flags[flag_index]);
}
+ for (size_t flag_index = 0; flag_index < always_false_flags_length;
+ ++flag_index) {
+ flags.Remove(always_false_flags[flag_index]);
+ }
TestParserSyncWithFlags(str, flags, result);
}
}
@@ -1399,7 +1565,7 @@ TEST(ParserSync) {
"if (false) {} else ;",
"if (false) {} else {}",
"if (false) {} else 12",
- "if (false) ;"
+ "if (false) ;",
"if (false) {}",
"if (false) 12",
"do {} while (false)",
@@ -1419,8 +1585,8 @@ TEST(ParserSync) {
"with ({}) ;",
"with ({}) {}",
"with ({}) 12",
- "switch ({}) { default: }"
- "label3: "
+ "switch ({}) { default: }",
+ "label3: ",
"throw",
"throw 12",
"throw\n12",
@@ -1447,16 +1613,6 @@ TEST(ParserSync) {
CcTest::i_isolate()->stack_guard()->SetStackLimit(
i::GetCurrentStackPosition() - 128 * 1024);
- static const ParserFlag flags1[] = {
- kAllowArrowFunctions,
- kAllowClasses,
- kAllowHarmonyNumericLiterals,
- kAllowHarmonyObjectLiterals,
- kAllowHarmonyScoping,
- kAllowLazy,
- kAllowModules,
- };
-
for (int i = 0; context_data[i][0] != NULL; ++i) {
for (int j = 0; statement_data[j] != NULL; ++j) {
for (int k = 0; termination_data[k] != NULL; ++k) {
@@ -1476,7 +1632,7 @@ TEST(ParserSync) {
termination_data[k],
context_data[i][1]);
CHECK(length == kProgramSize);
- TestParserSync(program.start(), flags1, arraysize(flags1));
+ TestParserSync(program.start(), NULL, 0);
}
}
}
@@ -1488,7 +1644,7 @@ TEST(ParserSync) {
TestParserSync("0o1234", flags2, arraysize(flags2));
TestParserSync("0b1011", flags2, arraysize(flags2));
- static const ParserFlag flags3[] = { kAllowNativesSyntax };
+ static const ParserFlag flags3[] = { kAllowNatives };
TestParserSync("%DebugPrint(123)", flags3, arraysize(flags3));
}
@@ -1522,7 +1678,9 @@ void RunParserSyncTest(const char* context_data[][2],
const ParserFlag* flags = NULL,
int flags_len = 0,
const ParserFlag* always_true_flags = NULL,
- int always_true_flags_len = 0) {
+ int always_true_len = 0,
+ const ParserFlag* always_false_flags = NULL,
+ int always_false_len = 0) {
v8::HandleScope handles(CcTest::isolate());
v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
v8::Context::Scope context_scope(context);
@@ -1530,36 +1688,32 @@ void RunParserSyncTest(const char* context_data[][2],
CcTest::i_isolate()->stack_guard()->SetStackLimit(
i::GetCurrentStackPosition() - 128 * 1024);
+ // Experimental feature flags should not go here; pass the flags as
+ // always_true_flags if the test needs them.
static const ParserFlag default_flags[] = {
- kAllowArrowFunctions,
- kAllowClasses,
- kAllowHarmonyNumericLiterals,
- kAllowHarmonyObjectLiterals,
- kAllowHarmonyScoping,
kAllowLazy,
- kAllowModules,
- kAllowNativesSyntax,
+ kAllowNatives,
};
ParserFlag* generated_flags = NULL;
if (flags == NULL) {
flags = default_flags;
flags_len = arraysize(default_flags);
- if (always_true_flags != NULL) {
- // Remove always_true_flags from default_flags.
- CHECK(always_true_flags_len < flags_len);
- generated_flags = new ParserFlag[flags_len - always_true_flags_len];
+ if (always_true_flags != NULL || always_false_flags != NULL) {
+ // Remove always_true/false_flags from default_flags (if present).
+ CHECK((always_true_flags != NULL) == (always_true_len > 0));
+ CHECK((always_false_flags != NULL) == (always_false_len > 0));
+ generated_flags = new ParserFlag[flags_len + always_true_len];
int flag_index = 0;
for (int i = 0; i < flags_len; ++i) {
bool use_flag = true;
- for (int j = 0; j < always_true_flags_len; ++j) {
- if (flags[i] == always_true_flags[j]) {
- use_flag = false;
- break;
- }
+ for (int j = 0; use_flag && j < always_true_len; ++j) {
+ if (flags[i] == always_true_flags[j]) use_flag = false;
+ }
+ for (int j = 0; use_flag && j < always_false_len; ++j) {
+ if (flags[i] == always_false_flags[j]) use_flag = false;
}
if (use_flag) generated_flags[flag_index++] = flags[i];
}
- CHECK(flag_index == flags_len - always_true_flags_len);
flags_len = flag_index;
flags = generated_flags;
}
@@ -1584,7 +1738,9 @@ void RunParserSyncTest(const char* context_data[][2],
flags_len,
result,
always_true_flags,
- always_true_flags_len);
+ always_true_len,
+ always_false_flags,
+ always_false_len);
}
}
delete[] generated_flags;
@@ -1691,45 +1847,70 @@ TEST(NoErrorsEvalAndArgumentsStrict) {
NULL
};
- static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ static const ParserFlag always_flags[] = {kAllowHarmonyArrowFunctions};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
+#define FUTURE_STRICT_RESERVED_WORDS(V) \
+ V(implements) \
+ V(interface) \
+ V(let) \
+ V(package) \
+ V(private) \
+ V(protected) \
+ V(public) \
+ V(static) \
+ V(yield)
+
+
+#define LIMITED_FUTURE_STRICT_RESERVED_WORDS(V) \
+ V(implements) \
+ V(let) \
+ V(static) \
+ V(yield)
+
+
+#define FUTURE_STRICT_RESERVED_STATEMENTS(NAME) \
+ "var " #NAME ";", \
+ "var foo, " #NAME ";", \
+ "try { } catch (" #NAME ") { }", \
+ "function " #NAME "() { }", \
+ "(function " #NAME "() { })", \
+ "function foo(" #NAME ") { }", \
+ "function foo(bar, " #NAME ") { }", \
+ #NAME " = 1;", \
+ #NAME " += 1;", \
+ "var foo = " #NAME " = 1;", \
+ "++" #NAME ";", \
+ #NAME " ++;",
+
+
TEST(ErrorsFutureStrictReservedWords) {
// Tests that both preparsing and parsing produce the right kind of errors for
// using future strict reserved words as identifiers. Without the strict mode,
// it's ok to use future strict reserved words as identifiers. With the strict
// mode, it isn't.
const char* context_data[][2] = {
- { "\"use strict\";", "" },
{ "function test_func() {\"use strict\"; ", "}"},
{ "() => { \"use strict\"; ", "}" },
{ NULL, NULL }
};
- const char* statement_data[] = {
- "var interface;",
- "var foo, interface;",
- "try { } catch (interface) { }",
- "function interface() { }",
- "function foo(interface) { }",
- "function foo(bar, interface) { }",
- "interface = 1;",
- "var foo = interface = 1;",
- "++interface;",
- "interface++;",
- "var yield = 13;",
+ const char* statement_data[] {
+ LIMITED_FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_STATEMENTS)
NULL
};
- static const ParserFlag always_flags[] = {kAllowArrowFunctions};
- RunParserSyncTest(context_data, statement_data, kError, NULL, 0, always_flags,
- arraysize(always_flags));
+ RunParserSyncTest(context_data, statement_data, kError);
+ RunParserSyncTest(context_data, statement_data, kError);
}
+#undef LIMITED_FUTURE_STRICT_RESERVED_WORDS
+
+
TEST(NoErrorsFutureStrictReservedWords) {
const char* context_data[][2] = {
{ "", "" },
@@ -1739,23 +1920,18 @@ TEST(NoErrorsFutureStrictReservedWords) {
};
const char* statement_data[] = {
- "var interface;",
- "var foo, interface;",
- "try { } catch (interface) { }",
- "function interface() { }",
- "function foo(interface) { }",
- "function foo(bar, interface) { }",
- "interface = 1;",
- "var foo = interface = 1;",
- "++interface;",
- "interface++;",
- "var yield = 13;",
+ FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_STATEMENTS)
NULL
};
- static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ static const ParserFlag always_flags[] = {kAllowHarmonyArrowFunctions};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
+
+ static const ParserFlag classes_flags[] = {
+ kAllowHarmonyArrowFunctions, kAllowHarmonyClasses, kAllowHarmonyScoping};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ classes_flags, arraysize(classes_flags));
}
@@ -2135,12 +2311,13 @@ TEST(ErrorsIllegalWordsAsLabelsStrict) {
{ NULL, NULL }
};
+#define LABELLED_WHILE(NAME) #NAME ": while (true) { break " #NAME "; }",
const char* statement_data[] = {
"super: while(true) { break super; }",
- "interface: while(true) { break interface; }",
- "yield: while(true) { break yield; }",
+ FUTURE_STRICT_RESERVED_WORDS(LABELLED_WHILE)
NULL
};
+#undef LABELLED_WHILE
RunParserSyncTest(context_data, statement_data, kError);
}
@@ -2165,7 +2342,28 @@ TEST(NoErrorsIllegalWordsAsLabels) {
NULL
};
- static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ static const ParserFlag always_flags[] = {kAllowHarmonyArrowFunctions};
+ RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(NoErrorsFutureStrictReservedAsLabelsSloppy) {
+ const char* context_data[][2] = {
+ { "", ""},
+ { "function test_func() {", "}" },
+ { "() => {", "}" },
+ { NULL, NULL }
+ };
+
+#define LABELLED_WHILE(NAME) #NAME ": while (true) { break " #NAME "; }",
+ const char* statement_data[] {
+ FUTURE_STRICT_RESERVED_WORDS(LABELLED_WHILE)
+ NULL
+ };
+#undef LABELLED_WHILE
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyArrowFunctions};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
@@ -2285,17 +2483,18 @@ TEST(DontRegressPreParserDataSizes) {
i::ScriptData* sd = NULL;
info.SetCachedData(&sd, v8::ScriptCompiler::kProduceParserCache);
i::Parser::Parse(&info, true);
- i::ParseData pd(sd);
+ i::ParseData* pd = i::ParseData::FromCachedData(sd);
- if (pd.FunctionCount() != test_cases[i].functions) {
+ if (pd->FunctionCount() != test_cases[i].functions) {
v8::base::OS::Print(
"Expected preparse data for program:\n"
"\t%s\n"
"to contain %d functions, however, received %d functions.\n",
- program, test_cases[i].functions, pd.FunctionCount());
+ program, test_cases[i].functions, pd->FunctionCount());
CHECK(false);
}
delete sd;
+ delete pd;
}
}
@@ -2416,9 +2615,9 @@ TEST(Intrinsics) {
NULL
};
- // This test requires kAllowNativesSyntax to succeed.
+ // This test requires kAllowNatives to succeed.
static const ParserFlag always_true_flags[] = {
- kAllowNativesSyntax
+ kAllowNatives
};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
@@ -2904,11 +3103,11 @@ TEST(SerializationOfMaybeAssignmentFlag) {
const i::AstRawString* name = avf.GetOneByteString("result");
i::Handle<i::String> str = name->string();
CHECK(str->IsInternalizedString());
- i::Scope* global_scope =
- new (&zone) i::Scope(NULL, i::GLOBAL_SCOPE, &avf, &zone);
- global_scope->Initialize();
- i::Scope* s = i::Scope::DeserializeScopeChain(context, global_scope, &zone);
- DCHECK(s != global_scope);
+ i::Scope* script_scope =
+ new (&zone) i::Scope(NULL, i::SCRIPT_SCOPE, &avf, &zone);
+ script_scope->Initialize();
+ i::Scope* s = i::Scope::DeserializeScopeChain(context, script_scope, &zone);
+ DCHECK(s != script_scope);
DCHECK(name != NULL);
// Get result from h's function context (that is f's context)
@@ -2951,11 +3150,11 @@ TEST(IfArgumentsArrayAccessedThenParametersMaybeAssigned) {
i::AstValueFactory avf(&zone, isolate->heap()->HashSeed());
avf.Internalize(isolate);
- i::Scope* global_scope =
- new (&zone) i::Scope(NULL, i::GLOBAL_SCOPE, &avf, &zone);
- global_scope->Initialize();
- i::Scope* s = i::Scope::DeserializeScopeChain(context, global_scope, &zone);
- DCHECK(s != global_scope);
+ i::Scope* script_scope =
+ new (&zone) i::Scope(NULL, i::SCRIPT_SCOPE, &avf, &zone);
+ script_scope->Initialize();
+ i::Scope* s = i::Scope::DeserializeScopeChain(context, script_scope, &zone);
+ DCHECK(s != script_scope);
const i::AstRawString* name_x = avf.GetOneByteString("x");
// Get result from f's function context (that is g's outer context)
@@ -2998,11 +3197,11 @@ TEST(ExportsMaybeAssigned) {
i::AstValueFactory avf(&zone, isolate->heap()->HashSeed());
avf.Internalize(isolate);
- i::Scope* global_scope =
- new (&zone) i::Scope(NULL, i::GLOBAL_SCOPE, &avf, &zone);
- global_scope->Initialize();
- i::Scope* s = i::Scope::DeserializeScopeChain(context, global_scope, &zone);
- DCHECK(s != global_scope);
+ i::Scope* script_scope =
+ new (&zone) i::Scope(NULL, i::SCRIPT_SCOPE, &avf, &zone);
+ script_scope->Initialize();
+ i::Scope* s = i::Scope::DeserializeScopeChain(context, script_scope, &zone);
+ DCHECK(s != script_scope);
const i::AstRawString* name_x = avf.GetOneByteString("x");
const i::AstRawString* name_f = avf.GetOneByteString("f");
const i::AstRawString* name_y = avf.GetOneByteString("y");
@@ -3156,8 +3355,7 @@ TEST(InnerAssignment) {
i::Parser parser(&info, &parse_info);
parser.set_allow_harmony_scoping(true);
CHECK(parser.Parse());
- CHECK(i::Rewriter::Rewrite(&info));
- CHECK(i::Scope::Analyze(&info));
+ CHECK(i::Compiler::Analyze(&info));
CHECK(info.function() != NULL);
i::Scope* scope = info.function()->scope();
@@ -3304,7 +3502,7 @@ TEST(ErrorsArrowFunctions) {
// The test is quite slow, so run it with a reduced set of flags.
static const ParserFlag flags[] = {kAllowLazy, kAllowHarmonyScoping};
- static const ParserFlag always_flags[] = { kAllowArrowFunctions };
+ static const ParserFlag always_flags[] = { kAllowHarmonyArrowFunctions };
RunParserSyncTest(context_data, statement_data, kError, flags,
arraysize(flags), always_flags, arraysize(always_flags));
}
@@ -3358,7 +3556,7 @@ TEST(NoErrorsArrowFunctions) {
NULL
};
- static const ParserFlag always_flags[] = {kAllowArrowFunctions};
+ static const ParserFlag always_flags[] = {kAllowHarmonyArrowFunctions};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
@@ -3383,7 +3581,7 @@ TEST(NoErrorsSuper) {
"z.super", // Ok, property lookup.
NULL};
- static const ParserFlag always_flags[] = {kAllowClasses};
+ static const ParserFlag always_flags[] = {kAllowHarmonyClasses};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
@@ -3402,7 +3600,7 @@ TEST(ErrorsSuper) {
"f(super)",
NULL};
- static const ParserFlag always_flags[] = {kAllowClasses};
+ static const ParserFlag always_flags[] = {kAllowHarmonyClasses};
RunParserSyncTest(context_data, statement_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
}
@@ -3585,7 +3783,8 @@ TEST(ClassExpressionNoErrors) {
"class name extends class base {} {}",
NULL};
- static const ParserFlag always_flags[] = {kAllowClasses};
+ static const ParserFlag always_flags[] = {
+ kAllowHarmonyClasses, kAllowHarmonySloppy};
RunParserSyncTest(context_data, class_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
@@ -3604,7 +3803,8 @@ TEST(ClassDeclarationNoErrors) {
"class name extends class base {} {}",
NULL};
- static const ParserFlag always_flags[] = {kAllowClasses};
+ static const ParserFlag always_flags[] = {
+ kAllowHarmonyClasses, kAllowHarmonySloppy};
RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
@@ -3648,8 +3848,9 @@ TEST(ClassBodyNoErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3706,8 +3907,9 @@ TEST(ClassPropertyNameNoErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3737,8 +3939,9 @@ TEST(ClassExpressionErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3774,8 +3977,9 @@ TEST(ClassDeclarationErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyNumericLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyNumericLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3804,8 +4008,9 @@ TEST(ClassNameErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_name, kError, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3837,8 +4042,9 @@ TEST(ClassGetterParamNameErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_name, kError, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3855,11 +4061,19 @@ TEST(ClassStaticPrototypeErrors) {
"static get prototype() {}",
"static set prototype(_) {}",
"static *prototype() {}",
+ "static 'prototype'() {}",
+ "static *'prototype'() {}",
+ "static prot\\u006ftype() {}",
+ "static 'prot\\u006ftype'() {}",
+ "static get 'prot\\u006ftype'() {}",
+ "static set 'prot\\u006ftype'(_) {}",
+ "static *'prot\\u006ftype'() {}",
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3875,11 +4089,19 @@ TEST(ClassSpecialConstructorErrors) {
"get constructor() {}",
"get constructor(_) {}",
"*constructor() {}",
+ "get 'constructor'() {}",
+ "*'constructor'() {}",
+ "get c\\u006fnstructor() {}",
+ "*c\\u006fnstructor() {}",
+ "get 'c\\u006fnstructor'() {}",
+ "get 'c\\u006fnstructor'(_) {}",
+ "*'c\\u006fnstructor'() {}",
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3900,8 +4122,9 @@ TEST(ClassConstructorNoErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3909,9 +4132,6 @@ TEST(ClassConstructorNoErrors) {
TEST(ClassMultipleConstructorErrors) {
- // We currently do not allow any duplicate properties in class bodies. This
- // test ensures that when we change that we still throw on duplicate
- // constructors.
const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"},
{NULL, NULL}};
@@ -3921,17 +4141,16 @@ TEST(ClassMultipleConstructorErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
}
-// TODO(arv): We should allow duplicate property names.
-// https://code.google.com/p/v8/issues/detail?id=3570
-DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) {
+TEST(ClassMultiplePropertyNamesNoErrors) {
const char* context_data[][2] = {{"class C {", "}"},
{"(class {", "});"},
{NULL, NULL}};
@@ -3940,11 +4159,14 @@ DISABLED_TEST(ClassMultiplePropertyNamesNoErrors) {
"constructor() {}; static constructor() {}",
"m() {}; static m() {}",
"m() {}; m() {}",
+ "static m() {}; static m() {}",
+ "get m() {}; set m(_) {}; get m() {}; set m(_) {};",
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
@@ -3963,9 +4185,422 @@ TEST(ClassesAreStrictErrors) {
NULL};
static const ParserFlag always_flags[] = {
- kAllowClasses,
- kAllowHarmonyObjectLiterals
+ kAllowHarmonyClasses,
+ kAllowHarmonyObjectLiterals,
+ kAllowHarmonySloppy
};
RunParserSyncTest(context_data, class_body_data, kError, NULL, 0,
always_flags, arraysize(always_flags));
}
+
+
+TEST(ObjectLiteralPropertyShorthandKeywordsError) {
+ const char* context_data[][2] = {{"({", "});"},
+ {"'use strict'; ({", "});"},
+ {NULL, NULL}};
+
+ const char* name_data[] = {
+ "break",
+ "case",
+ "catch",
+ "class",
+ "const",
+ "continue",
+ "debugger",
+ "default",
+ "delete",
+ "do",
+ "else",
+ "enum",
+ "export",
+ "extends",
+ "false",
+ "finally",
+ "for",
+ "function",
+ "if",
+ "import",
+ "in",
+ "instanceof",
+ "new",
+ "null",
+ "return",
+ "super",
+ "switch",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typeof",
+ "var",
+ "void",
+ "while",
+ "with",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, name_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ObjectLiteralPropertyShorthandStrictKeywords) {
+ const char* context_data[][2] = {{"({", "});"},
+ {NULL, NULL}};
+
+ const char* name_data[] = {
+ "implements",
+ "interface",
+ "let",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "static",
+ "yield",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
+ always_flags, arraysize(always_flags));
+
+ const char* context_strict_data[][2] = {{"'use strict'; ({", "});"},
+ {NULL, NULL}};
+ RunParserSyncTest(context_strict_data, name_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ObjectLiteralPropertyShorthandError) {
+ const char* context_data[][2] = {{"({", "});"},
+ {"'use strict'; ({", "});"},
+ {NULL, NULL}};
+
+ const char* name_data[] = {
+ "1",
+ "1.2",
+ "0",
+ "0.1",
+ "1.0",
+ "1e1",
+ "0x1",
+ "\"s\"",
+ "'s'",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, name_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ObjectLiteralPropertyShorthandYieldInGeneratorError) {
+ const char* context_data[][2] = {{"", ""},
+ {NULL, NULL}};
+
+ const char* name_data[] = {
+ "function* g() { ({yield}); }",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyObjectLiterals};
+ RunParserSyncTest(context_data, name_data, kError, NULL, 0,
+ always_flags, arraysize(always_flags));
+}
+
+
+TEST(ConstParsingInForIn) {
+ const char* context_data[][2] = {{"'use strict';", ""},
+ {"function foo(){ 'use strict';", "}"},
+ {NULL, NULL}};
+
+ const char* data[] = {
+ "for(const x = 1; ; ) {}",
+ "for(const x = 1, y = 2;;){}",
+ "for(const x in [1,2,3]) {}",
+ "for(const x of [1,2,3]) {}",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyScoping};
+ RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(ConstParsingInForInError) {
+ const char* context_data[][2] = {{"'use strict';", ""},
+ {"function foo(){ 'use strict';", "}"},
+ {NULL, NULL}};
+
+ const char* data[] = {
+ "for(const x,y = 1; ; ) {}",
+ "for(const x = 4 in [1,2,3]) {}",
+ "for(const x = 4, y in [1,2,3]) {}",
+ "for(const x = 4 of [1,2,3]) {}",
+ "for(const x = 4, y of [1,2,3]) {}",
+ "for(const x = 1, y = 2 in []) {}",
+ "for(const x,y in []) {}",
+ "for(const x = 1, y = 2 of []) {}",
+ "for(const x,y of []) {}",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyScoping};
+ RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(InvalidUnicodeEscapes) {
+ const char* context_data[][2] = {{"", ""},
+ {"'use strict';", ""},
+ {NULL, NULL}};
+ const char* data[] = {
+ "var foob\\u123r = 0;",
+ "var \\u123roo = 0;",
+ "\"foob\\u123rr\"",
+ // No escapes allowed in regexp flags
+ "/regex/\\u0069g",
+ "/regex/\\u006g",
+ // Braces gone wrong
+ "var foob\\u{c481r = 0;",
+ "var foob\\uc481}r = 0;",
+ "var \\u{0052oo = 0;",
+ "var \\u0052}oo = 0;",
+ "\"foob\\u{c481r\"",
+ "var foob\\u{}ar = 0;",
+ // Too high value for the unicode escape
+ "\"\\u{110000}\"",
+ // Not an unicode escape
+ "var foob\\v1234r = 0;",
+ "var foob\\U1234r = 0;",
+ "var foob\\v{1234}r = 0;",
+ "var foob\\U{1234}r = 0;",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyUnicode};
+ RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(UnicodeEscapes) {
+ const char* context_data[][2] = {{"", ""},
+ {"'use strict';", ""},
+ {NULL, NULL}};
+ const char* data[] = {
+ // Identifier starting with escape
+ "var \\u0052oo = 0;",
+ "var \\u{0052}oo = 0;",
+ "var \\u{52}oo = 0;",
+ "var \\u{00000000052}oo = 0;",
+ // Identifier with an escape but not starting with an escape
+ "var foob\\uc481r = 0;",
+ "var foob\\u{c481}r = 0;",
+ // String with an escape
+ "\"foob\\uc481r\"",
+ "\"foob\\{uc481}r\"",
+ // This character is a valid unicode character, representable as a surrogate
+ // pair, not representable as 4 hex digits.
+ "\"foo\\u{10e6d}\"",
+ // Max value for the unicode escape
+ "\"\\u{10ffff}\"",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyUnicode};
+ RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(ScanTemplateLiterals) {
+ const char* context_data[][2] = {{"'use strict';", ""},
+ {"function foo(){ 'use strict';"
+ " var a, b, c; return ", "}"},
+ {NULL, NULL}};
+
+ const char* data[] = {
+ "``",
+ "`no-subst-template`",
+ "`template-head${a}`",
+ "`${a}`",
+ "`${a}template-tail`",
+ "`template-head${a}template-tail`",
+ "`${a}${b}${c}`",
+ "`a${a}b${b}c${c}`",
+ "`${a}a${b}b${c}c`",
+ "`foo\n\nbar\r\nbaz`",
+ "`foo\n\n${ bar }\r\nbaz`",
+ "`foo${a /* comment */}`",
+ "`foo${a // comment\n}`",
+ "`foo${a \n}`",
+ "`foo${a \r\n}`",
+ "`foo${a \r}`",
+ "`foo${/* comment */ a}`",
+ "`foo${// comment\na}`",
+ "`foo${\n a}`",
+ "`foo${\r\n a}`",
+ "`foo${\r a}`",
+ "`foo${'a' in a}`",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
+ RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(ScanTaggedTemplateLiterals) {
+ const char* context_data[][2] = {{"'use strict';", ""},
+ {"function foo(){ 'use strict';"
+ " function tag() {}"
+ " var a, b, c; return ", "}"},
+ {NULL, NULL}};
+
+ const char* data[] = {
+ "tag ``",
+ "tag `no-subst-template`",
+ "tag`template-head${a}`",
+ "tag `${a}`",
+ "tag `${a}template-tail`",
+ "tag `template-head${a}template-tail`",
+ "tag\n`${a}${b}${c}`",
+ "tag\r\n`a${a}b${b}c${c}`",
+ "tag `${a}a${b}b${c}c`",
+ "tag\t`foo\n\nbar\r\nbaz`",
+ "tag\r`foo\n\n${ bar }\r\nbaz`",
+ "tag`foo${a /* comment */}`",
+ "tag`foo${a // comment\n}`",
+ "tag`foo${a \n}`",
+ "tag`foo${a \r\n}`",
+ "tag`foo${a \r}`",
+ "tag`foo${/* comment */ a}`",
+ "tag`foo${// comment\na}`",
+ "tag`foo${\n a}`",
+ "tag`foo${\r\n a}`",
+ "tag`foo${\r a}`",
+ "tag`foo${'a' in a}`",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
+ RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(TemplateMaterializedLiterals) {
+ const char* context_data[][2] = {
+ {
+ "'use strict';\n"
+ "function tag() {}\n"
+ "var a, b, c;\n"
+ "(", ")"
+ },
+ {NULL, NULL}
+ };
+
+ const char* data[] = {
+ "tag``",
+ "tag`a`",
+ "tag`a${1}b`",
+ "tag`a${1}b${2}c`",
+ "``",
+ "`a`",
+ "`a${1}b`",
+ "`a${1}b${2}c`",
+ NULL
+ };
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
+ RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(ScanUnterminatedTemplateLiterals) {
+ const char* context_data[][2] = {{"'use strict';", ""},
+ {"function foo(){ 'use strict';"
+ " var a, b, c; return ", "}"},
+ {NULL, NULL}};
+
+ const char* data[] = {
+ "`no-subst-template",
+ "`template-head${a}",
+ "`${a}template-tail",
+ "`template-head${a}template-tail",
+ "`${a}${b}${c}",
+ "`a${a}b${b}c${c}",
+ "`${a}a${b}b${c}c",
+ "`foo\n\nbar\r\nbaz",
+ "`foo\n\n${ bar }\r\nbaz",
+ "`foo${a /* comment } */`",
+ "`foo${a /* comment } `*/",
+ "`foo${a // comment}`",
+ "`foo${a \n`",
+ "`foo${a \r\n`",
+ "`foo${a \r`",
+ "`foo${/* comment */ a`",
+ "`foo${// commenta}`",
+ "`foo${\n a`",
+ "`foo${\r\n a`",
+ "`foo${\r a`",
+ "`foo${fn(}`",
+ "`foo${1 if}`",
+ NULL};
+ static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
+ RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(TemplateLiteralsIllegalTokens) {
+ const char* context_data[][2] = {{"'use strict';", ""},
+ {"function foo(){ 'use strict';"
+ " var a, b, c; return ", "}"},
+ {NULL, NULL}};
+ const char* data[] = {
+ "`hello\\x`",
+ "`hello\\x${1}`",
+ "`hello${1}\\x`",
+ "`hello${1}\\x${2}`",
+ "`hello\\x\n`",
+ "`hello\\x\n${1}`",
+ "`hello${1}\\x\n`",
+ "`hello${1}\\x\n${2}`",
+ NULL};
+
+ static const ParserFlag always_flags[] = {kAllowHarmonyTemplates};
+ RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
+ arraysize(always_flags));
+}
+
+
+TEST(LexicalScopingSloppyMode) {
+ const char* context_data[][2] = {
+ {"", ""},
+ {"function f() {", "}"},
+ {"{", "}"},
+ {NULL, NULL}};
+ const char* bad_data[] = {
+ "let x = 1;",
+ "for(let x = 1;;){}",
+ "for(let x of []){}",
+ "for(let x in []){}",
+ "class C {}",
+ "class C extends D {}",
+ "(class {})",
+ "(class extends D {})",
+ "(class C {})",
+ "(class C extends D {})",
+ NULL};
+ static const ParserFlag always_true_flags[] = {
+ kAllowHarmonyScoping, kAllowHarmonyClasses};
+ static const ParserFlag always_false_flags[] = {kAllowHarmonySloppy};
+ RunParserSyncTest(context_data, bad_data, kError, NULL, 0,
+ always_true_flags, arraysize(always_true_flags),
+ always_false_flags, arraysize(always_false_flags));
+
+ const char* good_data[] = {
+ "let = 1;",
+ "for(let = 1;;){}",
+ NULL};
+ RunParserSyncTest(context_data, good_data, kSuccess, NULL, 0,
+ always_true_flags, arraysize(always_true_flags),
+ always_false_flags, arraysize(always_false_flags));
+}
diff --git a/test/cctest/test-platform.cc b/test/cctest/test-platform.cc
index 100a5a78..90926d1a 100644
--- a/test/cctest/test-platform.cc
+++ b/test/cctest/test-platform.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "include/v8stdint.h"
+#include <stdint.h>
#include "src/base/build_config.h"
#include "src/base/platform/platform.h"
#include "test/cctest/cctest.h"
diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc
index 9d1d52e6..4a572c81 100644
--- a/test/cctest/test-regexp.cc
+++ b/test/cctest/test-regexp.cc
@@ -25,8 +25,8 @@
// (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 <cstdlib>
+#include <sstream>
#include "src/v8.h"
@@ -103,9 +103,9 @@ static void CheckParseEq(const char* input, const char* expected) {
&reader, false, &result, &zone));
CHECK(result.tree != NULL);
CHECK(result.error.is_null());
- OStringStream os;
+ std::ostringstream os;
result.tree->Print(os, &zone);
- CHECK_EQ(expected, os.c_str());
+ CHECK_EQ(expected, os.str().c_str());
}
@@ -435,11 +435,11 @@ TEST(Errors) {
// Check that we don't allow more than kMaxCapture captures
const int kMaxCaptures = 1 << 16; // Must match RegExpParser::kMaxCaptures.
const char* kTooManyCaptures = "Too many captures";
- OStringStream os;
+ std::ostringstream os;
for (int i = 0; i <= kMaxCaptures; i++) {
os << "()";
}
- ExpectError(os.c_str(), kTooManyCaptures);
+ ExpectError(os.str().c_str(), kTooManyCaptures);
}
diff --git a/test/cctest/test-sampler-api.cc b/test/cctest/test-sampler-api.cc
new file mode 100644
index 00000000..2f6f92eb
--- /dev/null
+++ b/test/cctest/test-sampler-api.cc
@@ -0,0 +1,245 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Tests the sampling API in include/v8.h
+
+#include <map>
+#include <string>
+#include "include/v8.h"
+#include "src/simulator.h"
+#include "test/cctest/cctest.h"
+
+namespace {
+
+class Sample {
+ public:
+ enum { kFramesLimit = 255 };
+
+ Sample() {}
+
+ typedef const void* const* const_iterator;
+ const_iterator begin() const { return data_.start(); }
+ const_iterator end() const { return &data_[data_.length()]; }
+
+ int size() const { return data_.length(); }
+ v8::internal::Vector<void*>& data() { return data_; }
+
+ private:
+ v8::internal::EmbeddedVector<void*, kFramesLimit> data_;
+};
+
+
+#if defined(USE_SIMULATOR)
+class SimulatorHelper {
+ public:
+ inline bool Init(v8::Isolate* isolate) {
+ simulator_ = reinterpret_cast<v8::internal::Isolate*>(isolate)
+ ->thread_local_top()
+ ->simulator_;
+ // Check if there is active simulator.
+ return simulator_ != NULL;
+ }
+
+ inline void FillRegisters(v8::RegisterState* state) {
+#if V8_TARGET_ARCH_ARM
+ state->pc = reinterpret_cast<void*>(simulator_->get_pc());
+ state->sp = reinterpret_cast<void*>(
+ simulator_->get_register(v8::internal::Simulator::sp));
+ state->fp = reinterpret_cast<void*>(
+ simulator_->get_register(v8::internal::Simulator::r11));
+#elif V8_TARGET_ARCH_ARM64
+ if (simulator_->sp() == 0 || simulator_->fp() == 0) {
+ // It's possible that the simulator is interrupted while it is updating
+ // the sp or fp register. ARM64 simulator does this in two steps:
+ // first setting it to zero and then setting it to a new value.
+ // Bailout if sp/fp doesn't contain the new value.
+ return;
+ }
+ state->pc = reinterpret_cast<void*>(simulator_->pc());
+ state->sp = reinterpret_cast<void*>(simulator_->sp());
+ state->fp = reinterpret_cast<void*>(simulator_->fp());
+#elif V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
+ state->pc = reinterpret_cast<void*>(simulator_->get_pc());
+ state->sp = reinterpret_cast<void*>(
+ simulator_->get_register(v8::internal::Simulator::sp));
+ state->fp = reinterpret_cast<void*>(
+ simulator_->get_register(v8::internal::Simulator::fp));
+#endif
+ }
+
+ private:
+ v8::internal::Simulator* simulator_;
+};
+#endif // USE_SIMULATOR
+
+
+class SamplingTestHelper {
+ public:
+ struct CodeEventEntry {
+ std::string name;
+ const void* code_start;
+ size_t code_len;
+ };
+ typedef std::map<const void*, CodeEventEntry> CodeEntries;
+
+ explicit SamplingTestHelper(const std::string& test_function)
+ : sample_is_taken_(false), isolate_(CcTest::isolate()) {
+ DCHECK_EQ(NULL, instance_);
+ instance_ = this;
+ v8::HandleScope scope(isolate_);
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
+ global->Set(v8::String::NewFromUtf8(isolate_, "CollectSample"),
+ v8::FunctionTemplate::New(isolate_, CollectSample));
+ LocalContext env(isolate_, NULL, global);
+ isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
+ JitCodeEventHandler);
+ v8::Script::Compile(
+ v8::String::NewFromUtf8(isolate_, test_function.c_str()))->Run();
+ }
+
+ ~SamplingTestHelper() {
+ isolate_->SetJitCodeEventHandler(v8::kJitCodeEventDefault, NULL);
+ instance_ = NULL;
+ }
+
+ Sample& sample() { return sample_; }
+
+ const CodeEventEntry* FindEventEntry(const void* address) {
+ CodeEntries::const_iterator it = code_entries_.upper_bound(address);
+ if (it == code_entries_.begin()) return NULL;
+ const CodeEventEntry& entry = (--it)->second;
+ const void* code_end =
+ static_cast<const uint8_t*>(entry.code_start) + entry.code_len;
+ return address < code_end ? &entry : NULL;
+ }
+
+ private:
+ static void CollectSample(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ instance_->DoCollectSample();
+ }
+
+ static void JitCodeEventHandler(const v8::JitCodeEvent* event) {
+ instance_->DoJitCodeEventHandler(event);
+ }
+
+ // The JavaScript calls this function when on full stack depth.
+ void DoCollectSample() {
+ v8::RegisterState state;
+#if defined(USE_SIMULATOR)
+ SimulatorHelper simulator_helper;
+ if (!simulator_helper.Init(isolate_)) return;
+ simulator_helper.FillRegisters(&state);
+#else
+ state.pc = NULL;
+ state.fp = &state;
+ state.sp = &state;
+#endif
+ v8::SampleInfo info;
+ isolate_->GetStackSample(state, sample_.data().start(),
+ static_cast<size_t>(sample_.size()), &info);
+ size_t frames_count = info.frames_count;
+ CHECK_LE(frames_count, static_cast<size_t>(sample_.size()));
+ sample_.data().Truncate(static_cast<int>(frames_count));
+ sample_is_taken_ = true;
+ }
+
+ void DoJitCodeEventHandler(const v8::JitCodeEvent* event) {
+ if (sample_is_taken_) return;
+ switch (event->type) {
+ case v8::JitCodeEvent::CODE_ADDED: {
+ CodeEventEntry entry;
+ entry.name = std::string(event->name.str, event->name.len);
+ entry.code_start = event->code_start;
+ entry.code_len = event->code_len;
+ code_entries_.insert(std::make_pair(entry.code_start, entry));
+ break;
+ }
+ case v8::JitCodeEvent::CODE_MOVED: {
+ CodeEntries::iterator it = code_entries_.find(event->code_start);
+ CHECK(it != code_entries_.end());
+ code_entries_.erase(it);
+ CodeEventEntry entry;
+ entry.name = std::string(event->name.str, event->name.len);
+ entry.code_start = event->new_code_start;
+ entry.code_len = event->code_len;
+ code_entries_.insert(std::make_pair(entry.code_start, entry));
+ break;
+ }
+ case v8::JitCodeEvent::CODE_REMOVED:
+ code_entries_.erase(event->code_start);
+ break;
+ default:
+ break;
+ }
+ }
+
+ Sample sample_;
+ bool sample_is_taken_;
+ v8::Isolate* isolate_;
+ CodeEntries code_entries_;
+
+ static SamplingTestHelper* instance_;
+};
+
+SamplingTestHelper* SamplingTestHelper::instance_;
+
+} // namespace
+
+
+// A JavaScript function which takes stack depth
+// (minimum value 2) as an argument.
+// When at the bottom of the recursion,
+// the JavaScript code calls into C++ test code,
+// waiting for the sampler to take a sample.
+static const char* test_function =
+ "function func(depth) {"
+ " if (depth == 2) CollectSample();"
+ " else return func(depth - 1);"
+ "}";
+
+
+TEST(StackDepthIsConsistent) {
+ SamplingTestHelper helper(std::string(test_function) + "func(8);");
+ CHECK_EQ(8, helper.sample().size());
+}
+
+
+TEST(StackDepthDoesNotExceedMaxValue) {
+ SamplingTestHelper helper(std::string(test_function) + "func(300);");
+ CHECK_EQ(Sample::kFramesLimit, helper.sample().size());
+}
+
+
+// The captured sample should have three pc values.
+// They should fall in the range where the compiled code resides.
+// The expected stack is:
+// bottom of stack [{anon script}, outer, inner] top of stack
+// ^ ^ ^
+// sample.stack indices 2 1 0
+TEST(StackFramesConsistent) {
+ // Note: The arguments.callee stuff is there so that the
+ // functions are not optimized away.
+ const char* test_script =
+ "function test_sampler_api_inner() {"
+ " CollectSample();"
+ " return arguments.callee.toString();"
+ "}"
+ "function test_sampler_api_outer() {"
+ " return test_sampler_api_inner() + arguments.callee.toString();"
+ "}"
+ "test_sampler_api_outer();";
+
+ SamplingTestHelper helper(test_script);
+ Sample& sample = helper.sample();
+ CHECK_EQ(3, sample.size());
+
+ const SamplingTestHelper::CodeEventEntry* entry;
+ entry = helper.FindEventEntry(sample.begin()[0]);
+ CHECK_NE(NULL, entry);
+ CHECK(std::string::npos != entry->name.find("test_sampler_api_inner"));
+
+ entry = helper.FindEventEntry(sample.begin()[1]);
+ CHECK_NE(NULL, entry);
+ CHECK(std::string::npos != entry->name.find("test_sampler_api_outer"));
+}
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 94b400e2..45da2502 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -37,7 +37,7 @@
#include "src/heap/spaces.h"
#include "src/natives.h"
#include "src/objects.h"
-#include "src/runtime.h"
+#include "src/runtime/runtime.h"
#include "src/scopeinfo.h"
#include "src/serialize.h"
#include "src/snapshot.h"
@@ -114,82 +114,27 @@ TEST(ExternalReferenceDecoder) {
}
-class FileByteSink : public SnapshotByteSink {
- public:
- explicit FileByteSink(const char* snapshot_file) {
- fp_ = v8::base::OS::FOpen(snapshot_file, "wb");
- file_name_ = snapshot_file;
- if (fp_ == NULL) {
- PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
- exit(1);
- }
- }
- virtual ~FileByteSink() {
- if (fp_ != NULL) {
- fclose(fp_);
- }
- }
- virtual void Put(byte b, const char* description) {
- if (fp_ != NULL) {
- fputc(b, fp_);
- }
+void WritePayload(const Vector<const byte>& payload, const char* file_name) {
+ FILE* file = v8::base::OS::FOpen(file_name, "wb");
+ if (file == NULL) {
+ PrintF("Unable to write to snapshot file \"%s\"\n", file_name);
+ exit(1);
}
- virtual int Position() {
- return ftell(fp_);
+ size_t written = fwrite(payload.begin(), 1, payload.length(), file);
+ if (written != static_cast<size_t>(payload.length())) {
+ i::PrintF("Writing snapshot file failed.. Aborting.\n");
+ exit(1);
}
- void WriteSpaceUsed(
- int new_space_used,
- int pointer_space_used,
- int data_space_used,
- int code_space_used,
- int map_space_used,
- int cell_space_used,
- int property_cell_space_used);
-
- private:
- FILE* fp_;
- const char* file_name_;
-};
-
-
-void FileByteSink::WriteSpaceUsed(
- int new_space_used,
- int pointer_space_used,
- int data_space_used,
- int code_space_used,
- int map_space_used,
- int cell_space_used,
- int property_cell_space_used) {
- int file_name_length = StrLength(file_name_) + 10;
- Vector<char> name = Vector<char>::New(file_name_length + 1);
- SNPrintF(name, "%s.size", file_name_);
- FILE* fp = v8::base::OS::FOpen(name.start(), "w");
- name.Dispose();
- fprintf(fp, "new %d\n", new_space_used);
- fprintf(fp, "pointer %d\n", pointer_space_used);
- fprintf(fp, "data %d\n", data_space_used);
- fprintf(fp, "code %d\n", code_space_used);
- fprintf(fp, "map %d\n", map_space_used);
- fprintf(fp, "cell %d\n", cell_space_used);
- fprintf(fp, "property cell %d\n", property_cell_space_used);
- fclose(fp);
+ fclose(file);
}
static bool WriteToFile(Isolate* isolate, const char* snapshot_file) {
- FileByteSink file(snapshot_file);
- StartupSerializer ser(isolate, &file);
+ SnapshotByteSink sink;
+ StartupSerializer ser(isolate, &sink);
ser.Serialize();
-
- file.WriteSpaceUsed(
- ser.CurrentAllocationAddress(NEW_SPACE),
- ser.CurrentAllocationAddress(OLD_POINTER_SPACE),
- ser.CurrentAllocationAddress(OLD_DATA_SPACE),
- ser.CurrentAllocationAddress(CODE_SPACE),
- ser.CurrentAllocationAddress(MAP_SPACE),
- ser.CurrentAllocationAddress(CELL_SPACE),
- ser.CurrentAllocationAddress(PROPERTY_CELL_SPACE));
-
+ SnapshotData snapshot_data(sink, ser);
+ WritePayload(snapshot_data.RawData(), snapshot_file);
return true;
}
@@ -206,7 +151,7 @@ static void Serialize(v8::Isolate* isolate) {
}
Isolate* internal_isolate = reinterpret_cast<Isolate*>(isolate);
- internal_isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags, "serialize");
+ internal_isolate->heap()->CollectAllAvailableGarbage("serialize");
WriteToFile(internal_isolate, FLAG_testing_serialization_file);
}
@@ -237,51 +182,14 @@ UNINITIALIZED_TEST(SerializeTwice) {
//----------------------------------------------------------------------------
// Tests that the heap can be deserialized.
-
-static void ReserveSpaceForSnapshot(Deserializer* deserializer,
- const char* file_name) {
- int file_name_length = StrLength(file_name) + 10;
- Vector<char> name = Vector<char>::New(file_name_length + 1);
- SNPrintF(name, "%s.size", file_name);
- FILE* fp = v8::base::OS::FOpen(name.start(), "r");
- name.Dispose();
- int new_size, pointer_size, data_size, code_size, map_size, cell_size,
- property_cell_size;
-#ifdef _MSC_VER
- // Avoid warning about unsafe fscanf from MSVC.
- // Please note that this is only fine if %c and %s are not being used.
-#define fscanf fscanf_s
-#endif
- CHECK_EQ(1, fscanf(fp, "new %d\n", &new_size));
- CHECK_EQ(1, fscanf(fp, "pointer %d\n", &pointer_size));
- CHECK_EQ(1, fscanf(fp, "data %d\n", &data_size));
- CHECK_EQ(1, fscanf(fp, "code %d\n", &code_size));
- CHECK_EQ(1, fscanf(fp, "map %d\n", &map_size));
- CHECK_EQ(1, fscanf(fp, "cell %d\n", &cell_size));
- CHECK_EQ(1, fscanf(fp, "property cell %d\n", &property_cell_size));
-#ifdef _MSC_VER
-#undef fscanf
-#endif
- fclose(fp);
- deserializer->set_reservation(NEW_SPACE, new_size);
- deserializer->set_reservation(OLD_POINTER_SPACE, pointer_size);
- deserializer->set_reservation(OLD_DATA_SPACE, data_size);
- deserializer->set_reservation(CODE_SPACE, code_size);
- deserializer->set_reservation(MAP_SPACE, map_size);
- deserializer->set_reservation(CELL_SPACE, cell_size);
- deserializer->set_reservation(PROPERTY_CELL_SPACE, property_cell_size);
-}
-
-
v8::Isolate* InitializeFromFile(const char* snapshot_file) {
int len;
byte* str = ReadBytes(snapshot_file, &len);
if (!str) return NULL;
v8::Isolate* v8_isolate = NULL;
{
- SnapshotByteSource source(str, len);
- Deserializer deserializer(&source);
- ReserveSpaceForSnapshot(&deserializer, snapshot_file);
+ SnapshotData snapshot_data(Vector<const byte>(str, len));
+ Deserializer deserializer(&snapshot_data);
Isolate* isolate = Isolate::NewForTesting();
v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
v8::Isolate::Scope isolate_scope(v8_isolate);
@@ -440,32 +348,23 @@ UNINITIALIZED_TEST(PartialSerialization) {
}
env.Reset();
- FileByteSink startup_sink(startup_name.start());
+ SnapshotByteSink startup_sink;
StartupSerializer startup_serializer(isolate, &startup_sink);
startup_serializer.SerializeStrongReferences();
- FileByteSink partial_sink(FLAG_testing_serialization_file);
- PartialSerializer p_ser(isolate, &startup_serializer, &partial_sink);
- p_ser.Serialize(&raw_foo);
+ SnapshotByteSink partial_sink;
+ PartialSerializer partial_serializer(isolate, &startup_serializer,
+ &partial_sink);
+ partial_serializer.Serialize(&raw_foo);
+
startup_serializer.SerializeWeakReferences();
- partial_sink.WriteSpaceUsed(
- p_ser.CurrentAllocationAddress(NEW_SPACE),
- p_ser.CurrentAllocationAddress(OLD_POINTER_SPACE),
- p_ser.CurrentAllocationAddress(OLD_DATA_SPACE),
- p_ser.CurrentAllocationAddress(CODE_SPACE),
- p_ser.CurrentAllocationAddress(MAP_SPACE),
- p_ser.CurrentAllocationAddress(CELL_SPACE),
- p_ser.CurrentAllocationAddress(PROPERTY_CELL_SPACE));
-
- startup_sink.WriteSpaceUsed(
- startup_serializer.CurrentAllocationAddress(NEW_SPACE),
- startup_serializer.CurrentAllocationAddress(OLD_POINTER_SPACE),
- startup_serializer.CurrentAllocationAddress(OLD_DATA_SPACE),
- startup_serializer.CurrentAllocationAddress(CODE_SPACE),
- startup_serializer.CurrentAllocationAddress(MAP_SPACE),
- startup_serializer.CurrentAllocationAddress(CELL_SPACE),
- startup_serializer.CurrentAllocationAddress(PROPERTY_CELL_SPACE));
+ SnapshotData startup_snapshot(startup_sink, startup_serializer);
+ SnapshotData partial_snapshot(partial_sink, partial_serializer);
+
+ WritePayload(partial_snapshot.RawData(), FLAG_testing_serialization_file);
+ WritePayload(startup_snapshot.RawData(), startup_name.start());
+
startup_name.Dispose();
}
v8_isolate->Exit();
@@ -494,9 +393,8 @@ UNINITIALIZED_DEPENDENT_TEST(PartialDeserialization, PartialSerialization) {
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
Object* root;
{
- SnapshotByteSource source(snapshot, snapshot_size);
- Deserializer deserializer(&source);
- ReserveSpaceForSnapshot(&deserializer, file_name);
+ SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
+ Deserializer deserializer(&snapshot_data);
deserializer.DeserializePartial(isolate, &root);
CHECK(root->IsString());
}
@@ -506,9 +404,8 @@ UNINITIALIZED_DEPENDENT_TEST(PartialDeserialization, PartialSerialization) {
Object* root2;
{
- SnapshotByteSource source(snapshot, snapshot_size);
- Deserializer deserializer(&source);
- ReserveSpaceForSnapshot(&deserializer, file_name);
+ SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
+ Deserializer deserializer(&snapshot_data);
deserializer.DeserializePartial(isolate, &root2);
CHECK(root2->IsString());
CHECK(*root_handle == root2);
@@ -563,32 +460,22 @@ UNINITIALIZED_TEST(ContextSerialization) {
env.Reset();
- FileByteSink startup_sink(startup_name.start());
+ SnapshotByteSink startup_sink;
StartupSerializer startup_serializer(isolate, &startup_sink);
startup_serializer.SerializeStrongReferences();
- FileByteSink partial_sink(FLAG_testing_serialization_file);
- PartialSerializer p_ser(isolate, &startup_serializer, &partial_sink);
- p_ser.Serialize(&raw_context);
+ SnapshotByteSink partial_sink;
+ PartialSerializer partial_serializer(isolate, &startup_serializer,
+ &partial_sink);
+ partial_serializer.Serialize(&raw_context);
startup_serializer.SerializeWeakReferences();
- partial_sink.WriteSpaceUsed(
- p_ser.CurrentAllocationAddress(NEW_SPACE),
- p_ser.CurrentAllocationAddress(OLD_POINTER_SPACE),
- p_ser.CurrentAllocationAddress(OLD_DATA_SPACE),
- p_ser.CurrentAllocationAddress(CODE_SPACE),
- p_ser.CurrentAllocationAddress(MAP_SPACE),
- p_ser.CurrentAllocationAddress(CELL_SPACE),
- p_ser.CurrentAllocationAddress(PROPERTY_CELL_SPACE));
-
- startup_sink.WriteSpaceUsed(
- startup_serializer.CurrentAllocationAddress(NEW_SPACE),
- startup_serializer.CurrentAllocationAddress(OLD_POINTER_SPACE),
- startup_serializer.CurrentAllocationAddress(OLD_DATA_SPACE),
- startup_serializer.CurrentAllocationAddress(CODE_SPACE),
- startup_serializer.CurrentAllocationAddress(MAP_SPACE),
- startup_serializer.CurrentAllocationAddress(CELL_SPACE),
- startup_serializer.CurrentAllocationAddress(PROPERTY_CELL_SPACE));
+ SnapshotData startup_snapshot(startup_sink, startup_serializer);
+ SnapshotData partial_snapshot(partial_sink, partial_serializer);
+
+ WritePayload(partial_snapshot.RawData(), FLAG_testing_serialization_file);
+ WritePayload(startup_snapshot.RawData(), startup_name.start());
+
startup_name.Dispose();
}
v8_isolate->Dispose();
@@ -616,9 +503,8 @@ UNINITIALIZED_DEPENDENT_TEST(ContextDeserialization, ContextSerialization) {
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
Object* root;
{
- SnapshotByteSource source(snapshot, snapshot_size);
- Deserializer deserializer(&source);
- ReserveSpaceForSnapshot(&deserializer, file_name);
+ SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
+ Deserializer deserializer(&snapshot_data);
deserializer.DeserializePartial(isolate, &root);
CHECK(root->IsContext());
}
@@ -628,9 +514,8 @@ UNINITIALIZED_DEPENDENT_TEST(ContextDeserialization, ContextSerialization) {
Object* root2;
{
- SnapshotByteSource source(snapshot, snapshot_size);
- Deserializer deserializer(&source);
- ReserveSpaceForSnapshot(&deserializer, file_name);
+ SnapshotData snapshot_data(Vector<const byte>(snapshot, snapshot_size));
+ Deserializer deserializer(&snapshot_data);
deserializer.DeserializePartial(isolate, &root2);
CHECK(root2->IsContext());
CHECK(*root_handle != root2);
@@ -786,6 +671,432 @@ TEST(SerializeToplevelInternalizedString) {
}
+Vector<const uint8_t> ConstructSource(Vector<const uint8_t> head,
+ Vector<const uint8_t> body,
+ Vector<const uint8_t> tail, int repeats) {
+ int source_length = head.length() + body.length() * repeats + tail.length();
+ uint8_t* source = NewArray<uint8_t>(static_cast<size_t>(source_length));
+ CopyChars(source, head.start(), head.length());
+ for (int i = 0; i < repeats; i++) {
+ CopyChars(source + head.length() + i * body.length(), body.start(),
+ body.length());
+ }
+ CopyChars(source + head.length() + repeats * body.length(), tail.start(),
+ tail.length());
+ return Vector<const uint8_t>(const_cast<const uint8_t*>(source),
+ source_length);
+}
+
+
+TEST(SerializeToplevelLargeCodeObject) {
+ FLAG_serialize_toplevel = true;
+ LocalContext context;
+ Isolate* isolate = CcTest::i_isolate();
+ isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
+
+ v8::HandleScope scope(CcTest::isolate());
+
+ Vector<const uint8_t> source =
+ ConstructSource(STATIC_CHAR_VECTOR("var j=1; try { if (j) throw 1;"),
+ STATIC_CHAR_VECTOR("for(var i=0;i<1;i++)j++;"),
+ STATIC_CHAR_VECTOR("} catch (e) { j=7; } j"), 10000);
+ Handle<String> source_str =
+ isolate->factory()->NewStringFromOneByte(source).ToHandleChecked();
+
+ Handle<JSObject> global(isolate->context()->global_object());
+ ScriptData* cache = NULL;
+
+ Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
+
+ CHECK(isolate->heap()->InSpace(orig->code(), LO_SPACE));
+
+ Handle<SharedFunctionInfo> copy;
+ {
+ DisallowCompilation no_compile_expected(isolate);
+ copy = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
+ }
+ CHECK_NE(*orig, *copy);
+
+ Handle<JSFunction> copy_fun =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ copy, isolate->native_context());
+
+ Handle<Object> copy_result =
+ Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
+
+ int result_int;
+ CHECK(copy_result->ToInt32(&result_int));
+ CHECK_EQ(7, result_int);
+
+ delete cache;
+ source.Dispose();
+}
+
+
+TEST(SerializeToplevelLargeStrings) {
+ FLAG_serialize_toplevel = true;
+ LocalContext context;
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* f = isolate->factory();
+ isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
+
+ v8::HandleScope scope(CcTest::isolate());
+
+ Vector<const uint8_t> source_s = ConstructSource(
+ STATIC_CHAR_VECTOR("var s = \""), STATIC_CHAR_VECTOR("abcdef"),
+ STATIC_CHAR_VECTOR("\";"), 1000000);
+ Vector<const uint8_t> source_t = ConstructSource(
+ STATIC_CHAR_VECTOR("var t = \""), STATIC_CHAR_VECTOR("uvwxyz"),
+ STATIC_CHAR_VECTOR("\"; s + t"), 999999);
+ Handle<String> source_str =
+ f->NewConsString(f->NewStringFromOneByte(source_s).ToHandleChecked(),
+ f->NewStringFromOneByte(source_t).ToHandleChecked())
+ .ToHandleChecked();
+
+ Handle<JSObject> global(isolate->context()->global_object());
+ ScriptData* cache = NULL;
+
+ Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
+
+ Handle<SharedFunctionInfo> copy;
+ {
+ DisallowCompilation no_compile_expected(isolate);
+ copy = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
+ }
+ CHECK_NE(*orig, *copy);
+
+ Handle<JSFunction> copy_fun =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ copy, isolate->native_context());
+
+ Handle<Object> copy_result =
+ Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
+
+ CHECK_EQ(6 * 1999999, Handle<String>::cast(copy_result)->length());
+ Handle<Object> property = JSObject::GetDataProperty(
+ isolate->global_object(), f->NewStringFromAsciiChecked("s"));
+ CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
+ property = JSObject::GetDataProperty(isolate->global_object(),
+ f->NewStringFromAsciiChecked("t"));
+ CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
+ // Make sure we do not serialize too much, e.g. include the source string.
+ CHECK_LT(cache->length(), 13000000);
+
+ delete cache;
+ source_s.Dispose();
+ source_t.Dispose();
+}
+
+
+TEST(SerializeToplevelThreeBigStrings) {
+ FLAG_serialize_toplevel = true;
+ LocalContext context;
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* f = isolate->factory();
+ isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
+
+ v8::HandleScope scope(CcTest::isolate());
+
+ Vector<const uint8_t> source_a =
+ ConstructSource(STATIC_CHAR_VECTOR("var a = \""), STATIC_CHAR_VECTOR("a"),
+ STATIC_CHAR_VECTOR("\";"), 700000);
+ Handle<String> source_a_str =
+ f->NewStringFromOneByte(source_a).ToHandleChecked();
+
+ Vector<const uint8_t> source_b =
+ ConstructSource(STATIC_CHAR_VECTOR("var b = \""), STATIC_CHAR_VECTOR("b"),
+ STATIC_CHAR_VECTOR("\";"), 600000);
+ Handle<String> source_b_str =
+ f->NewStringFromOneByte(source_b).ToHandleChecked();
+
+ Vector<const uint8_t> source_c =
+ ConstructSource(STATIC_CHAR_VECTOR("var c = \""), STATIC_CHAR_VECTOR("c"),
+ STATIC_CHAR_VECTOR("\";"), 500000);
+ Handle<String> source_c_str =
+ f->NewStringFromOneByte(source_c).ToHandleChecked();
+
+ Handle<String> source_str =
+ f->NewConsString(
+ f->NewConsString(source_a_str, source_b_str).ToHandleChecked(),
+ source_c_str).ToHandleChecked();
+
+ Handle<JSObject> global(isolate->context()->global_object());
+ ScriptData* cache = NULL;
+
+ Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
+
+ Handle<SharedFunctionInfo> copy;
+ {
+ DisallowCompilation no_compile_expected(isolate);
+ copy = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
+ }
+ CHECK_NE(*orig, *copy);
+
+ Handle<JSFunction> copy_fun =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ copy, isolate->native_context());
+
+ USE(Execution::Call(isolate, copy_fun, global, 0, NULL));
+
+ CHECK_EQ(600000 + 700000, CompileRun("(a + b).length")->Int32Value());
+ CHECK_EQ(500000 + 600000, CompileRun("(b + c).length")->Int32Value());
+ Heap* heap = isolate->heap();
+ CHECK(heap->InSpace(
+ *v8::Utils::OpenHandle(*CompileRun("a")->ToString(CcTest::isolate())),
+ OLD_DATA_SPACE));
+ CHECK(heap->InSpace(
+ *v8::Utils::OpenHandle(*CompileRun("b")->ToString(CcTest::isolate())),
+ OLD_DATA_SPACE));
+ CHECK(heap->InSpace(
+ *v8::Utils::OpenHandle(*CompileRun("c")->ToString(CcTest::isolate())),
+ OLD_DATA_SPACE));
+
+ delete cache;
+ source_a.Dispose();
+ source_b.Dispose();
+ source_c.Dispose();
+}
+
+
+class SerializerOneByteResource
+ : public v8::String::ExternalOneByteStringResource {
+ public:
+ SerializerOneByteResource(const char* data, size_t length)
+ : data_(data), length_(length) {}
+ virtual const char* data() const { return data_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ const char* data_;
+ size_t length_;
+};
+
+
+class SerializerTwoByteResource : public v8::String::ExternalStringResource {
+ public:
+ SerializerTwoByteResource(const char* data, size_t length)
+ : data_(AsciiToTwoByteString(data)), length_(length) {}
+ ~SerializerTwoByteResource() { DeleteArray<const uint16_t>(data_); }
+
+ virtual const uint16_t* data() const { return data_; }
+ virtual size_t length() const { return length_; }
+
+ private:
+ const uint16_t* data_;
+ size_t length_;
+};
+
+
+TEST(SerializeToplevelExternalString) {
+ FLAG_serialize_toplevel = true;
+ LocalContext context;
+ Isolate* isolate = CcTest::i_isolate();
+ isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
+
+ v8::HandleScope scope(CcTest::isolate());
+
+ // Obtain external internalized one-byte string.
+ SerializerOneByteResource one_byte_resource("one_byte", 8);
+ Handle<String> one_byte_string =
+ isolate->factory()->NewStringFromAsciiChecked("one_byte");
+ one_byte_string = isolate->factory()->InternalizeString(one_byte_string);
+ one_byte_string->MakeExternal(&one_byte_resource);
+ CHECK(one_byte_string->IsExternalOneByteString());
+ CHECK(one_byte_string->IsInternalizedString());
+
+ // Obtain external internalized two-byte string.
+ SerializerTwoByteResource two_byte_resource("two_byte", 8);
+ Handle<String> two_byte_string =
+ isolate->factory()->NewStringFromAsciiChecked("two_byte");
+ two_byte_string = isolate->factory()->InternalizeString(two_byte_string);
+ two_byte_string->MakeExternal(&two_byte_resource);
+ CHECK(two_byte_string->IsExternalTwoByteString());
+ CHECK(two_byte_string->IsInternalizedString());
+
+ const char* source =
+ "var o = {} \n"
+ "o.one_byte = 7; \n"
+ "o.two_byte = 8; \n"
+ "o.one_byte + o.two_byte; \n";
+ Handle<String> source_string = isolate->factory()
+ ->NewStringFromUtf8(CStrVector(source))
+ .ToHandleChecked();
+
+ Handle<JSObject> global(isolate->context()->global_object());
+ ScriptData* cache = NULL;
+
+ Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
+ source_string, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
+
+ Handle<SharedFunctionInfo> copy;
+ {
+ DisallowCompilation no_compile_expected(isolate);
+ copy = Compiler::CompileScript(
+ source_string, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
+ }
+ CHECK_NE(*orig, *copy);
+
+ Handle<JSFunction> copy_fun =
+ isolate->factory()->NewFunctionFromSharedFunctionInfo(
+ copy, isolate->native_context());
+
+ Handle<Object> copy_result =
+ Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
+
+ CHECK_EQ(15.0f, copy_result->Number());
+
+ delete cache;
+}
+
+
+TEST(SerializeToplevelLargeExternalString) {
+ FLAG_serialize_toplevel = true;
+ LocalContext context;
+ Isolate* isolate = CcTest::i_isolate();
+ isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
+
+ Factory* f = isolate->factory();
+
+ v8::HandleScope scope(CcTest::isolate());
+
+ // Create a huge external internalized string to use as variable name.
+ Vector<const uint8_t> string =
+ ConstructSource(STATIC_CHAR_VECTOR(""), STATIC_CHAR_VECTOR("abcdef"),
+ STATIC_CHAR_VECTOR(""), 999999);
+ Handle<String> name = f->NewStringFromOneByte(string).ToHandleChecked();
+ SerializerOneByteResource one_byte_resource(
+ reinterpret_cast<const char*>(string.start()), string.length());
+ name = f->InternalizeString(name);
+ name->MakeExternal(&one_byte_resource);
+ CHECK(name->IsExternalOneByteString());
+ CHECK(name->IsInternalizedString());
+ CHECK(isolate->heap()->InSpace(*name, LO_SPACE));
+
+ // Create the source, which is "var <literal> = 42; <literal>".
+ Handle<String> source_str =
+ f->NewConsString(
+ f->NewConsString(f->NewStringFromAsciiChecked("var "), name)
+ .ToHandleChecked(),
+ f->NewConsString(f->NewStringFromAsciiChecked(" = 42; "), name)
+ .ToHandleChecked()).ToHandleChecked();
+
+ Handle<JSObject> global(isolate->context()->global_object());
+ ScriptData* cache = NULL;
+
+ Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
+
+ Handle<SharedFunctionInfo> copy;
+ {
+ DisallowCompilation no_compile_expected(isolate);
+ copy = Compiler::CompileScript(
+ source_str, Handle<String>(), 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
+ }
+ CHECK_NE(*orig, *copy);
+
+ Handle<JSFunction> copy_fun =
+ f->NewFunctionFromSharedFunctionInfo(copy, isolate->native_context());
+
+ Handle<Object> copy_result =
+ Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
+
+ CHECK_EQ(42.0f, copy_result->Number());
+
+ delete cache;
+ string.Dispose();
+}
+
+
+TEST(SerializeToplevelExternalScriptName) {
+ FLAG_serialize_toplevel = true;
+ LocalContext context;
+ Isolate* isolate = CcTest::i_isolate();
+ isolate->compilation_cache()->Disable(); // Disable same-isolate code cache.
+
+ Factory* f = isolate->factory();
+
+ v8::HandleScope scope(CcTest::isolate());
+
+ const char* source =
+ "var a = [1, 2, 3, 4];"
+ "a.reduce(function(x, y) { return x + y }, 0)";
+
+ Handle<String> source_string =
+ f->NewStringFromUtf8(CStrVector(source)).ToHandleChecked();
+
+ const SerializerOneByteResource one_byte_resource("one_byte", 8);
+ Handle<String> name =
+ f->NewExternalStringFromOneByte(&one_byte_resource).ToHandleChecked();
+ CHECK(name->IsExternalOneByteString());
+ CHECK(!name->IsInternalizedString());
+
+ Handle<JSObject> global(isolate->context()->global_object());
+ ScriptData* cache = NULL;
+
+ Handle<SharedFunctionInfo> orig = Compiler::CompileScript(
+ source_string, name, 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
+
+ Handle<SharedFunctionInfo> copy;
+ {
+ DisallowCompilation no_compile_expected(isolate);
+ copy = Compiler::CompileScript(
+ source_string, name, 0, 0, false,
+ Handle<Context>(isolate->native_context()), NULL, &cache,
+ v8::ScriptCompiler::kConsumeCodeCache, NOT_NATIVES_CODE);
+ }
+ CHECK_NE(*orig, *copy);
+
+ Handle<JSFunction> copy_fun =
+ f->NewFunctionFromSharedFunctionInfo(copy, isolate->native_context());
+
+ Handle<Object> copy_result =
+ Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
+
+ CHECK_EQ(10.0f, copy_result->Number());
+
+ delete cache;
+}
+
+
+static bool toplevel_test_code_event_found = false;
+
+
+static void SerializerCodeEventListener(const v8::JitCodeEvent* event) {
+ if (event->type == v8::JitCodeEvent::CODE_ADDED &&
+ memcmp(event->name.str, "Script:~test", 12) == 0) {
+ toplevel_test_code_event_found = true;
+ }
+}
+
+
TEST(SerializeToplevelIsolates) {
FLAG_serialize_toplevel = true;
@@ -805,6 +1116,7 @@ TEST(SerializeToplevelIsolates) {
v8::Local<v8::UnboundScript> script = v8::ScriptCompiler::CompileUnbound(
isolate1, &source, v8::ScriptCompiler::kProduceCodeCache);
const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
+ CHECK(data);
// Persist cached data.
uint8_t* buffer = NewArray<uint8_t>(data->length);
MemCopy(buffer, data->data, data->length);
@@ -812,11 +1124,14 @@ TEST(SerializeToplevelIsolates) {
buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
v8::Local<v8::Value> result = script->BindToCurrentContext()->Run();
- CHECK(result->ToString()->Equals(v8_str("abcdef")));
+ CHECK(result->ToString(isolate1)->Equals(v8_str("abcdef")));
}
isolate1->Dispose();
v8::Isolate* isolate2 = v8::Isolate::New();
+ isolate2->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
+ SerializerCodeEventListener);
+ toplevel_test_code_event_found = false;
{
v8::Isolate::Scope iscope(isolate2);
v8::HandleScope scope(isolate2);
@@ -832,8 +1147,125 @@ TEST(SerializeToplevelIsolates) {
script = v8::ScriptCompiler::CompileUnbound(
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache);
}
+ CHECK(!cache->rejected);
+ v8::Local<v8::Value> result = script->BindToCurrentContext()->Run();
+ CHECK(result->ToString(isolate2)->Equals(v8_str("abcdef")));
+ }
+ DCHECK(toplevel_test_code_event_found);
+ isolate2->Dispose();
+}
+
+
+TEST(SerializeToplevelFlagChange) {
+ FLAG_serialize_toplevel = true;
+
+ const char* source = "function f() { return 'abc'; }; f() + 'def'";
+ v8::ScriptCompiler::CachedData* cache;
+
+ v8::Isolate* isolate1 = v8::Isolate::New();
+ {
+ v8::Isolate::Scope iscope(isolate1);
+ v8::HandleScope scope(isolate1);
+ v8::Local<v8::Context> context = v8::Context::New(isolate1);
+ v8::Context::Scope context_scope(context);
+
+ v8::Local<v8::String> source_str = v8_str(source);
+ v8::ScriptOrigin origin(v8_str("test"));
+ v8::ScriptCompiler::Source source(source_str, origin);
+ v8::Local<v8::UnboundScript> script = v8::ScriptCompiler::CompileUnbound(
+ isolate1, &source, v8::ScriptCompiler::kProduceCodeCache);
+ const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
+ CHECK(data);
+ // Persist cached data.
+ uint8_t* buffer = NewArray<uint8_t>(data->length);
+ MemCopy(buffer, data->data, data->length);
+ cache = new v8::ScriptCompiler::CachedData(
+ buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
+
+ v8::Local<v8::Value> result = script->BindToCurrentContext()->Run();
+ CHECK(result->ToString(isolate1)->Equals(v8_str("abcdef")));
+ }
+ isolate1->Dispose();
+
+ v8::Isolate* isolate2 = v8::Isolate::New();
+ FLAG_allow_natives_syntax = true; // Flag change should trigger cache reject.
+ {
+ v8::Isolate::Scope iscope(isolate2);
+ v8::HandleScope scope(isolate2);
+ v8::Local<v8::Context> context = v8::Context::New(isolate2);
+ v8::Context::Scope context_scope(context);
+
+ v8::Local<v8::String> source_str = v8_str(source);
+ v8::ScriptOrigin origin(v8_str("test"));
+ v8::ScriptCompiler::Source source(source_str, origin, cache);
+ v8::ScriptCompiler::CompileUnbound(isolate2, &source,
+ v8::ScriptCompiler::kConsumeCodeCache);
+ CHECK(cache->rejected);
+ }
+ isolate2->Dispose();
+}
+
+
+TEST(SerializeWithHarmonyScoping) {
+ FLAG_serialize_toplevel = true;
+ FLAG_harmony_scoping = true;
+
+ const char* source1 = "'use strict'; let x = 'X'";
+ const char* source2 = "'use strict'; let y = 'Y'";
+ const char* source3 = "'use strict'; x + y";
+
+ v8::ScriptCompiler::CachedData* cache;
+
+ v8::Isolate* isolate1 = v8::Isolate::New();
+ {
+ v8::Isolate::Scope iscope(isolate1);
+ v8::HandleScope scope(isolate1);
+ v8::Local<v8::Context> context = v8::Context::New(isolate1);
+ v8::Context::Scope context_scope(context);
+
+ CompileRun(source1);
+ CompileRun(source2);
+
+ v8::Local<v8::String> source_str = v8_str(source3);
+ v8::ScriptOrigin origin(v8_str("test"));
+ v8::ScriptCompiler::Source source(source_str, origin);
+ v8::Local<v8::UnboundScript> script = v8::ScriptCompiler::CompileUnbound(
+ isolate1, &source, v8::ScriptCompiler::kProduceCodeCache);
+ const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
+ CHECK(data);
+ // Persist cached data.
+ uint8_t* buffer = NewArray<uint8_t>(data->length);
+ MemCopy(buffer, data->data, data->length);
+ cache = new v8::ScriptCompiler::CachedData(
+ buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
+
+ v8::Local<v8::Value> result = script->BindToCurrentContext()->Run();
+ CHECK(result->ToString(isolate1)->Equals(v8_str("XY")));
+ }
+ isolate1->Dispose();
+
+ v8::Isolate* isolate2 = v8::Isolate::New();
+ {
+ v8::Isolate::Scope iscope(isolate2);
+ v8::HandleScope scope(isolate2);
+ v8::Local<v8::Context> context = v8::Context::New(isolate2);
+ v8::Context::Scope context_scope(context);
+
+ // Reverse order of prior running scripts.
+ CompileRun(source2);
+ CompileRun(source1);
+
+ v8::Local<v8::String> source_str = v8_str(source3);
+ v8::ScriptOrigin origin(v8_str("test"));
+ v8::ScriptCompiler::Source source(source_str, origin, cache);
+ v8::Local<v8::UnboundScript> script;
+ {
+ DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate2));
+ script = v8::ScriptCompiler::CompileUnbound(
+ isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache);
+ }
v8::Local<v8::Value> result = script->BindToCurrentContext()->Run();
- CHECK(result->ToString()->Equals(v8_str("abcdef")));
+ CHECK(result->ToString(isolate2)->Equals(v8_str("XY")));
}
isolate2->Dispose();
}
diff --git a/test/cctest/test-spaces.cc b/test/cctest/test-spaces.cc
index d09c128d..a84b867f 100644
--- a/test/cctest/test-spaces.cc
+++ b/test/cctest/test-spaces.cc
@@ -27,6 +27,7 @@
#include <stdlib.h>
+#include "src/base/platform/platform.h"
#include "src/snapshot.h"
#include "src/v8.h"
#include "test/cctest/cctest.h"
@@ -212,11 +213,17 @@ TEST(Regress3540) {
TestMemoryAllocatorScope test_allocator_scope(isolate, memory_allocator);
CodeRange* code_range = new CodeRange(isolate);
const size_t code_range_size = 4 * MB;
- if (!code_range->SetUp(code_range_size)) return;
+ if (!code_range->SetUp(
+ code_range_size +
+ RoundUp(v8::base::OS::CommitPageSize() * kReservedCodeRangePages,
+ MemoryChunk::kAlignment) +
+ v8::internal::MemoryAllocator::CodePageAreaSize())) {
+ return;
+ }
Address address;
size_t size;
- address = code_range->AllocateRawMemory(code_range_size - MB,
- code_range_size - MB, &size);
+ address = code_range->AllocateRawMemory(code_range_size - 2 * MB,
+ code_range_size - 2 * MB, &size);
CHECK(address != NULL);
Address null_address;
size_t null_size;
@@ -450,3 +457,55 @@ TEST(SizeOfFirstPageIsLargeEnough) {
// No large objects required to perform the above steps.
CHECK(isolate->heap()->lo_space()->IsEmpty());
}
+
+
+static inline void FillCurrentPage(v8::internal::NewSpace* space) {
+ int new_linear_size = static_cast<int>(*space->allocation_limit_address() -
+ *space->allocation_top_address());
+ if (new_linear_size == 0) return;
+ v8::internal::AllocationResult allocation =
+ space->AllocateRaw(new_linear_size);
+ v8::internal::FreeListNode* node =
+ v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
+ node->set_size(space->heap(), new_linear_size);
+}
+
+
+UNINITIALIZED_TEST(NewSpaceGrowsToTargetCapacity) {
+ FLAG_target_semi_space_size = 2;
+ if (FLAG_optimize_for_size) return;
+
+ v8::Isolate* isolate = v8::Isolate::New();
+ {
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope handle_scope(isolate);
+ v8::Context::New(isolate)->Enter();
+
+ Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
+
+ NewSpace* new_space = i_isolate->heap()->new_space();
+
+ // This test doesn't work if we start with a non-default new space
+ // configuration.
+ if (new_space->InitialTotalCapacity() == Page::kPageSize) {
+ CHECK(new_space->CommittedMemory() == new_space->InitialTotalCapacity());
+
+ // Fill up the first (and only) page of the semi space.
+ FillCurrentPage(new_space);
+
+ // Try to allocate out of the new space. A new page should be added and
+ // the
+ // allocation should succeed.
+ v8::internal::AllocationResult allocation = new_space->AllocateRaw(80);
+ CHECK(!allocation.IsRetry());
+ CHECK(new_space->CommittedMemory() == 2 * Page::kPageSize);
+
+ // Turn the allocation into a proper object so isolate teardown won't
+ // crash.
+ v8::internal::FreeListNode* node =
+ v8::internal::FreeListNode::cast(allocation.ToObjectChecked());
+ node->set_size(new_space->heap(), 80);
+ }
+ }
+ isolate->Dispose();
+}
diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc
index ef13c4da..d1f23f75 100644
--- a/test/cctest/test-strings.cc
+++ b/test/cctest/test-strings.cc
@@ -37,6 +37,7 @@
#include "src/api.h"
#include "src/factory.h"
#include "src/objects.h"
+#include "src/unicode-decoder.h"
#include "test/cctest/cctest.h"
// Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry
@@ -356,10 +357,10 @@ void AccumulateStats(Handle<String> cons_string, ConsStringStats* stats) {
void AccumulateStatsWithOperator(
ConsString* cons_string, ConsStringStats* stats) {
- ConsStringIteratorOp op(cons_string);
+ ConsStringIterator iter(cons_string);
String* string;
int offset;
- while (NULL != (string = op.Next(&offset))) {
+ while (NULL != (string = iter.Next(&offset))) {
// Accumulate stats.
CHECK_EQ(0, offset);
stats->leaves_++;
@@ -523,13 +524,10 @@ static Handle<String> ConstructBalanced(
}
-static ConsStringIteratorOp cons_string_iterator_op_1;
-static ConsStringIteratorOp cons_string_iterator_op_2;
-
static void Traverse(Handle<String> s1, Handle<String> s2) {
int i = 0;
- StringCharacterStream character_stream_1(*s1, &cons_string_iterator_op_1);
- StringCharacterStream character_stream_2(*s2, &cons_string_iterator_op_2);
+ StringCharacterStream character_stream_1(*s1);
+ StringCharacterStream character_stream_2(*s2);
while (character_stream_1.HasMore()) {
CHECK(character_stream_2.HasMore());
uint16_t c = character_stream_1.GetNext();
@@ -545,8 +543,8 @@ static void Traverse(Handle<String> s1, Handle<String> s2) {
static void TraverseFirst(Handle<String> s1, Handle<String> s2, int chars) {
int i = 0;
- StringCharacterStream character_stream_1(*s1, &cons_string_iterator_op_1);
- StringCharacterStream character_stream_2(*s2, &cons_string_iterator_op_2);
+ StringCharacterStream character_stream_1(*s1);
+ StringCharacterStream character_stream_2(*s2);
while (character_stream_1.HasMore() && i < chars) {
CHECK(character_stream_2.HasMore());
uint16_t c = character_stream_1.GetNext();
@@ -615,10 +613,8 @@ static void VerifyCharacterStream(
if (offset < 0) offset = 0;
// Want to test the offset == length case.
if (offset > length) offset = length;
- StringCharacterStream flat_stream(
- flat_string, &cons_string_iterator_op_1, offset);
- StringCharacterStream cons_stream(
- cons_string, &cons_string_iterator_op_2, offset);
+ StringCharacterStream flat_stream(flat_string, offset);
+ StringCharacterStream cons_stream(cons_string, offset);
for (int i = offset; i < length; i++) {
uint16_t c = flat_string->Get(i);
CHECK(flat_stream.HasMore());
@@ -634,14 +630,11 @@ static void VerifyCharacterStream(
static inline void PrintStats(const ConsStringGenerationData& data) {
#ifdef DEBUG
-printf(
- "%s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d], %s: [%d]\n",
- "leaves", data.stats_.leaves_,
- "empty", data.stats_.empty_leaves_,
- "chars", data.stats_.chars_,
- "lefts", data.stats_.left_traversals_,
- "rights", data.stats_.right_traversals_,
- "early_terminations", data.early_terminations_);
+ printf("%s: [%u], %s: [%u], %s: [%u], %s: [%u], %s: [%u], %s: [%u]\n",
+ "leaves", data.stats_.leaves_, "empty", data.stats_.empty_leaves_,
+ "chars", data.stats_.chars_, "lefts", data.stats_.left_traversals_,
+ "rights", data.stats_.right_traversals_, "early_terminations",
+ data.early_terminations_);
#endif
}
@@ -1027,11 +1020,13 @@ TEST(JSONStringifySliceMadeExternal) {
// into a two-byte external string. Check that JSON.stringify works.
v8::HandleScope handle_scope(CcTest::isolate());
v8::Handle<v8::String> underlying =
- CompileRun("var underlying = 'abcdefghijklmnopqrstuvwxyz';"
- "underlying")->ToString();
- v8::Handle<v8::String> slice =
- CompileRun("var slice = underlying.slice(1);"
- "slice")->ToString();
+ CompileRun(
+ "var underlying = 'abcdefghijklmnopqrstuvwxyz';"
+ "underlying")->ToString(CcTest::isolate());
+ v8::Handle<v8::String> slice = CompileRun(
+ "var slice = '';"
+ "slice = underlying.slice(1);"
+ "slice")->ToString(CcTest::isolate());
CHECK(v8::Utils::OpenHandle(*slice)->IsSlicedString());
CHECK(v8::Utils::OpenHandle(*underlying)->IsSeqOneByteString());
@@ -1089,7 +1084,7 @@ TEST(CachedHashOverflow) {
CHECK_EQ(results[i]->IsNumber(), result->IsNumber());
if (result->IsNumber()) {
CHECK_EQ(Object::ToSmi(isolate, results[i]).ToHandleChecked()->value(),
- result->ToInt32()->Value());
+ result->ToInt32(CcTest::isolate())->Value());
}
}
}
@@ -1189,7 +1184,7 @@ TEST(SliceFromSlice) {
v8::Local<v8::Value> result;
Handle<String> string;
const char* init = "var str = 'abcdefghijklmnopqrstuvwxyz';";
- const char* slice = "var slice = str.slice(1,-1); slice";
+ const char* slice = "var slice = ''; slice = str.slice(1,-1); slice";
const char* slice_from_slice = "slice.slice(1,-1);";
CompileRun(init);
@@ -1292,6 +1287,42 @@ TEST(RobustSubStringStub) {
}
+namespace {
+
+int* global_use_counts = NULL;
+
+void MockUseCounterCallback(v8::Isolate* isolate,
+ v8::Isolate::UseCounterFeature feature) {
+ ++global_use_counts[feature];
+}
+}
+
+
+TEST(CountBreakIterator) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ LocalContext context;
+ int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
+ global_use_counts = use_counts;
+ CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
+ CHECK_EQ(0, use_counts[v8::Isolate::kBreakIterator]);
+ v8::Local<v8::Value> result = CompileRun(
+ "(function() {"
+ " if (!this.Intl) return 0;"
+ " var iterator = Intl.v8BreakIterator(['en']);"
+ " iterator.adoptText('Now is the time');"
+ " iterator.next();"
+ " return iterator.next();"
+ "})();");
+ CHECK(result->IsNumber());
+ int uses = result->ToInt32(CcTest::isolate())->Value() == 0 ? 0 : 1;
+ CHECK_EQ(uses, use_counts[v8::Isolate::kBreakIterator]);
+ // Make sure GC cleans up the break iterator, so we don't get a memory leak
+ // reported by ASAN.
+ CcTest::isolate()->LowMemoryNotification();
+}
+
+
TEST(StringReplaceAtomTwoByteResult) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
diff --git a/test/cctest/test-transitions.cc b/test/cctest/test-transitions.cc
new file mode 100644
index 00000000..6bcdb35e
--- /dev/null
+++ b/test/cctest/test-transitions.cc
@@ -0,0 +1,301 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdlib.h>
+#include <utility>
+
+#include "src/v8.h"
+
+#include "src/compilation-cache.h"
+#include "src/execution.h"
+#include "src/factory.h"
+#include "src/global-handles.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+
+
+//
+// Helper functions.
+//
+
+static void ConnectTransition(Handle<Map> parent,
+ Handle<TransitionArray> transitions,
+ Handle<Map> child) {
+ if (!parent->HasTransitionArray() || *transitions != parent->transitions()) {
+ parent->set_transitions(*transitions);
+ }
+ child->SetBackPointer(*parent);
+}
+
+
+static void CheckPropertyDetailsFieldsConsistency(PropertyType type,
+ PropertyKind kind,
+ PropertyLocation location) {
+ int type_value = PropertyDetails::TypeField::encode(type);
+ int kind_location_value = PropertyDetails::KindField::encode(kind) |
+ PropertyDetails::LocationField::encode(location);
+ CHECK_EQ(type_value, kind_location_value);
+}
+
+
+TEST(PropertyDetailsFieldsConsistency) {
+ CheckPropertyDetailsFieldsConsistency(FIELD, DATA, IN_OBJECT);
+ CheckPropertyDetailsFieldsConsistency(CONSTANT, DATA, IN_DESCRIPTOR);
+ CheckPropertyDetailsFieldsConsistency(ACCESSOR_FIELD, ACCESSOR, IN_OBJECT);
+ CheckPropertyDetailsFieldsConsistency(CALLBACKS, ACCESSOR, IN_DESCRIPTOR);
+}
+
+
+TEST(TransitionArray_SimpleFieldTransitions) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ Handle<String> name1 = factory->InternalizeUtf8String("foo");
+ Handle<String> name2 = factory->InternalizeUtf8String("bar");
+ PropertyAttributes attributes = NONE;
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ Handle<Map> map1 =
+ Map::CopyWithField(map0, name1, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ Handle<Map> map2 =
+ Map::CopyWithField(map0, name2, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ int transition;
+ transitions =
+ transitions->Insert(map0, name1, map1, SIMPLE_PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map1);
+ CHECK(transitions->IsSimpleTransition());
+ transition = transitions->Search(DATA, *name1, attributes);
+ CHECK_EQ(TransitionArray::kSimpleTransitionIndex, transition);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transitions =
+ transitions->Insert(map0, name2, map2, SIMPLE_PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map2);
+ CHECK(transitions->IsFullTransitionArray());
+
+ transition = transitions->Search(DATA, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transition = transitions->Search(DATA, *name2, attributes);
+ CHECK_EQ(*name2, transitions->GetKey(transition));
+ CHECK_EQ(*map2, transitions->GetTarget(transition));
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_FullFieldTransitions) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ Handle<String> name1 = factory->InternalizeUtf8String("foo");
+ Handle<String> name2 = factory->InternalizeUtf8String("bar");
+ PropertyAttributes attributes = NONE;
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ Handle<Map> map1 =
+ Map::CopyWithField(map0, name1, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ Handle<Map> map2 =
+ Map::CopyWithField(map0, name2, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ int transition;
+ transitions = transitions->Insert(map0, name1, map1, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map1);
+ CHECK(transitions->IsFullTransitionArray());
+ transition = transitions->Search(DATA, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transitions = transitions->Insert(map0, name2, map2, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map2);
+ CHECK(transitions->IsFullTransitionArray());
+
+ transition = transitions->Search(DATA, *name1, attributes);
+ CHECK_EQ(*name1, transitions->GetKey(transition));
+ CHECK_EQ(*map1, transitions->GetTarget(transition));
+
+ transition = transitions->Search(DATA, *name2, attributes);
+ CHECK_EQ(*name2, transitions->GetKey(transition));
+ CHECK_EQ(*map2, transitions->GetTarget(transition));
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_DifferentFieldNames) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ const int PROPS_COUNT = 10;
+ Handle<String> names[PROPS_COUNT];
+ Handle<Map> maps[PROPS_COUNT];
+ PropertyAttributes attributes = NONE;
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ EmbeddedVector<char, 64> buffer;
+ SNPrintF(buffer, "prop%d", i);
+ Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ names[i] = name;
+ maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ int transition = transitions->Search(DATA, *names[i], attributes);
+ CHECK_EQ(*names[i], transitions->GetKey(transition));
+ CHECK_EQ(*maps[i], transitions->GetTarget(transition));
+ }
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_SameFieldNamesDifferentAttributesSimple) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
+ STATIC_ASSERT(ATTRS_COUNT == 8);
+ Handle<Map> attr_maps[ATTRS_COUNT];
+ Handle<String> name = factory->InternalizeUtf8String("foo");
+
+ // Add transitions for same field name but different attributes.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ attr_maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ // Ensure that transitions for |name| field are valid.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ int transition = transitions->Search(DATA, *name, attributes);
+ CHECK_EQ(*name, transitions->GetKey(transition));
+ CHECK_EQ(*attr_maps[i], transitions->GetTarget(transition));
+ }
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
+
+
+TEST(TransitionArray_SameFieldNamesDifferentAttributes) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+
+ const int PROPS_COUNT = 10;
+ Handle<String> names[PROPS_COUNT];
+ Handle<Map> maps[PROPS_COUNT];
+
+ Handle<Map> map0 = Map::Create(isolate, 0);
+ CHECK(!map0->HasTransitionArray());
+ Handle<TransitionArray> transitions = TransitionArray::Allocate(isolate, 0);
+ CHECK(transitions->IsFullTransitionArray());
+
+ // Some number of fields.
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ EmbeddedVector<char, 64> buffer;
+ SNPrintF(buffer, "prop%d", i);
+ Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate), NONE,
+ Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ names[i] = name;
+ maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ const int ATTRS_COUNT = (READ_ONLY | DONT_ENUM | DONT_DELETE) + 1;
+ STATIC_ASSERT(ATTRS_COUNT == 8);
+ Handle<Map> attr_maps[ATTRS_COUNT];
+ Handle<String> name = factory->InternalizeUtf8String("foo");
+
+ // Add transitions for same field name but different attributes.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ Handle<Map> map =
+ Map::CopyWithField(map0, name, handle(HeapType::Any(), isolate),
+ attributes, Representation::Tagged(),
+ OMIT_TRANSITION).ToHandleChecked();
+ attr_maps[i] = map;
+
+ transitions = transitions->Insert(map0, name, map, PROPERTY_TRANSITION);
+ ConnectTransition(map0, transitions, map);
+ }
+
+ // Ensure that transitions for |name| field are valid.
+ for (int i = 0; i < ATTRS_COUNT; i++) {
+ PropertyAttributes attributes = static_cast<PropertyAttributes>(i);
+
+ int transition = transitions->Search(DATA, *name, attributes);
+ CHECK_EQ(*name, transitions->GetKey(transition));
+ CHECK_EQ(*attr_maps[i], transitions->GetTarget(transition));
+ }
+
+ // Ensure that info about the other fields still valid.
+ for (int i = 0; i < PROPS_COUNT; i++) {
+ int transition = transitions->Search(DATA, *names[i], NONE);
+ CHECK_EQ(*names[i], transitions->GetKey(transition));
+ CHECK_EQ(*maps[i], transitions->GetTarget(transition));
+ }
+
+ DCHECK(transitions->IsSortedNoDuplicates());
+}
diff --git a/test/cctest/test-types.cc b/test/cctest/test-types.cc
index 0cd24728..ebef527d 100644
--- a/test/cctest/test-types.cc
+++ b/test/cctest/test-types.cc
@@ -8,12 +8,27 @@
#include "src/isolate-inl.h"
#include "src/types.h"
#include "test/cctest/cctest.h"
+#include "test/cctest/types-fuzz.h"
using namespace v8::internal;
+
// Testing auxiliaries (breaking the Type abstraction).
+
+
+static bool IsInteger(double x) {
+ return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
+}
+
+
+static bool IsInteger(i::Object* x) {
+ return x->IsNumber() && IsInteger(x->Number());
+}
+
+
typedef uint32_t bitset;
+
struct ZoneRep {
typedef void* Struct;
@@ -76,262 +91,6 @@ struct HeapRep {
};
-template<class Type, class TypeHandle, class Region>
-class Types {
- public:
- Types(Region* region, Isolate* isolate)
- : region_(region), rng_(isolate->random_number_generator()) {
- #define DECLARE_TYPE(name, value) \
- name = Type::name(region); \
- types.push_back(name);
- PROPER_BITSET_TYPE_LIST(DECLARE_TYPE)
- #undef DECLARE_TYPE
-
- object_map = isolate->factory()->NewMap(
- JS_OBJECT_TYPE, JSObject::kHeaderSize);
- array_map = isolate->factory()->NewMap(
- JS_ARRAY_TYPE, JSArray::kSize);
- number_map = isolate->factory()->NewMap(
- HEAP_NUMBER_TYPE, HeapNumber::kSize);
- uninitialized_map = isolate->factory()->uninitialized_map();
- ObjectClass = Type::Class(object_map, region);
- ArrayClass = Type::Class(array_map, region);
- NumberClass = Type::Class(number_map, region);
- UninitializedClass = Type::Class(uninitialized_map, region);
-
- maps.push_back(object_map);
- maps.push_back(array_map);
- maps.push_back(uninitialized_map);
- for (MapVector::iterator it = maps.begin(); it != maps.end(); ++it) {
- types.push_back(Type::Class(*it, region));
- }
-
- smi = handle(Smi::FromInt(666), isolate);
- signed32 = isolate->factory()->NewHeapNumber(0x40000000);
- object1 = isolate->factory()->NewJSObjectFromMap(object_map);
- object2 = isolate->factory()->NewJSObjectFromMap(object_map);
- array = isolate->factory()->NewJSArray(20);
- uninitialized = isolate->factory()->uninitialized_value();
- SmiConstant = Type::Constant(smi, region);
- Signed32Constant = Type::Constant(signed32, region);
- ObjectConstant1 = Type::Constant(object1, region);
- ObjectConstant2 = Type::Constant(object2, region);
- ArrayConstant = Type::Constant(array, region);
- UninitializedConstant = Type::Constant(uninitialized, region);
-
- values.push_back(smi);
- values.push_back(signed32);
- values.push_back(object1);
- values.push_back(object2);
- values.push_back(array);
- values.push_back(uninitialized);
- for (ValueVector::iterator it = values.begin(); it != values.end(); ++it) {
- types.push_back(Type::Constant(*it, region));
- }
-
- integers.push_back(isolate->factory()->NewNumber(-V8_INFINITY));
- integers.push_back(isolate->factory()->NewNumber(+V8_INFINITY));
- integers.push_back(isolate->factory()->NewNumber(-rng_->NextInt(10)));
- integers.push_back(isolate->factory()->NewNumber(+rng_->NextInt(10)));
- for (int i = 0; i < 10; ++i) {
- double x = rng_->NextInt();
- integers.push_back(isolate->factory()->NewNumber(x));
- x *= rng_->NextInt();
- if (!IsMinusZero(x)) integers.push_back(isolate->factory()->NewNumber(x));
- }
-
- NumberArray = Type::Array(Number, region);
- StringArray = Type::Array(String, region);
- AnyArray = Type::Array(Any, region);
-
- SignedFunction1 = Type::Function(SignedSmall, SignedSmall, region);
- NumberFunction1 = Type::Function(Number, Number, region);
- NumberFunction2 = Type::Function(Number, Number, Number, region);
- MethodFunction = Type::Function(String, Object, 0, region);
-
- for (int i = 0; i < 30; ++i) {
- types.push_back(Fuzz());
- }
- }
-
- Handle<i::Map> object_map;
- Handle<i::Map> array_map;
- Handle<i::Map> number_map;
- Handle<i::Map> uninitialized_map;
-
- Handle<i::Smi> smi;
- Handle<i::HeapNumber> signed32;
- Handle<i::JSObject> object1;
- Handle<i::JSObject> object2;
- Handle<i::JSArray> array;
- Handle<i::Oddball> uninitialized;
-
- #define DECLARE_TYPE(name, value) TypeHandle name;
- BITSET_TYPE_LIST(DECLARE_TYPE)
- #undef DECLARE_TYPE
-
- TypeHandle ObjectClass;
- TypeHandle ArrayClass;
- TypeHandle NumberClass;
- TypeHandle UninitializedClass;
-
- TypeHandle SmiConstant;
- TypeHandle Signed32Constant;
- TypeHandle ObjectConstant1;
- TypeHandle ObjectConstant2;
- TypeHandle ArrayConstant;
- TypeHandle UninitializedConstant;
-
- TypeHandle NumberArray;
- TypeHandle StringArray;
- TypeHandle AnyArray;
-
- TypeHandle SignedFunction1;
- TypeHandle NumberFunction1;
- TypeHandle NumberFunction2;
- TypeHandle MethodFunction;
-
- typedef std::vector<TypeHandle> TypeVector;
- typedef std::vector<Handle<i::Map> > MapVector;
- typedef std::vector<Handle<i::Object> > ValueVector;
-
- TypeVector types;
- MapVector maps;
- ValueVector values;
- ValueVector integers; // "Integer" values used for range limits.
-
- TypeHandle Of(Handle<i::Object> value) {
- return Type::Of(value, region_);
- }
-
- TypeHandle NowOf(Handle<i::Object> value) {
- return Type::NowOf(value, region_);
- }
-
- TypeHandle Class(Handle<i::Map> map) {
- return Type::Class(map, region_);
- }
-
- TypeHandle Constant(Handle<i::Object> value) {
- return Type::Constant(value, region_);
- }
-
- TypeHandle Range(Handle<i::Object> min, Handle<i::Object> max) {
- return Type::Range(min, max, region_);
- }
-
- TypeHandle Context(TypeHandle outer) {
- return Type::Context(outer, region_);
- }
-
- TypeHandle Array1(TypeHandle element) {
- return Type::Array(element, region_);
- }
-
- TypeHandle Function0(TypeHandle result, TypeHandle receiver) {
- return Type::Function(result, receiver, 0, region_);
- }
-
- TypeHandle Function1(TypeHandle result, TypeHandle receiver, TypeHandle arg) {
- TypeHandle type = Type::Function(result, receiver, 1, region_);
- type->AsFunction()->InitParameter(0, arg);
- return type;
- }
-
- TypeHandle Function2(TypeHandle result, TypeHandle arg1, TypeHandle arg2) {
- return Type::Function(result, arg1, arg2, region_);
- }
-
- TypeHandle Union(TypeHandle t1, TypeHandle t2) {
- return Type::Union(t1, t2, region_);
- }
- TypeHandle Intersect(TypeHandle t1, TypeHandle t2) {
- return Type::Intersect(t1, t2, region_);
- }
-
- template<class Type2, class TypeHandle2>
- TypeHandle Convert(TypeHandle2 t) {
- return Type::template Convert<Type2>(t, region_);
- }
-
- TypeHandle Random() {
- return types[rng_->NextInt(static_cast<int>(types.size()))];
- }
-
- TypeHandle Fuzz(int depth = 4) {
- switch (rng_->NextInt(depth == 0 ? 3 : 20)) {
- case 0: { // bitset
- int n = 0
- #define COUNT_BITSET_TYPES(type, value) + 1
- PROPER_BITSET_TYPE_LIST(COUNT_BITSET_TYPES)
- #undef COUNT_BITSET_TYPES
- ;
- int i = rng_->NextInt(n);
- #define PICK_BITSET_TYPE(type, value) \
- if (i-- == 0) return Type::type(region_);
- PROPER_BITSET_TYPE_LIST(PICK_BITSET_TYPE)
- #undef PICK_BITSET_TYPE
- UNREACHABLE();
- }
- case 1: { // class
- int i = rng_->NextInt(static_cast<int>(maps.size()));
- return Type::Class(maps[i], region_);
- }
- case 2: { // constant
- int i = rng_->NextInt(static_cast<int>(values.size()));
- return Type::Constant(values[i], region_);
- }
- case 3: { // range
- int i = rng_->NextInt(static_cast<int>(integers.size()));
- int j = rng_->NextInt(static_cast<int>(integers.size()));
- i::Handle<i::Object> min = integers[i];
- i::Handle<i::Object> max = integers[j];
- if (min->Number() > max->Number()) std::swap(min, max);
- return Type::Range(min, max, region_);
- }
- case 4: { // context
- int depth = rng_->NextInt(3);
- TypeHandle type = Type::Internal(region_);
- for (int i = 0; i < depth; ++i) type = Type::Context(type, region_);
- return type;
- }
- case 5: { // array
- TypeHandle element = Fuzz(depth / 2);
- return Type::Array(element, region_);
- }
- case 6:
- case 7: { // function
- TypeHandle result = Fuzz(depth / 2);
- TypeHandle receiver = Fuzz(depth / 2);
- int arity = rng_->NextInt(3);
- TypeHandle type = Type::Function(result, receiver, arity, region_);
- for (int i = 0; i < type->AsFunction()->Arity(); ++i) {
- TypeHandle parameter = Fuzz(depth / 2);
- type->AsFunction()->InitParameter(i, parameter);
- }
- return type;
- }
- default: { // union
- int n = rng_->NextInt(10);
- TypeHandle type = None;
- for (int i = 0; i < n; ++i) {
- TypeHandle operand = Fuzz(depth - 1);
- type = Type::Union(type, operand, region_);
- }
- return type;
- }
- }
- UNREACHABLE();
- }
-
- Region* region() { return region_; }
-
- private:
- Region* region_;
- v8::base::RandomNumberGenerator* rng_;
-};
-
-
template<class Type, class TypeHandle, class Region, class Rep>
struct Tests : Rep {
typedef Types<Type, TypeHandle, Region> TypesInstance;
@@ -535,39 +294,55 @@ struct Tests : Rep {
CHECK(T.Constant(fac->NewNumber(0))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(1))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(0x3fffffff))->Is(T.UnsignedSmall));
- CHECK(T.Constant(fac->NewNumber(-1))->Is(T.OtherSignedSmall));
- CHECK(T.Constant(fac->NewNumber(-0x3fffffff))->Is(T.OtherSignedSmall));
- CHECK(T.Constant(fac->NewNumber(-0x40000000))->Is(T.OtherSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-1))->Is(T.NegativeSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x3fffffff))->Is(T.NegativeSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x40000000))->Is(T.NegativeSignedSmall));
if (SmiValuesAre31Bits()) {
- CHECK(T.Constant(fac->NewNumber(0x40000000))->Is(T.OtherUnsigned31));
- CHECK(T.Constant(fac->NewNumber(0x7fffffff))->Is(T.OtherUnsigned31));
- CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.OtherSigned32));
- CHECK(T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.OtherSigned32));
- CHECK(T.Constant(fac->NewNumber(-0x7fffffff-1))->Is(T.OtherSigned32));
+ CHECK(T.Constant(fac->NewNumber(0x40000000))->Is(T.NonNegativeSigned32));
+ CHECK(!T.Constant(fac->NewNumber(0x40000000))->Is(T.UnsignedSmall));
+ CHECK(T.Constant(fac->NewNumber(0x7fffffff))->Is(T.NonNegativeSigned32));
+ CHECK(!T.Constant(fac->NewNumber(0x7fffffff))->Is(T.UnsignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.NegativeSigned32));
+ CHECK(
+ !T.Constant(fac->NewNumber(-0x40000001))->Is(T.NegativeSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.NegativeSigned32));
+ CHECK(!T.Constant(fac->NewNumber(-0x7fffffff - 1))
+ ->Is(T.NegativeSignedSmall));
} else {
CHECK(SmiValuesAre32Bits());
CHECK(T.Constant(fac->NewNumber(0x40000000))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(0x7fffffff))->Is(T.UnsignedSmall));
- CHECK(!T.Constant(fac->NewNumber(0x40000000))->Is(T.OtherUnsigned31));
- CHECK(!T.Constant(fac->NewNumber(0x7fffffff))->Is(T.OtherUnsigned31));
- CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.OtherSignedSmall));
- CHECK(T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.OtherSignedSmall));
- CHECK(T.Constant(fac->NewNumber(-0x7fffffff-1))->Is(T.OtherSignedSmall));
- CHECK(!T.Constant(fac->NewNumber(-0x40000001))->Is(T.OtherSigned32));
- CHECK(!T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.OtherSigned32));
- CHECK(!T.Constant(fac->NewNumber(-0x7fffffff-1))->Is(T.OtherSigned32));
- }
- CHECK(T.Constant(fac->NewNumber(0x80000000u))->Is(T.OtherUnsigned32));
- CHECK(T.Constant(fac->NewNumber(0xffffffffu))->Is(T.OtherUnsigned32));
- CHECK(T.Constant(fac->NewNumber(0xffffffffu+1.0))->Is(T.OtherNumber));
- CHECK(T.Constant(fac->NewNumber(-0x7fffffff-2.0))->Is(T.OtherNumber));
- CHECK(T.Constant(fac->NewNumber(0.1))->Is(T.OtherNumber));
- CHECK(T.Constant(fac->NewNumber(-10.1))->Is(T.OtherNumber));
- CHECK(T.Constant(fac->NewNumber(10e60))->Is(T.OtherNumber));
+ CHECK(T.Constant(fac->NewNumber(0x40000000))->Is(T.NonNegativeSigned32));
+ CHECK(T.Constant(fac->NewNumber(0x7fffffff))->Is(T.NonNegativeSigned32));
+ CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.NegativeSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.NegativeSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x7fffffff - 1))
+ ->Is(T.NegativeSignedSmall));
+ CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.NegativeSigned32));
+ CHECK(T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.NegativeSigned32));
+ CHECK(
+ T.Constant(fac->NewNumber(-0x7fffffff - 1))->Is(T.NegativeSigned32));
+ }
+ CHECK(T.Constant(fac->NewNumber(0x80000000u))->Is(T.Unsigned32));
+ CHECK(!T.Constant(fac->NewNumber(0x80000000u))->Is(T.NonNegativeSigned32));
+ CHECK(T.Constant(fac->NewNumber(0xffffffffu))->Is(T.Unsigned32));
+ CHECK(!T.Constant(fac->NewNumber(0xffffffffu))->Is(T.NonNegativeSigned32));
+ CHECK(T.Constant(fac->NewNumber(0xffffffffu + 1.0))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(0xffffffffu + 1.0))->Is(T.Integral32));
+ CHECK(T.Constant(fac->NewNumber(-0x7fffffff - 2.0))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(-0x7fffffff - 2.0))->Is(T.Integral32));
+ CHECK(T.Constant(fac->NewNumber(0.1))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(0.1))->Is(T.Integral32));
+ CHECK(T.Constant(fac->NewNumber(-10.1))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(-10.1))->Is(T.Integral32));
+ CHECK(T.Constant(fac->NewNumber(10e60))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(10e60))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(-1.0*0.0))->Is(T.MinusZero));
CHECK(T.Constant(fac->NewNumber(v8::base::OS::nan_value()))->Is(T.NaN));
- CHECK(T.Constant(fac->NewNumber(V8_INFINITY))->Is(T.OtherNumber));
- CHECK(T.Constant(fac->NewNumber(-V8_INFINITY))->Is(T.OtherNumber));
+ CHECK(T.Constant(fac->NewNumber(V8_INFINITY))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(V8_INFINITY))->Is(T.Integral32));
+ CHECK(T.Constant(fac->NewNumber(-V8_INFINITY))->Is(T.PlainNumber));
+ CHECK(!T.Constant(fac->NewNumber(-V8_INFINITY))->Is(T.Integral32));
}
void Range() {
@@ -598,11 +373,11 @@ struct Tests : Rep {
// Range(min1, max1) = Range(min2, max2) <=> min1 = min2 /\ max1 = max2
for (ValueIterator i1 = T.integers.begin();
i1 != T.integers.end(); ++i1) {
- for (ValueIterator j1 = T.integers.begin();
+ for (ValueIterator j1 = i1;
j1 != T.integers.end(); ++j1) {
for (ValueIterator i2 = T.integers.begin();
i2 != T.integers.end(); ++i2) {
- for (ValueIterator j2 = T.integers.begin();
+ for (ValueIterator j2 = i2;
j2 != T.integers.end(); ++j2) {
i::Handle<i::Object> min1 = *i1;
i::Handle<i::Object> max1 = *j1;
@@ -619,6 +394,33 @@ struct Tests : Rep {
}
}
+ void Context() {
+ // Constructor
+ for (int i = 0; i < 20; ++i) {
+ TypeHandle type = T.Random();
+ TypeHandle context = T.Context(type);
+ CHECK(context->Iscontext());
+ }
+
+ // Attributes
+ for (int i = 0; i < 20; ++i) {
+ TypeHandle type = T.Random();
+ TypeHandle context = T.Context(type);
+ CheckEqual(type, context->AsContext()->Outer());
+ }
+
+ // Functionality & Injectivity: Context(T1) = Context(T2) iff T1 = T2
+ for (int i = 0; i < 20; ++i) {
+ for (int j = 0; j < 20; ++j) {
+ TypeHandle type1 = T.Random();
+ TypeHandle type2 = T.Random();
+ TypeHandle context1 = T.Context(type1);
+ TypeHandle context2 = T.Context(type2);
+ CHECK(Equal(context1, context2) == Equal(type1, type2));
+ }
+ }
+ }
+
void Array() {
// Constructor
for (int i = 0; i < 20; ++i) {
@@ -803,6 +605,66 @@ struct Tests : Rep {
}
}
+ void MinMax() {
+ Factory* fac = isolate->factory();
+
+ // If b is regular numeric bitset, then Range(b->Min(), b->Max())->Is(b).
+ // TODO(neis): Need to ignore representation for this to be true.
+ /*
+ for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
+ TypeHandle type = *it;
+ if (this->IsBitset(type) && type->Is(T.Number) &&
+ !type->Is(T.None) && !type->Is(T.NaN)) {
+ TypeHandle range = T.Range(
+ isolate->factory()->NewNumber(type->Min()),
+ isolate->factory()->NewNumber(type->Max()));
+ CHECK(range->Is(type));
+ }
+ }
+ */
+
+ // If b is regular numeric bitset, then b->Min() and b->Max() are integers.
+ for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
+ TypeHandle type = *it;
+ if (this->IsBitset(type) && type->Is(T.Number) && !type->Is(T.NaN)) {
+ CHECK(IsInteger(type->Min()) && IsInteger(type->Max()));
+ }
+ }
+
+ // If b1 and b2 are regular numeric bitsets with b1->Is(b2), then
+ // b1->Min() >= b2->Min() and b1->Max() <= b2->Max().
+ for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
+ for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
+ TypeHandle type1 = *it1;
+ TypeHandle type2 = *it2;
+ if (this->IsBitset(type1) && type1->Is(type2) && type2->Is(T.Number) &&
+ !type1->Is(T.NaN) && !type2->Is(T.NaN)) {
+ CHECK(type1->Min() >= type2->Min());
+ CHECK(type1->Max() <= type2->Max());
+ }
+ }
+ }
+
+ // Lub(Range(x,y))->Min() <= x and y <= Lub(Range(x,y))->Max()
+ for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
+ TypeHandle type = *it;
+ if (type->IsRange()) {
+ TypeHandle lub = Rep::BitsetType::New(
+ Rep::BitsetType::Lub(type), T.region());
+ CHECK(lub->Min() <= type->Min() && type->Max() <= lub->Max());
+ }
+ }
+
+ // Rangification: If T->Is(Range(-inf,+inf)) and !T->Is(None), then
+ // T->Is(Range(T->Min(), T->Max())).
+ for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
+ TypeHandle type = *it;
+ CHECK(!(type->Is(T.Integer) && !type->Is(T.None)) ||
+ type->Is(T.Range(fac->NewNumber(type->Min()),
+ fac->NewNumber(type->Max()))));
+ }
+ }
+
void BitsetGlb() {
// Lower: (T->BitsetGlb())->Is(T)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
@@ -871,7 +733,7 @@ struct Tests : Rep {
}
}
- void Is() {
+ void Is1() {
// Least Element (Bottom): None->Is(T)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
TypeHandle type = *it;
@@ -923,6 +785,26 @@ struct Tests : Rep {
}
}
+ // (In-)Compatibilities.
+ for (TypeIterator i = T.types.begin(); i != T.types.end(); ++i) {
+ for (TypeIterator j = T.types.begin(); j != T.types.end(); ++j) {
+ TypeHandle type1 = *i;
+ TypeHandle type2 = *j;
+ CHECK(!type1->Is(type2) || this->IsBitset(type2) ||
+ this->IsUnion(type2) || this->IsUnion(type1) ||
+ (type1->IsClass() && type2->IsClass()) ||
+ (type1->IsConstant() && type2->IsConstant()) ||
+ (type1->IsConstant() && type2->IsRange()) ||
+ (type1->IsRange() && type2->IsRange()) ||
+ (type1->IsContext() && type2->IsContext()) ||
+ (type1->IsArray() && type2->IsArray()) ||
+ (type1->IsFunction() && type2->IsFunction()) ||
+ type1->Equals(T.None));
+ }
+ }
+ }
+
+ void Is2() {
// Class(M1)->Is(Class(M2)) iff M1 = M2
for (MapIterator mt1 = T.maps.begin(); mt1 != T.maps.end(); ++mt1) {
for (MapIterator mt2 = T.maps.begin(); mt2 != T.maps.end(); ++mt2) {
@@ -934,26 +816,14 @@ struct Tests : Rep {
}
}
- // Constant(V1)->Is(Constant(V2)) iff V1 = V2
- for (ValueIterator vt1 = T.values.begin(); vt1 != T.values.end(); ++vt1) {
- for (ValueIterator vt2 = T.values.begin(); vt2 != T.values.end(); ++vt2) {
- Handle<i::Object> value1 = *vt1;
- Handle<i::Object> value2 = *vt2;
- TypeHandle const_type1 = T.Constant(value1);
- TypeHandle const_type2 = T.Constant(value2);
- CHECK(const_type1->Is(const_type2) == (*value1 == *value2));
- }
- }
-
- // Range(min1, max1)->Is(Range(min2, max2)) iff
- // min1 >= min2 /\ max1 <= max2
+ // Range(X1, Y1)->Is(Range(X2, Y2)) iff X1 >= X2 /\ Y1 <= Y2
for (ValueIterator i1 = T.integers.begin();
i1 != T.integers.end(); ++i1) {
- for (ValueIterator j1 = T.integers.begin();
+ for (ValueIterator j1 = i1;
j1 != T.integers.end(); ++j1) {
for (ValueIterator i2 = T.integers.begin();
i2 != T.integers.end(); ++i2) {
- for (ValueIterator j2 = T.integers.begin();
+ for (ValueIterator j2 = i2;
j2 != T.integers.end(); ++j2) {
i::Handle<i::Object> min1 = *i1;
i::Handle<i::Object> max1 = *j1;
@@ -964,13 +834,24 @@ struct Tests : Rep {
TypeHandle type1 = T.Range(min1, max1);
TypeHandle type2 = T.Range(min2, max2);
CHECK(type1->Is(type2) ==
- (min2->Number() <= min1->Number() &&
+ (min1->Number() >= min2->Number() &&
max1->Number() <= max2->Number()));
}
}
}
}
+ // Constant(V1)->Is(Constant(V2)) iff V1 = V2
+ for (ValueIterator vt1 = T.values.begin(); vt1 != T.values.end(); ++vt1) {
+ for (ValueIterator vt2 = T.values.begin(); vt2 != T.values.end(); ++vt2) {
+ Handle<i::Object> value1 = *vt1;
+ Handle<i::Object> value2 = *vt2;
+ TypeHandle const_type1 = T.Constant(value1);
+ TypeHandle const_type2 = T.Constant(value2);
+ CHECK(const_type1->Is(const_type2) == (*value1 == *value2));
+ }
+ }
+
// Context(T1)->Is(Context(T2)) iff T1 = T2
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1007,25 +888,45 @@ struct Tests : Rep {
}
}
- // (In-)Compatibilities.
- for (TypeIterator i = T.types.begin(); i != T.types.end(); ++i) {
- for (TypeIterator j = T.types.begin(); j != T.types.end(); ++j) {
- TypeHandle type1 = *i;
- TypeHandle type2 = *j;
- CHECK(!type1->Is(type2) || this->IsBitset(type2) ||
- this->IsUnion(type2) || this->IsUnion(type1) ||
- (type1->IsClass() && type2->IsClass()) ||
- (type1->IsConstant() && type2->IsConstant()) ||
- (type1->IsConstant() && type2->IsRange()) ||
- (type1->IsRange() && type2->IsRange()) ||
- (type1->IsContext() && type2->IsContext()) ||
- (type1->IsArray() && type2->IsArray()) ||
- (type1->IsFunction() && type2->IsFunction()) ||
- type1->Equals(T.None));
+
+ // Range-specific subtyping
+
+ // If IsInteger(v) then Constant(v)->Is(Range(v, v)).
+ for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
+ TypeHandle type = *it;
+ if (type->IsConstant() && IsInteger(*type->AsConstant()->Value())) {
+ CHECK(type->Is(
+ T.Range(type->AsConstant()->Value(), type->AsConstant()->Value())));
}
}
- // Basic types
+ // If Constant(x)->Is(Range(min,max)) then IsInteger(v) and min <= x <= max.
+ for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
+ for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
+ TypeHandle type1 = *it1;
+ TypeHandle type2 = *it2;
+ if (type1->IsConstant() && type2->IsRange() && type1->Is(type2)) {
+ double x = type1->AsConstant()->Value()->Number();
+ double min = type2->AsRange()->Min()->Number();
+ double max = type2->AsRange()->Max()->Number();
+ CHECK(IsInteger(x) && min <= x && x <= max);
+ }
+ }
+ }
+
+ // Lub(Range(x,y))->Is(T.Union(T.Integral32, T.OtherNumber))
+ for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
+ TypeHandle type = *it;
+ if (type->IsRange()) {
+ TypeHandle lub = Rep::BitsetType::New(
+ Rep::BitsetType::Lub(type), T.region());
+ CHECK(lub->Is(T.PlainNumber));
+ }
+ }
+
+
+ // Subtyping between concrete basic types
+
CheckUnordered(T.Boolean, T.Null);
CheckUnordered(T.Undefined, T.Null);
CheckUnordered(T.Boolean, T.Undefined);
@@ -1049,12 +950,12 @@ struct Tests : Rep {
CheckSub(T.Object, T.Receiver);
CheckSub(T.Array, T.Object);
- CheckSub(T.Function, T.Object);
CheckSub(T.Proxy, T.Receiver);
CheckUnordered(T.Object, T.Proxy);
- CheckUnordered(T.Array, T.Function);
- // Structural types
+
+ // Subtyping between concrete structural types
+
CheckSub(T.ObjectClass, T.Object);
CheckSub(T.ArrayClass, T.Object);
CheckSub(T.ArrayClass, T.Array);
@@ -1086,7 +987,7 @@ struct Tests : Rep {
CheckSub(T.NumberArray, T.Object);
CheckUnordered(T.StringArray, T.AnyArray);
- CheckSub(T.MethodFunction, T.Function);
+ CheckSub(T.MethodFunction, T.Object);
CheckSub(T.NumberFunction1, T.Object);
CheckUnordered(T.SignedFunction1, T.NumberFunction1);
CheckUnordered(T.NumberFunction1, T.NumberFunction2);
@@ -1372,10 +1273,8 @@ struct Tests : Rep {
CheckDisjoint(T.InternalizedString, T.Symbol);
CheckOverlap(T.Object, T.Receiver);
CheckOverlap(T.Array, T.Object);
- CheckOverlap(T.Function, T.Object);
CheckOverlap(T.Proxy, T.Receiver);
CheckDisjoint(T.Object, T.Proxy);
- CheckDisjoint(T.Array, T.Function);
// Structural types
CheckOverlap(T.ObjectClass, T.Object);
@@ -1399,7 +1298,7 @@ struct Tests : Rep {
CheckOverlap(T.NumberArray, T.Array);
CheckDisjoint(T.NumberArray, T.AnyArray);
CheckDisjoint(T.NumberArray, T.StringArray);
- CheckOverlap(T.MethodFunction, T.Function);
+ CheckOverlap(T.MethodFunction, T.Object);
CheckDisjoint(T.SignedFunction1, T.NumberFunction1);
CheckDisjoint(T.SignedFunction1, T.NumberFunction2);
CheckDisjoint(T.NumberFunction1, T.NumberFunction2);
@@ -1443,7 +1342,9 @@ struct Tests : Rep {
}
// Associativity: Union(T1, Union(T2, T3)) = Union(Union(T1, T2), T3)
- // This does NOT hold!
+ // This does NOT hold! For example:
+ // (Unsigned32 \/ Range(0,5)) \/ Range(-5,0) = Unsigned32 \/ Range(-5,0)
+ // Unsigned32 \/ (Range(0,5) \/ Range(-5,0)) = Unsigned32 \/ Range(-5,5)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1483,7 +1384,9 @@ struct Tests : Rep {
}
// Monotonicity: T1->Is(T2) implies Union(T1, T3)->Is(Union(T2, T3))
- // This does NOT hold.
+ // This does NOT hold. For example:
+ // Range(-5,-1) <= Signed32
+ // Range(-5,-1) \/ Range(1,5) = Range(-5,5) </= Signed32 \/ Range(1,5)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1502,8 +1405,10 @@ struct Tests : Rep {
void Union2() {
// Monotonicity: T1->Is(T3) and T2->Is(T3) implies Union(T1, T2)->Is(T3)
- // This does NOT hold. TODO(neis): Could fix this by splitting
- // OtherNumber into a negative and a positive part.
+ // This does NOT hold. For example:
+ // Range(-2^33, -2^33) <= OtherNumber
+ // Range(2^33, 2^33) <= OtherNumber
+ // Range(-2^33, 2^33) </= OtherNumber
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1523,7 +1428,7 @@ struct Tests : Rep {
// Monotonicity: T1->Is(T2) or T1->Is(T3) implies T1->Is(Union(T2, T3))
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
- for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) {
+ for (TypeIterator it3 = it2; it3 != T.types.end(); ++it3) {
TypeHandle type1 = *it1;
TypeHandle type2 = *it2;
TypeHandle type3 = *it3;
@@ -1563,11 +1468,11 @@ struct Tests : Rep {
CheckDisjoint(T.Union(T.NumberArray, T.String), T.Number);
// Bitset-function
- CHECK(this->IsBitset(T.Union(T.MethodFunction, T.Function)));
+ CHECK(this->IsBitset(T.Union(T.MethodFunction, T.Object)));
CHECK(this->IsUnion(T.Union(T.NumberFunction1, T.Number)));
- CheckEqual(T.Union(T.MethodFunction, T.Function), T.Function);
- CheckUnordered(T.Union(T.NumberFunction1, T.String), T.Function);
+ CheckEqual(T.Union(T.MethodFunction, T.Object), T.Object);
+ CheckUnordered(T.Union(T.NumberFunction1, T.String), T.Object);
CheckOverlap(T.Union(T.NumberFunction2, T.String), T.Object);
CheckDisjoint(T.Union(T.NumberFunction1, T.String), T.Number);
@@ -1635,7 +1540,7 @@ struct Tests : Rep {
CheckEqual(
T.Union(T.NumberFunction1, T.NumberFunction2),
T.Union(T.NumberFunction2, T.NumberFunction1));
- CheckSub(T.Union(T.SignedFunction1, T.MethodFunction), T.Function);
+ CheckSub(T.Union(T.SignedFunction1, T.MethodFunction), T.Object);
// Union-union
CheckEqual(
@@ -1685,7 +1590,11 @@ struct Tests : Rep {
// Associativity:
// Intersect(T1, Intersect(T2, T3)) = Intersect(Intersect(T1, T2), T3)
- // This does NOT hold.
+ // This does NOT hold. For example:
+ // (Class(..stringy1..) /\ Class(..stringy2..)) /\ Constant(..string..) =
+ // None
+ // Class(..stringy1..) /\ (Class(..stringy2..) /\ Constant(..string..)) =
+ // Constant(..string..)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1704,7 +1613,11 @@ struct Tests : Rep {
*/
// Join: Intersect(T1, T2)->Is(T1) and Intersect(T1, T2)->Is(T2)
- // This does NOT hold. Not even the disjunction.
+ // This does NOT hold. For example:
+ // Class(..stringy..) /\ Constant(..string..) = Constant(..string..)
+ // Currently, not even the disjunction holds:
+ // Class(Internal/TaggedPtr) /\ (Any/Untagged \/ Context(..)) =
+ // Class(Internal/TaggedPtr) \/ Context(..)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1728,7 +1641,10 @@ struct Tests : Rep {
}
// Monotonicity: T1->Is(T2) implies Intersect(T1, T3)->Is(Intersect(T2, T3))
- // This does NOT hold.
+ // This does NOT hold. For example:
+ // Class(OtherObject/TaggedPtr) <= Any/TaggedPtr
+ // Class(OtherObject/TaggedPtr) /\ Any/UntaggedInt1 = Class(..)
+ // Any/TaggedPtr /\ Any/UntaggedInt1 = None
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1745,7 +1661,10 @@ struct Tests : Rep {
*/
// Monotonicity: T1->Is(T3) or T2->Is(T3) implies Intersect(T1, T2)->Is(T3)
- // This does NOT hold.
+ // This does NOT hold. For example:
+ // Class(..stringy..) <= Class(..stringy..)
+ // Class(..stringy..) /\ Constant(..string..) = Constant(..string..)
+ // Constant(..string..) </= Class(..stringy..)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1762,8 +1681,6 @@ struct Tests : Rep {
*/
// Monotonicity: T1->Is(T2) and T1->Is(T3) implies T1->Is(Intersect(T2, T3))
- // This does NOT hold.
- /*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) {
@@ -1776,7 +1693,6 @@ struct Tests : Rep {
}
}
}
- */
// Bitset-class
CheckEqual(T.Intersect(T.ObjectClass, T.Object), T.ObjectClass);
@@ -1785,11 +1701,11 @@ struct Tests : Rep {
// Bitset-array
CheckEqual(T.Intersect(T.NumberArray, T.Object), T.NumberArray);
- CheckEqual(T.Intersect(T.AnyArray, T.Function), T.None);
+ CheckEqual(T.Intersect(T.AnyArray, T.Proxy), T.None);
// Bitset-function
CheckEqual(T.Intersect(T.MethodFunction, T.Object), T.MethodFunction);
- CheckEqual(T.Intersect(T.NumberFunction1, T.Array), T.None);
+ CheckEqual(T.Intersect(T.NumberFunction1, T.Proxy), T.None);
// Bitset-union
CheckEqual(
@@ -1880,7 +1796,11 @@ struct Tests : Rep {
void Distributivity() {
// Union(T1, Intersect(T2, T3)) = Intersect(Union(T1, T2), Union(T1, T3))
- // This does NOT hold.
+ // This does NOT hold. For example:
+ // Untagged \/ (Untagged /\ Class(../Tagged)) = Untagged \/ Class(../Tagged)
+ // (Untagged \/ Untagged) /\ (Untagged \/ Class(../Tagged)) =
+ // Untagged /\ (Untagged \/ Class(../Tagged)) = Untagged
+ // because Untagged <= Untagged \/ Class(../Tagged)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1900,7 +1820,10 @@ struct Tests : Rep {
*/
// Intersect(T1, Union(T2, T3)) = Union(Intersect(T1, T2), Intersect(T1,T3))
- // This does NOT hold.
+ // This does NOT hold. For example:
+ // Untagged /\ (Untagged \/ Class(../Tagged)) = Untagged
+ // (Untagged /\ Untagged) \/ (Untagged /\ Class(../Tagged)) =
+ // Untagged \/ Class(../Tagged)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
@@ -1920,6 +1843,32 @@ struct Tests : Rep {
*/
}
+ void GetRange() {
+ // GetRange(Range(a, b)) = Range(a, b).
+ for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
+ TypeHandle type1 = *it1;
+ if (type1->IsRange()) {
+ typename Type::RangeType* range = type1->GetRange();
+ CHECK(type1->Min() == range->Min()->Number());
+ CHECK(type1->Max() == range->Max()->Number());
+ }
+ }
+
+ // GetRange(Union(Constant(x), Range(min,max))) == Range(min, max).
+ for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
+ for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
+ TypeHandle type1 = *it1;
+ TypeHandle type2 = *it2;
+ if (type1->IsConstant() && type2->IsRange()) {
+ TypeHandle u = T.Union(type1, type2);
+
+ CHECK(type2->Min() == u->GetRange()->Min()->Number());
+ CHECK(type2->Max() == u->GetRange()->Max()->Number());
+ }
+ }
+ }
+ }
+
template<class Type2, class TypeHandle2, class Region2, class Rep2>
void Convert() {
Types<Type2, TypeHandle2, Region2> T2(
@@ -2012,6 +1961,13 @@ TEST(NowOf) {
}
+TEST(MinMax) {
+ CcTest::InitializeVM();
+ ZoneTests().MinMax();
+ HeapTests().MinMax();
+}
+
+
TEST(BitsetGlb) {
CcTest::InitializeVM();
ZoneTests().BitsetGlb();
@@ -2026,10 +1982,17 @@ TEST(BitsetLub) {
}
-TEST(Is) {
+TEST(Is1) {
+ CcTest::InitializeVM();
+ ZoneTests().Is1();
+ HeapTests().Is1();
+}
+
+
+TEST(Is2) {
CcTest::InitializeVM();
- ZoneTests().Is();
- HeapTests().Is();
+ ZoneTests().Is2();
+ HeapTests().Is2();
}
@@ -2098,13 +2061,18 @@ TEST(Intersect) {
}
-/*
TEST(Distributivity) {
CcTest::InitializeVM();
ZoneTests().Distributivity();
HeapTests().Distributivity();
}
-*/
+
+
+TEST(GetRange) {
+ CcTest::InitializeVM();
+ ZoneTests().GetRange();
+ HeapTests().GetRange();
+}
TEST(Convert) {
diff --git a/test/cctest/test-unboxed-doubles.cc b/test/cctest/test-unboxed-doubles.cc
new file mode 100644
index 00000000..a12bf47f
--- /dev/null
+++ b/test/cctest/test-unboxed-doubles.cc
@@ -0,0 +1,1162 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdlib.h>
+#include <utility>
+
+#include "src/v8.h"
+
+#include "src/compilation-cache.h"
+#include "src/execution.h"
+#include "src/factory.h"
+#include "src/global-handles.h"
+#include "src/ic/ic.h"
+#include "src/macro-assembler.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::base;
+using namespace v8::internal;
+
+#if (V8_DOUBLE_FIELDS_UNBOXING)
+
+
+static double GetDoubleFieldValue(JSObject* obj, FieldIndex field_index) {
+ if (obj->IsUnboxedDoubleField(field_index)) {
+ return obj->RawFastDoublePropertyAt(field_index);
+ } else {
+ Object* value = obj->RawFastPropertyAt(field_index);
+ DCHECK(value->IsMutableHeapNumber());
+ return HeapNumber::cast(value)->value();
+ }
+}
+
+const int kNumberOfBits = 32;
+
+
+enum TestPropertyKind {
+ PROP_CONSTANT,
+ PROP_SMI,
+ PROP_DOUBLE,
+ PROP_TAGGED,
+ PROP_KIND_NUMBER
+};
+
+static Representation representations[PROP_KIND_NUMBER] = {
+ Representation::None(), Representation::Smi(), Representation::Double(),
+ Representation::Tagged()};
+
+
+static Handle<DescriptorArray> CreateDescriptorArray(Isolate* isolate,
+ TestPropertyKind* props,
+ int kPropsCount) {
+ Factory* factory = isolate->factory();
+
+ Handle<String> func_name = factory->InternalizeUtf8String("func");
+ Handle<JSFunction> func = factory->NewFunction(func_name);
+
+ Handle<DescriptorArray> descriptors =
+ DescriptorArray::Allocate(isolate, 0, kPropsCount);
+
+ int next_field_offset = 0;
+ for (int i = 0; i < kPropsCount; i++) {
+ EmbeddedVector<char, 64> buffer;
+ SNPrintF(buffer, "prop%d", i);
+ Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+
+ TestPropertyKind kind = props[i];
+
+ if (kind == PROP_CONSTANT) {
+ ConstantDescriptor d(name, func, NONE);
+ descriptors->Append(&d);
+
+ } else {
+ FieldDescriptor f(name, next_field_offset, NONE, representations[kind]);
+ next_field_offset += f.GetDetails().field_width_in_words();
+ descriptors->Append(&f);
+ }
+ }
+ return descriptors;
+}
+
+
+TEST(LayoutDescriptorBasicFast) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout();
+
+ CHECK(!layout_desc->IsSlowLayout());
+ CHECK(layout_desc->IsFastPointerLayout());
+ CHECK_EQ(kSmiValueSize, layout_desc->capacity());
+
+ for (int i = 0; i < kSmiValueSize + 13; i++) {
+ CHECK_EQ(true, layout_desc->IsTagged(i));
+ }
+ CHECK_EQ(true, layout_desc->IsTagged(-1));
+ CHECK_EQ(true, layout_desc->IsTagged(-12347));
+ CHECK_EQ(true, layout_desc->IsTagged(15635));
+ CHECK(layout_desc->IsFastPointerLayout());
+
+ for (int i = 0; i < kSmiValueSize; i++) {
+ layout_desc = layout_desc->SetTaggedForTesting(i, false);
+ CHECK_EQ(false, layout_desc->IsTagged(i));
+ layout_desc = layout_desc->SetTaggedForTesting(i, true);
+ CHECK_EQ(true, layout_desc->IsTagged(i));
+ }
+ CHECK(layout_desc->IsFastPointerLayout());
+
+ int sequence_length;
+ CHECK_EQ(true, layout_desc->IsTagged(0, std::numeric_limits<int>::max(),
+ &sequence_length));
+ CHECK_EQ(std::numeric_limits<int>::max(), sequence_length);
+
+ CHECK_EQ(true, layout_desc->IsTagged(0, 7, &sequence_length));
+ CHECK_EQ(7, sequence_length);
+}
+
+
+TEST(LayoutDescriptorBasicSlow) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ // All properties tagged.
+ props[i] = PROP_TAGGED;
+ }
+
+ {
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ Handle<Map> map = Map::Create(isolate, kPropsCount);
+
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ CHECK_EQ(kSmiValueSize, layout_descriptor->capacity());
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ props[0] = PROP_DOUBLE;
+ props[kPropsCount - 1] = PROP_DOUBLE;
+
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ {
+ int inobject_properties = kPropsCount - 1;
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+ // Should be fast as the only double property is the first one.
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ CHECK(!layout_descriptor->IsSlowLayout());
+ CHECK(!layout_descriptor->IsFastPointerLayout());
+
+ CHECK_EQ(false, layout_descriptor->IsTagged(0));
+ for (int i = 1; i < kPropsCount; i++) {
+ CHECK_EQ(true, layout_descriptor->IsTagged(i));
+ }
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ {
+ int inobject_properties = kPropsCount;
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ CHECK(layout_descriptor->IsSlowLayout());
+ CHECK(!layout_descriptor->IsFastPointerLayout());
+ CHECK(layout_descriptor->capacity() > kSmiValueSize);
+
+ CHECK_EQ(false, layout_descriptor->IsTagged(0));
+ CHECK_EQ(false, layout_descriptor->IsTagged(kPropsCount - 1));
+ for (int i = 1; i < kPropsCount - 1; i++) {
+ CHECK_EQ(true, layout_descriptor->IsTagged(i));
+ }
+
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+
+ // Here we have truly slow layout descriptor, so play with the bits.
+ CHECK_EQ(true, layout_descriptor->IsTagged(-1));
+ CHECK_EQ(true, layout_descriptor->IsTagged(-12347));
+ CHECK_EQ(true, layout_descriptor->IsTagged(15635));
+
+ LayoutDescriptor* layout_desc = *layout_descriptor;
+ // Play with the bits but leave it in consistent state with map at the end.
+ for (int i = 1; i < kPropsCount - 1; i++) {
+ layout_desc = layout_desc->SetTaggedForTesting(i, false);
+ CHECK_EQ(false, layout_desc->IsTagged(i));
+ layout_desc = layout_desc->SetTaggedForTesting(i, true);
+ CHECK_EQ(true, layout_desc->IsTagged(i));
+ }
+ CHECK(layout_desc->IsSlowLayout());
+ CHECK(!layout_desc->IsFastPointerLayout());
+
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+}
+
+
+static void TestLayoutDescriptorQueries(int layout_descriptor_length,
+ int* bit_flip_positions,
+ int max_sequence_length) {
+ Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::NewForTesting(
+ CcTest::i_isolate(), layout_descriptor_length);
+ layout_descriptor_length = layout_descriptor->capacity();
+ LayoutDescriptor* layout_desc = *layout_descriptor;
+
+ {
+ // Fill in the layout descriptor.
+ int cur_bit_flip_index = 0;
+ bool tagged = true;
+ for (int i = 0; i < layout_descriptor_length; i++) {
+ if (i == bit_flip_positions[cur_bit_flip_index]) {
+ tagged = !tagged;
+ ++cur_bit_flip_index;
+ CHECK(i < bit_flip_positions[cur_bit_flip_index]); // check test data
+ }
+ layout_desc = layout_desc->SetTaggedForTesting(i, tagged);
+ }
+ }
+
+ if (layout_desc->IsFastPointerLayout()) {
+ return;
+ }
+
+ {
+ // Check queries.
+ int cur_bit_flip_index = 0;
+ bool tagged = true;
+ for (int i = 0; i < layout_descriptor_length; i++) {
+ if (i == bit_flip_positions[cur_bit_flip_index]) {
+ tagged = !tagged;
+ ++cur_bit_flip_index;
+ }
+ CHECK_EQ(tagged, layout_desc->IsTagged(i));
+
+ int next_bit_flip_position = bit_flip_positions[cur_bit_flip_index];
+ int expected_sequence_length;
+ if (next_bit_flip_position < layout_desc->capacity()) {
+ expected_sequence_length = next_bit_flip_position - i;
+ } else {
+ expected_sequence_length = tagged ? std::numeric_limits<int>::max()
+ : (layout_desc->capacity() - i);
+ }
+ expected_sequence_length =
+ Min(expected_sequence_length, max_sequence_length);
+ int sequence_length;
+ CHECK_EQ(tagged,
+ layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
+ CHECK(sequence_length > 0);
+
+ CHECK_EQ(expected_sequence_length, sequence_length);
+ }
+
+ int sequence_length;
+ CHECK_EQ(true,
+ layout_desc->IsTagged(layout_descriptor_length,
+ max_sequence_length, &sequence_length));
+ CHECK_EQ(max_sequence_length, sequence_length);
+ }
+}
+
+
+static void TestLayoutDescriptorQueriesFast(int max_sequence_length) {
+ {
+ LayoutDescriptor* layout_desc = LayoutDescriptor::FastPointerLayout();
+ int sequence_length;
+ for (int i = 0; i < kNumberOfBits; i++) {
+ CHECK_EQ(true,
+ layout_desc->IsTagged(i, max_sequence_length, &sequence_length));
+ CHECK(sequence_length > 0);
+ CHECK_EQ(max_sequence_length, sequence_length);
+ }
+ }
+
+ {
+ int bit_flip_positions[] = {1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kNumberOfBits + 1];
+ for (int i = 0; i <= kNumberOfBits; i++) {
+ bit_flip_positions[i] = i;
+ }
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30, 1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 1, 2, 3, 5, 7, 9,
+ 12, 15, 18, 22, 26, 29, 1000};
+ TestLayoutDescriptorQueries(kSmiValueSize, bit_flip_positions,
+ max_sequence_length);
+ }
+}
+
+
+TEST(LayoutDescriptorQueriesFastLimited7) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesFast(7);
+}
+
+
+TEST(LayoutDescriptorQueriesFastLimited13) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesFast(13);
+}
+
+
+TEST(LayoutDescriptorQueriesFastUnlimited) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesFast(std::numeric_limits<int>::max());
+}
+
+
+static void TestLayoutDescriptorQueriesSlow(int max_sequence_length) {
+ {
+ int bit_flip_positions[] = {10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kMaxNumberOfDescriptors + 1];
+ for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
+ bit_flip_positions[i] = i;
+ }
+ bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {3, 7, 8, 10, 15, 21, 30,
+ 37, 54, 80, 99, 383, 10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[] = {0, 10, 20, 30, 50, 70, 90,
+ 120, 150, 180, 220, 260, 290, 10000};
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kMaxNumberOfDescriptors + 1];
+ int cur = 0;
+ for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
+ bit_flip_positions[i] = cur;
+ cur = (cur + 1) * 2;
+ }
+ CHECK(cur < 10000);
+ bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+
+ {
+ int bit_flip_positions[kMaxNumberOfDescriptors + 1];
+ int cur = 3;
+ for (int i = 0; i < kMaxNumberOfDescriptors; i++) {
+ bit_flip_positions[i] = cur;
+ cur = (cur + 1) * 2;
+ }
+ CHECK(cur < 10000);
+ bit_flip_positions[kMaxNumberOfDescriptors] = 10000;
+ TestLayoutDescriptorQueries(kMaxNumberOfDescriptors, bit_flip_positions,
+ max_sequence_length);
+ }
+}
+
+
+TEST(LayoutDescriptorQueriesSlowLimited7) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(7);
+}
+
+
+TEST(LayoutDescriptorQueriesSlowLimited13) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(13);
+}
+
+
+TEST(LayoutDescriptorQueriesSlowLimited42) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(42);
+}
+
+
+TEST(LayoutDescriptorQueriesSlowUnlimited) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ TestLayoutDescriptorQueriesSlow(std::numeric_limits<int>::max());
+}
+
+
+TEST(LayoutDescriptorCreateNewFast) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ TestPropertyKind props[] = {
+ PROP_CONSTANT,
+ PROP_TAGGED, // field #0
+ PROP_CONSTANT,
+ PROP_DOUBLE, // field #1
+ PROP_CONSTANT,
+ PROP_TAGGED, // field #2
+ PROP_CONSTANT,
+ };
+ const int kPropsCount = arraysize(props);
+
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ {
+ Handle<Map> map = Map::Create(isolate, 0);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ {
+ Handle<Map> map = Map::Create(isolate, 1);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ {
+ Handle<Map> map = Map::Create(isolate, 2);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ CHECK(!layout_descriptor->IsSlowLayout());
+ CHECK_EQ(true, layout_descriptor->IsTagged(0));
+ CHECK_EQ(false, layout_descriptor->IsTagged(1));
+ CHECK_EQ(true, layout_descriptor->IsTagged(2));
+ CHECK_EQ(true, layout_descriptor->IsTagged(125));
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+}
+
+
+TEST(LayoutDescriptorCreateNewSlow) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
+ }
+
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ {
+ Handle<Map> map = Map::Create(isolate, 0);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ {
+ Handle<Map> map = Map::Create(isolate, 1);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_EQ(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ {
+ Handle<Map> map = Map::Create(isolate, 2);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ CHECK(!layout_descriptor->IsSlowLayout());
+ CHECK_EQ(true, layout_descriptor->IsTagged(0));
+ CHECK_EQ(false, layout_descriptor->IsTagged(1));
+ CHECK_EQ(true, layout_descriptor->IsTagged(2));
+ CHECK_EQ(true, layout_descriptor->IsTagged(125));
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ }
+
+ {
+ int inobject_properties = kPropsCount / 2;
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+ layout_descriptor = LayoutDescriptor::New(map, descriptors, kPropsCount);
+ CHECK_NE(LayoutDescriptor::FastPointerLayout(), *layout_descriptor);
+ CHECK(layout_descriptor->IsSlowLayout());
+ for (int i = 0; i < inobject_properties; i++) {
+ // PROP_DOUBLE has index 1 among FIELD properties.
+ const bool tagged = (i % (PROP_KIND_NUMBER - 1)) != 1;
+ CHECK_EQ(tagged, layout_descriptor->IsTagged(i));
+ }
+ // Every property after inobject_properties must be tagged.
+ for (int i = inobject_properties; i < kPropsCount; i++) {
+ CHECK_EQ(true, layout_descriptor->IsTagged(i));
+ }
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+
+ // Now test LayoutDescriptor::cast_gc_safe().
+ Handle<LayoutDescriptor> layout_descriptor_copy =
+ LayoutDescriptor::New(map, descriptors, kPropsCount);
+
+ LayoutDescriptor* layout_desc = *layout_descriptor;
+ CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
+ CHECK_EQ(layout_desc, LayoutDescriptor::cast_gc_safe(layout_desc));
+ CHECK(layout_descriptor->IsFixedTypedArrayBase());
+ // Now make it look like a forwarding pointer to layout_descriptor_copy.
+ MapWord map_word = layout_desc->map_word();
+ CHECK(!map_word.IsForwardingAddress());
+ layout_desc->set_map_word(
+ MapWord::FromForwardingAddress(*layout_descriptor_copy));
+ CHECK(layout_desc->map_word().IsForwardingAddress());
+ CHECK_EQ(*layout_descriptor_copy,
+ LayoutDescriptor::cast_gc_safe(layout_desc));
+
+ // Restore it back.
+ layout_desc->set_map_word(map_word);
+ CHECK_EQ(layout_desc, LayoutDescriptor::cast(layout_desc));
+ }
+}
+
+
+static Handle<LayoutDescriptor> TestLayoutDescriptorAppend(
+ Isolate* isolate, int inobject_properties, TestPropertyKind* props,
+ int kPropsCount) {
+ Factory* factory = isolate->factory();
+
+ Handle<String> func_name = factory->InternalizeUtf8String("func");
+ Handle<JSFunction> func = factory->NewFunction(func_name);
+
+ Handle<DescriptorArray> descriptors =
+ DescriptorArray::Allocate(isolate, 0, kPropsCount);
+
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+ map->InitializeDescriptors(*descriptors,
+ LayoutDescriptor::FastPointerLayout());
+
+ int next_field_offset = 0;
+ for (int i = 0; i < kPropsCount; i++) {
+ EmbeddedVector<char, 64> buffer;
+ SNPrintF(buffer, "prop%d", i);
+ Handle<String> name = factory->InternalizeUtf8String(buffer.start());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ TestPropertyKind kind = props[i];
+ if (kind == PROP_CONSTANT) {
+ ConstantDescriptor d(name, func, NONE);
+ layout_descriptor = LayoutDescriptor::Append(map, d.GetDetails());
+ descriptors->Append(&d);
+
+ } else {
+ FieldDescriptor f(name, next_field_offset, NONE, representations[kind]);
+ int field_width_in_words = f.GetDetails().field_width_in_words();
+ next_field_offset += field_width_in_words;
+ layout_descriptor = LayoutDescriptor::Append(map, f.GetDetails());
+ descriptors->Append(&f);
+
+ int field_index = f.GetDetails().field_index();
+ bool is_inobject = field_index < map->inobject_properties();
+ for (int bit = 0; bit < field_width_in_words; bit++) {
+ CHECK_EQ(is_inobject && (kind == PROP_DOUBLE),
+ !layout_descriptor->IsTagged(field_index + bit));
+ }
+ CHECK(layout_descriptor->IsTagged(next_field_offset));
+ }
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ }
+ Handle<LayoutDescriptor> layout_descriptor(map->layout_descriptor(), isolate);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ return layout_descriptor;
+}
+
+
+TEST(LayoutDescriptorAppend) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
+ }
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2,
+ props, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+}
+
+
+TEST(LayoutDescriptorAppendAllDoubles) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_DOUBLE;
+ }
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, 0, props, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, 13, props, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, kSmiValueSize, props, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize + 1,
+ props, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppend(isolate, kSmiValueSize * 2,
+ props, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, kPropsCount, props, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ {
+ // Ensure layout descriptor switches into slow mode at the right moment.
+ layout_descriptor =
+ TestLayoutDescriptorAppend(isolate, kPropsCount, props, kSmiValueSize);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppend(isolate, kPropsCount, props,
+ kSmiValueSize + 1);
+ CHECK(layout_descriptor->IsSlowLayout());
+ }
+}
+
+
+static Handle<LayoutDescriptor> TestLayoutDescriptorAppendIfFastOrUseFull(
+ Isolate* isolate, int inobject_properties,
+ Handle<DescriptorArray> descriptors, int number_of_descriptors) {
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+ Handle<LayoutDescriptor> full_layout_descriptor = LayoutDescriptor::New(
+ map, descriptors, descriptors->number_of_descriptors());
+
+ int nof = 0;
+ bool switched_to_slow_mode = false;
+
+ for (int i = 0; i < number_of_descriptors; i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+
+ // This method calls LayoutDescriptor::AppendIfFastOrUseFull() internally
+ // and does all the required map-descriptors related book keeping.
+ map = Map::CopyInstallDescriptorsForTesting(map, i, descriptors,
+ full_layout_descriptor);
+
+ LayoutDescriptor* layout_desc = map->layout_descriptor();
+
+ if (layout_desc->IsSlowLayout()) {
+ switched_to_slow_mode = true;
+ CHECK_EQ(*full_layout_descriptor, layout_desc);
+ } else {
+ CHECK(!switched_to_slow_mode);
+ if (details.type() == FIELD) {
+ nof++;
+ int field_index = details.field_index();
+ int field_width_in_words = details.field_width_in_words();
+
+ bool is_inobject = field_index < map->inobject_properties();
+ for (int bit = 0; bit < field_width_in_words; bit++) {
+ CHECK_EQ(is_inobject && details.representation().IsDouble(),
+ !layout_desc->IsTagged(field_index + bit));
+ }
+ CHECK(layout_desc->IsTagged(field_index + field_width_in_words));
+ }
+ }
+ DCHECK(map->layout_descriptor()->IsConsistentWithMap(*map));
+ }
+
+ Handle<LayoutDescriptor> layout_descriptor(map->GetLayoutDescriptor(),
+ isolate);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+ return layout_descriptor;
+}
+
+
+TEST(LayoutDescriptorAppendIfFastOrUseFull) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, 0, descriptors, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, 13, descriptors, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kSmiValueSize, descriptors, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kSmiValueSize * 2, descriptors, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kPropsCount, descriptors, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+}
+
+
+TEST(LayoutDescriptorAppendIfFastOrUseFullAllDoubles) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_DOUBLE;
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, 0, descriptors, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, 13, descriptors, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kSmiValueSize, descriptors, kPropsCount);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kSmiValueSize + 1, descriptors, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kSmiValueSize * 2, descriptors, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kPropsCount, descriptors, kPropsCount);
+ CHECK(layout_descriptor->IsSlowLayout());
+
+ {
+ // Ensure layout descriptor switches into slow mode at the right moment.
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kPropsCount, descriptors, kSmiValueSize);
+ CHECK(!layout_descriptor->IsSlowLayout());
+
+ layout_descriptor = TestLayoutDescriptorAppendIfFastOrUseFull(
+ isolate, kPropsCount, descriptors, kSmiValueSize + 1);
+ CHECK(layout_descriptor->IsSlowLayout());
+ }
+}
+
+
+TEST(Regress436816) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ v8::HandleScope scope(CcTest::isolate());
+
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_DOUBLE;
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ Handle<Map> map = Map::Create(isolate, kPropsCount);
+ Handle<LayoutDescriptor> layout_descriptor =
+ LayoutDescriptor::New(map, descriptors, kPropsCount);
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+
+ Handle<JSObject> object = factory->NewJSObjectFromMap(map, TENURED);
+
+ Address fake_address = reinterpret_cast<Address>(~kHeapObjectTagMask);
+ HeapObject* fake_object = HeapObject::FromAddress(fake_address);
+ CHECK(fake_object->IsHeapObject());
+
+ double boom_value = bit_cast<double>(fake_object);
+ for (int i = 0; i < kPropsCount; i++) {
+ FieldIndex index = FieldIndex::ForDescriptor(*map, i);
+ CHECK(map->IsUnboxedDoubleField(index));
+ object->RawFastDoublePropertyAtPut(index, boom_value);
+ }
+ CHECK(object->HasFastProperties());
+ CHECK(!object->map()->HasFastPointerLayout());
+
+ Handle<Map> normalized_map =
+ Map::Normalize(map, KEEP_INOBJECT_PROPERTIES, "testing");
+ JSObject::MigrateToMap(object, normalized_map);
+ CHECK(!object->HasFastProperties());
+ CHECK(object->map()->HasFastPointerLayout());
+
+ // Trigger GCs and heap verification.
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+}
+
+
+TEST(DoScavenge) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ v8::HandleScope scope(CcTest::isolate());
+
+ CompileRun(
+ "function A() {"
+ " this.x = 42.5;"
+ " this.o = {};"
+ "};"
+ "var o = new A();");
+
+ Handle<String> obj_name = factory->InternalizeUtf8String("o");
+
+ Handle<Object> obj_value =
+ Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked();
+ CHECK(obj_value->IsJSObject());
+ Handle<JSObject> obj = Handle<JSObject>::cast(obj_value);
+
+ {
+ // Ensure the object is properly set up.
+ Map* map = obj->map();
+ DescriptorArray* descriptors = map->instance_descriptors();
+ CHECK(map->NumberOfOwnDescriptors() == 2);
+ CHECK(descriptors->GetDetails(0).representation().IsDouble());
+ CHECK(descriptors->GetDetails(1).representation().IsHeapObject());
+ FieldIndex field_index = FieldIndex::ForDescriptor(map, 0);
+ CHECK(field_index.is_inobject() && field_index.is_double());
+ CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
+ CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
+ }
+ CHECK(isolate->heap()->new_space()->Contains(*obj));
+
+ // Trigger GCs so that the newly allocated object moves to old gen.
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
+
+ // Create temp object in the new space.
+ Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED);
+ CHECK(isolate->heap()->new_space()->Contains(*temp));
+
+ // Construct a double value that looks like a pointer to the new space object
+ // and store it into the obj.
+ Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize;
+ double boom_value = bit_cast<double>(fake_object);
+
+ FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0);
+ Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE);
+ obj->FastPropertyAtPut(field_index, *boom_number);
+
+ // Now the object moves to old gen and it has a double field that looks like
+ // a pointer to a from semi-space.
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
+
+ CHECK(isolate->heap()->old_pointer_space()->Contains(*obj));
+
+ CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index));
+}
+
+
+static void TestLayoutDescriptorHelper(Isolate* isolate,
+ int inobject_properties,
+ Handle<DescriptorArray> descriptors,
+ int number_of_descriptors) {
+ Handle<Map> map = Map::Create(isolate, inobject_properties);
+
+ Handle<LayoutDescriptor> layout_descriptor = LayoutDescriptor::New(
+ map, descriptors, descriptors->number_of_descriptors());
+ map->InitializeDescriptors(*descriptors, *layout_descriptor);
+ DCHECK(layout_descriptor->IsConsistentWithMap(*map));
+
+ LayoutDescriptorHelper helper(*map);
+ bool all_fields_tagged = true;
+
+ int instance_size = map->instance_size();
+
+ int end_offset = instance_size * 2;
+ int first_non_tagged_field_offset = end_offset;
+ for (int i = 0; i < number_of_descriptors; i++) {
+ PropertyDetails details = descriptors->GetDetails(i);
+ if (details.type() != FIELD) continue;
+ FieldIndex index = FieldIndex::ForDescriptor(*map, i);
+ if (!index.is_inobject()) continue;
+ all_fields_tagged &= !details.representation().IsDouble();
+ bool expected_tagged = !index.is_double();
+ if (!expected_tagged) {
+ first_non_tagged_field_offset =
+ Min(first_non_tagged_field_offset, index.offset());
+ }
+
+ int end_of_region_offset;
+ CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
+ CHECK_EQ(expected_tagged, helper.IsTagged(index.offset(), instance_size,
+ &end_of_region_offset));
+ CHECK(end_of_region_offset > 0);
+ CHECK(end_of_region_offset % kPointerSize == 0);
+ CHECK(end_of_region_offset <= instance_size);
+
+ for (int offset = index.offset(); offset < end_of_region_offset;
+ offset += kPointerSize) {
+ CHECK_EQ(expected_tagged, helper.IsTagged(index.offset()));
+ }
+ if (end_of_region_offset < instance_size) {
+ CHECK_EQ(!expected_tagged, helper.IsTagged(end_of_region_offset));
+ } else {
+ CHECK_EQ(true, helper.IsTagged(end_of_region_offset));
+ }
+ }
+
+ for (int offset = 0; offset < JSObject::kHeaderSize; offset += kPointerSize) {
+ // Header queries
+ CHECK_EQ(true, helper.IsTagged(offset));
+ int end_of_region_offset;
+ CHECK_EQ(true, helper.IsTagged(offset, end_offset, &end_of_region_offset));
+ CHECK_EQ(first_non_tagged_field_offset, end_of_region_offset);
+
+ // Out of bounds queries
+ CHECK_EQ(true, helper.IsTagged(offset + instance_size));
+ }
+
+ CHECK_EQ(all_fields_tagged, helper.all_fields_tagged());
+}
+
+
+TEST(LayoutDescriptorHelperMixed) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = static_cast<TestPropertyKind>(i % PROP_KIND_NUMBER);
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
+ kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
+}
+
+
+TEST(LayoutDescriptorHelperAllTagged) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_TAGGED;
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
+ kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
+}
+
+
+TEST(LayoutDescriptorHelperAllDoubles) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ v8::HandleScope scope(CcTest::isolate());
+
+ Handle<LayoutDescriptor> layout_descriptor;
+ const int kPropsCount = kSmiValueSize * 3;
+ TestPropertyKind props[kPropsCount];
+ for (int i = 0; i < kPropsCount; i++) {
+ props[i] = PROP_DOUBLE;
+ }
+ Handle<DescriptorArray> descriptors =
+ CreateDescriptorArray(isolate, props, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 0, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, 13, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize, descriptors, kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kSmiValueSize * 2, descriptors,
+ kPropsCount);
+
+ TestLayoutDescriptorHelper(isolate, kPropsCount, descriptors, kPropsCount);
+}
+
+
+TEST(StoreBufferScanOnScavenge) {
+ CcTest::InitializeVM();
+ Isolate* isolate = CcTest::i_isolate();
+ Factory* factory = isolate->factory();
+ v8::HandleScope scope(CcTest::isolate());
+
+ CompileRun(
+ "function A() {"
+ " this.x = 42.5;"
+ " this.o = {};"
+ "};"
+ "var o = new A();");
+
+ Handle<String> obj_name = factory->InternalizeUtf8String("o");
+
+ Handle<Object> obj_value =
+ Object::GetProperty(isolate->global_object(), obj_name).ToHandleChecked();
+ CHECK(obj_value->IsJSObject());
+ Handle<JSObject> obj = Handle<JSObject>::cast(obj_value);
+
+ {
+ // Ensure the object is properly set up.
+ Map* map = obj->map();
+ DescriptorArray* descriptors = map->instance_descriptors();
+ CHECK(map->NumberOfOwnDescriptors() == 2);
+ CHECK(descriptors->GetDetails(0).representation().IsDouble());
+ CHECK(descriptors->GetDetails(1).representation().IsHeapObject());
+ FieldIndex field_index = FieldIndex::ForDescriptor(map, 0);
+ CHECK(field_index.is_inobject() && field_index.is_double());
+ CHECK_EQ(FLAG_unbox_double_fields, map->IsUnboxedDoubleField(field_index));
+ CHECK_EQ(42.5, GetDoubleFieldValue(*obj, field_index));
+ }
+ CHECK(isolate->heap()->new_space()->Contains(*obj));
+
+ // Trigger GCs so that the newly allocated object moves to old gen.
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now
+ CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now
+
+ CHECK(isolate->heap()->old_pointer_space()->Contains(*obj));
+
+ // Create temp object in the new space.
+ Handle<JSArray> temp = factory->NewJSArray(FAST_ELEMENTS, NOT_TENURED);
+ CHECK(isolate->heap()->new_space()->Contains(*temp));
+
+ // Construct a double value that looks like a pointer to the new space object
+ // and store it into the obj.
+ Address fake_object = reinterpret_cast<Address>(*temp) + kPointerSize;
+ double boom_value = bit_cast<double>(fake_object);
+
+ FieldIndex field_index = FieldIndex::ForDescriptor(obj->map(), 0);
+ Handle<HeapNumber> boom_number = factory->NewHeapNumber(boom_value, MUTABLE);
+ obj->FastPropertyAtPut(field_index, *boom_number);
+
+ // Enforce scan on scavenge for the obj's page.
+ MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
+ chunk->set_scan_on_scavenge(true);
+
+ // Trigger GCs and force evacuation. Should not crash there.
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+
+ CHECK_EQ(boom_value, GetDoubleFieldValue(*obj, field_index));
+}
+
+#endif
diff --git a/test/cctest/test-utils.cc b/test/cctest/test-utils.cc
index 9ea8b2b6..05a12f5c 100644
--- a/test/cctest/test-utils.cc
+++ b/test/cctest/test-utils.cc
@@ -76,6 +76,47 @@ TEST(Utils1) {
}
+TEST(BitSetComputer) {
+ typedef BitSetComputer<bool, 1, kSmiValueSize, uint32_t> BoolComputer;
+ CHECK_EQ(0, BoolComputer::word_count(0));
+ CHECK_EQ(1, BoolComputer::word_count(8));
+ CHECK_EQ(2, BoolComputer::word_count(50));
+ CHECK_EQ(0, BoolComputer::index(0, 8));
+ CHECK_EQ(100, BoolComputer::index(100, 8));
+ CHECK_EQ(1, BoolComputer::index(0, 40));
+ uint32_t data = 0;
+ data = BoolComputer::encode(data, 1, true);
+ data = BoolComputer::encode(data, 4, true);
+ CHECK_EQ(true, BoolComputer::decode(data, 1));
+ CHECK_EQ(true, BoolComputer::decode(data, 4));
+ CHECK_EQ(false, BoolComputer::decode(data, 0));
+ CHECK_EQ(false, BoolComputer::decode(data, 2));
+ CHECK_EQ(false, BoolComputer::decode(data, 3));
+
+ // Lets store 2 bits per item with 3000 items and verify the values are
+ // correct.
+ typedef BitSetComputer<unsigned char, 2, 8, unsigned char> TwoBits;
+ const int words = 750;
+ CHECK_EQ(words, TwoBits::word_count(3000));
+ const int offset = 10;
+ Vector<unsigned char> buffer = Vector<unsigned char>::New(offset + words);
+ memset(buffer.start(), 0, sizeof(unsigned char) * buffer.length());
+ for (int i = 0; i < words; i++) {
+ const int index = TwoBits::index(offset, i);
+ unsigned char data = buffer[index];
+ data = TwoBits::encode(data, i, i % 4);
+ buffer[index] = data;
+ }
+
+ for (int i = 0; i < words; i++) {
+ const int index = TwoBits::index(offset, i);
+ unsigned char data = buffer[index];
+ CHECK_EQ(i % 4, TwoBits::decode(data, i));
+ }
+ buffer.Dispose();
+}
+
+
TEST(SNPrintF) {
// Make sure that strings that are truncated because of too small
// buffers are zero-terminated anyway.
@@ -222,8 +263,6 @@ TEST(SequenceCollectorRegression) {
}
-// TODO(svenpanne) Unconditionally test this when our infrastructure is fixed.
-#if !V8_OS_NACL
TEST(CPlusPlus11Features) {
struct S {
bool x;
@@ -256,4 +295,3 @@ TEST(CPlusPlus11Features) {
j += 11;
}
}
-#endif
diff --git a/test/cctest/test-weakmaps.cc b/test/cctest/test-weakmaps.cc
index bb412a82..6c19cb8e 100644
--- a/test/cctest/test-weakmaps.cc
+++ b/test/cctest/test-weakmaps.cc
@@ -97,19 +97,20 @@ TEST(Weakness) {
}
CHECK(!global_handles->IsWeak(key.location()));
- // Put entry into weak map.
+ // Put two chained entries into weak map.
{
HandleScope scope(isolate);
- PutIntoWeakMap(weakmap,
- Handle<JSObject>(JSObject::cast(*key)),
- Handle<Smi>(Smi::FromInt(23), isolate));
+ Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ Handle<JSObject> object = factory->NewJSObjectFromMap(map);
+ PutIntoWeakMap(weakmap, Handle<JSObject>(JSObject::cast(*key)), object);
+ PutIntoWeakMap(weakmap, object, Handle<Smi>(Smi::FromInt(23), isolate));
}
- CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
+ CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
// Force a full GC.
heap->CollectAllGarbage(false);
CHECK_EQ(0, NumberOfWeakCalls);
- CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
+ CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
CHECK_EQ(
0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
@@ -128,14 +129,14 @@ TEST(Weakness) {
// weak references whereas the second one will also clear weak maps.
heap->CollectAllGarbage(false);
CHECK_EQ(1, NumberOfWeakCalls);
- CHECK_EQ(1, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
+ CHECK_EQ(2, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
CHECK_EQ(
0, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
heap->CollectAllGarbage(false);
CHECK_EQ(1, NumberOfWeakCalls);
CHECK_EQ(0, ObjectHashTable::cast(weakmap->table())->NumberOfElements());
- CHECK_EQ(
- 1, ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
+ CHECK_EQ(2,
+ ObjectHashTable::cast(weakmap->table())->NumberOfDeletedElements());
}
diff --git a/test/cctest/trace-extension.cc b/test/cctest/trace-extension.cc
index 8f390e4b..0a1ff87d 100644
--- a/test/cctest/trace-extension.cc
+++ b/test/cctest/trace-extension.cc
@@ -91,7 +91,8 @@ void TraceExtension::DoTrace(Address fp) {
// sp is only used to define stack high bound
regs.sp =
reinterpret_cast<Address>(trace_env.sample) - 10240;
- trace_env.sample->Init(CcTest::i_isolate(), regs);
+ trace_env.sample->Init(CcTest::i_isolate(), regs,
+ TickSample::kSkipCEntryFrame);
}
diff --git a/test/cctest/types-fuzz.h b/test/cctest/types-fuzz.h
new file mode 100644
index 00000000..4eac64c8
--- /dev/null
+++ b/test/cctest/types-fuzz.h
@@ -0,0 +1,311 @@
+// Copyright 2014 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.
+
+#ifndef V8_TEST_CCTEST_TYPES_H_
+#define V8_TEST_CCTEST_TYPES_H_
+
+#include "src/v8.h"
+
+namespace v8 {
+namespace internal {
+
+
+template<class Type, class TypeHandle, class Region>
+class Types {
+ public:
+ Types(Region* region, Isolate* isolate)
+ : region_(region), rng_(isolate->random_number_generator()) {
+ #define DECLARE_TYPE(name, value) \
+ name = Type::name(region); \
+ types.push_back(name);
+ PROPER_BITSET_TYPE_LIST(DECLARE_TYPE)
+ #undef DECLARE_TYPE
+
+ object_map = isolate->factory()->NewMap(
+ JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ array_map = isolate->factory()->NewMap(
+ JS_ARRAY_TYPE, JSArray::kSize);
+ number_map = isolate->factory()->NewMap(
+ HEAP_NUMBER_TYPE, HeapNumber::kSize);
+ uninitialized_map = isolate->factory()->uninitialized_map();
+ ObjectClass = Type::Class(object_map, region);
+ ArrayClass = Type::Class(array_map, region);
+ NumberClass = Type::Class(number_map, region);
+ UninitializedClass = Type::Class(uninitialized_map, region);
+
+ maps.push_back(object_map);
+ maps.push_back(array_map);
+ maps.push_back(uninitialized_map);
+ for (MapVector::iterator it = maps.begin(); it != maps.end(); ++it) {
+ types.push_back(Type::Class(*it, region));
+ }
+
+ smi = handle(Smi::FromInt(666), isolate);
+ signed32 = isolate->factory()->NewHeapNumber(0x40000000);
+ object1 = isolate->factory()->NewJSObjectFromMap(object_map);
+ object2 = isolate->factory()->NewJSObjectFromMap(object_map);
+ array = isolate->factory()->NewJSArray(20);
+ uninitialized = isolate->factory()->uninitialized_value();
+ SmiConstant = Type::Constant(smi, region);
+ Signed32Constant = Type::Constant(signed32, region);
+ ObjectConstant1 = Type::Constant(object1, region);
+ ObjectConstant2 = Type::Constant(object2, region);
+ ArrayConstant = Type::Constant(array, region);
+ UninitializedConstant = Type::Constant(uninitialized, region);
+
+ values.push_back(smi);
+ values.push_back(signed32);
+ values.push_back(object1);
+ values.push_back(object2);
+ values.push_back(array);
+ values.push_back(uninitialized);
+ for (ValueVector::iterator it = values.begin(); it != values.end(); ++it) {
+ types.push_back(Type::Constant(*it, region));
+ }
+
+ integers.push_back(isolate->factory()->NewNumber(-V8_INFINITY));
+ integers.push_back(isolate->factory()->NewNumber(+V8_INFINITY));
+ integers.push_back(isolate->factory()->NewNumber(-rng_->NextInt(10)));
+ integers.push_back(isolate->factory()->NewNumber(+rng_->NextInt(10)));
+ for (int i = 0; i < 10; ++i) {
+ double x = rng_->NextInt();
+ integers.push_back(isolate->factory()->NewNumber(x));
+ x *= rng_->NextInt();
+ if (!IsMinusZero(x)) integers.push_back(isolate->factory()->NewNumber(x));
+ }
+
+ Integer = Type::Range(isolate->factory()->NewNumber(-V8_INFINITY),
+ isolate->factory()->NewNumber(+V8_INFINITY), region);
+
+ NumberArray = Type::Array(Number, region);
+ StringArray = Type::Array(String, region);
+ AnyArray = Type::Array(Any, region);
+
+ SignedFunction1 = Type::Function(SignedSmall, SignedSmall, region);
+ NumberFunction1 = Type::Function(Number, Number, region);
+ NumberFunction2 = Type::Function(Number, Number, Number, region);
+ MethodFunction = Type::Function(String, Object, 0, region);
+
+ for (int i = 0; i < 30; ++i) {
+ types.push_back(Fuzz());
+ }
+ }
+
+ Handle<i::Map> object_map;
+ Handle<i::Map> array_map;
+ Handle<i::Map> number_map;
+ Handle<i::Map> uninitialized_map;
+
+ Handle<i::Smi> smi;
+ Handle<i::HeapNumber> signed32;
+ Handle<i::JSObject> object1;
+ Handle<i::JSObject> object2;
+ Handle<i::JSArray> array;
+ Handle<i::Oddball> uninitialized;
+
+ #define DECLARE_TYPE(name, value) TypeHandle name;
+ PROPER_BITSET_TYPE_LIST(DECLARE_TYPE)
+ #undef DECLARE_TYPE
+
+ TypeHandle ObjectClass;
+ TypeHandle ArrayClass;
+ TypeHandle NumberClass;
+ TypeHandle UninitializedClass;
+
+ TypeHandle SmiConstant;
+ TypeHandle Signed32Constant;
+ TypeHandle ObjectConstant1;
+ TypeHandle ObjectConstant2;
+ TypeHandle ArrayConstant;
+ TypeHandle UninitializedConstant;
+
+ TypeHandle Integer;
+
+ TypeHandle NumberArray;
+ TypeHandle StringArray;
+ TypeHandle AnyArray;
+
+ TypeHandle SignedFunction1;
+ TypeHandle NumberFunction1;
+ TypeHandle NumberFunction2;
+ TypeHandle MethodFunction;
+
+ typedef std::vector<TypeHandle> TypeVector;
+ typedef std::vector<Handle<i::Map> > MapVector;
+ typedef std::vector<Handle<i::Object> > ValueVector;
+
+ TypeVector types;
+ MapVector maps;
+ ValueVector values;
+ ValueVector integers; // "Integer" values used for range limits.
+
+ TypeHandle Of(Handle<i::Object> value) {
+ return Type::Of(value, region_);
+ }
+
+ TypeHandle NowOf(Handle<i::Object> value) {
+ return Type::NowOf(value, region_);
+ }
+
+ TypeHandle Class(Handle<i::Map> map) {
+ return Type::Class(map, region_);
+ }
+
+ TypeHandle Constant(Handle<i::Object> value) {
+ return Type::Constant(value, region_);
+ }
+
+ TypeHandle Range(Handle<i::Object> min, Handle<i::Object> max) {
+ return Type::Range(min, max, region_);
+ }
+
+ TypeHandle Context(TypeHandle outer) {
+ return Type::Context(outer, region_);
+ }
+
+ TypeHandle Array1(TypeHandle element) {
+ return Type::Array(element, region_);
+ }
+
+ TypeHandle Function0(TypeHandle result, TypeHandle receiver) {
+ return Type::Function(result, receiver, 0, region_);
+ }
+
+ TypeHandle Function1(TypeHandle result, TypeHandle receiver, TypeHandle arg) {
+ TypeHandle type = Type::Function(result, receiver, 1, region_);
+ type->AsFunction()->InitParameter(0, arg);
+ return type;
+ }
+
+ TypeHandle Function2(TypeHandle result, TypeHandle arg1, TypeHandle arg2) {
+ return Type::Function(result, arg1, arg2, region_);
+ }
+
+ TypeHandle Union(TypeHandle t1, TypeHandle t2) {
+ return Type::Union(t1, t2, region_);
+ }
+ TypeHandle Intersect(TypeHandle t1, TypeHandle t2) {
+ return Type::Intersect(t1, t2, region_);
+ }
+
+ template<class Type2, class TypeHandle2>
+ TypeHandle Convert(TypeHandle2 t) {
+ return Type::template Convert<Type2>(t, region_);
+ }
+
+ TypeHandle Random() {
+ return types[rng_->NextInt(static_cast<int>(types.size()))];
+ }
+
+ TypeHandle Fuzz(int depth = 4) {
+ switch (rng_->NextInt(depth == 0 ? 3 : 20)) {
+ case 0: { // bitset
+ #define COUNT_BITSET_TYPES(type, value) + 1
+ int n = 0 PROPER_BITSET_TYPE_LIST(COUNT_BITSET_TYPES);
+ #undef COUNT_BITSET_TYPES
+ // Pick a bunch of named bitsets and return their intersection.
+ TypeHandle result = Type::Any(region_);
+ for (int i = 0, m = 1 + rng_->NextInt(3); i < m; ++i) {
+ int j = rng_->NextInt(n);
+ #define PICK_BITSET_TYPE(type, value) \
+ if (j-- == 0) { \
+ TypeHandle tmp = Type::Intersect( \
+ result, Type::type(region_), region_); \
+ if (tmp->Is(Type::None()) && i != 0) { \
+ break; \
+ } else { \
+ result = tmp; \
+ continue; \
+ } \
+ }
+ PROPER_BITSET_TYPE_LIST(PICK_BITSET_TYPE)
+ #undef PICK_BITSET_TYPE
+ }
+ return result;
+ }
+ case 1: { // class
+ int i = rng_->NextInt(static_cast<int>(maps.size()));
+ return Type::Class(maps[i], region_);
+ }
+ case 2: { // constant
+ int i = rng_->NextInt(static_cast<int>(values.size()));
+ return Type::Constant(values[i], region_);
+ }
+ case 3: { // range
+ int i = rng_->NextInt(static_cast<int>(integers.size()));
+ int j = rng_->NextInt(static_cast<int>(integers.size()));
+ i::Handle<i::Object> min = integers[i];
+ i::Handle<i::Object> max = integers[j];
+ if (min->Number() > max->Number()) std::swap(min, max);
+ return Type::Range(min, max, region_);
+ }
+ case 4: { // context
+ int depth = rng_->NextInt(3);
+ TypeHandle type = Type::Internal(region_);
+ for (int i = 0; i < depth; ++i) type = Type::Context(type, region_);
+ return type;
+ }
+ case 5: { // array
+ TypeHandle element = Fuzz(depth / 2);
+ return Type::Array(element, region_);
+ }
+ case 6:
+ case 7: { // function
+ TypeHandle result = Fuzz(depth / 2);
+ TypeHandle receiver = Fuzz(depth / 2);
+ int arity = rng_->NextInt(3);
+ TypeHandle type = Type::Function(result, receiver, arity, region_);
+ for (int i = 0; i < type->AsFunction()->Arity(); ++i) {
+ TypeHandle parameter = Fuzz(depth / 2);
+ type->AsFunction()->InitParameter(i, parameter);
+ }
+ return type;
+ }
+ default: { // union
+ int n = rng_->NextInt(10);
+ TypeHandle type = None;
+ for (int i = 0; i < n; ++i) {
+ TypeHandle operand = Fuzz(depth - 1);
+ type = Type::Union(type, operand, region_);
+ }
+ return type;
+ }
+ }
+ UNREACHABLE();
+ }
+
+ Region* region() { return region_; }
+
+ private:
+ Region* region_;
+ v8::base::RandomNumberGenerator* rng_;
+};
+
+
+} } // namespace v8::internal
+
+#endif