diff options
author | Emily Bernier <ember@google.com> | 2015-03-24 16:35:39 -0400 |
---|---|---|
committer | Emily Bernier <ember@google.com> | 2015-06-23 16:55:40 -0400 |
commit | 958fae7ec3f466955f8e5b50fa5b8d38b9e91675 (patch) | |
tree | a63ee37f93192ad427f88ed926743f6bb6014312 /test/cctest | |
parent | 57a14c9a8621270b0e6c697dce28a9c453ebe55f (diff) | |
download | android_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')
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(®exp_interruption_data.loop_count, 0); + v8::base::NoBarrier_Load(®exp_interruption_data.loop_count) < 7; + v8::base::NoBarrier_AtomicIncrement( + ®exp_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(®exp_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 |