summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2011-11-30 15:57:28 +0000
committerBen Murdoch <benm@google.com>2011-12-02 17:27:08 +0000
commit257744e915dfc84d6d07a6b2accf8402d9ffc708 (patch)
tree19d8782d5686697f36b1771e7fcd46f290b82c3c /src
parent92022043ea907575278de828a5c9cf6939b51e5e (diff)
downloadandroid_external_v8-257744e915dfc84d6d07a6b2accf8402d9ffc708.tar.gz
android_external_v8-257744e915dfc84d6d07a6b2accf8402d9ffc708.tar.bz2
android_external_v8-257744e915dfc84d6d07a6b2accf8402d9ffc708.zip
Upgrade to V8 3.3
Merge V8 at 3.3.10.39 Simple merge required updates to makefiles only. Bug: 5688872 Change-Id: I14703f418235f5ce6013b9b3e2e502407a9f6dfd
Diffstat (limited to 'src')
-rwxr-xr-xsrc/SConscript21
-rw-r--r--src/accessors.cc101
-rw-r--r--src/accessors.h2
-rw-r--r--src/api.cc318
-rw-r--r--src/api.h29
-rw-r--r--src/arguments.h2
-rw-r--r--src/arm/assembler-arm.cc249
-rw-r--r--src/arm/assembler-arm.h37
-rw-r--r--src/arm/builtins-arm.cc72
-rw-r--r--src/arm/code-stubs-arm.cc1475
-rw-r--r--src/arm/code-stubs-arm.h351
-rw-r--r--src/arm/deoptimizer-arm.cc20
-rw-r--r--src/arm/disasm-arm.cc19
-rw-r--r--src/arm/full-codegen-arm.cc403
-rw-r--r--src/arm/ic-arm.cc384
-rw-r--r--src/arm/lithium-arm.cc318
-rw-r--r--src/arm/lithium-arm.h210
-rw-r--r--src/arm/lithium-codegen-arm.cc567
-rw-r--r--src/arm/lithium-codegen-arm.h27
-rw-r--r--src/arm/macro-assembler-arm.cc327
-rw-r--r--src/arm/macro-assembler-arm.h136
-rw-r--r--src/arm/simulator-arm.cc198
-rw-r--r--src/arm/simulator-arm.h33
-rw-r--r--src/arm/stub-cache-arm.cc686
-rw-r--r--src/array.js137
-rw-r--r--src/assembler.cc311
-rw-r--r--src/assembler.h182
-rw-r--r--src/ast-inl.h1
-rw-r--r--src/ast.cc18
-rw-r--r--src/ast.h15
-rw-r--r--src/atomicops_internals_x86_gcc.cc13
-rw-r--r--src/atomicops_internals_x86_gcc.h6
-rw-r--r--src/bootstrapper.cc504
-rw-r--r--src/bootstrapper.h5
-rw-r--r--src/builtins.cc54
-rw-r--r--src/builtins.h233
-rw-r--r--src/char-predicates.h4
-rw-r--r--src/code-stubs.cc29
-rw-r--r--src/code-stubs.h154
-rw-r--r--src/code.h2
-rw-r--r--src/codegen.cc29
-rwxr-xr-xsrc/compiler.cc124
-rw-r--r--src/compiler.h10
-rw-r--r--src/contexts.cc24
-rw-r--r--src/contexts.h13
-rw-r--r--src/conversions.cc13
-rw-r--r--src/conversions.h4
-rw-r--r--src/cpu-profiler.cc13
-rw-r--r--src/cpu-profiler.h23
-rw-r--r--src/cpu.h2
-rw-r--r--src/d8-readline.cc2
-rw-r--r--src/d8.cc7
-rw-r--r--src/d8.gyp5
-rw-r--r--src/d8.h4
-rw-r--r--src/data-flow.cc473
-rw-r--r--src/data-flow.h178
-rw-r--r--src/date.js4
-rw-r--r--src/dateparser.h1
-rw-r--r--src/debug-debugger.js2
-rw-r--r--src/debug.cc57
-rw-r--r--src/debug.h6
-rw-r--r--src/deoptimizer.h1
-rw-r--r--src/disassembler.cc3
-rw-r--r--src/disassembler.h2
-rw-r--r--src/execution.cc80
-rw-r--r--src/execution.h10
-rw-r--r--src/extensions/experimental/collator.cc218
-rw-r--r--src/extensions/experimental/collator.h69
-rw-r--r--src/extensions/experimental/experimental.gyp49
-rw-r--r--src/extensions/experimental/i18n-extension.cc165
-rw-r--r--src/extensions/experimental/i18n-extension.h12
-rw-r--r--src/extensions/experimental/i18n-locale.cc112
-rw-r--r--src/extensions/experimental/i18n-locale.h (renamed from src/mips/register-allocator-mips.cc)49
-rw-r--r--src/extensions/experimental/i18n-utils.cc (renamed from src/frame-element.cc)14
-rw-r--r--src/extensions/experimental/i18n-utils.h (renamed from src/mips/codegen-mips-inl.h)45
-rw-r--r--src/extensions/experimental/i18n.js177
-rw-r--r--src/extensions/experimental/language-matcher.cc251
-rw-r--r--src/extensions/experimental/language-matcher.h95
-rw-r--r--src/factory.cc50
-rw-r--r--src/factory.h20
-rw-r--r--src/flag-definitions.h13
-rw-r--r--src/frame-element.h269
-rw-r--r--src/frames.cc40
-rw-r--r--src/frames.h1
-rw-r--r--src/full-codegen.cc2
-rw-r--r--src/full-codegen.h32
-rw-r--r--src/func-name-inferrer.cc3
-rw-r--r--src/gdb-jit.h2
-rw-r--r--src/global-handles.cc76
-rw-r--r--src/global-handles.h19
-rw-r--r--src/handles.cc25
-rw-r--r--src/handles.h5
-rw-r--r--src/hashmap.h4
-rw-r--r--src/heap-inl.h5
-rw-r--r--src/heap-profiler.cc8
-rw-r--r--src/heap-profiler.h1
-rw-r--r--src/heap.cc136
-rw-r--r--src/heap.h68
-rw-r--r--src/hydrogen-instructions.cc337
-rw-r--r--src/hydrogen-instructions.h701
-rw-r--r--src/hydrogen.cc1975
-rw-r--r--src/hydrogen.h131
-rw-r--r--src/ia32/assembler-ia32-inl.h22
-rw-r--r--src/ia32/assembler-ia32.cc457
-rw-r--r--src/ia32/assembler-ia32.h50
-rw-r--r--src/ia32/builtins-ia32.cc151
-rw-r--r--src/ia32/code-stubs-ia32.cc1900
-rw-r--r--src/ia32/code-stubs-ia32.h229
-rw-r--r--src/ia32/disasm-ia32.cc10
-rw-r--r--src/ia32/frames-ia32.h8
-rw-r--r--src/ia32/full-codegen-ia32.cc384
-rw-r--r--src/ia32/ic-ia32.cc455
-rw-r--r--src/ia32/lithium-codegen-ia32.cc786
-rw-r--r--src/ia32/lithium-codegen-ia32.h19
-rw-r--r--src/ia32/lithium-gap-resolver-ia32.cc27
-rw-r--r--src/ia32/lithium-ia32.cc366
-rw-r--r--src/ia32/lithium-ia32.h223
-rw-r--r--src/ia32/macro-assembler-ia32.cc273
-rw-r--r--src/ia32/macro-assembler-ia32.h123
-rw-r--r--src/ia32/regexp-macro-assembler-ia32.cc21
-rw-r--r--src/ia32/regexp-macro-assembler-ia32.h2
-rw-r--r--src/ia32/simulator-ia32.h4
-rw-r--r--src/ia32/stub-cache-ia32.cc702
-rw-r--r--src/ic.cc868
-rw-r--r--src/ic.h240
-rw-r--r--src/isolate-inl.h (renamed from src/mips/register-allocator-mips.h)27
-rw-r--r--src/isolate.cc1121
-rw-r--r--src/isolate.h100
-rw-r--r--src/json-parser.cc513
-rw-r--r--src/json-parser.h161
-rw-r--r--src/jsregexp.cc278
-rw-r--r--src/jsregexp.h1
-rw-r--r--src/list-inl.h12
-rw-r--r--src/list.h14
-rw-r--r--src/lithium-allocator.cc30
-rw-r--r--src/lithium-allocator.h4
-rw-r--r--src/lithium.cc21
-rw-r--r--src/lithium.h5
-rw-r--r--src/liveedit.cc54
-rw-r--r--src/liveedit.h1
-rw-r--r--src/liveobjectlist.cc2
-rw-r--r--src/log-utils.cc31
-rw-r--r--src/log-utils.h6
-rw-r--r--src/log.cc418
-rw-r--r--src/log.h124
-rw-r--r--src/macros.py2
-rw-r--r--src/mark-compact.cc97
-rw-r--r--src/messages.cc6
-rw-r--r--src/messages.js74
-rw-r--r--src/mips/assembler-mips-inl.h73
-rw-r--r--src/mips/assembler-mips.cc491
-rw-r--r--src/mips/assembler-mips.h172
-rw-r--r--src/mips/builtins-mips.cc1522
-rw-r--r--src/mips/code-stubs-mips.cc6326
-rw-r--r--src/mips/code-stubs-mips.h523
-rw-r--r--src/mips/codegen-mips.cc1165
-rw-r--r--src/mips/codegen-mips.h543
-rw-r--r--src/mips/constants-mips.cc14
-rw-r--r--src/mips/constants-mips.h48
-rw-r--r--src/mips/cpu-mips.cc20
-rw-r--r--src/mips/debug-mips.cc203
-rw-r--r--src/mips/disasm-mips.cc251
-rw-r--r--src/mips/frames-mips.cc3
-rw-r--r--src/mips/frames-mips.h60
-rw-r--r--src/mips/full-codegen-mips.cc3951
-rw-r--r--src/mips/ic-mips.cc1466
-rw-r--r--src/mips/lithium-codegen-mips.h2
-rw-r--r--src/mips/lithium-mips.h13
-rw-r--r--src/mips/macro-assembler-mips.cc1167
-rw-r--r--src/mips/macro-assembler-mips.h400
-rw-r--r--src/mips/regexp-macro-assembler-mips.cc947
-rw-r--r--src/mips/regexp-macro-assembler-mips.h10
-rw-r--r--src/mips/register-allocator-mips-inl.h134
-rw-r--r--src/mips/simulator-mips.cc525
-rw-r--r--src/mips/simulator-mips.h55
-rw-r--r--src/mips/stub-cache-mips.cc3948
-rw-r--r--src/mips/virtual-frame-mips.cc307
-rw-r--r--src/mips/virtual-frame-mips.h530
-rw-r--r--src/mirror-debugger.js11
-rw-r--r--src/mksnapshot.cc164
-rw-r--r--src/natives.h5
-rw-r--r--src/objects-debug.cc46
-rw-r--r--src/objects-inl.h254
-rw-r--r--src/objects-printer.cc42
-rw-r--r--src/objects-visiting.cc14
-rw-r--r--src/objects-visiting.h9
-rw-r--r--src/objects.cc375
-rw-r--r--src/objects.h321
-rw-r--r--src/parser.cc265
-rw-r--r--src/parser.h61
-rw-r--r--src/platform-freebsd.cc36
-rw-r--r--src/platform-linux.cc75
-rw-r--r--src/platform-nullos.cc5
-rw-r--r--src/platform-win32.cc10
-rw-r--r--src/platform.h4
-rw-r--r--src/preparse-data-format.h62
-rw-r--r--src/preparse-data.cc12
-rw-r--r--src/preparse-data.h36
-rw-r--r--src/preparser-api.cc1
-rw-r--r--src/preparser.cc473
-rw-r--r--src/preparser.h261
-rw-r--r--src/prettyprinter.cc9
-rw-r--r--src/prettyprinter.h1
-rw-r--r--src/profile-generator.cc19
-rw-r--r--src/profile-generator.h1
-rw-r--r--src/property.cc3
-rw-r--r--src/property.h32
-rw-r--r--src/proxy.js (renamed from src/mips/jump-target-mips.cc)95
-rw-r--r--src/regexp.js12
-rw-r--r--src/runtime-profiler.cc5
-rw-r--r--src/runtime.cc387
-rw-r--r--src/runtime.h8
-rw-r--r--src/runtime.js52
-rw-r--r--src/safepoint-table.h1
-rw-r--r--src/scanner-base.cc58
-rw-r--r--src/scanner-base.h43
-rwxr-xr-xsrc/scanner.cc240
-rw-r--r--src/scanner.h50
-rw-r--r--src/scopeinfo.cc25
-rw-r--r--src/scopeinfo.h7
-rw-r--r--src/scopes.cc30
-rw-r--r--src/scopes.h8
-rw-r--r--src/serialize.cc4
-rw-r--r--src/serialize.h58
-rw-r--r--src/snapshot-common.cc5
-rw-r--r--src/snapshot-empty.cc4
-rw-r--r--src/snapshot.h20
-rw-r--r--src/spaces-inl.h3
-rw-r--r--src/spaces.cc23
-rw-r--r--src/spaces.h27
-rw-r--r--src/splay-tree.h2
-rw-r--r--src/string.js83
-rw-r--r--src/stub-cache.cc372
-rw-r--r--src/stub-cache.h187
-rw-r--r--src/top.cc983
-rw-r--r--src/type-info.cc273
-rw-r--r--src/type-info.h72
-rw-r--r--src/unbound-queue.h2
-rw-r--r--src/uri.js10
-rw-r--r--src/utils-inl.h (renamed from src/mips/virtual-frame-mips-inl.h)36
-rw-r--r--src/utils.h22
-rw-r--r--src/v8-counters.h5
-rw-r--r--src/v8.h1
-rw-r--r--src/v8dll-main.cc11
-rw-r--r--src/v8globals.h44
-rw-r--r--src/v8natives.js52
-rw-r--r--src/v8threads.cc127
-rw-r--r--src/version.cc4
-rw-r--r--src/vm-state.h1
-rw-r--r--src/x64/assembler-x64-inl.h10
-rw-r--r--src/x64/assembler-x64.cc176
-rw-r--r--src/x64/assembler-x64.h59
-rw-r--r--src/x64/builtins-x64.cc111
-rw-r--r--src/x64/code-stubs-x64.cc1380
-rw-r--r--src/x64/code-stubs-x64.h226
-rw-r--r--src/x64/disasm-x64.cc48
-rw-r--r--src/x64/full-codegen-x64.cc392
-rw-r--r--src/x64/ic-x64.cc303
-rw-r--r--src/x64/lithium-codegen-x64.cc823
-rw-r--r--src/x64/lithium-codegen-x64.h24
-rw-r--r--src/x64/lithium-gap-resolver-x64.cc8
-rw-r--r--src/x64/lithium-x64.cc333
-rw-r--r--src/x64/lithium-x64.h218
-rw-r--r--src/x64/macro-assembler-x64.cc958
-rw-r--r--src/x64/macro-assembler-x64.h939
-rw-r--r--src/x64/regexp-macro-assembler-x64.cc4
-rw-r--r--src/x64/simulator-x64.h3
-rw-r--r--src/x64/stub-cache-x64.cc574
-rw-r--r--src/zone-inl.h17
-rw-r--r--src/zone.h14
270 files changed, 43040 insertions, 18852 deletions
diff --git a/src/SConscript b/src/SConscript
index 06ee907c..fe21d02f 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -68,7 +68,6 @@ SOURCES = {
execution.cc
factory.cc
flags.cc
- frame-element.cc
frames.cc
full-codegen.cc
func-name-inferrer.cc
@@ -86,6 +85,7 @@ SOURCES = {
inspector.cc
interpreter-irregexp.cc
isolate.cc
+ json-parser.cc
jsregexp.cc
lithium-allocator.cc
lithium.cc
@@ -122,7 +122,6 @@ SOURCES = {
strtod.cc
stub-cache.cc
token.cc
- top.cc
type-info.cc
unicode.cc
utils.cc
@@ -297,6 +296,11 @@ debug-debugger.js
'''.split()
+EXPERIMENTAL_LIBRARY_FILES = '''
+proxy.js
+'''.split()
+
+
def Abort(message):
print message
sys.exit(1)
@@ -321,9 +325,16 @@ def ConfigureObjectFiles():
# compile it.
library_files = [s for s in LIBRARY_FILES]
library_files.append('macros.py')
- libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE')
+ libraries_src = env.JS2C(['libraries.cc'], library_files, TYPE='CORE')
libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.'])
+ # Combine the experimental JavaScript library files into a C++ file
+ # and compile it.
+ experimental_library_files = [ s for s in EXPERIMENTAL_LIBRARY_FILES ]
+ experimental_library_files.append('macros.py')
+ experimental_libraries_src = env.JS2C(['experimental-libraries.cc'], experimental_library_files, TYPE='EXPERIMENTAL')
+ experimental_libraries_obj = context.ConfigureObject(env, experimental_libraries_src, CPPPATH=['.'])
+
source_objs = context.ConfigureObject(env, source_files)
non_snapshot_files = [source_objs]
@@ -340,7 +351,7 @@ def ConfigureObjectFiles():
mksnapshot_env = env.Copy()
mksnapshot_env.Replace(**context.flags['mksnapshot'])
mksnapshot_src = 'mksnapshot.cc'
- mksnapshot = mksnapshot_env.Program('mksnapshot', [mksnapshot_src, libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb')
+ mksnapshot = mksnapshot_env.Program('mksnapshot', [mksnapshot_src, libraries_obj, experimental_libraries_obj, non_snapshot_files, empty_snapshot_obj], PDB='mksnapshot.exe.pdb')
if context.use_snapshot:
if context.build_snapshot:
snapshot_cc = env.Snapshot('snapshot.cc', mksnapshot, LOGFILE=File('snapshot.log').abspath)
@@ -349,7 +360,7 @@ def ConfigureObjectFiles():
snapshot_obj = context.ConfigureObject(env, snapshot_cc, CPPPATH=['.'])
else:
snapshot_obj = empty_snapshot_obj
- library_objs = [non_snapshot_files, libraries_obj, snapshot_obj]
+ library_objs = [non_snapshot_files, libraries_obj, experimental_libraries_obj, snapshot_obj]
return (library_objs, d8_objs, [mksnapshot], preparser_objs)
diff --git a/src/accessors.cc b/src/accessors.cc
index 7fa69824..255e3ddb 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -32,6 +32,7 @@
#include "deoptimizer.h"
#include "execution.h"
#include "factory.h"
+#include "list-inl.h"
#include "safepoint-table.h"
#include "scopeinfo.h"
@@ -679,6 +680,52 @@ static MaybeObject* CheckNonStrictCallerOrThrow(
}
+class FrameFunctionIterator {
+ public:
+ FrameFunctionIterator(Isolate* isolate, const AssertNoAllocation& promise)
+ : frame_iterator_(isolate),
+ functions_(2),
+ index_(0) {
+ GetFunctions();
+ }
+
+ JSFunction* next() {
+ if (functions_.length() == 0) return NULL;
+ JSFunction* next_function = functions_[index_];
+ index_--;
+ if (index_ < 0) {
+ GetFunctions();
+ }
+ return next_function;
+ }
+
+ // Iterate through functions until the first occurence of 'function'.
+ // Returns true if 'function' is found, and false if the iterator ends
+ // without finding it.
+ bool Find(JSFunction* function) {
+ JSFunction* next_function;
+ do {
+ next_function = next();
+ if (next_function == function) return true;
+ } while (next_function != NULL);
+ return false;
+ }
+ private:
+ void GetFunctions() {
+ functions_.Rewind(0);
+ if (frame_iterator_.done()) return;
+ JavaScriptFrame* frame = frame_iterator_.frame();
+ frame->GetFunctions(&functions_);
+ ASSERT(functions_.length() > 0);
+ frame_iterator_.Advance();
+ index_ = functions_.length() - 1;
+ }
+ JavaScriptFrameIterator frame_iterator_;
+ List<JSFunction*> functions_;
+ int index_;
+};
+
+
MaybeObject* Accessors::FunctionGetCaller(Object* object, void*) {
Isolate* isolate = Isolate::Current();
HandleScope scope(isolate);
@@ -688,38 +735,30 @@ MaybeObject* Accessors::FunctionGetCaller(Object* object, void*) {
if (!found_it) return isolate->heap()->undefined_value();
Handle<JSFunction> function(holder, isolate);
- List<JSFunction*> functions(2);
- for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
- JavaScriptFrame* frame = it.frame();
- frame->GetFunctions(&functions);
- for (int i = functions.length() - 1; i >= 0; i--) {
- if (functions[i] == *function) {
- // Once we have found the frame, we need to go to the caller
- // frame. This may require skipping through a number of top-level
- // frames, e.g. frames for scripts not functions.
- if (i > 0) {
- ASSERT(!functions[i - 1]->shared()->is_toplevel());
- return CheckNonStrictCallerOrThrow(isolate, functions[i - 1]);
- } else {
- for (it.Advance(); !it.done(); it.Advance()) {
- frame = it.frame();
- functions.Rewind(0);
- frame->GetFunctions(&functions);
- if (!functions.last()->shared()->is_toplevel()) {
- return CheckNonStrictCallerOrThrow(isolate, functions.last());
- }
- ASSERT(functions.length() == 1);
- }
- if (it.done()) return isolate->heap()->null_value();
- break;
- }
- }
- }
- functions.Rewind(0);
+ FrameFunctionIterator it(isolate, no_alloc);
+
+ // Find the function from the frames.
+ if (!it.Find(*function)) {
+ // No frame corresponding to the given function found. Return null.
+ return isolate->heap()->null_value();
}
- // No frame corresponding to the given function found. Return null.
- return isolate->heap()->null_value();
+ // Find previously called non-toplevel function.
+ JSFunction* caller;
+ do {
+ caller = it.next();
+ if (caller == NULL) return isolate->heap()->null_value();
+ } while (caller->shared()->is_toplevel());
+
+ // If caller is a built-in function and caller's caller is also built-in,
+ // use that instead.
+ JSFunction* potential_caller = caller;
+ while (potential_caller != NULL && potential_caller->IsBuiltin()) {
+ caller = potential_caller;
+ potential_caller = it.next();
+ }
+
+ return CheckNonStrictCallerOrThrow(isolate, caller);
}
diff --git a/src/accessors.h b/src/accessors.h
index 14ccc8fb..385536d2 100644
--- a/src/accessors.h
+++ b/src/accessors.h
@@ -28,6 +28,8 @@
#ifndef V8_ACCESSORS_H_
#define V8_ACCESSORS_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/api.cc b/src/api.cc
index 3ae6304d..59808543 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -53,7 +53,6 @@
#define LOG_API(isolate, expr) LOG(isolate, ApiEntryCall(expr))
-// TODO(isolates): avoid repeated TLS reads in function prologues.
#ifdef ENABLE_VMSTATE_TRACKING
#define ENTER_V8(isolate) \
ASSERT((isolate)->IsInitialized()); \
@@ -89,7 +88,7 @@ namespace v8 {
if (has_pending_exception) { \
if (handle_scope_implementer->CallDepthIsZero() && \
(isolate)->is_out_of_memory()) { \
- if (!(isolate)->ignore_out_of_memory()) \
+ if (!handle_scope_implementer->ignore_out_of_memory()) \
i::V8::FatalProcessOutOfMemory(NULL); \
} \
bool call_depth_is_zero = handle_scope_implementer->CallDepthIsZero(); \
@@ -290,6 +289,7 @@ static inline bool EnsureInitializedForIsolate(i::Isolate* isolate,
if (isolate != NULL) {
if (isolate->IsInitialized()) return true;
}
+ ASSERT(isolate == i::Isolate::Current());
return ApiCheck(InitializeHelper(), location, "Error initializing V8");
}
@@ -311,12 +311,74 @@ static inline i::Isolate* EnterIsolateIfNeeded() {
}
+StartupData::CompressionAlgorithm V8::GetCompressedStartupDataAlgorithm() {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ return StartupData::kBZip2;
+#else
+ return StartupData::kUncompressed;
+#endif
+}
+
+
+enum CompressedStartupDataItems {
+ kSnapshot = 0,
+ kSnapshotContext,
+ kCompressedStartupDataCount
+};
+
+int V8::GetCompressedStartupDataCount() {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ return kCompressedStartupDataCount;
+#else
+ return 0;
+#endif
+}
+
+
+void V8::GetCompressedStartupData(StartupData* compressed_data) {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ compressed_data[kSnapshot].data =
+ reinterpret_cast<const char*>(i::Snapshot::data());
+ compressed_data[kSnapshot].compressed_size = i::Snapshot::size();
+ compressed_data[kSnapshot].raw_size = i::Snapshot::raw_size();
+
+ compressed_data[kSnapshotContext].data =
+ reinterpret_cast<const char*>(i::Snapshot::context_data());
+ compressed_data[kSnapshotContext].compressed_size =
+ i::Snapshot::context_size();
+ compressed_data[kSnapshotContext].raw_size = i::Snapshot::context_raw_size();
+#endif
+}
+
+
+void V8::SetDecompressedStartupData(StartupData* decompressed_data) {
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ ASSERT_EQ(i::Snapshot::raw_size(), decompressed_data[kSnapshot].raw_size);
+ i::Snapshot::set_raw_data(
+ reinterpret_cast<const i::byte*>(decompressed_data[kSnapshot].data));
+
+ ASSERT_EQ(i::Snapshot::context_raw_size(),
+ decompressed_data[kSnapshotContext].raw_size);
+ i::Snapshot::set_context_raw_data(
+ reinterpret_cast<const i::byte*>(
+ decompressed_data[kSnapshotContext].data));
+#endif
+}
+
+
void V8::SetFatalErrorHandler(FatalErrorCallback that) {
i::Isolate* isolate = EnterIsolateIfNeeded();
isolate->set_exception_behavior(that);
}
+void V8::SetAllowCodeGenerationFromStringsCallback(
+ AllowCodeGenerationFromStringsCallback callback) {
+ i::Isolate* isolate = EnterIsolateIfNeeded();
+ isolate->set_allow_code_gen_callback(callback);
+}
+
+
#ifdef DEBUG
void ImplementationUtilities::ZapHandleRange(i::Object** begin,
i::Object** end) {
@@ -477,6 +539,13 @@ void V8::ClearWeak(i::Object** obj) {
}
+void V8::MarkIndependent(i::Object** object) {
+ i::Isolate* isolate = i::Isolate::Current();
+ LOG_API(isolate, "MakeIndependent");
+ isolate->global_handles()->MarkIndependent(object);
+}
+
+
bool V8::IsGlobalNearDeath(i::Object** obj) {
i::Isolate* isolate = i::Isolate::Current();
LOG_API(isolate, "IsGlobalNearDeath");
@@ -867,9 +936,9 @@ int TypeSwitch::match(v8::Handle<Value> value) {
}
-#define SET_FIELD_WRAPPED(obj, setter, cdata) do { \
- i::Handle<i::Object> proxy = FromCData(cdata); \
- (obj)->setter(*proxy); \
+#define SET_FIELD_WRAPPED(obj, setter, cdata) do { \
+ i::Handle<i::Object> foreign = FromCData(cdata); \
+ (obj)->setter(*foreign); \
} while (false)
@@ -1989,7 +2058,7 @@ bool Value::IsExternal() const {
if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsExternal()")) {
return false;
}
- return Utils::OpenHandle(this)->IsProxy();
+ return Utils::OpenHandle(this)->IsForeign();
}
@@ -2150,7 +2219,7 @@ Local<Integer> Value::ToInteger() const {
void External::CheckCast(v8::Value* that) {
if (IsDeadCheck(i::Isolate::Current(), "v8::External::Cast()")) return;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
- ApiCheck(obj->IsProxy(),
+ ApiCheck(obj->IsForeign(),
"v8::External::Cast()",
"Could not convert to external");
}
@@ -2751,6 +2820,15 @@ bool Object::SetAccessor(Handle<String> name,
}
+bool v8::Object::HasOwnProperty(Handle<String> key) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::HasOwnProperty()",
+ return false);
+ return Utils::OpenHandle(this)->HasLocalProperty(
+ *Utils::OpenHandle(*key));
+}
+
+
bool v8::Object::HasRealNamedProperty(Handle<String> key) {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ON_BAILOUT(isolate, "v8::Object::HasRealNamedProperty()",
@@ -3161,6 +3239,8 @@ ExternalArrayType v8::Object::GetIndexedPropertiesExternalArrayDataType() {
return kExternalUnsignedIntArray;
case i::EXTERNAL_FLOAT_ARRAY_TYPE:
return kExternalFloatArray;
+ case i::EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return kExternalDoubleArray;
case i::EXTERNAL_PIXEL_ARRAY_TYPE:
return kExternalPixelArray;
default:
@@ -3182,6 +3262,85 @@ int v8::Object::GetIndexedPropertiesExternalArrayDataLength() {
}
+bool v8::Object::IsCallable() {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::IsCallable()", return false);
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ if (obj->IsJSFunction()) return true;
+ return i::Execution::GetFunctionDelegate(obj)->IsJSFunction();
+}
+
+
+Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc,
+ v8::Handle<v8::Value> argv[]) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::CallAsFunction()",
+ return Local<v8::Value>());
+ LOG_API(isolate, "Object::CallAsFunction");
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv);
+ STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
+ i::Object*** args = reinterpret_cast<i::Object***>(argv);
+ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>();
+ if (obj->IsJSFunction()) {
+ fun = i::Handle<i::JSFunction>::cast(obj);
+ } else {
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> delegate =
+ i::Execution::TryGetFunctionDelegate(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+ fun = i::Handle<i::JSFunction>::cast(delegate);
+ recv_obj = obj;
+ }
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> returned =
+ i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>());
+ return Utils::ToLocal(scope.CloseAndEscape(returned));
+}
+
+
+Local<v8::Value> Object::CallAsConstructor(int argc,
+ v8::Handle<v8::Value> argv[]) {
+ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+ ON_BAILOUT(isolate, "v8::Object::CallAsConstructor()",
+ return Local<v8::Object>());
+ LOG_API(isolate, "Object::CallAsConstructor");
+ ENTER_V8(isolate);
+ i::HandleScope scope(isolate);
+ i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
+ STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
+ i::Object*** args = reinterpret_cast<i::Object***>(argv);
+ if (obj->IsJSFunction()) {
+ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(obj);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> returned =
+ i::Execution::New(fun, argc, args, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+ return Utils::ToLocal(scope.CloseAndEscape(
+ i::Handle<i::JSObject>::cast(returned)));
+ }
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> delegate =
+ i::Execution::TryGetConstructorDelegate(obj, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+ if (!delegate->IsUndefined()) {
+ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(delegate);
+ EXCEPTION_PREAMBLE(isolate);
+ i::Handle<i::Object> returned =
+ i::Execution::Call(fun, obj, argc, args, &has_pending_exception);
+ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>());
+ ASSERT(!delegate->IsUndefined());
+ return Utils::ToLocal(scope.CloseAndEscape(returned));
+ }
+ return Local<v8::Object>();
+}
+
+
Local<v8::Object> Function::NewInstance() const {
return NewInstance(0, NULL);
}
@@ -3570,11 +3729,11 @@ void v8::Object::SetPointerInInternalField(int index, void* value) {
Utils::OpenHandle(this)->SetInternalField(index, EncodeAsSmi(value));
} else {
HandleScope scope;
- i::Handle<i::Proxy> proxy =
- isolate->factory()->NewProxy(
+ i::Handle<i::Foreign> foreign =
+ isolate->factory()->NewForeign(
reinterpret_cast<i::Address>(value), i::TENURED);
- if (!proxy.is_null())
- Utils::OpenHandle(this)->SetInternalField(index, *proxy);
+ if (!foreign.is_null())
+ Utils::OpenHandle(this)->SetInternalField(index, *foreign);
}
ASSERT_EQ(value, GetPointerFromInternalField(index));
}
@@ -3707,6 +3866,7 @@ Persistent<Context> v8::Context::New(
// Create the environment.
env = isolate->bootstrapper()->CreateEnvironment(
+ isolate,
Utils::OpenHandle(*global_object),
proxy_template,
extensions);
@@ -3779,7 +3939,7 @@ bool Context::InContext() {
v8::Local<v8::Context> Context::GetEntered() {
i::Isolate* isolate = i::Isolate::Current();
- if (!EnsureInitializedForIsolate(isolate, "v8::Context::GetEntered()")) {
+ if (IsDeadCheck(isolate, "v8::Context::GetEntered()")) {
return Local<Context>();
}
i::Handle<i::Object> last =
@@ -3851,6 +4011,20 @@ void Context::ReattachGlobal(Handle<Object> global_object) {
}
+void Context::AllowCodeGenerationFromStrings(bool allow) {
+ i::Isolate* isolate = i::Isolate::Current();
+ if (IsDeadCheck(isolate, "v8::Context::AllowCodeGenerationFromStrings()")) {
+ return;
+ }
+ ENTER_V8(isolate);
+ i::Object** ctx = reinterpret_cast<i::Object**>(this);
+ i::Handle<i::Context> context =
+ i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
+ context->set_allow_code_gen_from_strings(
+ allow ? isolate->heap()->true_value() : isolate->heap()->false_value());
+}
+
+
void V8::SetWrapperClassId(i::Object** global_handle, uint16_t class_id) {
i::GlobalHandles::SetWrapperClassId(global_handle, class_id);
}
@@ -3895,19 +4069,19 @@ bool FunctionTemplate::HasInstance(v8::Handle<v8::Value> value) {
static Local<External> ExternalNewImpl(void* data) {
- return Utils::ToLocal(FACTORY->NewProxy(static_cast<i::Address>(data)));
+ return Utils::ToLocal(FACTORY->NewForeign(static_cast<i::Address>(data)));
}
static void* ExternalValueImpl(i::Handle<i::Object> obj) {
- return reinterpret_cast<void*>(i::Proxy::cast(*obj)->proxy());
+ return reinterpret_cast<void*>(i::Foreign::cast(*obj)->address());
}
Local<Value> v8::External::Wrap(void* data) {
i::Isolate* isolate = i::Isolate::Current();
STATIC_ASSERT(sizeof(data) == sizeof(i::Address));
- EnsureInitializedForIsolate(isolate, "v8::External::Wrap()");
LOG_API(isolate, "External::Wrap");
+ EnsureInitializedForIsolate(isolate, "v8::External::Wrap()");
ENTER_V8(isolate);
v8::Local<v8::Value> result = CanBeEncodedAsSmi(data)
@@ -3924,8 +4098,8 @@ void* v8::Object::SlowGetPointerFromInternalField(int index) {
i::Object* value = obj->GetInternalField(index);
if (value->IsSmi()) {
return i::Internals::GetExternalPointerFromSmi(value);
- } else if (value->IsProxy()) {
- return reinterpret_cast<void*>(i::Proxy::cast(value)->proxy());
+ } else if (value->IsForeign()) {
+ return reinterpret_cast<void*>(i::Foreign::cast(value)->address());
} else {
return NULL;
}
@@ -3938,7 +4112,7 @@ void* v8::External::FullUnwrap(v8::Handle<v8::Value> wrapper) {
void* result;
if (obj->IsSmi()) {
result = i::Internals::GetExternalPointerFromSmi(*obj);
- } else if (obj->IsProxy()) {
+ } else if (obj->IsForeign()) {
result = ExternalValueImpl(obj);
} else {
result = NULL;
@@ -3951,8 +4125,8 @@ void* v8::External::FullUnwrap(v8::Handle<v8::Value> wrapper) {
Local<External> v8::External::New(void* data) {
STATIC_ASSERT(sizeof(data) == sizeof(i::Address));
i::Isolate* isolate = i::Isolate::Current();
- EnsureInitializedForIsolate(isolate, "v8::External::New()");
LOG_API(isolate, "External::New");
+ EnsureInitializedForIsolate(isolate, "v8::External::New()");
ENTER_V8(isolate);
return ExternalNewImpl(data);
}
@@ -4371,7 +4545,8 @@ Local<Integer> Integer::NewFromUnsigned(uint32_t value) {
void V8::IgnoreOutOfMemoryException() {
- EnterIsolateIfNeeded()->set_ignore_out_of_memory(true);
+ EnterIsolateIfNeeded()->handle_scope_implementer()->set_ignore_out_of_memory(
+ true);
}
@@ -4383,7 +4558,7 @@ bool V8::AddMessageListener(MessageCallback that, Handle<Value> data) {
i::HandleScope scope(isolate);
NeanderArray listeners(isolate->factory()->message_listeners());
NeanderObject obj(2);
- obj.set(0, *isolate->factory()->NewProxy(FUNCTION_ADDR(that)));
+ obj.set(0, *isolate->factory()->NewForeign(FUNCTION_ADDR(that)));
obj.set(1, data.IsEmpty() ?
isolate->heap()->undefined_value() :
*Utils::OpenHandle(*data));
@@ -4403,8 +4578,8 @@ void V8::RemoveMessageListeners(MessageCallback that) {
if (listeners.get(i)->IsUndefined()) continue; // skip deleted ones
NeanderObject listener(i::JSObject::cast(listeners.get(i)));
- i::Handle<i::Proxy> callback_obj(i::Proxy::cast(listener.get(0)));
- if (callback_obj->proxy() == FUNCTION_ADDR(that)) {
+ i::Handle<i::Foreign> callback_obj(i::Foreign::cast(listener.get(0)));
+ if (callback_obj->address() == FUNCTION_ADDR(that)) {
listeners.set(i, isolate->heap()->undefined_value());
}
}
@@ -4695,26 +4870,30 @@ void Isolate::Exit() {
}
-String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) {
+void Isolate::SetData(void* data) {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ isolate->SetData(data);
+}
+
+void* Isolate::GetData() {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+ return isolate->GetData();
+}
+
+
+String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj)
+ : str_(NULL), length_(0) {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::String::Utf8Value::Utf8Value()")) return;
- if (obj.IsEmpty()) {
- str_ = NULL;
- length_ = 0;
- return;
- }
+ if (obj.IsEmpty()) return;
ENTER_V8(isolate);
i::HandleScope scope(isolate);
TryCatch try_catch;
Handle<String> str = obj->ToString();
- if (str.IsEmpty()) {
- str_ = NULL;
- length_ = 0;
- } else {
- length_ = str->Utf8Length();
- str_ = i::NewArray<char>(length_ + 1);
- str->WriteUtf8(str_);
- }
+ if (str.IsEmpty()) return;
+ length_ = str->Utf8Length();
+ str_ = i::NewArray<char>(length_ + 1);
+ str->WriteUtf8(str_);
}
@@ -4723,26 +4902,19 @@ String::Utf8Value::~Utf8Value() {
}
-String::AsciiValue::AsciiValue(v8::Handle<v8::Value> obj) {
+String::AsciiValue::AsciiValue(v8::Handle<v8::Value> obj)
+ : str_(NULL), length_(0) {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::String::AsciiValue::AsciiValue()")) return;
- if (obj.IsEmpty()) {
- str_ = NULL;
- length_ = 0;
- return;
- }
+ if (obj.IsEmpty()) return;
ENTER_V8(isolate);
i::HandleScope scope(isolate);
TryCatch try_catch;
Handle<String> str = obj->ToString();
- if (str.IsEmpty()) {
- str_ = NULL;
- length_ = 0;
- } else {
- length_ = str->Length();
- str_ = i::NewArray<char>(length_ + 1);
- str->WriteAscii(str_);
- }
+ if (str.IsEmpty()) return;
+ length_ = str->Length();
+ str_ = i::NewArray<char>(length_ + 1);
+ str->WriteAscii(str_);
}
@@ -4751,26 +4923,19 @@ String::AsciiValue::~AsciiValue() {
}
-String::Value::Value(v8::Handle<v8::Value> obj) {
+String::Value::Value(v8::Handle<v8::Value> obj)
+ : str_(NULL), length_(0) {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::String::Value::Value()")) return;
- if (obj.IsEmpty()) {
- str_ = NULL;
- length_ = 0;
- return;
- }
+ if (obj.IsEmpty()) return;
ENTER_V8(isolate);
i::HandleScope scope(isolate);
TryCatch try_catch;
Handle<String> str = obj->ToString();
- if (str.IsEmpty()) {
- str_ = NULL;
- length_ = 0;
- } else {
- length_ = str->Length();
- str_ = i::NewArray<uint16_t>(length_ + 1);
- str->Write(str_);
- }
+ if (str.IsEmpty()) return;
+ length_ = str->Length();
+ str_ = i::NewArray<uint16_t>(length_ + 1);
+ str->Write(str_);
}
@@ -4884,11 +5049,12 @@ bool Debug::SetDebugEventListener(EventCallback that, Handle<Value> data) {
isolate->set_debug_event_callback(that);
i::HandleScope scope(isolate);
- i::Handle<i::Object> proxy = isolate->factory()->undefined_value();
+ i::Handle<i::Object> foreign = isolate->factory()->undefined_value();
if (that != NULL) {
- proxy = isolate->factory()->NewProxy(FUNCTION_ADDR(EventCallbackWrapper));
+ foreign =
+ isolate->factory()->NewForeign(FUNCTION_ADDR(EventCallbackWrapper));
}
- isolate->debugger()->SetEventListener(proxy, Utils::OpenHandle(*data));
+ isolate->debugger()->SetEventListener(foreign, Utils::OpenHandle(*data));
return true;
}
@@ -4899,12 +5065,11 @@ bool Debug::SetDebugEventListener2(EventCallback2 that, Handle<Value> data) {
ON_BAILOUT(isolate, "v8::Debug::SetDebugEventListener2()", return false);
ENTER_V8(isolate);
i::HandleScope scope(isolate);
- i::Handle<i::Object> proxy = isolate->factory()->undefined_value();
+ i::Handle<i::Object> foreign = isolate->factory()->undefined_value();
if (that != NULL) {
- proxy = isolate->factory()->NewProxy(FUNCTION_ADDR(that));
+ foreign = isolate->factory()->NewForeign(FUNCTION_ADDR(that));
}
- isolate->debugger()->SetEventListener(proxy,
- Utils::OpenHandle(*data));
+ isolate->debugger()->SetEventListener(foreign, Utils::OpenHandle(*data));
return true;
}
@@ -5619,9 +5784,8 @@ void HandleScopeImplementer::FreeThreadResources() {
char* HandleScopeImplementer::ArchiveThread(char* storage) {
- Isolate* isolate = Isolate::Current();
v8::ImplementationUtilities::HandleScopeData* current =
- isolate->handle_scope_data();
+ isolate_->handle_scope_data();
handle_scope_data_ = *current;
memcpy(storage, this, sizeof(*this));
@@ -5639,7 +5803,7 @@ int HandleScopeImplementer::ArchiveSpacePerThread() {
char* HandleScopeImplementer::RestoreThread(char* storage) {
memcpy(this, storage, sizeof(*this));
- *Isolate::Current()->handle_scope_data() = handle_scope_data_;
+ *isolate_->handle_scope_data() = handle_scope_data_;
return storage + ArchiveSpacePerThread();
}
@@ -5665,7 +5829,7 @@ void HandleScopeImplementer::IterateThis(ObjectVisitor* v) {
void HandleScopeImplementer::Iterate(ObjectVisitor* v) {
v8::ImplementationUtilities::HandleScopeData* current =
- Isolate::Current()->handle_scope_data();
+ isolate_->handle_scope_data();
handle_scope_data_ = *current;
IterateThis(v);
}
diff --git a/src/api.h b/src/api.h
index d38a1d5d..5846414d 100644
--- a/src/api.h
+++ b/src/api.h
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -115,14 +115,14 @@ void NeanderObject::set(int offset, v8::internal::Object* value) {
template <typename T> static inline T ToCData(v8::internal::Object* obj) {
STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
return reinterpret_cast<T>(
- reinterpret_cast<intptr_t>(v8::internal::Proxy::cast(obj)->proxy()));
+ reinterpret_cast<intptr_t>(v8::internal::Foreign::cast(obj)->address()));
}
template <typename T>
static inline v8::internal::Handle<v8::internal::Object> FromCData(T obj) {
STATIC_ASSERT(sizeof(T) == sizeof(v8::internal::Address));
- return FACTORY->NewProxy(
+ return FACTORY->NewForeign(
reinterpret_cast<v8::internal::Address>(reinterpret_cast<intptr_t>(obj)));
}
@@ -182,7 +182,7 @@ class Utils {
static inline Local<Array> ToLocal(
v8::internal::Handle<v8::internal::JSArray> obj);
static inline Local<External> ToLocal(
- v8::internal::Handle<v8::internal::Proxy> obj);
+ v8::internal::Handle<v8::internal::Foreign> obj);
static inline Local<Message> MessageToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<StackTrace> StackTraceToLocal(
@@ -236,7 +236,7 @@ class Utils {
OpenHandle(const v8::Signature* sig);
static inline v8::internal::Handle<v8::internal::TypeSwitchInfo>
OpenHandle(const v8::TypeSwitch* that);
- static inline v8::internal::Handle<v8::internal::Proxy>
+ static inline v8::internal::Handle<v8::internal::Foreign>
OpenHandle(const v8::External* that);
};
@@ -273,7 +273,7 @@ MAKE_TO_LOCAL(ToLocal, String, String)
MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp)
MAKE_TO_LOCAL(ToLocal, JSObject, Object)
MAKE_TO_LOCAL(ToLocal, JSArray, Array)
-MAKE_TO_LOCAL(ToLocal, Proxy, External)
+MAKE_TO_LOCAL(ToLocal, Foreign, External)
MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate)
MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate)
MAKE_TO_LOCAL(ToLocal, SignatureInfo, Signature)
@@ -311,7 +311,7 @@ MAKE_OPEN_HANDLE(Script, Object)
MAKE_OPEN_HANDLE(Function, JSFunction)
MAKE_OPEN_HANDLE(Message, JSObject)
MAKE_OPEN_HANDLE(Context, Context)
-MAKE_OPEN_HANDLE(External, Proxy)
+MAKE_OPEN_HANDLE(External, Foreign)
MAKE_OPEN_HANDLE(StackTrace, JSArray)
MAKE_OPEN_HANDLE(StackFrame, JSObject)
@@ -396,14 +396,16 @@ class StringTracker {
// data. In multithreaded V8 programs this data is copied in and out of storage
// so that the currently executing thread always has its own copy of this
// data.
-ISOLATED_CLASS HandleScopeImplementer {
+class HandleScopeImplementer {
public:
- HandleScopeImplementer()
- : blocks_(0),
+ explicit HandleScopeImplementer(Isolate* isolate)
+ : isolate_(isolate),
+ blocks_(0),
entered_contexts_(0),
saved_contexts_(0),
spare_(NULL),
+ ignore_out_of_memory_(false),
call_depth_(0) { }
// Threading support for handle data.
@@ -436,6 +438,10 @@ ISOLATED_CLASS HandleScopeImplementer {
inline bool HasSavedContexts();
inline List<internal::Object**>* blocks() { return &blocks_; }
+ inline bool ignore_out_of_memory() { return ignore_out_of_memory_; }
+ inline void set_ignore_out_of_memory(bool value) {
+ ignore_out_of_memory_ = value;
+ }
private:
void ResetAfterArchive() {
@@ -443,6 +449,7 @@ ISOLATED_CLASS HandleScopeImplementer {
entered_contexts_.Initialize(0);
saved_contexts_.Initialize(0);
spare_ = NULL;
+ ignore_out_of_memory_ = false;
call_depth_ = 0;
}
@@ -460,12 +467,14 @@ ISOLATED_CLASS HandleScopeImplementer {
ASSERT(call_depth_ == 0);
}
+ Isolate* isolate_;
List<internal::Object**> blocks_;
// Used as a stack to keep track of entered contexts.
List<Handle<Object> > entered_contexts_;
// Used as a stack to keep track of saved contexts.
List<Context*> saved_contexts_;
Object** spare_;
+ bool ignore_out_of_memory_;
int call_depth_;
// This is only used for threading support.
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
diff --git a/src/arguments.h b/src/arguments.h
index a7a30e29..a0805812 100644
--- a/src/arguments.h
+++ b/src/arguments.h
@@ -28,6 +28,8 @@
#ifndef V8_ARGUMENTS_H_
#define V8_ARGUMENTS_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index fd8e8b5d..c7050a78 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -51,24 +51,30 @@ unsigned CpuFeatures::supported_ = 0;
unsigned CpuFeatures::found_by_runtime_probing_ = 0;
-#ifdef __arm__
+// Get the CPU features enabled by the build. For cross compilation the
+// preprocessor symbols CAN_USE_ARMV7_INSTRUCTIONS and CAN_USE_VFP_INSTRUCTIONS
+// can be defined to enable ARMv7 and VFPv3 instructions when building the
+// snapshot.
static uint64_t CpuFeaturesImpliedByCompiler() {
uint64_t answer = 0;
#ifdef CAN_USE_ARMV7_INSTRUCTIONS
answer |= 1u << ARMv7;
#endif // def CAN_USE_ARMV7_INSTRUCTIONS
+#ifdef CAN_USE_VFP_INSTRUCTIONS
+ answer |= 1u << VFP3 | 1u << ARMv7;
+#endif // def CAN_USE_VFP_INSTRUCTIONS
+
+#ifdef __arm__
// If the compiler is allowed to use VFP then we can use VFP too in our code
// generation even when generating snapshots. This won't work for cross
// compilation. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
#if defined(__VFP_FP__) && !defined(__SOFTFP__)
answer |= 1u << VFP3 | 1u << ARMv7;
#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
-#ifdef CAN_USE_VFP_INSTRUCTIONS
- answer |= 1u << VFP3 | 1u << ARMv7;
-#endif // def CAN_USE_VFP_INSTRUCTIONS
+#endif // def __arm__
+
return answer;
}
-#endif // def __arm__
void CpuFeatures::Probe() {
@@ -76,6 +82,18 @@ void CpuFeatures::Probe() {
#ifdef DEBUG
initialized_ = true;
#endif
+
+ // Get the features implied by the OS and the compiler settings. This is the
+ // minimal set of features which is also alowed for generated code in the
+ // snapshot.
+ supported_ |= OS::CpuFeaturesImpliedByPlatform();
+ supported_ |= CpuFeaturesImpliedByCompiler();
+
+ if (Serializer::enabled()) {
+ // No probing for features if we might serialize (generate snapshot).
+ return;
+ }
+
#ifndef __arm__
// For the simulator=arm build, use VFP when FLAG_enable_vfp3 is
// enabled. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
@@ -87,13 +105,8 @@ void CpuFeatures::Probe() {
supported_ |= 1u << ARMv7;
}
#else // def __arm__
- if (Serializer::enabled()) {
- supported_ |= OS::CpuFeaturesImpliedByPlatform();
- supported_ |= CpuFeaturesImpliedByCompiler();
- return; // No features if we might serialize.
- }
-
- if (OS::ArmCpuHasFeature(VFP3)) {
+ // Probe for additional features not already known to be available.
+ if (!IsSupported(VFP3) && OS::ArmCpuHasFeature(VFP3)) {
// This implementation also sets the VFP flags if runtime
// detection of VFP returns true. VFPv3 implies ARMv7, see ARM DDI
// 0406B, page A1-6.
@@ -101,7 +114,7 @@ void CpuFeatures::Probe() {
found_by_runtime_probing_ |= 1u << VFP3 | 1u << ARMv7;
}
- if (OS::ArmCpuHasFeature(ARMv7)) {
+ if (!IsSupported(ARMv7) && OS::ArmCpuHasFeature(ARMv7)) {
supported_ |= 1u << ARMv7;
found_by_runtime_probing_ |= 1u << ARMv7;
}
@@ -276,9 +289,7 @@ static const int kMinimalBufferSize = 4*KB;
Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
: AssemblerBase(arg_isolate),
positions_recorder_(this),
- allow_peephole_optimization_(false),
emit_debug_code_(FLAG_debug_code) {
- allow_peephole_optimization_ = FLAG_peephole_optimization;
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@@ -315,6 +326,7 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
no_const_pool_before_ = 0;
last_const_pool_end_ = 0;
last_bound_pos_ = 0;
+ ast_id_for_reloc_info_ = kNoASTId;
}
@@ -1082,20 +1094,6 @@ void Assembler::rsb(Register dst, Register src1, const Operand& src2,
void Assembler::add(Register dst, Register src1, const Operand& src2,
SBit s, Condition cond) {
addrmod1(cond | ADD | s, src1, dst, src2);
-
- // Eliminate pattern: push(r), pop()
- // str(src, MemOperand(sp, 4, NegPreIndex), al);
- // add(sp, sp, Operand(kPointerSize));
- // Both instructions can be eliminated.
- if (can_peephole_optimize(2) &&
- // Pattern.
- instr_at(pc_ - 1 * kInstrSize) == kPopInstruction &&
- (instr_at(pc_ - 2 * kInstrSize) & ~kRdMask) == kPushRegPattern) {
- pc_ -= 2 * kInstrSize;
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push(reg)/pop() eliminated\n", pc_offset());
- }
- }
}
@@ -1400,195 +1398,11 @@ void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) {
positions_recorder()->WriteRecordedPositions();
}
addrmod2(cond | B26 | L, dst, src);
-
- // Eliminate pattern: push(ry), pop(rx)
- // str(ry, MemOperand(sp, 4, NegPreIndex), al)
- // ldr(rx, MemOperand(sp, 4, PostIndex), al)
- // Both instructions can be eliminated if ry = rx.
- // If ry != rx, a register copy from ry to rx is inserted
- // after eliminating the push and the pop instructions.
- if (can_peephole_optimize(2)) {
- Instr push_instr = instr_at(pc_ - 2 * kInstrSize);
- Instr pop_instr = instr_at(pc_ - 1 * kInstrSize);
-
- if (IsPush(push_instr) && IsPop(pop_instr)) {
- if (Instruction::RdValue(pop_instr) != Instruction::RdValue(push_instr)) {
- // For consecutive push and pop on different registers,
- // we delete both the push & pop and insert a register move.
- // push ry, pop rx --> mov rx, ry
- Register reg_pushed, reg_popped;
- reg_pushed = GetRd(push_instr);
- reg_popped = GetRd(pop_instr);
- pc_ -= 2 * kInstrSize;
- // Insert a mov instruction, which is better than a pair of push & pop
- mov(reg_popped, reg_pushed);
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop (diff reg) replaced by a reg move\n",
- pc_offset());
- }
- } else {
- // For consecutive push and pop on the same register,
- // both the push and the pop can be deleted.
- pc_ -= 2 * kInstrSize;
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop (same reg) eliminated\n", pc_offset());
- }
- }
- }
- }
-
- if (can_peephole_optimize(2)) {
- Instr str_instr = instr_at(pc_ - 2 * kInstrSize);
- Instr ldr_instr = instr_at(pc_ - 1 * kInstrSize);
-
- if ((IsStrRegFpOffset(str_instr) &&
- IsLdrRegFpOffset(ldr_instr)) ||
- (IsStrRegFpNegOffset(str_instr) &&
- IsLdrRegFpNegOffset(ldr_instr))) {
- if ((ldr_instr & kLdrStrInstrArgumentMask) ==
- (str_instr & kLdrStrInstrArgumentMask)) {
- // Pattern: Ldr/str same fp+offset, same register.
- //
- // The following:
- // str rx, [fp, #-12]
- // ldr rx, [fp, #-12]
- //
- // Becomes:
- // str rx, [fp, #-12]
-
- pc_ -= 1 * kInstrSize;
- if (FLAG_print_peephole_optimization) {
- PrintF("%x str/ldr (fp + same offset), same reg\n", pc_offset());
- }
- } else if ((ldr_instr & kLdrStrOffsetMask) ==
- (str_instr & kLdrStrOffsetMask)) {
- // Pattern: Ldr/str same fp+offset, different register.
- //
- // The following:
- // str rx, [fp, #-12]
- // ldr ry, [fp, #-12]
- //
- // Becomes:
- // str rx, [fp, #-12]
- // mov ry, rx
-
- Register reg_stored, reg_loaded;
- reg_stored = GetRd(str_instr);
- reg_loaded = GetRd(ldr_instr);
- pc_ -= 1 * kInstrSize;
- // Insert a mov instruction, which is better than ldr.
- mov(reg_loaded, reg_stored);
- if (FLAG_print_peephole_optimization) {
- PrintF("%x str/ldr (fp + same offset), diff reg \n", pc_offset());
- }
- }
- }
- }
-
- if (can_peephole_optimize(3)) {
- Instr mem_write_instr = instr_at(pc_ - 3 * kInstrSize);
- Instr ldr_instr = instr_at(pc_ - 2 * kInstrSize);
- Instr mem_read_instr = instr_at(pc_ - 1 * kInstrSize);
- if (IsPush(mem_write_instr) &&
- IsPop(mem_read_instr)) {
- if ((IsLdrRegFpOffset(ldr_instr) ||
- IsLdrRegFpNegOffset(ldr_instr))) {
- if (Instruction::RdValue(mem_write_instr) ==
- Instruction::RdValue(mem_read_instr)) {
- // Pattern: push & pop from/to same register,
- // with a fp+offset ldr in between
- //
- // The following:
- // str rx, [sp, #-4]!
- // ldr rz, [fp, #-24]
- // ldr rx, [sp], #+4
- //
- // Becomes:
- // if(rx == rz)
- // delete all
- // else
- // ldr rz, [fp, #-24]
-
- if (Instruction::RdValue(mem_write_instr) ==
- Instruction::RdValue(ldr_instr)) {
- pc_ -= 3 * kInstrSize;
- } else {
- pc_ -= 3 * kInstrSize;
- // Reinsert back the ldr rz.
- emit(ldr_instr);
- }
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop -dead ldr fp+offset in middle\n", pc_offset());
- }
- } else {
- // Pattern: push & pop from/to different registers
- // with a fp+offset ldr in between
- //
- // The following:
- // str rx, [sp, #-4]!
- // ldr rz, [fp, #-24]
- // ldr ry, [sp], #+4
- //
- // Becomes:
- // if(ry == rz)
- // mov ry, rx;
- // else if(rx != rz)
- // ldr rz, [fp, #-24]
- // mov ry, rx
- // else if((ry != rz) || (rx == rz)) becomes:
- // mov ry, rx
- // ldr rz, [fp, #-24]
-
- Register reg_pushed, reg_popped;
- if (Instruction::RdValue(mem_read_instr) ==
- Instruction::RdValue(ldr_instr)) {
- reg_pushed = GetRd(mem_write_instr);
- reg_popped = GetRd(mem_read_instr);
- pc_ -= 3 * kInstrSize;
- mov(reg_popped, reg_pushed);
- } else if (Instruction::RdValue(mem_write_instr) !=
- Instruction::RdValue(ldr_instr)) {
- reg_pushed = GetRd(mem_write_instr);
- reg_popped = GetRd(mem_read_instr);
- pc_ -= 3 * kInstrSize;
- emit(ldr_instr);
- mov(reg_popped, reg_pushed);
- } else if ((Instruction::RdValue(mem_read_instr) !=
- Instruction::RdValue(ldr_instr)) ||
- (Instruction::RdValue(mem_write_instr) ==
- Instruction::RdValue(ldr_instr))) {
- reg_pushed = GetRd(mem_write_instr);
- reg_popped = GetRd(mem_read_instr);
- pc_ -= 3 * kInstrSize;
- mov(reg_popped, reg_pushed);
- emit(ldr_instr);
- }
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop (ldr fp+off in middle)\n", pc_offset());
- }
- }
- }
- }
- }
}
void Assembler::str(Register src, const MemOperand& dst, Condition cond) {
addrmod2(cond | B26, src, dst);
-
- // Eliminate pattern: pop(), push(r)
- // add sp, sp, #4 LeaveCC, al; str r, [sp, #-4], al
- // -> str r, [sp, 0], al
- if (can_peephole_optimize(2) &&
- // Pattern.
- instr_at(pc_ - 1 * kInstrSize) == (kPushRegPattern | src.code() * B12) &&
- instr_at(pc_ - 2 * kInstrSize) == kPopInstruction) {
- pc_ -= 2 * kInstrSize;
- emit(al | B26 | 0 | Offset | sp.code() * B16 | src.code() * B12);
- if (FLAG_print_peephole_optimization) {
- PrintF("%x pop()/push(reg) eliminated\n", pc_offset());
- }
- }
}
@@ -2722,7 +2536,14 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
}
}
ASSERT(buffer_space() >= kMaxRelocSize); // too late to grow buffer here
- reloc_info_writer.Write(&rinfo);
+ if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
+ ASSERT(ast_id_for_reloc_info_ != kNoASTId);
+ RelocInfo reloc_info_with_ast_id(pc_, rmode, ast_id_for_reloc_info_);
+ ast_id_for_reloc_info_ = kNoASTId;
+ reloc_info_writer.Write(&reloc_info_with_ast_id);
+ } else {
+ reloc_info_writer.Write(&rinfo);
+ }
}
}
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index 3f2daab5..2ab46b3b 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -72,6 +72,7 @@ namespace internal {
struct Register {
static const int kNumRegisters = 16;
static const int kNumAllocatableRegisters = 8;
+ static const int kSizeInBytes = 4;
static int ToAllocationIndex(Register reg) {
ASSERT(reg.code() < kNumAllocatableRegisters);
@@ -1170,6 +1171,10 @@ class Assembler : public AssemblerBase {
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
+ // Record the AST id of the CallIC being compiled, so that it can be placed
+ // in the relocation information.
+ void RecordAstId(unsigned ast_id) { ast_id_for_reloc_info_ = ast_id; }
+
// Record a comment relocation entry that can be used by a disassembler.
// Use --code-comments to enable.
void RecordComment(const char* msg);
@@ -1185,12 +1190,6 @@ class Assembler : public AssemblerBase {
PositionsRecorder* positions_recorder() { return &positions_recorder_; }
- bool can_peephole_optimize(int instructions) {
- if (!allow_peephole_optimization_) return false;
- if (last_bound_pos_ > pc_offset() - instructions * kInstrSize) return false;
- return reloc_info_writer.last_pc() <= pc_ - instructions * kInstrSize;
- }
-
// Read/patch instructions
static Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); }
static void instr_at_put(byte* pc, Instr instr) {
@@ -1223,10 +1222,25 @@ class Assembler : public AssemblerBase {
static int GetCmpImmediateRawImmediate(Instr instr);
static bool IsNop(Instr instr, int type = NON_MARKING_NOP);
+ // Buffer size and constant pool distance are checked together at regular
+ // intervals of kBufferCheckInterval emitted bytes
+ static const int kBufferCheckInterval = 1*KB/2;
+ // Constants in pools are accessed via pc relative addressing, which can
+ // reach +/-4KB thereby defining a maximum distance between the instruction
+ // and the accessed constant. We satisfy this constraint by limiting the
+ // distance between pools.
+ static const int kMaxDistBetweenPools = 4*KB - 2*kBufferCheckInterval;
+ static const int kMaxNumPRInfo = kMaxDistBetweenPools/kInstrSize;
+
// Check if is time to emit a constant pool for pending reloc info entries
void CheckConstPool(bool force_emit, bool require_jump);
protected:
+ // Relocation for a type-recording IC has the AST id added to it. This
+ // member variable is a way to pass the information from the call site to
+ // the relocation info.
+ unsigned ast_id_for_reloc_info_;
+
bool emit_debug_code() const { return emit_debug_code_; }
int buffer_space() const { return reloc_info_writer.pos() - pc_; }
@@ -1264,9 +1278,6 @@ class Assembler : public AssemblerBase {
// True if the assembler owns the buffer, false if buffer is external.
bool own_buffer_;
- // Buffer size and constant pool distance are checked together at regular
- // intervals of kBufferCheckInterval emitted bytes
- static const int kBufferCheckInterval = 1*KB/2;
int next_buffer_check_; // pc offset of next buffer check
// Code generation
@@ -1299,12 +1310,6 @@ class Assembler : public AssemblerBase {
// regular intervals of kDistBetweenPools bytes
static const int kDistBetweenPools = 1*KB;
- // Constants in pools are accessed via pc relative addressing, which can
- // reach +/-4KB thereby defining a maximum distance between the instruction
- // and the accessed constant. We satisfy this constraint by limiting the
- // distance between pools.
- static const int kMaxDistBetweenPools = 4*KB - 2*kBufferCheckInterval;
-
// Emission of the constant pool may be blocked in some code sequences.
int const_pool_blocked_nesting_; // Block emission if this is not zero.
int no_const_pool_before_; // Block emission before this pc offset.
@@ -1322,7 +1327,6 @@ class Assembler : public AssemblerBase {
// stored in a separate buffer until a constant pool is emitted.
// If every instruction in a long sequence is accessing the pool, we need one
// pending relocation entry per instruction.
- static const int kMaxNumPRInfo = kMaxDistBetweenPools/kInstrSize;
RelocInfo prinfo_[kMaxNumPRInfo]; // the buffer of pending relocation info
int num_prinfo_; // number of pending reloc info entries in the buffer
@@ -1356,7 +1360,6 @@ class Assembler : public AssemblerBase {
friend class BlockConstPoolScope;
PositionsRecorder positions_recorder_;
- bool allow_peephole_optimization_;
bool emit_debug_code_;
friend class PositionsRecorder;
friend class EnsureSpace;
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index 5235dd31..794b370d 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -584,7 +584,7 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
__ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4);
__ EnterInternalFrame();
__ push(r0);
- __ InvokeBuiltin(Builtins::TO_STRING, CALL_JS);
+ __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
__ LeaveInternalFrame();
__ pop(function);
__ mov(argument, r0);
@@ -636,6 +636,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// Set expected number of arguments to zero (not changing r0).
__ mov(r2, Operand(0, RelocInfo::NONE));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
+ __ SetCallKind(r5, CALL_AS_METHOD);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
}
@@ -914,10 +915,11 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
masm->isolate()->builtins()->HandleApiCallConstruct();
ParameterCount expected(0);
__ InvokeCode(code, expected, expected,
- RelocInfo::CODE_TARGET, CALL_FUNCTION);
+ RelocInfo::CODE_TARGET, CALL_FUNCTION, CALL_AS_METHOD);
} else {
ParameterCount actual(r0);
- __ InvokeFunction(r1, actual, CALL_FUNCTION);
+ __ InvokeFunction(r1, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// Pop the function from the stack.
@@ -1049,7 +1051,8 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(r0);
- __ InvokeFunction(r1, actual, CALL_FUNCTION);
+ __ InvokeFunction(r1, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// Exit the JS frame and remove the parameters (except function), and return.
@@ -1077,12 +1080,17 @@ void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
// Preserve the function.
__ push(r1);
+ // Push call kind information.
+ __ push(r5);
// Push the function on the stack as the argument to the runtime function.
__ push(r1);
__ CallRuntime(Runtime::kLazyCompile, 1);
// Calculate the entry point.
__ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Restore call kind information.
+ __ pop(r5);
// Restore saved function.
__ pop(r1);
@@ -1100,12 +1108,17 @@ void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
// Preserve the function.
__ push(r1);
+ // Push call kind information.
+ __ push(r5);
// Push the function on the stack as the argument to the runtime function.
__ push(r1);
__ CallRuntime(Runtime::kLazyRecompile, 1);
// Calculate the entry point.
__ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Restore call kind information.
+ __ pop(r5);
// Restore saved function.
__ pop(r1);
@@ -1238,8 +1251,13 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Do not transform the receiver for strict mode functions.
__ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset));
- __ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ __ ldr(r3, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset));
+ __ tst(r3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ kSmiTagSize)));
+ __ b(ne, &shift_arguments);
+
+ // Do not transform the receiver for native (Compilerhints already in r3).
+ __ tst(r3, Operand(1 << (SharedFunctionInfo::kES5Native +
kSmiTagSize)));
__ b(ne, &shift_arguments);
@@ -1252,17 +1270,17 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ tst(r2, Operand(kSmiTagMask));
__ b(eq, &convert_to_object);
- __ LoadRoot(r3, Heap::kNullValueRootIndex);
+ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
__ cmp(r2, r3);
__ b(eq, &use_global_receiver);
- __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
+ __ LoadRoot(r3, Heap::kNullValueRootIndex);
__ cmp(r2, r3);
__ b(eq, &use_global_receiver);
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
__ CompareObjectType(r2, r3, r3, FIRST_JS_OBJECT_TYPE);
- __ b(lt, &convert_to_object);
- __ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
- __ b(le, &shift_arguments);
+ __ b(ge, &shift_arguments);
__ bind(&convert_to_object);
__ EnterInternalFrame(); // In order to preserve argument count.
@@ -1270,7 +1288,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ push(r0);
__ push(r2);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(r2, r0);
__ pop(r0);
@@ -1340,6 +1358,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Expected number of arguments is 0 for CALL_NON_FUNCTION.
__ mov(r2, Operand(0, RelocInfo::NONE));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
+ __ SetCallKind(r5, CALL_AS_METHOD);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
__ bind(&function);
@@ -1355,13 +1374,15 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(r2, Operand(r2, ASR, kSmiTagSize));
__ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
+ __ SetCallKind(r5, CALL_AS_METHOD);
__ cmp(r2, r0); // Check formal and actual parameter counts.
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET,
ne);
ParameterCount expected(0);
- __ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
+ __ InvokeCode(r3, expected, expected, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
@@ -1378,7 +1399,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
__ push(r0);
__ ldr(r0, MemOperand(fp, kArgsOffset)); // get the args array
__ push(r0);
- __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_JS);
+ __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
// Check the stack for overflow. We are not trying need to catch
// interruptions (e.g. debug break and preemption) here, so the "real stack
@@ -1396,7 +1417,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
__ ldr(r1, MemOperand(fp, kFunctionOffset));
__ push(r1);
__ push(r0);
- __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_JS);
+ __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
// End of stack check.
// Push current limit and index.
@@ -1416,8 +1437,13 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
__ ldr(r0, MemOperand(fp, kRecvOffset));
// Do not transform the receiver for strict mode functions.
- __ ldr(r1, FieldMemOperand(r1, SharedFunctionInfo::kCompilerHintsOffset));
- __ tst(r1, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ __ ldr(r2, FieldMemOperand(r1, SharedFunctionInfo::kCompilerHintsOffset));
+ __ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ kSmiTagSize)));
+ __ b(ne, &push_receiver);
+
+ // Do not transform the receiver for strict mode functions.
+ __ tst(r2, Operand(1 << (SharedFunctionInfo::kES5Native +
kSmiTagSize)));
__ b(ne, &push_receiver);
@@ -1433,16 +1459,16 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// Check if the receiver is already a JavaScript object.
// r0: receiver
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
__ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE);
- __ b(lt, &call_to_object);
- __ cmp(r1, Operand(LAST_JS_OBJECT_TYPE));
- __ b(le, &push_receiver);
+ __ b(ge, &push_receiver);
// Convert the receiver to a regular object.
// r0: receiver
__ bind(&call_to_object);
__ push(r0);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ b(&push_receiver);
// Use the current global receiver object as the receiver.
@@ -1492,7 +1518,8 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
ParameterCount actual(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
__ ldr(r1, MemOperand(fp, kFunctionOffset));
- __ InvokeFunction(r1, actual, CALL_FUNCTION);
+ __ InvokeFunction(r1, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
// Tear down the internal frame and remove function, receiver and args.
__ LeaveInternalFrame();
@@ -1529,6 +1556,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// -- r1 : function (passed through to callee)
// -- r2 : expected number of arguments
// -- r3 : code entry to call
+ // -- r5 : call kind information
// -----------------------------------
Label invoke, dont_adapt_arguments;
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index fad9339f..5e6c0c38 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -55,6 +55,17 @@ static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
Register rhs);
+// Check if the operand is a heap number.
+static void EmitCheckForHeapNumber(MacroAssembler* masm, Register operand,
+ Register scratch1, Register scratch2,
+ Label* not_a_heap_number) {
+ __ ldr(scratch1, FieldMemOperand(operand, HeapObject::kMapOffset));
+ __ LoadRoot(scratch2, Heap::kHeapNumberMapRootIndex);
+ __ cmp(scratch1, scratch2);
+ __ b(ne, not_a_heap_number);
+}
+
+
void ToNumberStub::Generate(MacroAssembler* masm) {
// The ToNumber stub takes one argument in eax.
Label check_heap_number, call_builtin;
@@ -63,15 +74,12 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
__ Ret();
__ bind(&check_heap_number);
- __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
- __ cmp(r1, ip);
- __ b(ne, &call_builtin);
+ EmitCheckForHeapNumber(masm, r0, r1, ip, &call_builtin);
__ Ret();
__ bind(&call_builtin);
__ push(r0);
- __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS);
+ __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
}
@@ -364,136 +372,6 @@ void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
}
-class FloatingPointHelper : public AllStatic {
- public:
-
- enum Destination {
- kVFPRegisters,
- kCoreRegisters
- };
-
-
- // Loads smis from r0 and r1 (right and left in binary operations) into
- // floating point registers. Depending on the destination the values ends up
- // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is
- // floating point registers VFP3 must be supported. If core registers are
- // requested when VFP3 is supported d6 and d7 will be scratched.
- static void LoadSmis(MacroAssembler* masm,
- Destination destination,
- Register scratch1,
- Register scratch2);
-
- // Loads objects from r0 and r1 (right and left in binary operations) into
- // floating point registers. Depending on the destination the values ends up
- // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is
- // floating point registers VFP3 must be supported. If core registers are
- // requested when VFP3 is supported d6 and d7 will still be scratched. If
- // either r0 or r1 is not a number (not smi and not heap number object) the
- // not_number label is jumped to with r0 and r1 intact.
- static void LoadOperands(MacroAssembler* masm,
- FloatingPointHelper::Destination destination,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Label* not_number);
-
- // Convert the smi or heap number in object to an int32 using the rules
- // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
- // and brought into the range -2^31 .. +2^31 - 1.
- static void ConvertNumberToInt32(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- DwVfpRegister double_scratch,
- Label* not_int32);
-
- // Load the number from object into double_dst in the double format.
- // Control will jump to not_int32 if the value cannot be exactly represented
- // by a 32-bit integer.
- // Floating point value in the 32-bit integer range that are not exact integer
- // won't be loaded.
- static void LoadNumberAsInt32Double(MacroAssembler* masm,
- Register object,
- Destination destination,
- DwVfpRegister double_dst,
- Register dst1,
- Register dst2,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- SwVfpRegister single_scratch,
- Label* not_int32);
-
- // Loads the number from object into dst as a 32-bit integer.
- // Control will jump to not_int32 if the object cannot be exactly represented
- // by a 32-bit integer.
- // Floating point value in the 32-bit integer range that are not exact integer
- // won't be converted.
- // scratch3 is not used when VFP3 is supported.
- static void LoadNumberAsInt32(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- DwVfpRegister double_scratch,
- Label* not_int32);
-
- // Generate non VFP3 code to check if a double can be exactly represented by a
- // 32-bit integer. This does not check for 0 or -0, which need
- // to be checked for separately.
- // Control jumps to not_int32 if the value is not a 32-bit integer, and falls
- // through otherwise.
- // src1 and src2 will be cloberred.
- //
- // Expected input:
- // - src1: higher (exponent) part of the double value.
- // - src2: lower (mantissa) part of the double value.
- // Output status:
- // - dst: 32 higher bits of the mantissa. (mantissa[51:20])
- // - src2: contains 1.
- // - other registers are clobbered.
- static void DoubleIs32BitInteger(MacroAssembler* masm,
- Register src1,
- Register src2,
- Register dst,
- Register scratch,
- Label* not_int32);
-
- // Generates code to call a C function to do a double operation using core
- // registers. (Used when VFP3 is not supported.)
- // This code never falls through, but returns with a heap number containing
- // the result in r0.
- // Register heapnumber_result must be a heap number in which the
- // result of the operation will be stored.
- // Requires the following layout on entry:
- // r0: Left value (least significant part of mantissa).
- // r1: Left value (sign, exponent, top of mantissa).
- // r2: Right value (least significant part of mantissa).
- // r3: Right value (sign, exponent, top of mantissa).
- static void CallCCodeForDoubleOperation(MacroAssembler* masm,
- Token::Value op,
- Register heap_number_result,
- Register scratch);
-
- private:
- static void LoadNumber(MacroAssembler* masm,
- FloatingPointHelper::Destination destination,
- Register object,
- DwVfpRegister dst,
- Register dst1,
- Register dst2,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Label* not_number);
-};
-
-
void FloatingPointHelper::LoadSmis(MacroAssembler* masm,
FloatingPointHelper::Destination destination,
Register scratch1,
@@ -651,30 +529,23 @@ void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
}
-void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
- Register object,
- Destination destination,
- DwVfpRegister double_dst,
- Register dst1,
- Register dst2,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- SwVfpRegister single_scratch,
- Label* not_int32) {
- ASSERT(!scratch1.is(object) && !scratch2.is(object));
- ASSERT(!scratch1.is(scratch2));
- ASSERT(!heap_number_map.is(object) &&
- !heap_number_map.is(scratch1) &&
- !heap_number_map.is(scratch2));
+void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm,
+ Register int_scratch,
+ Destination destination,
+ DwVfpRegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register scratch2,
+ SwVfpRegister single_scratch) {
+ ASSERT(!int_scratch.is(scratch2));
+ ASSERT(!int_scratch.is(dst1));
+ ASSERT(!int_scratch.is(dst2));
- Label done, obj_is_not_smi;
+ Label done;
- __ JumpIfNotSmi(object, &obj_is_not_smi);
- __ SmiUntag(scratch1, object);
if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
- __ vmov(single_scratch, scratch1);
+ __ vmov(single_scratch, int_scratch);
__ vcvt_f64_s32(double_dst, single_scratch);
if (destination == kCoreRegisters) {
__ vmov(dst1, dst2, double_dst);
@@ -686,20 +557,20 @@ void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
// | s | exp | mantissa |
// Check for zero.
- __ cmp(scratch1, Operand(0));
- __ mov(dst2, scratch1);
- __ mov(dst1, scratch1);
+ __ cmp(int_scratch, Operand(0));
+ __ mov(dst2, int_scratch);
+ __ mov(dst1, int_scratch);
__ b(eq, &done);
// Preload the sign of the value.
- __ and_(dst2, scratch1, Operand(HeapNumber::kSignMask), SetCC);
+ __ and_(dst2, int_scratch, Operand(HeapNumber::kSignMask), SetCC);
// Get the absolute value of the object (as an unsigned integer).
- __ rsb(scratch1, scratch1, Operand(0), SetCC, mi);
+ __ rsb(int_scratch, int_scratch, Operand(0), SetCC, mi);
// Get mantisssa[51:20].
// Get the position of the first set bit.
- __ CountLeadingZeros(dst1, scratch1, scratch2);
+ __ CountLeadingZeros(dst1, int_scratch, scratch2);
__ rsb(dst1, dst1, Operand(31));
// Set the exponent.
@@ -709,26 +580,52 @@ void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
// Clear the first non null bit.
__ mov(scratch2, Operand(1));
- __ bic(scratch1, scratch1, Operand(scratch2, LSL, dst1));
+ __ bic(int_scratch, int_scratch, Operand(scratch2, LSL, dst1));
__ cmp(dst1, Operand(HeapNumber::kMantissaBitsInTopWord));
// Get the number of bits to set in the lower part of the mantissa.
__ sub(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC);
__ b(mi, &fewer_than_20_useful_bits);
// Set the higher 20 bits of the mantissa.
- __ orr(dst2, dst2, Operand(scratch1, LSR, scratch2));
+ __ orr(dst2, dst2, Operand(int_scratch, LSR, scratch2));
__ rsb(scratch2, scratch2, Operand(32));
- __ mov(dst1, Operand(scratch1, LSL, scratch2));
+ __ mov(dst1, Operand(int_scratch, LSL, scratch2));
__ b(&done);
__ bind(&fewer_than_20_useful_bits);
__ rsb(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord));
- __ mov(scratch2, Operand(scratch1, LSL, scratch2));
+ __ mov(scratch2, Operand(int_scratch, LSL, scratch2));
__ orr(dst2, dst2, scratch2);
// Set dst1 to 0.
__ mov(dst1, Operand(0));
}
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ DwVfpRegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ SwVfpRegister single_scratch,
+ Label* not_int32) {
+ ASSERT(!scratch1.is(object) && !scratch2.is(object));
+ ASSERT(!scratch1.is(scratch2));
+ ASSERT(!heap_number_map.is(object) &&
+ !heap_number_map.is(scratch1) &&
+ !heap_number_map.is(scratch2));
+
+ Label done, obj_is_not_smi;
+ __ JumpIfNotSmi(object, &obj_is_not_smi);
+ __ SmiUntag(scratch1, object);
+ ConvertIntToDouble(masm, scratch1, destination, double_dst, dst1, dst2,
+ scratch2, single_scratch);
__ b(&done);
__ bind(&obj_is_not_smi);
@@ -943,14 +840,25 @@ void FloatingPointHelper::CallCCodeForDoubleOperation(
// Push the current return address before the C call. Return will be
// through pop(pc) below.
__ push(lr);
- __ PrepareCallCFunction(4, scratch); // Two doubles are 4 arguments.
+ __ PrepareCallCFunction(0, 2, scratch);
+ if (masm->use_eabi_hardfloat()) {
+ CpuFeatures::Scope scope(VFP3);
+ __ vmov(d0, r0, r1);
+ __ vmov(d1, r2, r3);
+ }
// Call C routine that may not cause GC or other trouble.
__ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()),
- 4);
+ 0, 2);
// Store answer in the overwritable heap number. Double returned in
- // registers r0 and r1.
- __ Strd(r0, r1, FieldMemOperand(heap_number_result,
- HeapNumber::kValueOffset));
+ // registers r0 and r1 or in d0.
+ if (masm->use_eabi_hardfloat()) {
+ CpuFeatures::Scope scope(VFP3);
+ __ vstr(d0,
+ FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ } else {
+ __ Strd(r0, r1, FieldMemOperand(heap_number_result,
+ HeapNumber::kValueOffset));
+ }
// Place heap_number_result in r0 and return to the pushed return address.
__ mov(r0, Operand(heap_number_result));
__ pop(pc);
@@ -1292,8 +1200,14 @@ static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm,
// Call a native function to do a comparison between two non-NaNs.
// Call C routine that may not cause GC or other trouble.
__ push(lr);
- __ PrepareCallCFunction(4, r5); // Two doubles count as 4 arguments.
- __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()), 4);
+ __ PrepareCallCFunction(0, 2, r5);
+ if (masm->use_eabi_hardfloat()) {
+ CpuFeatures::Scope scope(VFP3);
+ __ vmov(d0, r0, r1);
+ __ vmov(d1, r2, r3);
+ }
+ __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()),
+ 0, 2);
__ pop(pc); // Return.
}
}
@@ -1457,7 +1371,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
scratch1,
Heap::kHeapNumberMapRootIndex,
not_found,
- true);
+ DONT_DO_SMI_CHECK);
STATIC_ASSERT(8 == kDoubleSize);
__ add(scratch1,
@@ -1656,13 +1570,22 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ JumpIfNonSmisNotBothSequentialAsciiStrings(lhs_, rhs_, r2, r3, &slow);
__ IncrementCounter(isolate->counters()->string_compare_native(), 1, r2, r3);
- StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ if (cc_ == eq) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
lhs_,
rhs_,
r2,
r3,
- r4,
- r5);
+ r4);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ lhs_,
+ rhs_,
+ r2,
+ r3,
+ r4,
+ r5);
+ }
// Never falls through to here.
__ bind(&slow);
@@ -1687,7 +1610,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
// tagged as a small integer.
- __ InvokeBuiltin(native, JUMP_JS);
+ __ InvokeBuiltin(native, JUMP_FUNCTION);
}
@@ -1695,12 +1618,36 @@ void CompareStub::Generate(MacroAssembler* masm) {
// The stub returns zero for false, and a non-zero value for true.
void ToBooleanStub::Generate(MacroAssembler* masm) {
// This stub uses VFP3 instructions.
- ASSERT(CpuFeatures::IsEnabled(VFP3));
+ CpuFeatures::Scope scope(VFP3);
Label false_result;
Label not_heap_number;
Register scratch = r9.is(tos_) ? r7 : r9;
+ // undefined -> false
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(tos_, ip);
+ __ b(eq, &false_result);
+
+ // Boolean -> its value
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+ __ cmp(tos_, ip);
+ __ b(eq, &false_result);
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ cmp(tos_, ip);
+ // "tos_" is a register and contains a non-zero value. Hence we implicitly
+ // return true if the equal condition is satisfied.
+ __ Ret(eq);
+
+ // Smis: 0 -> false, all other -> true
+ __ tst(tos_, tos_);
+ __ b(eq, &false_result);
+ __ tst(tos_, Operand(kSmiTagMask));
+ // "tos_" is a register and contains a non-zero value. Hence we implicitly
+ // return true if the not equal condition is satisfied.
+ __ Ret(eq);
+
+ // 'null' -> false
__ LoadRoot(ip, Heap::kNullValueRootIndex);
__ cmp(tos_, ip);
__ b(eq, &false_result);
@@ -1710,9 +1657,7 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
__ cmp(scratch, ip);
__ b(&not_heap_number, ne);
-
- __ sub(ip, tos_, Operand(kHeapObjectTag));
- __ vldr(d1, ip, HeapNumber::kValueOffset);
+ __ vldr(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset));
__ VFPCompareAndSetFlags(d1, 0.0);
// "tos_" is a register, and contains a non zero value by default.
// Hence we only need to overwrite "tos_" with zero to return false for
@@ -1723,12 +1668,6 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
__ bind(&not_heap_number);
- // Check if the value is 'null'.
- // 'null' => false.
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(tos_, ip);
- __ b(&false_result, eq);
-
// It can be an undetectable object.
// Undetectable => false.
__ ldr(ip, FieldMemOperand(tos_, HeapObject::kMapOffset));
@@ -1768,15 +1707,310 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
}
-Handle<Code> GetTypeRecordingBinaryOpStub(int key,
- TRBinaryOpIC::TypeInfo type_info,
- TRBinaryOpIC::TypeInfo result_type_info) {
- TypeRecordingBinaryOpStub stub(key, type_info, result_type_info);
+Handle<Code> GetUnaryOpStub(int key, UnaryOpIC::TypeInfo type_info) {
+ UnaryOpStub stub(key, type_info);
+ return stub.GetCode();
+}
+
+
+const char* UnaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name = NULL; // Make g++ happy.
+ switch (mode_) {
+ case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "UnaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ UnaryOpIC::GetName(operand_type_));
+ return name_;
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operand_type_) {
+ case UnaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case UnaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case UnaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case UnaryOpIC::GENERIC:
+ GenerateGenericStub(masm);
+ break;
+ }
+}
+
+
+void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ // Prepare to push argument.
+ __ mov(r3, Operand(r0));
+
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ mov(r2, Operand(Smi::FromInt(MinorKey())));
+ __ mov(r1, Operand(Smi::FromInt(op_)));
+ __ mov(r0, Operand(Smi::FromInt(operand_type_)));
+
+ __ Push(r3, r2, r1, r0);
+
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kUnaryOp_Patch),
+ masm->isolate()),
+ 4,
+ 1);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateSmiStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateSmiStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &slow);
+ __ bind(&non_smi);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
+ Label non_smi;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* slow) {
+ __ JumpIfNotSmi(r0, non_smi);
+
+ // The result of negating zero or the smallest negative smi is not a smi.
+ __ bic(ip, r0, Operand(0x80000000), SetCC);
+ __ b(eq, slow);
+
+ // Return '0 - value'.
+ __ rsb(r0, r0, Operand(0, RelocInfo::NONE));
+ __ Ret();
+}
+
+
+void UnaryOpStub::GenerateSmiCodeBitNot(MacroAssembler* masm,
+ Label* non_smi) {
+ __ JumpIfNotSmi(r0, non_smi);
+
+ // Flip bits and revert inverted smi-tag.
+ __ mvn(r0, Operand(r0));
+ __ bic(r0, r0, Operand(kSmiTagMask));
+ __ Ret();
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateHeapNumberStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateHeapNumberStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
+ Label non_smi, slow, call_builtin;
+ GenerateSmiCodeSub(masm, &non_smi, &call_builtin);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+ __ bind(&call_builtin);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
+ Label* slow) {
+ EmitCheckForHeapNumber(masm, r0, r1, r6, slow);
+ // r0 is a heap number. Get a new heap number in r1.
+ if (mode_ == UNARY_OVERWRITE) {
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
+ __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ } else {
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(r1, r2, r3, r6, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(r0);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ mov(r1, Operand(r0));
+ __ pop(r0);
+ __ LeaveInternalFrame();
+
+ __ bind(&heapnumber_allocated);
+ __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
+ __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
+ __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
+ __ mov(r0, Operand(r1));
+ }
+ __ Ret();
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeBitNot(
+ MacroAssembler* masm, Label* slow) {
+ EmitCheckForHeapNumber(masm, r0, r1, r6, slow);
+ // Convert the heap number is r0 to an untagged integer in r1.
+ __ ConvertToInt32(r0, r1, r2, r3, d0, slow);
+
+ // Do the bitwise operation and check if the result fits in a smi.
+ Label try_float;
+ __ mvn(r1, Operand(r1));
+ __ add(r2, r1, Operand(0x40000000), SetCC);
+ __ b(mi, &try_float);
+
+ // Tag the result as a smi and we're done.
+ __ mov(r0, Operand(r1, LSL, kSmiTagSize));
+ __ Ret();
+
+ // Try to store the result in a heap number.
+ __ bind(&try_float);
+ if (mode_ == UNARY_NO_OVERWRITE) {
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(r0, r2, r3, r6, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(r1);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ pop(r1);
+ __ LeaveInternalFrame();
+
+ __ bind(&heapnumber_allocated);
+ }
+
+ if (CpuFeatures::IsSupported(VFP3)) {
+ // Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
+ CpuFeatures::Scope scope(VFP3);
+ __ vmov(s0, r1);
+ __ vcvt_f64_s32(d0, s0);
+ __ sub(r2, r0, Operand(kHeapObjectTag));
+ __ vstr(d0, r2, HeapNumber::kValueOffset);
+ __ Ret();
+ } else {
+ // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
+ // have to set up a frame.
+ WriteInt32ToHeapNumberStub stub(r1, r0, r2);
+ __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
+ }
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateGenericStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateGenericStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &slow);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
+ // Handle the slow case by jumping to the JavaScript builtin.
+ __ push(r0);
+ switch (op_) {
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
+ break;
+ case Token::BIT_NOT:
+ __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+Handle<Code> GetBinaryOpStub(int key,
+ BinaryOpIC::TypeInfo type_info,
+ BinaryOpIC::TypeInfo result_type_info) {
+ BinaryOpStub stub(key, type_info, result_type_info);
return stub.GetCode();
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
Label get_result;
__ Push(r1, r0);
@@ -1787,40 +2021,43 @@ void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
__ Push(r2, r1, r0);
__ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch),
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
masm->isolate()),
5,
1);
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransitionWithSavedArgs(
+void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(
MacroAssembler* masm) {
UNIMPLEMENTED();
}
-void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
+void BinaryOpStub::Generate(MacroAssembler* masm) {
switch (operands_type_) {
- case TRBinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::UNINITIALIZED:
GenerateTypeTransition(masm);
break;
- case TRBinaryOpIC::SMI:
+ case BinaryOpIC::SMI:
GenerateSmiStub(masm);
break;
- case TRBinaryOpIC::INT32:
+ case BinaryOpIC::INT32:
GenerateInt32Stub(masm);
break;
- case TRBinaryOpIC::HEAP_NUMBER:
+ case BinaryOpIC::HEAP_NUMBER:
GenerateHeapNumberStub(masm);
break;
- case TRBinaryOpIC::ODDBALL:
+ case BinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
- case TRBinaryOpIC::STRING:
+ case BinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
+ case BinaryOpIC::STRING:
GenerateStringStub(masm);
break;
- case TRBinaryOpIC::GENERIC:
+ case BinaryOpIC::GENERIC:
GenerateGeneric(masm);
break;
default:
@@ -1829,7 +2066,7 @@ void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
}
-const char* TypeRecordingBinaryOpStub::GetName() {
+const char* BinaryOpStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
@@ -1845,16 +2082,15 @@ const char* TypeRecordingBinaryOpStub::GetName() {
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "TypeRecordingBinaryOpStub_%s_%s_%s",
+ "BinaryOpStub_%s_%s_%s",
op_name,
overwrite_name,
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
return name_;
}
-void TypeRecordingBinaryOpStub::GenerateSmiSmiOperation(
- MacroAssembler* masm) {
+void BinaryOpStub::GenerateSmiSmiOperation(MacroAssembler* masm) {
Register left = r1;
Register right = r0;
Register scratch1 = r7;
@@ -1979,10 +2215,10 @@ void TypeRecordingBinaryOpStub::GenerateSmiSmiOperation(
}
-void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
- bool smi_operands,
- Label* not_numbers,
- Label* gc_required) {
+void BinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
+ bool smi_operands,
+ Label* not_numbers,
+ Label* gc_required) {
Register left = r1;
Register right = r0;
Register scratch1 = r7;
@@ -2193,7 +2429,8 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
// generated. If the result is not a smi and heap number allocation is not
// requested the code falls through. If number allocation is requested but a
// heap number cannot be allocated the code jumps to the lable gc_required.
-void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+void BinaryOpStub::GenerateSmiCode(
+ MacroAssembler* masm,
Label* use_runtime,
Label* gc_required,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
@@ -2222,11 +2459,11 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
}
-void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
Label not_smis, call_runtime;
- if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
- result_type_ == TRBinaryOpIC::SMI) {
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
// Only allow smi results.
GenerateSmiCode(masm, &call_runtime, NULL, NO_HEAPNUMBER_RESULTS);
} else {
@@ -2247,18 +2484,48 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
- ASSERT(operands_type_ == TRBinaryOpIC::STRING);
+void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::STRING);
ASSERT(op_ == Token::ADD);
// Try to add arguments as strings, otherwise, transition to the generic
- // TRBinaryOpIC type.
+ // BinaryOpIC type.
GenerateAddStrings(masm);
GenerateTypeTransition(masm);
}
-void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
- ASSERT(operands_type_ == TRBinaryOpIC::INT32);
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
+ __ b(ge, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::INT32);
Register left = r1;
Register right = r0;
@@ -2355,7 +2622,7 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
scratch1,
scratch2);
- if (result_type_ <= TRBinaryOpIC::INT32) {
+ if (result_type_ <= BinaryOpIC::INT32) {
// If the ne condition is set, result does
// not fit in a 32-bit integer.
__ b(ne, &transition);
@@ -2382,8 +2649,8 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
// DIV just falls through to allocating a heap number.
}
- if (result_type_ >= (op_ == Token::DIV) ? TRBinaryOpIC::HEAP_NUMBER
- : TRBinaryOpIC::INT32) {
+ if (result_type_ >= (op_ == Token::DIV) ? BinaryOpIC::HEAP_NUMBER
+ : BinaryOpIC::INT32) {
__ bind(&return_heap_number);
// We are using vfp registers so r5 is available.
heap_number_result = r5;
@@ -2492,12 +2759,13 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
// The non vfp3 code does not support this special case, so jump to
// runtime if we don't support it.
if (CpuFeatures::IsSupported(VFP3)) {
- __ b(mi,
- (result_type_ <= TRBinaryOpIC::INT32) ? &transition
- : &return_heap_number);
+ __ b(mi, (result_type_ <= BinaryOpIC::INT32)
+ ? &transition
+ : &return_heap_number);
} else {
- __ b(mi, (result_type_ <= TRBinaryOpIC::INT32) ? &transition
- : &call_runtime);
+ __ b(mi, (result_type_ <= BinaryOpIC::INT32)
+ ? &transition
+ : &call_runtime);
}
break;
case Token::SHL:
@@ -2567,7 +2835,7 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
Label call_runtime;
if (op_ == Token::ADD) {
@@ -2600,7 +2868,7 @@ void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
Label call_runtime;
GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
@@ -2609,7 +2877,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
Label call_runtime, call_string_add_or_runtime;
GenerateSmiCode(masm, &call_runtime, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
@@ -2626,7 +2894,7 @@ void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
ASSERT(op_ == Token::ADD);
Label left_not_string, call_runtime;
@@ -2657,41 +2925,41 @@ void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
+void BinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
GenerateRegisterArgsPush(masm);
switch (op_) {
case Token::ADD:
- __ InvokeBuiltin(Builtins::ADD, JUMP_JS);
+ __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
break;
case Token::SUB:
- __ InvokeBuiltin(Builtins::SUB, JUMP_JS);
+ __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
break;
case Token::MUL:
- __ InvokeBuiltin(Builtins::MUL, JUMP_JS);
+ __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
break;
case Token::DIV:
- __ InvokeBuiltin(Builtins::DIV, JUMP_JS);
+ __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
break;
case Token::MOD:
- __ InvokeBuiltin(Builtins::MOD, JUMP_JS);
+ __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
break;
case Token::BIT_OR:
- __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS);
+ __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
break;
case Token::BIT_AND:
- __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS);
+ __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
break;
case Token::BIT_XOR:
- __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS);
+ __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
break;
case Token::SAR:
- __ InvokeBuiltin(Builtins::SAR, JUMP_JS);
+ __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
break;
case Token::SHR:
- __ InvokeBuiltin(Builtins::SHR, JUMP_JS);
+ __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
break;
case Token::SHL:
- __ InvokeBuiltin(Builtins::SHL, JUMP_JS);
+ __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
break;
default:
UNREACHABLE();
@@ -2699,14 +2967,12 @@ void TypeRecordingBinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
- MacroAssembler* masm,
- Register result,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Label* gc_required) {
-
+void BinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm,
+ Register result,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
// Code below will scratch result if allocation fails. To keep both arguments
// intact for the runtime call result cannot be one of these.
ASSERT(!result.is(r0) && !result.is(r1));
@@ -2733,7 +2999,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
}
-void TypeRecordingBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
__ Push(r1, r0);
}
@@ -2771,7 +3037,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
r1,
Heap::kHeapNumberMapRootIndex,
&calculate,
- true);
+ DONT_DO_SMI_CHECK);
// Input is a HeapNumber. Load it to a double register and store the
// low and high words into r2, r3.
__ vldr(d0, FieldMemOperand(r0, HeapNumber::kValueOffset));
@@ -2914,17 +3180,24 @@ void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm,
Isolate* isolate = masm->isolate();
__ push(lr);
- __ PrepareCallCFunction(2, scratch);
- __ vmov(r0, r1, d2);
+ __ PrepareCallCFunction(0, 1, scratch);
+ if (masm->use_eabi_hardfloat()) {
+ __ vmov(d0, d2);
+ } else {
+ __ vmov(r0, r1, d2);
+ }
switch (type_) {
case TranscendentalCache::SIN:
- __ CallCFunction(ExternalReference::math_sin_double_function(isolate), 2);
+ __ CallCFunction(ExternalReference::math_sin_double_function(isolate),
+ 0, 1);
break;
case TranscendentalCache::COS:
- __ CallCFunction(ExternalReference::math_cos_double_function(isolate), 2);
+ __ CallCFunction(ExternalReference::math_cos_double_function(isolate),
+ 0, 1);
break;
case TranscendentalCache::LOG:
- __ CallCFunction(ExternalReference::math_log_double_function(isolate), 2);
+ __ CallCFunction(ExternalReference::math_log_double_function(isolate),
+ 0, 1);
break;
default:
UNIMPLEMENTED();
@@ -2952,141 +3225,6 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
}
-void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
- Label slow, done;
-
- Register heap_number_map = r6;
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
-
- if (op_ == Token::SUB) {
- if (include_smi_code_) {
- // Check whether the value is a smi.
- Label try_float;
- __ tst(r0, Operand(kSmiTagMask));
- __ b(ne, &try_float);
-
- // Go slow case if the value of the expression is zero
- // to make sure that we switch between 0 and -0.
- if (negative_zero_ == kStrictNegativeZero) {
- // If we have to check for zero, then we can check for the max negative
- // smi while we are at it.
- __ bic(ip, r0, Operand(0x80000000), SetCC);
- __ b(eq, &slow);
- __ rsb(r0, r0, Operand(0, RelocInfo::NONE));
- __ Ret();
- } else {
- // The value of the expression is a smi and 0 is OK for -0. Try
- // optimistic subtraction '0 - value'.
- __ rsb(r0, r0, Operand(0, RelocInfo::NONE), SetCC);
- __ Ret(vc);
- // We don't have to reverse the optimistic neg since the only case
- // where we fall through is the minimum negative Smi, which is the case
- // where the neg leaves the register unchanged.
- __ jmp(&slow); // Go slow on max negative Smi.
- }
- __ bind(&try_float);
- } else if (FLAG_debug_code) {
- __ tst(r0, Operand(kSmiTagMask));
- __ Assert(ne, "Unexpected smi operand.");
- }
-
- __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ cmp(r1, heap_number_map);
- __ b(ne, &slow);
- // r0 is a heap number. Get a new heap number in r1.
- if (overwrite_ == UNARY_OVERWRITE) {
- __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
- __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
- __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
- } else {
- __ AllocateHeapNumber(r1, r2, r3, r6, &slow);
- __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
- __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
- __ str(r3, FieldMemOperand(r1, HeapNumber::kMantissaOffset));
- __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
- __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset));
- __ mov(r0, Operand(r1));
- }
- } else if (op_ == Token::BIT_NOT) {
- if (include_smi_code_) {
- Label non_smi;
- __ JumpIfNotSmi(r0, &non_smi);
- __ mvn(r0, Operand(r0));
- // Bit-clear inverted smi-tag.
- __ bic(r0, r0, Operand(kSmiTagMask));
- __ Ret();
- __ bind(&non_smi);
- } else if (FLAG_debug_code) {
- __ tst(r0, Operand(kSmiTagMask));
- __ Assert(ne, "Unexpected smi operand.");
- }
-
- // Check if the operand is a heap number.
- __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ cmp(r1, heap_number_map);
- __ b(ne, &slow);
-
- // Convert the heap number is r0 to an untagged integer in r1.
- __ ConvertToInt32(r0, r1, r2, r3, d0, &slow);
-
- // Do the bitwise operation (move negated) and check if the result
- // fits in a smi.
- Label try_float;
- __ mvn(r1, Operand(r1));
- __ add(r2, r1, Operand(0x40000000), SetCC);
- __ b(mi, &try_float);
- __ mov(r0, Operand(r1, LSL, kSmiTagSize));
- __ b(&done);
-
- __ bind(&try_float);
- if (!overwrite_ == UNARY_OVERWRITE) {
- // Allocate a fresh heap number, but don't overwrite r0 until
- // we're sure we can do it without going through the slow case
- // that needs the value in r0.
- __ AllocateHeapNumber(r2, r3, r4, r6, &slow);
- __ mov(r0, Operand(r2));
- }
-
- if (CpuFeatures::IsSupported(VFP3)) {
- // Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
- CpuFeatures::Scope scope(VFP3);
- __ vmov(s0, r1);
- __ vcvt_f64_s32(d0, s0);
- __ sub(r2, r0, Operand(kHeapObjectTag));
- __ vstr(d0, r2, HeapNumber::kValueOffset);
- } else {
- // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
- // have to set up a frame.
- WriteInt32ToHeapNumberStub stub(r1, r0, r2);
- __ push(lr);
- __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
- }
- } else {
- UNIMPLEMENTED();
- }
-
- __ bind(&done);
- __ Ret();
-
- // Handle the slow case by jumping to the JavaScript builtin.
- __ bind(&slow);
- __ push(r0);
- switch (op_) {
- case Token::SUB:
- __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS);
- break;
- case Token::BIT_NOT:
- __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_JS);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
void MathPowStub::Generate(MacroAssembler* masm) {
Label call_runtime;
@@ -3141,11 +3279,11 @@ void MathPowStub::Generate(MacroAssembler* masm) {
heapnumbermap,
&call_runtime);
__ push(lr);
- __ PrepareCallCFunction(3, scratch);
- __ mov(r2, exponent);
- __ vmov(r0, r1, double_base);
+ __ PrepareCallCFunction(1, 1, scratch);
+ __ SetCallCDoubleArguments(double_base, exponent);
__ CallCFunction(
- ExternalReference::power_double_int_function(masm->isolate()), 3);
+ ExternalReference::power_double_int_function(masm->isolate()),
+ 1, 1);
__ pop(lr);
__ GetCFunctionDoubleResult(double_result);
__ vstr(double_result,
@@ -3171,11 +3309,11 @@ void MathPowStub::Generate(MacroAssembler* masm) {
heapnumbermap,
&call_runtime);
__ push(lr);
- __ PrepareCallCFunction(4, scratch);
- __ vmov(r0, r1, double_base);
- __ vmov(r2, r3, double_exponent);
+ __ PrepareCallCFunction(0, 2, scratch);
+ __ SetCallCDoubleArguments(double_base, double_exponent);
__ CallCFunction(
- ExternalReference::power_double_double_function(masm->isolate()), 4);
+ ExternalReference::power_double_double_function(masm->isolate()),
+ 0, 2);
__ pop(lr);
__ GetCFunctionDoubleResult(double_result);
__ vstr(double_result,
@@ -3219,8 +3357,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
if (do_gc) {
// Passing r0.
- __ PrepareCallCFunction(1, r1);
- __ CallCFunction(ExternalReference::perform_gc_function(isolate), 1);
+ __ PrepareCallCFunction(1, 0, r1);
+ __ CallCFunction(ExternalReference::perform_gc_function(isolate),
+ 1, 0);
}
ExternalReference scope_depth =
@@ -3707,7 +3846,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ b(ne, &slow);
// Null is not instance of anything.
- __ cmp(scratch, Operand(FACTORY->null_value()));
+ __ cmp(scratch, Operand(masm->isolate()->factory()->null_value()));
__ b(ne, &object_not_null);
__ mov(r0, Operand(Smi::FromInt(1)));
__ Ret(HasArgsInRegisters() ? 0 : 2);
@@ -3730,11 +3869,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
if (HasArgsInRegisters()) {
__ Push(r0, r1);
}
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_JS);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
} else {
__ EnterInternalFrame();
__ Push(r0, r1);
- __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_JS);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
__ LeaveInternalFrame();
__ cmp(r0, Operand(0));
__ LoadRoot(r0, Heap::kTrueValueRootIndex, eq);
@@ -4087,9 +4226,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// Check that the irregexp code has been generated for the actual string
// encoding. If it has, the field contains a code object otherwise it contains
- // the hole.
- __ CompareObjectType(r7, r0, r0, CODE_TYPE);
- __ b(ne, &runtime);
+ // a smi (code flushing support).
+ __ JumpIfSmi(r7, &runtime);
// r3: encoding of subject string (1 if ASCII, 0 if two_byte);
// r7: code
@@ -4205,7 +4343,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ bind(&failure);
// For failure and exception return null.
- __ mov(r0, Operand(FACTORY->null_value()));
+ __ mov(r0, Operand(masm->isolate()->factory()->null_value()));
__ add(sp, sp, Operand(4 * kPointerSize));
__ Ret();
@@ -4276,6 +4414,8 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
const int kMaxInlineLength = 100;
Label slowcase;
Label done;
+ Factory* factory = masm->isolate()->factory();
+
__ ldr(r1, MemOperand(sp, kPointerSize * 2));
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
@@ -4310,7 +4450,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
// Interleave operations for better latency.
__ ldr(r2, ContextOperand(cp, Context::GLOBAL_INDEX));
__ add(r3, r0, Operand(JSRegExpResult::kSize));
- __ mov(r4, Operand(FACTORY->empty_fixed_array()));
+ __ mov(r4, Operand(factory->empty_fixed_array()));
__ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalContextOffset));
__ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset));
__ ldr(r2, ContextOperand(r2, Context::REGEXP_RESULT_MAP_INDEX));
@@ -4331,13 +4471,13 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
// r5: Number of elements in array, untagged.
// Set map.
- __ mov(r2, Operand(FACTORY->fixed_array_map()));
+ __ mov(r2, Operand(factory->fixed_array_map()));
__ str(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
// Set FixedArray length.
__ mov(r6, Operand(r5, LSL, kSmiTagSize));
__ str(r6, FieldMemOperand(r3, FixedArray::kLengthOffset));
// Fill contents of fixed-array with the-hole.
- __ mov(r2, Operand(FACTORY->the_hole_value()));
+ __ mov(r2, Operand(factory->the_hole_value()));
__ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
// Fill fixed array elements with hole.
// r0: JSArray, tagged.
@@ -4364,30 +4504,22 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow;
- // If the receiver might be a value (string, number or boolean) check for this
- // and box it if it is.
- if (ReceiverMightBeValue()) {
+ // The receiver might implicitly be the global object. This is
+ // indicated by passing the hole as the receiver to the call
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
// Get the receiver from the stack.
// function, receiver [, arguments]
- Label receiver_is_value, receiver_is_js_object;
- __ ldr(r1, MemOperand(sp, argc_ * kPointerSize));
-
- // Check if receiver is a smi (which is a number value).
- __ JumpIfSmi(r1, &receiver_is_value);
-
- // Check if the receiver is a valid JS object.
- __ CompareObjectType(r1, r2, r2, FIRST_JS_OBJECT_TYPE);
- __ b(ge, &receiver_is_js_object);
-
- // Call the runtime to box the value.
- __ bind(&receiver_is_value);
- __ EnterInternalFrame();
- __ push(r1);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
- __ LeaveInternalFrame();
- __ str(r0, MemOperand(sp, argc_ * kPointerSize));
-
- __ bind(&receiver_is_js_object);
+ __ ldr(r4, MemOperand(sp, argc_ * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
+ __ b(ne, &call);
+ // Patch the receiver on the stack with the global receiver object.
+ __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ __ str(r1, MemOperand(sp, argc_ * kPointerSize));
+ __ bind(&call);
}
// Get the function to call from the stack.
@@ -4404,7 +4536,23 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Fast-case: Invoke the function now.
// r1: pushed function
ParameterCount actual(argc_);
- __ InvokeFunction(r1, actual, JUMP_FUNCTION);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
+ __ b(eq, &call_as_function);
+ __ InvokeFunction(r1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(r1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
// Slow-case: Non-function called.
__ bind(&slow);
@@ -4587,7 +4735,7 @@ void StringCharCodeAtGenerator::GenerateSlow(
scratch_,
Heap::kHeapNumberMapRootIndex,
index_not_number_,
- true);
+ DONT_DO_SMI_CHECK);
call_helper.BeforeCall(masm);
__ Push(object_, index_);
__ push(index_); // Consumed by runtime conversion function.
@@ -5299,6 +5447,45 @@ void SubStringStub::Generate(MacroAssembler* masm) {
}
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label strings_not_equal, check_zero_length;
+ __ ldr(length, FieldMemOperand(left, String::kLengthOffset));
+ __ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ cmp(length, scratch2);
+ __ b(eq, &check_zero_length);
+ __ bind(&strings_not_equal);
+ __ mov(r0, Operand(Smi::FromInt(NOT_EQUAL)));
+ __ Ret();
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ tst(length, Operand(length));
+ __ b(ne, &compare_chars);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, length, scratch2, scratch3,
+ &strings_not_equal);
+
+ // Characters are equal.
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
+}
+
+
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -5306,7 +5493,7 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register scratch2,
Register scratch3,
Register scratch4) {
- Label compare_lengths;
+ Label result_not_equal, compare_lengths;
// Find minimum length and length difference.
__ ldr(scratch1, FieldMemOperand(left, String::kLengthOffset));
__ ldr(scratch2, FieldMemOperand(right, String::kLengthOffset));
@@ -5318,46 +5505,56 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
__ tst(min_length, Operand(min_length));
__ b(eq, &compare_lengths);
- // Untag smi.
- __ mov(min_length, Operand(min_length, ASR, kSmiTagSize));
+ // Compare loop.
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, min_length, scratch2, scratch4,
+ &result_not_equal);
- // Setup registers so that we only need to increment one register
- // in the loop.
- __ add(scratch2, min_length,
- Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
- __ add(left, left, Operand(scratch2));
- __ add(right, right, Operand(scratch2));
- // Registers left and right points to the min_length character of strings.
- __ rsb(min_length, min_length, Operand(-1));
- Register index = min_length;
- // Index starts at -min_length.
-
- {
- // Compare loop.
- Label loop;
- __ bind(&loop);
- // Compare characters.
- __ add(index, index, Operand(1), SetCC);
- __ ldrb(scratch2, MemOperand(left, index), ne);
- __ ldrb(scratch4, MemOperand(right, index), ne);
- // Skip to compare lengths with eq condition true.
- __ b(eq, &compare_lengths);
- __ cmp(scratch2, scratch4);
- __ b(eq, &loop);
- // Fallthrough with eq condition false.
- }
- // Compare lengths - strings up to min-length are equal.
+ // Compare lengths - strings up to min-length are equal.
__ bind(&compare_lengths);
ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
- // Use zero length_delta as result.
- __ mov(r0, Operand(length_delta), SetCC, eq);
- // Fall through to here if characters compare not-equal.
+ // Use length_delta as result if it's zero.
+ __ mov(r0, Operand(length_delta), SetCC);
+ __ bind(&result_not_equal);
+ // Conditionally update the result based either on length_delta or
+ // the last comparion performed in the loop above.
__ mov(r0, Operand(Smi::FromInt(GREATER)), LeaveCC, gt);
__ mov(r0, Operand(Smi::FromInt(LESS)), LeaveCC, lt);
__ Ret();
}
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* chars_not_equal) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiUntag(length);
+ __ add(scratch1, length,
+ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ __ add(left, left, Operand(scratch1));
+ __ add(right, right, Operand(scratch1));
+ __ rsb(length, length, Operand(0));
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ ldrb(scratch1, MemOperand(left, index));
+ __ ldrb(scratch2, MemOperand(right, index));
+ __ cmp(scratch1, scratch2);
+ __ b(ne, chars_not_equal);
+ __ add(index, index, Operand(1), SetCC);
+ __ b(ne, &loop);
+}
+
+
void StringCompareStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -5684,7 +5881,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
if (call_builtin.is_linked()) {
__ bind(&call_builtin);
- __ InvokeBuiltin(builtin_id, JUMP_JS);
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
}
}
@@ -5810,6 +6007,109 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
}
+void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SYMBOLS);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+ Register tmp1 = r2;
+ Register tmp2 = r3;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are symbols.
+ __ ldr(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ ldrb(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ ldrb(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp1, Operand(tmp2));
+ __ tst(tmp1, Operand(kIsSymbolMask));
+ __ b(eq, &miss);
+
+ // Symbols are compared by identity.
+ __ cmp(left, right);
+ // Make sure r0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(r0));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)), LeaveCC, eq);
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRINGS);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = r1;
+ Register right = r0;
+ Register tmp1 = r2;
+ Register tmp2 = r3;
+ Register tmp3 = r4;
+ Register tmp4 = r5;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ ldr(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ ldr(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ ldrb(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ ldrb(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ orr(tmp3, tmp1, tmp2);
+ __ tst(tmp3, Operand(kIsNotStringMask));
+ __ b(ne, &miss);
+
+ // Fast check for identical strings.
+ __ cmp(left, right);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(r0, Operand(Smi::FromInt(EQUAL)), LeaveCC, eq);
+ __ Ret(eq);
+
+ // Handle not identical strings.
+
+ // Check that both strings are symbols. If they are, we're done
+ // because we already know they are not identical.
+ ASSERT(GetCondition() == eq);
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp3, tmp1, Operand(tmp2));
+ __ tst(tmp3, Operand(kIsSymbolMask));
+ // Make sure r0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(r0));
+ __ Ret(ne);
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(tmp1, tmp2, tmp3, tmp4,
+ &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2, tmp3);
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ Push(left, right);
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::OBJECTS);
Label miss;
@@ -5880,6 +6180,239 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
}
+MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ String* name,
+ Register scratch0) {
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // scratch0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = scratch0;
+ // Capacity is smi 2^n.
+ __ ldr(index, FieldMemOperand(properties, kCapacityOffset));
+ __ sub(index, index, Operand(1));
+ __ and_(index, index, Operand(
+ Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ add(index, index, Operand(index, LSL, 1)); // index *= 3.
+
+ Register entity_name = scratch0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ Register tmp = properties;
+ __ add(tmp, properties, Operand(index, LSL, 1));
+ __ ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset));
+
+ ASSERT(!tmp.is(entity_name));
+ __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
+ __ cmp(entity_name, tmp);
+ __ b(eq, done);
+
+ if (i != kInlinedProbes - 1) {
+ // Stop if found the property.
+ __ cmp(entity_name, Operand(Handle<String>(name)));
+ __ b(eq, miss);
+
+ // Check if the entry name is not a symbol.
+ __ ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset));
+ __ ldrb(entity_name,
+ FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
+ __ tst(entity_name, Operand(kIsSymbolMask));
+ __ b(eq, miss);
+
+ // Restore the properties.
+ __ ldr(properties,
+ FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ }
+ }
+
+ const int spill_mask =
+ (lr.bit() | r6.bit() | r5.bit() | r4.bit() | r3.bit() |
+ r2.bit() | r1.bit() | r0.bit());
+
+ __ stm(db_w, sp, spill_mask);
+ __ ldr(r0, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ mov(r1, Operand(Handle<String>(name)));
+ StringDictionaryLookupStub stub(NEGATIVE_LOOKUP);
+ MaybeObject* result = masm->TryCallStub(&stub);
+ if (result->IsFailure()) return result;
+ __ tst(r0, Operand(r0));
+ __ ldm(ia_w, sp, spill_mask);
+
+ __ b(eq, done);
+ __ b(ne, miss);
+ return result;
+}
+
+
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found. Jump to
+// the |miss| label otherwise.
+// If lookup was successful |scratch2| will be equal to elements + 4 * index.
+void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register scratch1,
+ Register scratch2) {
+ // Assert that name contains a string.
+ if (FLAG_debug_code) __ AbortIfNotString(name);
+
+ // Compute the capacity mask.
+ __ ldr(scratch1, FieldMemOperand(elements, kCapacityOffset));
+ __ mov(scratch1, Operand(scratch1, ASR, kSmiTagSize)); // convert smi to int
+ __ sub(scratch1, scratch1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up. Measurements done on Gmail indicate that 2 probes
+ // cover ~93% of loads from dictionaries.
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ ldr(scratch2, FieldMemOperand(name, String::kHashFieldOffset));
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(StringDictionary::GetProbeOffset(i) <
+ 1 << (32 - String::kHashFieldOffset));
+ __ add(scratch2, scratch2, Operand(
+ StringDictionary::GetProbeOffset(i) << String::kHashShift));
+ }
+ __ and_(scratch2, scratch1, Operand(scratch2, LSR, String::kHashShift));
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ // scratch2 = scratch2 * 3.
+ __ add(scratch2, scratch2, Operand(scratch2, LSL, 1));
+
+ // Check if the key is identical to the name.
+ __ add(scratch2, elements, Operand(scratch2, LSL, 2));
+ __ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
+ __ cmp(name, Operand(ip));
+ __ b(eq, done);
+ }
+
+ const int spill_mask =
+ (lr.bit() | r6.bit() | r5.bit() | r4.bit() |
+ r3.bit() | r2.bit() | r1.bit() | r0.bit()) &
+ ~(scratch1.bit() | scratch2.bit());
+
+ __ stm(db_w, sp, spill_mask);
+ __ Move(r0, elements);
+ __ Move(r1, name);
+ StringDictionaryLookupStub stub(POSITIVE_LOOKUP);
+ __ CallStub(&stub);
+ __ tst(r0, Operand(r0));
+ __ mov(scratch2, Operand(r2));
+ __ ldm(ia_w, sp, spill_mask);
+
+ __ b(ne, done);
+ __ b(eq, miss);
+}
+
+
+void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
+ // Registers:
+ // result: StringDictionary to probe
+ // r1: key
+ // : StringDictionary to probe.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Register result = r0;
+ Register dictionary = r0;
+ Register key = r1;
+ Register index = r2;
+ Register mask = r3;
+ Register hash = r4;
+ Register undefined = r5;
+ Register entry_key = r6;
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ __ ldr(mask, FieldMemOperand(dictionary, kCapacityOffset));
+ __ mov(mask, Operand(mask, ASR, kSmiTagSize));
+ __ sub(mask, mask, Operand(1));
+
+ __ ldr(hash, FieldMemOperand(key, String::kHashFieldOffset));
+
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ // Capacity is smi 2^n.
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(StringDictionary::GetProbeOffset(i) <
+ 1 << (32 - String::kHashFieldOffset));
+ __ add(index, hash, Operand(
+ StringDictionary::GetProbeOffset(i) << String::kHashShift));
+ } else {
+ __ mov(index, Operand(hash));
+ }
+ __ and_(index, mask, Operand(index, LSR, String::kHashShift));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ add(index, index, Operand(index, LSL, 1)); // index *= 3.
+
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ add(index, dictionary, Operand(index, LSL, 2));
+ __ ldr(entry_key, FieldMemOperand(index, kElementsStartOffset));
+
+ // Having undefined at this place means the name is not contained.
+ __ cmp(entry_key, Operand(undefined));
+ __ b(eq, &not_in_dictionary);
+
+ // Stop if found the property.
+ __ cmp(entry_key, Operand(key));
+ __ b(eq, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // Check if the entry name is not a symbol.
+ __ ldr(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset));
+ __ ldrb(entry_key,
+ FieldMemOperand(entry_key, Map::kInstanceTypeOffset));
+ __ tst(entry_key, Operand(kIsSymbolMask));
+ __ b(eq, &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ mov(result, Operand(0));
+ __ Ret();
+ }
+
+ __ bind(&in_dictionary);
+ __ mov(result, Operand(1));
+ __ Ret();
+
+ __ bind(&not_in_dictionary);
+ __ mov(result, Operand(0));
+ __ Ret();
+}
+
+
#undef __
} } // namespace v8::internal
diff --git a/src/arm/code-stubs-arm.h b/src/arm/code-stubs-arm.h
index d82afc74..fb05cd2d 100644
--- a/src/arm/code-stubs-arm.h
+++ b/src/arm/code-stubs-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -71,22 +71,108 @@ class ToBooleanStub: public CodeStub {
};
-class TypeRecordingBinaryOpStub: public CodeStub {
+class UnaryOpStub: public CodeStub {
public:
- TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
+ UnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
- operands_type_(TRBinaryOpIC::UNINITIALIZED),
- result_type_(TRBinaryOpIC::UNINITIALIZED),
+ operand_type_(UnaryOpIC::UNINITIALIZED),
+ name_(NULL) {
+ }
+
+ UnaryOpStub(
+ int key,
+ UnaryOpIC::TypeInfo operand_type)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ operand_type_(operand_type),
+ name_(NULL) {
+ }
+
+ private:
+ Token::Value op_;
+ UnaryOverwriteMode mode_;
+
+ // Operand type information determined at runtime.
+ UnaryOpIC::TypeInfo operand_type_;
+
+ char* name_;
+
+ const char* GetName();
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("UnaryOpStub %d (op %s), "
+ "(mode %d, runtime_type_info %s)\n",
+ MinorKey(),
+ Token::String(op_),
+ static_cast<int>(mode_),
+ UnaryOpIC::GetName(operand_type_));
+ }
+#endif
+
+ class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
+ class OpBits: public BitField<Token::Value, 1, 7> {};
+ class OperandTypeInfoBits: public BitField<UnaryOpIC::TypeInfo, 8, 3> {};
+
+ Major MajorKey() { return UnaryOp; }
+ int MinorKey() {
+ return ModeBits::encode(mode_)
+ | OpBits::encode(op_)
+ | OperandTypeInfoBits::encode(operand_type_);
+ }
+
+ // Note: A lot of the helper functions below will vanish when we use virtual
+ // function instead of switch more often.
+ void Generate(MacroAssembler* masm);
+
+ void GenerateTypeTransition(MacroAssembler* masm);
+
+ void GenerateSmiStub(MacroAssembler* masm);
+ void GenerateSmiStubSub(MacroAssembler* masm);
+ void GenerateSmiStubBitNot(MacroAssembler* masm);
+ void GenerateSmiCodeSub(MacroAssembler* masm, Label* non_smi, Label* slow);
+ void GenerateSmiCodeBitNot(MacroAssembler* masm, Label* slow);
+
+ void GenerateHeapNumberStub(MacroAssembler* masm);
+ void GenerateHeapNumberStubSub(MacroAssembler* masm);
+ void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
+ void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
+ void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
+
+ void GenerateGenericStub(MacroAssembler* masm);
+ void GenerateGenericStubSub(MacroAssembler* masm);
+ void GenerateGenericStubBitNot(MacroAssembler* masm);
+ void GenerateGenericCodeFallback(MacroAssembler* masm);
+
+ virtual int GetCodeKind() { return Code::UNARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return UnaryOpIC::ToState(operand_type_);
+ }
+
+ virtual void FinishCode(Code* code) {
+ code->set_unary_op_type(operand_type_);
+ }
+};
+
+
+class BinaryOpStub: public CodeStub {
+ public:
+ BinaryOpStub(Token::Value op, OverwriteMode mode)
+ : op_(op),
+ mode_(mode),
+ operands_type_(BinaryOpIC::UNINITIALIZED),
+ result_type_(BinaryOpIC::UNINITIALIZED),
name_(NULL) {
use_vfp3_ = CpuFeatures::IsSupported(VFP3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
- TypeRecordingBinaryOpStub(
+ BinaryOpStub(
int key,
- TRBinaryOpIC::TypeInfo operands_type,
- TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED)
+ BinaryOpIC::TypeInfo operands_type,
+ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
use_vfp3_(VFP3Bits::decode(key)),
@@ -105,8 +191,8 @@ class TypeRecordingBinaryOpStub: public CodeStub {
bool use_vfp3_;
// Operand type information determined at runtime.
- TRBinaryOpIC::TypeInfo operands_type_;
- TRBinaryOpIC::TypeInfo result_type_;
+ BinaryOpIC::TypeInfo operands_type_;
+ BinaryOpIC::TypeInfo result_type_;
char* name_;
@@ -114,12 +200,12 @@ class TypeRecordingBinaryOpStub: public CodeStub {
#ifdef DEBUG
void Print() {
- PrintF("TypeRecordingBinaryOpStub %d (op %s), "
+ PrintF("BinaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
}
#endif
@@ -127,10 +213,10 @@ class TypeRecordingBinaryOpStub: public CodeStub {
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
class VFP3Bits: public BitField<bool, 9, 1> {};
- class OperandTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 10, 3> {};
- class ResultTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 13, 3> {};
+ class OperandTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 10, 3> {};
+ class ResultTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 13, 3> {};
- Major MajorKey() { return TypeRecordingBinaryOp; }
+ Major MajorKey() { return BinaryOp; }
int MinorKey() {
return OpBits::encode(op_)
| ModeBits::encode(mode_)
@@ -158,6 +244,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
void GenerateCallRuntime(MacroAssembler* masm);
@@ -172,15 +259,15 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
- virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; }
+ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
virtual InlineCacheState GetICState() {
- return TRBinaryOpIC::ToState(operands_type_);
+ return BinaryOpIC::ToState(operands_type_);
}
virtual void FinishCode(Code* code) {
- code->set_type_recording_binary_op_type(operands_type_);
- code->set_type_recording_binary_op_result_type(result_type_);
+ code->set_binary_op_type(operands_type_);
+ code->set_binary_op_result_type(result_type_);
}
friend class CodeGenerator;
@@ -240,8 +327,7 @@ class StringCompareStub: public CodeStub {
public:
StringCompareStub() { }
- // Compare two flat ASCII strings and returns result in r0.
- // Does not use the stack.
+ // Compares two flat ASCII strings and returns result in r0.
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -250,11 +336,27 @@ class StringCompareStub: public CodeStub {
Register scratch3,
Register scratch4);
- private:
- Major MajorKey() { return StringCompare; }
- int MinorKey() { return 0; }
+ // Compares two flat ASCII strings for equality and returns result
+ // in r0.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3);
- void Generate(MacroAssembler* masm);
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Label* chars_not_equal);
};
@@ -367,6 +469,205 @@ class DirectCEntryStub: public CodeStub {
};
+class FloatingPointHelper : public AllStatic {
+ public:
+
+ enum Destination {
+ kVFPRegisters,
+ kCoreRegisters
+ };
+
+
+ // Loads smis from r0 and r1 (right and left in binary operations) into
+ // floating point registers. Depending on the destination the values ends up
+ // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is
+ // floating point registers VFP3 must be supported. If core registers are
+ // requested when VFP3 is supported d6 and d7 will be scratched.
+ static void LoadSmis(MacroAssembler* masm,
+ Destination destination,
+ Register scratch1,
+ Register scratch2);
+
+ // Loads objects from r0 and r1 (right and left in binary operations) into
+ // floating point registers. Depending on the destination the values ends up
+ // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is
+ // floating point registers VFP3 must be supported. If core registers are
+ // requested when VFP3 is supported d6 and d7 will still be scratched. If
+ // either r0 or r1 is not a number (not smi and not heap number object) the
+ // not_number label is jumped to with r0 and r1 intact.
+ static void LoadOperands(MacroAssembler* masm,
+ FloatingPointHelper::Destination destination,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* not_number);
+
+ // Convert the smi or heap number in object to an int32 using the rules
+ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+ // and brought into the range -2^31 .. +2^31 - 1.
+ static void ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_int32);
+
+ // Converts the integer (untagged smi) in |int_scratch| to a double, storing
+ // the result either in |double_dst| or |dst2:dst1|, depending on
+ // |destination|.
+ // Warning: The value in |int_scratch| will be changed in the process!
+ static void ConvertIntToDouble(MacroAssembler* masm,
+ Register int_scratch,
+ Destination destination,
+ DwVfpRegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register scratch2,
+ SwVfpRegister single_scratch);
+
+ // Load the number from object into double_dst in the double format.
+ // Control will jump to not_int32 if the value cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be loaded.
+ static void LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ DwVfpRegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ SwVfpRegister single_scratch,
+ Label* not_int32);
+
+ // Loads the number from object into dst as a 32-bit integer.
+ // Control will jump to not_int32 if the object cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be converted.
+ // scratch3 is not used when VFP3 is supported.
+ static void LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ DwVfpRegister double_scratch,
+ Label* not_int32);
+
+ // Generate non VFP3 code to check if a double can be exactly represented by a
+ // 32-bit integer. This does not check for 0 or -0, which need
+ // to be checked for separately.
+ // Control jumps to not_int32 if the value is not a 32-bit integer, and falls
+ // through otherwise.
+ // src1 and src2 will be cloberred.
+ //
+ // Expected input:
+ // - src1: higher (exponent) part of the double value.
+ // - src2: lower (mantissa) part of the double value.
+ // Output status:
+ // - dst: 32 higher bits of the mantissa. (mantissa[51:20])
+ // - src2: contains 1.
+ // - other registers are clobbered.
+ static void DoubleIs32BitInteger(MacroAssembler* masm,
+ Register src1,
+ Register src2,
+ Register dst,
+ Register scratch,
+ Label* not_int32);
+
+ // Generates code to call a C function to do a double operation using core
+ // registers. (Used when VFP3 is not supported.)
+ // This code never falls through, but returns with a heap number containing
+ // the result in r0.
+ // Register heapnumber_result must be a heap number in which the
+ // result of the operation will be stored.
+ // Requires the following layout on entry:
+ // r0: Left value (least significant part of mantissa).
+ // r1: Left value (sign, exponent, top of mantissa).
+ // r2: Right value (least significant part of mantissa).
+ // r3: Right value (sign, exponent, top of mantissa).
+ static void CallCCodeForDoubleOperation(MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch);
+
+ private:
+ static void LoadNumber(MacroAssembler* masm,
+ FloatingPointHelper::Destination destination,
+ Register object,
+ DwVfpRegister dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* not_number);
+};
+
+
+class StringDictionaryLookupStub: public CodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ explicit StringDictionaryLookupStub(LookupMode mode) : mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ String* name,
+ Register scratch0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("StringDictionaryLookupStub\n");
+ }
+#endif
+
+ Major MajorKey() { return StringDictionaryNegativeLookup; }
+
+ int MinorKey() {
+ return LookupModeBits::encode(mode_);
+ }
+
+ class LookupModeBits: public BitField<LookupMode, 0, 1> {};
+
+ LookupMode mode_;
+};
+
+
} } // namespace v8::internal
#endif // V8_ARM_CODE_STUBS_ARM_H_
diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc
index 6c82c122..5b62d822 100644
--- a/src/arm/deoptimizer-arm.cc
+++ b/src/arm/deoptimizer-arm.cc
@@ -552,13 +552,21 @@ void Deoptimizer::EntryGenerator::Generate() {
const int kDoubleRegsSize =
kDoubleSize * DwVfpRegister::kNumAllocatableRegisters;
- // Save all general purpose registers before messing with them.
- __ sub(sp, sp, Operand(kDoubleRegsSize));
- for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; ++i) {
- DwVfpRegister vfp_reg = DwVfpRegister::FromAllocationIndex(i);
- int offset = i * kDoubleSize;
- __ vstr(vfp_reg, sp, offset);
+ // Save all VFP registers before messing with them.
+ DwVfpRegister first = DwVfpRegister::FromAllocationIndex(0);
+ DwVfpRegister last =
+ DwVfpRegister::FromAllocationIndex(
+ DwVfpRegister::kNumAllocatableRegisters - 1);
+ ASSERT(last.code() > first.code());
+ ASSERT((last.code() - first.code()) ==
+ (DwVfpRegister::kNumAllocatableRegisters - 1));
+#ifdef DEBUG
+ for (int i = 0; i <= (DwVfpRegister::kNumAllocatableRegisters - 1); i++) {
+ ASSERT((DwVfpRegister::FromAllocationIndex(i).code() <= last.code()) &&
+ (DwVfpRegister::FromAllocationIndex(i).code() >= first.code()));
}
+#endif
+ __ vstm(db_w, sp, first, last);
// Push all 16 registers (needed to populate FrameDescription::registers_).
__ stm(db_w, sp, restored_regs | sp.bit() | lr.bit() | pc.bit());
diff --git a/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc
index a3775b5f..d4bd81ce 100644
--- a/src/arm/disasm-arm.cc
+++ b/src/arm/disasm-arm.cc
@@ -502,13 +502,16 @@ int Decoder::FormatOption(Instruction* instr, const char* format) {
ASSERT(STRING_STARTS_WITH(format, "memop"));
if (instr->HasL()) {
Print("ldr");
- } else if ((instr->Bits(27, 25) == 0) && (instr->Bit(20) == 0)) {
- if (instr->Bits(7, 4) == 0xf) {
- Print("strd");
- } else {
- Print("ldrd");
- }
} else {
+ if ((instr->Bits(27, 25) == 0) && (instr->Bit(20) == 0) &&
+ (instr->Bits(7, 6) == 3) && (instr->Bit(4) == 1)) {
+ if (instr->Bit(5) == 1) {
+ Print("strd");
+ } else {
+ Print("ldrd");
+ }
+ return 5;
+ }
Print("str");
}
return 5;
@@ -1086,10 +1089,10 @@ void Decoder::DecodeTypeVFP(Instruction* instr) {
}
} else if ((instr->Opc2Value() == 0x0) && (instr->Opc3Value() == 0x3)) {
// vabs
- Format(instr, "vabs'cond 'Dd, 'Dm");
+ Format(instr, "vabs.f64'cond 'Dd, 'Dm");
} else if ((instr->Opc2Value() == 0x1) && (instr->Opc3Value() == 0x1)) {
// vneg
- Format(instr, "vneg'cond 'Dd, 'Dm");
+ Format(instr, "vneg.f64'cond 'Dd, 'Dm");
} else if ((instr->Opc2Value() == 0x7) && (instr->Opc3Value() == 0x3)) {
DecodeVCVTBetweenDoubleAndSingle(instr);
} else if ((instr->Opc2Value() == 0x8) && (instr->Opc3Value() & 0x1)) {
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 871b4539..61165135 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -46,6 +46,12 @@ namespace internal {
#define __ ACCESS_MASM(masm_)
+static unsigned GetPropertyId(Property* property) {
+ if (property->is_synthetic()) return AstNode::kNoNumber;
+ return property->id();
+}
+
+
// A patch site is a location in the code which it is possible to patch. This
// class has a number of methods to emit the code which is patchable and the
// method EmitPatchInfo to record a marker back to the patchable code. This
@@ -133,6 +139,20 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
}
#endif
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). r5 is zero for method calls and non-zero for function
+ // calls.
+ if (info->is_strict_mode()) {
+ Label ok;
+ __ cmp(r5, Operand(0));
+ __ b(eq, &ok);
+ int receiver_offset = scope()->num_parameters() * kPointerSize;
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ str(r2, MemOperand(sp, receiver_offset));
+ __ bind(&ok);
+ }
+
int locals_count = scope()->num_stack_slots();
__ Push(lr, fp, cp, r1);
@@ -562,23 +582,6 @@ void FullCodeGenerator::DoTest(Label* if_true,
Label* if_false,
Label* fall_through) {
if (CpuFeatures::IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- // Emit the inlined tests assumed by the stub.
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(result_register(), ip);
- __ b(eq, if_false);
- __ LoadRoot(ip, Heap::kTrueValueRootIndex);
- __ cmp(result_register(), ip);
- __ b(eq, if_true);
- __ LoadRoot(ip, Heap::kFalseValueRootIndex);
- __ cmp(result_register(), ip);
- __ b(eq, if_false);
- STATIC_ASSERT(kSmiTag == 0);
- __ tst(result_register(), result_register());
- __ b(eq, if_false);
- __ JumpIfSmi(result_register(), if_true);
-
- // Call the ToBoolean stub for all other cases.
ToBooleanStub stub(result_register());
__ CallStub(&stub);
__ tst(result_register(), result_register());
@@ -590,8 +593,6 @@ void FullCodeGenerator::DoTest(Label* if_true,
__ LoadRoot(ip, Heap::kFalseValueRootIndex);
__ cmp(r0, ip);
}
-
- // The stub returns nonzero for true.
Split(ne, if_true, if_false, fall_through);
}
@@ -759,23 +760,22 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
}
} else if (prop != NULL) {
- if (function != NULL || mode == Variable::CONST) {
- // We are declaring a function or constant that rewrites to a
- // property. Use (keyed) IC to set the initial value. We
- // cannot visit the rewrite because it's shared and we risk
- // recording duplicate AST IDs for bailouts from optimized code.
+ // A const declaration aliasing a parameter is an illegal redeclaration.
+ ASSERT(mode != Variable::CONST);
+ if (function != NULL) {
+ // We are declaring a function that rewrites to a property.
+ // Use (keyed) IC to set the initial value. We cannot visit the
+ // rewrite because it's shared and we risk recording duplicate AST
+ // IDs for bailouts from optimized code.
ASSERT(prop->obj()->AsVariableProxy() != NULL);
{ AccumulatorValueContext for_object(this);
EmitVariableLoad(prop->obj()->AsVariableProxy()->var());
}
- if (function != NULL) {
- __ push(r0);
- VisitForAccumulatorValue(function);
- __ pop(r2);
- } else {
- __ mov(r2, r0);
- __ LoadRoot(r0, Heap::kTheHoleValueRootIndex);
- }
+
+ __ push(r0);
+ VisitForAccumulatorValue(function);
+ __ pop(r2);
+
ASSERT(prop->key()->AsLiteral() != NULL &&
prop->key()->AsLiteral()->handle()->IsSmi());
__ mov(r1, Operand(prop->key()->AsLiteral()->handle()));
@@ -783,7 +783,7 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
// Value in r0 is ignored (declarations are statements).
}
}
@@ -857,7 +857,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT);
- EmitCallIC(ic, &patch_site);
+ EmitCallIC(ic, &patch_site, clause->CompareId());
+
__ cmp(r0, Operand(0));
__ b(ne, &next_test);
__ Drop(1); // Switch value is no longer needed.
@@ -915,7 +916,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ b(hs, &done_convert);
__ bind(&convert);
__ push(r0);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ bind(&done_convert);
__ push(r0);
@@ -943,9 +944,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// check for an enum cache. Leave the map in r2 for the subsequent
// prototype load.
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ ldr(r3, FieldMemOperand(r2, Map::kInstanceDescriptorsOffset));
- __ cmp(r3, empty_descriptor_array_value);
- __ b(eq, &call_runtime);
+ __ ldr(r3, FieldMemOperand(r2, Map::kInstanceDescriptorsOrBitField3Offset));
+ __ JumpIfSmi(r3, &call_runtime);
// Check that there is an enum cache in the non-empty instance
// descriptors (r3). This is the case if the next enumeration
@@ -990,7 +990,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// We got a map in register r0. Get the enumeration cache from it.
__ bind(&use_cache);
- __ ldr(r1, FieldMemOperand(r0, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(r0, r1);
__ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
__ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset));
@@ -1039,7 +1039,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// just skip it.
__ push(r1); // Enumerable.
__ push(r3); // Current entry.
- __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS);
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
__ mov(r3, Operand(r0), SetCC);
__ b(eq, loop_statement.continue_target());
@@ -1109,6 +1109,67 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
}
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
+ Slot* slot,
+ TypeofState typeof_state,
+ Label* slow) {
+ Register current = cp;
+ Register next = r1;
+ Register temp = r2;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ }
+ // Load next context in chain.
+ __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX));
+ __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ // Walk the rest of the chain without clobbering cp.
+ current = next;
+ }
+ // If no outer scope calls eval, we do not need to check more
+ // context extensions.
+ if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s->is_eval_scope()) {
+ Label loop, fast;
+ if (!current.is(next)) {
+ __ Move(next, current);
+ }
+ __ bind(&loop);
+ // Terminate at global context.
+ __ ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
+ __ cmp(temp, ip);
+ __ b(eq, &fast);
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(next, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ // Load next context in chain.
+ __ ldr(next, ContextOperand(next, Context::CLOSURE_INDEX));
+ __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ __ b(&loop);
+ __ bind(&fast);
+ }
+
+ __ ldr(r0, GlobalObjectOperand());
+ __ mov(r2, Operand(slot->var()->name()));
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ EmitCallIC(ic, mode, AstNode::kNoNumber);
+}
+
+
MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
Slot* slot,
Label* slow) {
@@ -1187,7 +1248,7 @@ void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
__ mov(r0, Operand(key_literal->handle()));
Handle<Code> ic =
isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
__ jmp(done);
}
}
@@ -1196,67 +1257,6 @@ void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
}
-void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
- Slot* slot,
- TypeofState typeof_state,
- Label* slow) {
- Register current = cp;
- Register next = r1;
- Register temp = r2;
-
- Scope* s = scope();
- while (s != NULL) {
- if (s->num_heap_slots() > 0) {
- if (s->calls_eval()) {
- // Check that extension is NULL.
- __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
- __ tst(temp, temp);
- __ b(ne, slow);
- }
- // Load next context in chain.
- __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX));
- __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
- // Walk the rest of the chain without clobbering cp.
- current = next;
- }
- // If no outer scope calls eval, we do not need to check more
- // context extensions.
- if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
- s = s->outer_scope();
- }
-
- if (s->is_eval_scope()) {
- Label loop, fast;
- if (!current.is(next)) {
- __ Move(next, current);
- }
- __ bind(&loop);
- // Terminate at global context.
- __ ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
- __ cmp(temp, ip);
- __ b(eq, &fast);
- // Check that extension is NULL.
- __ ldr(temp, ContextOperand(next, Context::EXTENSION_INDEX));
- __ tst(temp, temp);
- __ b(ne, slow);
- // Load next context in chain.
- __ ldr(next, ContextOperand(next, Context::CLOSURE_INDEX));
- __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
- __ b(&loop);
- __ bind(&fast);
- }
-
- __ ldr(r0, GlobalObjectOperand());
- __ mov(r2, Operand(slot->var()->name()));
- RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
- ? RelocInfo::CODE_TARGET
- : RelocInfo::CODE_TARGET_CONTEXT;
- Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, mode);
-}
-
-
void FullCodeGenerator::EmitVariableLoad(Variable* var) {
// Four cases: non-this global variables, lookup slots, all other
// types of slots, and parameters that rewrite to explicit property
@@ -1271,7 +1271,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
__ ldr(r0, GlobalObjectOperand());
__ mov(r2, Operand(var->name()));
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
context()->Plug(r0);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
@@ -1330,7 +1330,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
// Call keyed load IC. It has arguments key and receiver in r0 and r1.
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
context()->Plug(r0);
}
}
@@ -1438,8 +1438,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
VisitForAccumulatorValue(value);
__ mov(r2, Operand(key->handle()));
__ ldr(r1, MemOperand(sp));
- Handle<Code> ic = isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, key->id());
PrepareForBailoutForId(key->id(), NO_REGISTERS);
} else {
VisitForEffect(value);
@@ -1651,13 +1653,13 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
SetSourcePosition(expr->position() + 1);
AccumulatorValueContext context(this);
if (ShouldInlineSmiCase(op)) {
- EmitInlineSmiBinaryOp(expr,
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
op,
mode,
expr->target(),
expr->value());
} else {
- EmitBinaryOp(op, mode);
+ EmitBinaryOp(expr->binary_operation(), op, mode);
}
// Deoptimization point in case the binary operation may have side effects.
@@ -1693,7 +1695,7 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
__ mov(r2, Operand(key->handle()));
// Call load IC. It has arguments receiver and property name r0 and r2.
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
@@ -1701,11 +1703,11 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
// Call keyed load IC. It has arguments key and receiver in r0 and r1.
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
-void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
Expression* left_expr,
@@ -1727,14 +1729,14 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
patch_site.EmitJumpIfSmi(scratch1, &smi_case);
__ bind(&stub_call);
- TypeRecordingBinaryOpStub stub(op, mode);
- EmitCallIC(stub.GetCode(), &patch_site);
+ BinaryOpStub stub(op, mode);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->id());
__ jmp(&done);
__ bind(&smi_case);
// Smi case. This code works the same way as the smi-smi case in the type
// recording binary operation stub, see
- // TypeRecordingBinaryOpStub::GenerateSmiSmiOperation for comments.
+ // BinaryOpStub::GenerateSmiSmiOperation for comments.
switch (op) {
case Token::SAR:
__ b(&stub_call);
@@ -1804,11 +1806,12 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
}
-void FullCodeGenerator::EmitBinaryOp(Token::Value op,
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
OverwriteMode mode) {
__ pop(r1);
- TypeRecordingBinaryOpStub stub(op, mode);
- EmitCallIC(stub.GetCode(), NULL);
+ BinaryOpStub stub(op, mode);
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
context()->Plug(r0);
}
@@ -1848,7 +1851,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
break;
}
case KEYED_PROPERTY: {
@@ -1871,7 +1874,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
break;
}
}
@@ -1897,7 +1900,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
} else if (op == Token::INIT_CONST) {
// Like var declarations, const declarations are hoisted to function
@@ -2006,7 +2009,7 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -2052,7 +2055,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -2103,8 +2106,8 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- EmitCallIC(ic, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ EmitCallIC(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
@@ -2113,8 +2116,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
- Expression* key,
- RelocInfo::Mode mode) {
+ Expression* key) {
// Load the key.
VisitForAccumulatorValue(key);
@@ -2139,7 +2141,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
Handle<Code> ic =
isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
__ ldr(r2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
- EmitCallIC(ic, mode);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
@@ -2147,7 +2149,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
}
-void FullCodeGenerator::EmitCallWithStub(Call* expr) {
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@@ -2159,7 +2161,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Record source position for debugger.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arg_count, in_loop, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2255,7 +2257,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Record source position for debugger.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2305,7 +2307,10 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ bind(&call);
}
- EmitCallWithStub(expr);
+ // The receiver is either the global receiver or an object found
+ // by LoadContextSlot. That object could be the hole if the
+ // receiver is implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
} else if (fun->AsProperty() != NULL) {
// Call to an object property.
Property* prop = fun->AsProperty();
@@ -2319,7 +2324,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
} else {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
- // for a regular property use keyed CallIC.
+ // for a regular property use keyed EmitCallIC.
if (prop->is_synthetic()) {
// Do not visit the object and key subexpressions (they are shared
// by all occurrences of the same rewritten parameter).
@@ -2337,16 +2342,16 @@ void FullCodeGenerator::VisitCall(Call* expr) {
SetSourcePosition(prop->position());
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
__ ldr(r1, GlobalObjectOperand());
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
__ Push(r0, r1); // Function, receiver.
- EmitCallWithStub(expr);
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
} else {
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
- EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
+ EmitKeyedCallWithIC(expr, prop->key());
}
}
} else {
@@ -2358,7 +2363,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
__ push(r1);
// Emit function call.
- EmitCallWithStub(expr);
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
}
#ifdef DEBUG
@@ -2548,7 +2553,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
- __ ldr(r4, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(r1, r4);
__ ldr(r3, FieldMemOperand(r4, FixedArray::kLengthOffset));
// r4: descriptor array
// r3: length of descriptor array
@@ -3161,17 +3166,17 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);
- int arg_count = args->length() - 2; // For receiver and function.
- VisitForStackValue(args->at(0)); // Receiver.
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i + 1));
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
}
- VisitForAccumulatorValue(args->at(arg_count + 1)); // Function.
+ VisitForAccumulatorValue(args->last()); // Function.
- // InvokeFunction requires function in r1. Move it in there.
- if (!result_register().is(r1)) __ mov(r1, result_register());
+ // InvokeFunction requires the function in r1. Move it in there.
+ __ mov(r1, result_register());
ParameterCount count(arg_count);
- __ InvokeFunction(r1, count, CALL_FUNCTION);
+ __ InvokeFunction(r1, count, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
context()->Plug(r0);
}
@@ -3656,9 +3661,12 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
if (expr->is_jsruntime()) {
// Call the JS runtime function.
__ mov(r2, Operand(expr->name()));
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, NOT_IN_LOOP);
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count,
+ NOT_IN_LOOP,
+ mode);
+ EmitCallIC(ic, mode, expr->id());
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
} else {
@@ -3686,7 +3694,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
VisitForStackValue(prop->key());
__ mov(r1, Operand(Smi::FromInt(strict_mode_flag())));
__ push(r1);
- __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
context()->Plug(r0);
}
} else if (var != NULL) {
@@ -3698,7 +3706,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
__ mov(r1, Operand(var->name()));
__ mov(r0, Operand(Smi::FromInt(kNonStrictMode)));
__ Push(r2, r1, r0);
- __ InvokeBuiltin(Builtins::DELETE, CALL_JS);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
context()->Plug(r0);
} else if (var->AsSlot() != NULL &&
var->AsSlot()->type() != Slot::LOOKUP) {
@@ -3775,48 +3783,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
break;
}
- case Token::SUB: {
- Comment cmt(masm_, "[ UnaryOperation (SUB)");
- bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode overwrite =
- can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
- // GenericUnaryOpStub expects the argument to be in the
- // accumulator register r0.
- VisitForAccumulatorValue(expr->expression());
- __ CallStub(&stub);
- context()->Plug(r0);
+ case Token::SUB:
+ EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
break;
- }
- case Token::BIT_NOT: {
- Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
- // The generic unary operation stub expects the argument to be
- // in the accumulator register r0.
- VisitForAccumulatorValue(expr->expression());
- Label done;
- bool inline_smi_code = ShouldInlineSmiCase(expr->op());
- if (inline_smi_code) {
- Label call_stub;
- __ JumpIfNotSmi(r0, &call_stub);
- __ mvn(r0, Operand(r0));
- // Bit-clear inverted smi-tag.
- __ bic(r0, r0, Operand(kSmiTagMask));
- __ b(&done);
- __ bind(&call_stub);
- }
- bool overwrite = expr->expression()->ResultOverwriteAllowed();
- UnaryOpFlags flags = inline_smi_code
- ? NO_UNARY_SMI_CODE_IN_STUB
- : NO_UNARY_FLAGS;
- UnaryOverwriteMode mode =
- overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
- __ CallStub(&stub);
- __ bind(&done);
- context()->Plug(r0);
+ case Token::BIT_NOT:
+ EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
break;
- }
default:
UNREACHABLE();
@@ -3824,6 +3797,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
+void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
+ const char* comment) {
+ // TODO(svenpanne): Allowing format strings in Comment would be nice here...
+ Comment cmt(masm_, comment);
+ bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+ UnaryOpStub stub(expr->op(), overwrite);
+ // UnaryOpStub expects the argument to be in the
+ // accumulator register r0.
+ VisitForAccumulatorValue(expr->expression());
+ SetSourcePosition(expr->position());
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
+ context()->Plug(r0);
+}
+
+
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
SetSourcePosition(expr->position());
@@ -3936,8 +3926,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
// Record position before stub call.
SetSourcePosition(expr->position());
- TypeRecordingBinaryOpStub stub(Token::ADD, NO_OVERWRITE);
- EmitCallIC(stub.GetCode(), &patch_site);
+ BinaryOpStub stub(Token::ADD, NO_OVERWRITE);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->CountId());
__ bind(&done);
// Store the value returned in r0.
@@ -3968,7 +3958,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
@@ -3985,7 +3975,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
@@ -4011,7 +4001,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
// Use a regular load, not a contextual load, to avoid a reference
// error.
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
PrepareForBailout(expr, TOS_REG);
context()->Plug(r0);
} else if (proxy != NULL &&
@@ -4144,7 +4134,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
switch (op) {
case Token::IN:
VisitForStackValue(expr->right());
- __ InvokeBuiltin(Builtins::IN, CALL_JS);
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
__ cmp(r0, ip);
@@ -4214,7 +4204,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CompareIC::GetUninitialized(op);
- EmitCallIC(ic, &patch_site);
+ EmitCallIC(ic, &patch_site, expr->id());
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ cmp(r0, Operand(0));
Split(cond, if_true, if_false, fall_through);
@@ -4276,7 +4266,9 @@ Register FullCodeGenerator::context_register() {
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ RelocInfo::Mode mode,
+ unsigned ast_id) {
ASSERT(mode == RelocInfo::CODE_TARGET ||
mode == RelocInfo::CODE_TARGET_CONTEXT);
Counters* counters = isolate()->counters();
@@ -4295,12 +4287,19 @@ void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
default:
break;
}
-
- __ Call(ic, mode);
+ if (ast_id == kNoASTId || mode == RelocInfo::CODE_TARGET_CONTEXT) {
+ __ Call(ic, mode);
+ } else {
+ ASSERT(mode == RelocInfo::CODE_TARGET);
+ mode = RelocInfo::CODE_TARGET_WITH_ID;
+ __ CallWithAstId(ic, mode, ast_id);
+ }
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) {
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ JumpPatchSite* patch_site,
+ unsigned ast_id) {
Counters* counters = isolate()->counters();
switch (ic->kind()) {
case Code::LOAD_IC:
@@ -4318,7 +4317,11 @@ void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) {
break;
}
- __ Call(ic, RelocInfo::CODE_TARGET);
+ if (ast_id == kNoASTId) {
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ } else {
+ __ CallWithAstId(ic, RelocInfo::CODE_TARGET_WITH_ID, ast_id);
+ }
if (patch_site != NULL && patch_site->is_bound()) {
patch_site->EmitPatchInfo();
} else {
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 8acf7c2e..21231630 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -105,65 +105,6 @@ static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
}
-// Probe the string dictionary in the |elements| register. Jump to the
-// |done| label if a property with the given name is found. Jump to
-// the |miss| label otherwise.
-static void GenerateStringDictionaryProbes(MacroAssembler* masm,
- Label* miss,
- Label* done,
- Register elements,
- Register name,
- Register scratch1,
- Register scratch2) {
- // Assert that name contains a string.
- if (FLAG_debug_code) __ AbortIfNotString(name);
-
- // Compute the capacity mask.
- const int kCapacityOffset = StringDictionary::kHeaderSize +
- StringDictionary::kCapacityIndex * kPointerSize;
- __ ldr(scratch1, FieldMemOperand(elements, kCapacityOffset));
- __ mov(scratch1, Operand(scratch1, ASR, kSmiTagSize)); // convert smi to int
- __ sub(scratch1, scratch1, Operand(1));
-
- const int kElementsStartOffset = StringDictionary::kHeaderSize +
- StringDictionary::kElementsStartIndex * kPointerSize;
-
- // Generate an unrolled loop that performs a few probes before
- // giving up. Measurements done on Gmail indicate that 2 probes
- // cover ~93% of loads from dictionaries.
- static const int kProbes = 4;
- for (int i = 0; i < kProbes; i++) {
- // Compute the masked index: (hash + i + i * i) & mask.
- __ ldr(scratch2, FieldMemOperand(name, String::kHashFieldOffset));
- if (i > 0) {
- // Add the probe offset (i + i * i) left shifted to avoid right shifting
- // the hash in a separate instruction. The value hash + i + i * i is right
- // shifted in the following and instruction.
- ASSERT(StringDictionary::GetProbeOffset(i) <
- 1 << (32 - String::kHashFieldOffset));
- __ add(scratch2, scratch2, Operand(
- StringDictionary::GetProbeOffset(i) << String::kHashShift));
- }
- __ and_(scratch2, scratch1, Operand(scratch2, LSR, String::kHashShift));
-
- // Scale the index by multiplying by the element size.
- ASSERT(StringDictionary::kEntrySize == 3);
- // scratch2 = scratch2 * 3.
- __ add(scratch2, scratch2, Operand(scratch2, LSL, 1));
-
- // Check if the key is identical to the name.
- __ add(scratch2, elements, Operand(scratch2, LSL, 2));
- __ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
- __ cmp(name, Operand(ip));
- if (i != kProbes - 1) {
- __ b(eq, done);
- } else {
- __ b(ne, miss);
- }
- }
-}
-
-
// Helper function used from LoadIC/CallIC GenerateNormal.
//
// elements: Property dictionary. It is not clobbered if a jump to the miss
@@ -191,13 +132,13 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
Label done;
// Probe the dictionary.
- GenerateStringDictionaryProbes(masm,
- miss,
- &done,
- elements,
- name,
- scratch1,
- scratch2);
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
// If probing finds an entry check that the value is a normal
// property.
@@ -240,13 +181,13 @@ static void GenerateDictionaryStore(MacroAssembler* masm,
Label done;
// Probe the dictionary.
- GenerateStringDictionaryProbes(masm,
- miss,
- &done,
- elements,
- name,
- scratch1,
- scratch2);
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
// If probing finds an entry in the dictionary check that the value
// is a normal property that is not read only.
@@ -538,7 +479,8 @@ Object* CallIC_Miss(Arguments args);
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
int argc,
- Code::Kind kind) {
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- r1 : receiver
// -- r2 : name
@@ -549,7 +491,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
MONOMORPHIC,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc);
Isolate::Current()->stub_cache()->GenerateProbe(
@@ -615,7 +557,8 @@ static void GenerateFunctionTailCall(MacroAssembler* masm,
// Invoke the function.
ParameterCount actual(argc);
- __ InvokeFunction(r1, actual, JUMP_FUNCTION);
+ __ InvokeFunction(r1, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
@@ -641,7 +584,10 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
}
-static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
+static void GenerateCallMiss(MacroAssembler* masm,
+ int argc,
+ IC::UtilityId id,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
@@ -693,22 +639,33 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
}
// Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
ParameterCount actual(argc);
- __ InvokeFunction(r1, actual, JUMP_FUNCTION);
+ __ InvokeFunction(r1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMiss(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state);
}
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
@@ -716,8 +673,8 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack into r1.
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
- GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
- GenerateMiss(masm, argc);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+ GenerateMiss(masm, argc, extra_ic_state);
}
@@ -728,7 +685,7 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
// -----------------------------------
GenerateCallNormal(masm, argc);
- GenerateMiss(masm, argc);
+ GenerateMiss(masm, argc, Code::kNoExtraICState);
}
@@ -738,7 +695,7 @@ void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// -- lr : return address
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState);
}
@@ -824,7 +781,10 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
__ bind(&lookup_monomorphic_cache);
__ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1, r0, r3);
- GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
// Fall through on miss.
__ bind(&slow_call);
@@ -926,222 +886,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
__ TailCallExternalReference(ref, 2, 1);
}
-// Returns the code marker, or the 0 if the code is not marked.
-static inline int InlinedICSiteMarker(Address address,
- Address* inline_end_address) {
- if (V8::UseCrankshaft()) return false;
-
- // If the instruction after the call site is not the pseudo instruction nop1
- // then this is not related to an inlined in-object property load. The nop1
- // instruction is located just after the call to the IC in the deferred code
- // handling the miss in the inlined code. After the nop1 instruction there is
- // a branch instruction for jumping back from the deferred code.
- Address address_after_call = address + Assembler::kCallTargetAddressOffset;
- Instr instr_after_call = Assembler::instr_at(address_after_call);
- int code_marker = MacroAssembler::GetCodeMarker(instr_after_call);
-
- // A negative result means the code is not marked.
- if (code_marker <= 0) return 0;
-
- Address address_after_nop = address_after_call + Assembler::kInstrSize;
- Instr instr_after_nop = Assembler::instr_at(address_after_nop);
- // There may be some reg-reg move and frame merging code to skip over before
- // the branch back from the DeferredReferenceGetKeyedValue code to the inlined
- // code.
- while (!Assembler::IsBranch(instr_after_nop)) {
- address_after_nop += Assembler::kInstrSize;
- instr_after_nop = Assembler::instr_at(address_after_nop);
- }
-
- // Find the end of the inlined code for handling the load.
- int b_offset =
- Assembler::GetBranchOffset(instr_after_nop) + Assembler::kPcLoadDelta;
- ASSERT(b_offset < 0); // Jumping back from deferred code.
- *inline_end_address = address_after_nop + b_offset;
-
- return code_marker;
-}
-
-
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // Find the end of the inlined code for handling the load if this is an
- // inlined IC call site.
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Patch the offset of the property load instruction (ldr r0, [r1, #+XXX]).
- // The immediate must be representable in 12 bits.
- ASSERT((JSObject::kMaxInstanceSize - JSObject::kHeaderSize) < (1 << 12));
- Address ldr_property_instr_address =
- inline_end_address - Assembler::kInstrSize;
- ASSERT(Assembler::IsLdrRegisterImmediate(
- Assembler::instr_at(ldr_property_instr_address)));
- Instr ldr_property_instr = Assembler::instr_at(ldr_property_instr_address);
- ldr_property_instr = Assembler::SetLdrRegisterImmediateOffset(
- ldr_property_instr, offset - kHeapObjectTag);
- Assembler::instr_at_put(ldr_property_instr_address, ldr_property_instr);
-
- // Indicate that code has changed.
- CPU::FlushICache(ldr_property_instr_address, 1 * Assembler::kInstrSize);
-
- // Patch the map check.
- // For PROPERTY_ACCESS_INLINED, the load map instruction is generated
- // 4 instructions before the end of the inlined code.
- // See codgen-arm.cc CodeGenerator::EmitNamedLoad.
- int ldr_map_offset = -4;
- Address ldr_map_instr_address =
- inline_end_address + ldr_map_offset * Assembler::kInstrSize;
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- return true;
-}
-
-
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- // Find the end of the inlined code for handling the contextual load if
- // this is inlined IC call site.
- Address inline_end_address = 0;
- int marker = InlinedICSiteMarker(address, &inline_end_address);
- if (!((marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT) ||
- (marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE))) {
- return false;
- }
- // On ARM we don't rely on the is_dont_delete argument as the hint is already
- // embedded in the code marker.
- bool marker_is_dont_delete =
- marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE;
-
- // These are the offsets from the end of the inlined code.
- // See codgen-arm.cc CodeGenerator::EmitNamedLoad.
- int ldr_map_offset = marker_is_dont_delete ? -5: -8;
- int ldr_cell_offset = marker_is_dont_delete ? -2: -5;
- if (FLAG_debug_code && marker_is_dont_delete) {
- // Three extra instructions were generated to check for the_hole_value.
- ldr_map_offset -= 3;
- ldr_cell_offset -= 3;
- }
- Address ldr_map_instr_address =
- inline_end_address + ldr_map_offset * Assembler::kInstrSize;
- Address ldr_cell_instr_address =
- inline_end_address + ldr_cell_offset * Assembler::kInstrSize;
-
- // Patch the map check.
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- // Patch the cell address.
- Assembler::set_target_address_at(ldr_cell_instr_address,
- reinterpret_cast<Address>(cell));
-
- return true;
-}
-
-
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
- // Find the end of the inlined code for the store if there is an
- // inlined version of the store.
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Compute the address of the map load instruction.
- Address ldr_map_instr_address =
- inline_end_address -
- (CodeGenerator::GetInlinedNamedStoreInstructionsAfterPatch() *
- Assembler::kInstrSize);
-
- // Update the offsets if initializing the inlined store. No reason
- // to update the offsets when clearing the inlined version because
- // it will bail out in the map check.
- if (map != HEAP->null_value()) {
- // Patch the offset in the actual store instruction.
- Address str_property_instr_address =
- ldr_map_instr_address + 3 * Assembler::kInstrSize;
- Instr str_property_instr = Assembler::instr_at(str_property_instr_address);
- ASSERT(Assembler::IsStrRegisterImmediate(str_property_instr));
- str_property_instr = Assembler::SetStrRegisterImmediateOffset(
- str_property_instr, offset - kHeapObjectTag);
- Assembler::instr_at_put(str_property_instr_address, str_property_instr);
-
- // Patch the offset in the add instruction that is part of the
- // write barrier.
- Address add_offset_instr_address =
- str_property_instr_address + Assembler::kInstrSize;
- Instr add_offset_instr = Assembler::instr_at(add_offset_instr_address);
- ASSERT(Assembler::IsAddRegisterImmediate(add_offset_instr));
- add_offset_instr = Assembler::SetAddRegisterImmediateOffset(
- add_offset_instr, offset - kHeapObjectTag);
- Assembler::instr_at_put(add_offset_instr_address, add_offset_instr);
-
- // Indicate that code has changed.
- CPU::FlushICache(str_property_instr_address, 2 * Assembler::kInstrSize);
- }
-
- // Patch the map check.
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
-
- return true;
-}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Patch the map check.
- Address ldr_map_instr_address =
- inline_end_address -
- (CodeGenerator::GetInlinedKeyedLoadInstructionsAfterPatch() *
- Assembler::kInstrSize);
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- return true;
-}
-
-
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- // Find the end of the inlined code for handling the store if this is an
- // inlined IC call site.
- Address inline_end_address = 0;
- if (InlinedICSiteMarker(address, &inline_end_address)
- != Assembler::PROPERTY_ACCESS_INLINED) {
- return false;
- }
-
- // Patch the map check.
- Address ldr_map_instr_address =
- inline_end_address -
- (CodeGenerator::kInlinedKeyedStoreInstructionsAfterPatch *
- Assembler::kInstrSize);
- Assembler::set_target_address_at(ldr_map_instr_address,
- reinterpret_cast<Address>(map));
- return true;
-}
-
-
-Object* KeyedLoadIC_Miss(Arguments args);
-
-
-void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
// ---------- S t a t e --------------
// -- lr : return address
// -- r0 : key
@@ -1153,8 +899,11 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
__ Push(r1, r0);
- ExternalReference ref =
- ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
+ // Perform tail call to the entry.
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric), isolate)
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
+
__ TailCallExternalReference(ref, 2, 1);
}
@@ -1345,7 +1094,7 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
}
@@ -1385,11 +1134,30 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
1);
__ bind(&slow);
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(r2, r1, r0);
+
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
}
-void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
// ---------- S t a t e --------------
// -- r0 : value
// -- r1 : key
@@ -1400,8 +1168,10 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// Push receiver, key and value for runtime call.
__ Push(r2, r1, r0);
+ // The slow case calls into the runtime to complete the store without causing
+ // an IC miss that would otherwise cause a transition to the generic stub.
ExternalReference ref =
- ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
__ TailCallExternalReference(ref, 3, 1);
}
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index efd226ea..e32cd0c8 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -61,22 +61,21 @@ void LOsrEntry::MarkSpilledRegister(int allocation_index,
#ifdef DEBUG
void LInstruction::VerifyCall() {
- // Call instructions can use only fixed registers as
- // temporaries and outputs because all registers
- // are blocked by the calling convention.
- // Inputs must use a fixed register.
+ // Call instructions can use only fixed registers as temporaries and
+ // outputs because all registers are blocked by the calling convention.
+ // Inputs operands must use a fixed register or use-at-start policy or
+ // a non-register policy.
ASSERT(Output() == NULL ||
LUnallocated::cast(Output())->HasFixedPolicy() ||
!LUnallocated::cast(Output())->HasRegisterPolicy());
for (UseIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||
+ operand->IsUsedAtStart());
}
for (TempIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
}
}
#endif
@@ -151,7 +150,7 @@ bool LGap::IsRedundant() const {
}
-void LGap::PrintDataTo(StringStream* stream) const {
+void LGap::PrintDataTo(StringStream* stream) {
for (int i = 0; i < 4; i++) {
stream->Add("(");
if (parallel_moves_[i] != NULL) {
@@ -238,6 +237,13 @@ void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
}
+void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_undetectable(");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
@@ -301,6 +307,13 @@ void LStoreContextSlot::PrintDataTo(StringStream* stream) {
}
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[r2] #%d / ", arity());
}
@@ -449,7 +462,7 @@ void LChunk::MarkEmptyBlocks() {
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
- LGap* gap = new LGap(block);
+ LInstructionGap* gap = new LInstructionGap(block);
int index = -1;
if (instr->IsControl()) {
instructions_.Add(gap);
@@ -852,22 +865,20 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
// Shift operations can only deoptimize if we do a logical shift
// by 0 and the result cannot be truncated to int32.
- bool can_deopt = (op == Token::SHR && constant_value == 0);
- if (can_deopt) {
- bool can_truncate = true;
- for (int i = 0; i < instr->uses()->length(); i++) {
- if (!instr->uses()->at(i)->CheckFlag(HValue::kTruncatingToInt32)) {
- can_truncate = false;
+ bool may_deopt = (op == Token::SHR && constant_value == 0);
+ bool does_deopt = false;
+ if (may_deopt) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
break;
}
}
- can_deopt = !can_truncate;
}
LInstruction* result =
- DefineSameAsFirst(new LShiftI(op, left, right, can_deopt));
- if (can_deopt) AssignEnvironment(result);
- return result;
+ DefineSameAsFirst(new LShiftI(op, left, right, does_deopt));
+ return does_deopt ? AssignEnvironment(result) : result;
}
@@ -1011,6 +1022,8 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
outer);
int argument_index = 0;
for (int i = 0; i < value_count; ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
HValue* value = hydrogen_env->values()->at(i);
LOperand* op = NULL;
if (value->IsArgumentsObject()) {
@@ -1037,97 +1050,97 @@ LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
LInstruction* LChunkBuilder::DoTest(HTest* instr) {
HValue* v = instr->value();
- if (v->EmitAtUses()) {
- if (v->IsClassOfTest()) {
- HClassOfTest* compare = HClassOfTest::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
- TempRegister());
- } else if (v->IsCompare()) {
- HCompare* compare = HCompare::cast(v);
- Token::Value op = compare->token();
- HValue* left = compare->left();
- HValue* right = compare->right();
- Representation r = compare->GetInputRepresentation();
- if (r.IsInteger32()) {
- ASSERT(left->representation().IsInteger32());
- ASSERT(right->representation().IsInteger32());
- return new LCmpIDAndBranch(UseRegisterAtStart(left),
- UseRegisterAtStart(right));
- } else if (r.IsDouble()) {
- ASSERT(left->representation().IsDouble());
- ASSERT(right->representation().IsDouble());
- return new LCmpIDAndBranch(UseRegisterAtStart(left),
- UseRegisterAtStart(right));
- } else {
- ASSERT(left->representation().IsTagged());
- ASSERT(right->representation().IsTagged());
- bool reversed = op == Token::GT || op == Token::LTE;
- LOperand* left_operand = UseFixed(left, reversed ? r0 : r1);
- LOperand* right_operand = UseFixed(right, reversed ? r1 : r0);
- LInstruction* result = new LCmpTAndBranch(left_operand,
- right_operand);
- return MarkAsCall(result, instr);
- }
- } else if (v->IsIsSmi()) {
- HIsSmi* compare = HIsSmi::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LIsSmiAndBranch(Use(compare->value()));
- } else if (v->IsHasInstanceType()) {
- HHasInstanceType* compare = HHasInstanceType::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
- return new LHasInstanceTypeAndBranch(
- UseRegisterAtStart(compare->value()));
- } else if (v->IsHasCachedArrayIndex()) {
- HHasCachedArrayIndex* compare = HHasCachedArrayIndex::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LHasCachedArrayIndexAndBranch(
- UseRegisterAtStart(compare->value()));
- } else if (v->IsIsNull()) {
- HIsNull* compare = HIsNull::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LIsNullAndBranch(UseRegisterAtStart(compare->value()));
- } else if (v->IsIsObject()) {
- HIsObject* compare = HIsObject::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- LOperand* temp = TempRegister();
- return new LIsObjectAndBranch(UseRegister(compare->value()), temp);
- } else if (v->IsCompareJSObjectEq()) {
- HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
- return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
- UseRegisterAtStart(compare->right()));
- } else if (v->IsInstanceOf()) {
- HInstanceOf* instance_of = HInstanceOf::cast(v);
- LInstruction* result =
- new LInstanceOfAndBranch(UseFixed(instance_of->left(), r0),
- UseFixed(instance_of->right(), r1));
- return MarkAsCall(result, instr);
- } else if (v->IsTypeofIs()) {
- HTypeofIs* typeof_is = HTypeofIs::cast(v);
- return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
- } else if (v->IsIsConstructCall()) {
- return new LIsConstructCallAndBranch(TempRegister());
+ if (!v->EmitAtUses()) {
+ return new LBranch(UseRegisterAtStart(v));
+ } else if (v->IsClassOfTest()) {
+ HClassOfTest* compare = HClassOfTest::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
+ TempRegister());
+ } else if (v->IsCompare()) {
+ HCompare* compare = HCompare::cast(v);
+ Token::Value op = compare->token();
+ HValue* left = compare->left();
+ HValue* right = compare->right();
+ Representation r = compare->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(left->representation().IsInteger32());
+ ASSERT(right->representation().IsInteger32());
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
+ UseRegisterAtStart(right));
+ } else if (r.IsDouble()) {
+ ASSERT(left->representation().IsDouble());
+ ASSERT(right->representation().IsDouble());
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
+ UseRegisterAtStart(right));
} else {
- if (v->IsConstant()) {
- if (HConstant::cast(v)->ToBoolean()) {
- return new LGoto(instr->FirstSuccessor()->block_id());
- } else {
- return new LGoto(instr->SecondSuccessor()->block_id());
- }
- }
- Abort("Undefined compare before branch");
- return NULL;
+ ASSERT(left->representation().IsTagged());
+ ASSERT(right->representation().IsTagged());
+ bool reversed = op == Token::GT || op == Token::LTE;
+ LOperand* left_operand = UseFixed(left, reversed ? r0 : r1);
+ LOperand* right_operand = UseFixed(right, reversed ? r1 : r0);
+ LInstruction* result = new LCmpTAndBranch(left_operand, right_operand);
+ return MarkAsCall(result, instr);
}
+ } else if (v->IsIsSmi()) {
+ HIsSmi* compare = HIsSmi::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsSmiAndBranch(Use(compare->value()));
+ } else if (v->IsIsUndetectable()) {
+ HIsUndetectable* compare = HIsUndetectable::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsUndetectableAndBranch(UseRegisterAtStart(compare->value()),
+ TempRegister());
+ } else if (v->IsHasInstanceType()) {
+ HHasInstanceType* compare = HHasInstanceType::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LHasInstanceTypeAndBranch(UseRegisterAtStart(compare->value()));
+ } else if (v->IsHasCachedArrayIndex()) {
+ HHasCachedArrayIndex* compare = HHasCachedArrayIndex::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LHasCachedArrayIndexAndBranch(
+ UseRegisterAtStart(compare->value()));
+ } else if (v->IsIsNull()) {
+ HIsNull* compare = HIsNull::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsNullAndBranch(UseRegisterAtStart(compare->value()));
+ } else if (v->IsIsObject()) {
+ HIsObject* compare = HIsObject::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ LOperand* temp = TempRegister();
+ return new LIsObjectAndBranch(UseRegister(compare->value()), temp);
+ } else if (v->IsCompareJSObjectEq()) {
+ HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
+ return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
+ UseRegisterAtStart(compare->right()));
+ } else if (v->IsCompareSymbolEq()) {
+ HCompareSymbolEq* compare = HCompareSymbolEq::cast(v);
+ return new LCmpSymbolEqAndBranch(UseRegisterAtStart(compare->left()),
+ UseRegisterAtStart(compare->right()));
+ } else if (v->IsInstanceOf()) {
+ HInstanceOf* instance_of = HInstanceOf::cast(v);
+ LInstruction* result =
+ new LInstanceOfAndBranch(UseFixed(instance_of->left(), r0),
+ UseFixed(instance_of->right(), r1));
+ return MarkAsCall(result, instr);
+ } else if (v->IsTypeofIs()) {
+ HTypeofIs* typeof_is = HTypeofIs::cast(v);
+ return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
+ } else if (v->IsIsConstructCall()) {
+ return new LIsConstructCallAndBranch(TempRegister());
+ } else if (v->IsConstant()) {
+ HBasicBlock* successor = HConstant::cast(v)->ToBoolean()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new LGoto(successor->block_id());
+ } else {
+ Abort("Undefined compare before branch");
+ return NULL;
}
- return new LBranch(UseRegisterAtStart(v));
}
+
LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1183,7 +1196,7 @@ LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
LInstruction* LChunkBuilder::DoContext(HContext* instr) {
- return DefineAsRegister(new LContext);
+ return instr->HasNoUses() ? NULL : DefineAsRegister(new LContext);
}
@@ -1212,6 +1225,14 @@ LInstruction* LChunkBuilder::DoCallConstantFunction(
}
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), r1);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, r0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
if (op == kMathLog || op == kMathSin || op == kMathCos) {
@@ -1493,6 +1514,15 @@ LInstruction* LChunkBuilder::DoCompareJSObjectEq(
}
+LInstruction* LChunkBuilder::DoCompareSymbolEq(
+ HCompareSymbolEq* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ LCmpSymbolEq* result = new LCmpSymbolEq(left, right);
+ return DefineAsRegister(result);
+}
+
+
LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1517,6 +1547,14 @@ LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
}
+LInstruction* LChunkBuilder::DoIsUndetectable(HIsUndetectable* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new LIsUndetectable(value));
+}
+
+
LInstruction* LChunkBuilder::DoHasInstanceType(HHasInstanceType* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1595,6 +1633,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
}
+LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
+ // All HForceRepresentation instructions should be eliminated in the
+ // representation change phase of Hydrogen.
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoChange(HChange* instr) {
Representation from = instr->from();
Representation to = instr->to();
@@ -1703,6 +1749,24 @@ LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
}
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ LOperand* reg = UseRegister(value);
+ if (input_rep.IsDouble()) {
+ return DefineAsRegister(new LClampDToUint8(reg, FixedTemp(d1)));
+ } else if (input_rep.IsInteger32()) {
+ return DefineAsRegister(new LClampIToUint8(reg));
+ } else {
+ ASSERT(input_rep.IsTagged());
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve d1 explicitly.
+ LClampTToUint8* result = new LClampTToUint8(reg, FixedTemp(d1));
+ return AssignEnvironment(DefineAsRegister(result));
+ }
+}
+
+
LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
return new LReturn(UseFixed(instr->value(), r0));
}
@@ -1842,11 +1906,14 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
ExternalArrayType array_type = instr->array_type();
Representation representation(instr->representation());
- ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
- (representation.IsDouble() && array_type == kExternalFloatArray));
+ ASSERT(
+ (representation.IsInteger32() && (array_type != kExternalFloatArray &&
+ array_type != kExternalDoubleArray)) ||
+ (representation.IsDouble() && (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray)));
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
- LOperand* key = UseRegister(instr->key());
+ LOperand* key = UseRegisterOrConstant(instr->key());
LLoadKeyedSpecializedArrayElement* result =
new LLoadKeyedSpecializedArrayElement(external_pointer, key);
LInstruction* load_instr = DefineAsRegister(result);
@@ -1890,8 +1957,11 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
Representation representation(instr->value()->representation());
ExternalArrayType array_type = instr->array_type();
- ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
- (representation.IsDouble() && array_type == kExternalFloatArray));
+ ASSERT(
+ (representation.IsInteger32() && (array_type != kExternalFloatArray &&
+ array_type != kExternalDoubleArray)) ||
+ (representation.IsDouble() && (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray)));
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
@@ -1901,7 +1971,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
LOperand* val = val_is_temp_register
? UseTempRegister(instr->value())
: UseRegister(instr->value());
- LOperand* key = UseRegister(instr->key());
+ LOperand* key = UseRegisterOrConstant(instr->key());
return new LStoreKeyedSpecializedArrayElement(external_pointer,
key,
@@ -1946,6 +2016,13 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
}
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new LStringAdd(left, right), r0), instr);
+}
+
+
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
@@ -2106,8 +2183,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->function(),
- false,
- undefined);
+ HEnvironment::LITHIUM,
+ undefined,
+ instr->call_kind());
current_block_->UpdateEnvironment(inner);
chunk_->AddInlinedClosure(instr->closure());
return NULL;
@@ -2121,4 +2199,12 @@ LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
}
+LInstruction* LChunkBuilder::DoIn(HIn* instr) {
+ LOperand* key = UseRegisterAtStart(instr->key());
+ LOperand* object = UseRegisterAtStart(instr->object());
+ LIn* result = new LIn(key, object);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
} } // namespace v8::internal
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index e1c65d22..73c4a878 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -73,6 +73,9 @@ class LCodeGen;
V(CheckMap) \
V(CheckPrototypeMaps) \
V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
V(ClassOfTest) \
V(ClassOfTestAndBranch) \
V(CmpID) \
@@ -80,6 +83,8 @@ class LCodeGen;
V(CmpJSObjectEq) \
V(CmpJSObjectEqAndBranch) \
V(CmpMapAndBranch) \
+ V(CmpSymbolEq) \
+ V(CmpSymbolEqAndBranch) \
V(CmpT) \
V(CmpTAndBranch) \
V(ConstantD) \
@@ -93,7 +98,6 @@ class LCodeGen;
V(ExternalArrayLength) \
V(FixedArrayLength) \
V(FunctionLiteral) \
- V(Gap) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
V(GlobalReceiver) \
@@ -102,16 +106,23 @@ class LCodeGen;
V(HasCachedArrayIndexAndBranch) \
V(HasInstanceType) \
V(HasInstanceTypeAndBranch) \
+ V(In) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
V(InstanceOfKnownGlobal) \
+ V(InstructionGap) \
V(Integer32ToDouble) \
+ V(InvokeFunction) \
+ V(IsConstructCall) \
+ V(IsConstructCallAndBranch) \
V(IsNull) \
V(IsNullAndBranch) \
V(IsObject) \
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
+ V(IsUndetectable) \
+ V(IsUndetectableAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
@@ -152,6 +163,7 @@ class LCodeGen;
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -162,27 +174,21 @@ class LCodeGen;
V(Typeof) \
V(TypeofIs) \
V(TypeofIsAndBranch) \
- V(IsConstructCall) \
- V(IsConstructCallAndBranch) \
V(UnaryMathOperation) \
V(UnknownOSRValue) \
V(ValueOf)
-#define DECLARE_INSTRUCTION(type) \
- virtual bool Is##type() const { return true; } \
- static L##type* cast(LInstruction* instr) { \
- ASSERT(instr->Is##type()); \
- return reinterpret_cast<L##type*>(instr); \
+#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
+ virtual Opcode opcode() const { return LInstruction::k##type; } \
+ virtual void CompileToNative(LCodeGen* generator); \
+ virtual const char* Mnemonic() const { return mnemonic; } \
+ static L##type* cast(LInstruction* instr) { \
+ ASSERT(instr->Is##type()); \
+ return reinterpret_cast<L##type*>(instr); \
}
-#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
- virtual void CompileToNative(LCodeGen* generator); \
- virtual const char* Mnemonic() const { return mnemonic; } \
- DECLARE_INSTRUCTION(type)
-
-
#define DECLARE_HYDROGEN_ACCESSOR(type) \
H##type* hydrogen() const { \
return H##type::cast(hydrogen_value()); \
@@ -204,10 +210,25 @@ class LInstruction: public ZoneObject {
virtual void PrintDataTo(StringStream* stream) = 0;
virtual void PrintOutputOperandTo(StringStream* stream) = 0;
- // Declare virtual type testers.
-#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
- LITHIUM_ALL_INSTRUCTION_LIST(DECLARE_DO)
-#undef DECLARE_DO
+ enum Opcode {
+ // Declare a unique enum value for each instruction.
+#define DECLARE_OPCODE(type) k##type,
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE)
+ kNumberOfInstructions
+#undef DECLARE_OPCODE
+ };
+
+ virtual Opcode opcode() const = 0;
+
+ // Declare non-virtual type testers for all leaf IR classes.
+#define DECLARE_PREDICATE(type) \
+ bool Is##type() const { return opcode() == k##type; }
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE)
+#undef DECLARE_PREDICATE
+
+ // Declare virtual predicates for instructions that don't have
+ // an opcode.
+ virtual bool IsGap() const { return false; }
virtual bool IsControl() const { return false; }
virtual void SetBranchTargets(int true_block_id, int false_block_id) { }
@@ -334,8 +355,13 @@ class LGap: public LTemplateInstruction<0, 0, 0> {
parallel_moves_[AFTER] = NULL;
}
- DECLARE_CONCRETE_INSTRUCTION(Gap, "gap")
- virtual void PrintDataTo(StringStream* stream) const;
+ // Can't use the DECLARE-macro here because of sub-classes.
+ virtual bool IsGap() const { return true; }
+ virtual void PrintDataTo(StringStream* stream);
+ static LGap* cast(LInstruction* instr) {
+ ASSERT(instr->IsGap());
+ return reinterpret_cast<LGap*>(instr);
+ }
bool IsRedundant() const;
@@ -365,6 +391,14 @@ class LGap: public LTemplateInstruction<0, 0, 0> {
};
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
class LGoto: public LTemplateInstruction<0, 0, 0> {
public:
LGoto(int block_id, bool include_stack_check = false)
@@ -453,7 +487,6 @@ class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
template<int I, int T>
class LControlInstruction: public LTemplateInstruction<0, I, T> {
public:
- DECLARE_INSTRUCTION(ControlInstruction)
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -655,6 +688,28 @@ class LCmpJSObjectEqAndBranch: public LControlInstruction<2, 0> {
};
+class LCmpSymbolEq: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LCmpSymbolEq(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpSymbolEq, "cmp-symbol-eq")
+};
+
+
+class LCmpSymbolEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpSymbolEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpSymbolEqAndBranch, "cmp-symbol-eq-and-branch")
+};
+
+
class LIsNull: public LTemplateInstruction<1, 1, 0> {
public:
explicit LIsNull(LOperand* value) {
@@ -728,6 +783,31 @@ class LIsSmiAndBranch: public LControlInstruction<1, 0> {
};
+class LIsUndetectable: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LIsUndetectable(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectable, "is-undetectable")
+ DECLARE_HYDROGEN_ACCESSOR(IsUndetectable)
+};
+
+
+class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
+ public:
+ explicit LIsUndetectableAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch,
+ "is-undetectable-and-branch")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
class LHasInstanceType: public LTemplateInstruction<1, 1, 0> {
public:
explicit LHasInstanceType(LOperand* value) {
@@ -1104,6 +1184,7 @@ class LArithmeticD: public LTemplateInstruction<1, 2, 0> {
Token::Value op() const { return op_; }
+ virtual Opcode opcode() const { return LInstruction::kArithmeticD; }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1120,6 +1201,7 @@ class LArithmeticT: public LTemplateInstruction<1, 2, 0> {
inputs_[1] = right;
}
+ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1412,6 +1494,23 @@ class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> {
};
+class LInvokeFunction: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInvokeFunction(LOperand* function) {
+ inputs_[0] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ LOperand* function() { return inputs_[0]; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
class LCallKeyed: public LTemplateInstruction<1, 1, 0> {
public:
explicit LCallKeyed(LOperand* key) {
@@ -1707,6 +1806,22 @@ class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> {
};
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+};
+
+
+
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
@@ -1816,6 +1931,44 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
};
+class LClampDToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampDToUint8(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampIToUint8(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampTToUint8(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
+};
+
+
class LArrayLiteral: public LTemplateInstruction<1, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal")
@@ -1958,6 +2111,20 @@ class LStackCheck: public LTemplateInstruction<0, 0, 0> {
};
+class LIn: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LIn(LOperand* key, LOperand* object) {
+ inputs_[0] = key;
+ inputs_[1] = object;
+ }
+
+ LOperand* key() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(In, "in")
+};
+
+
class LChunkBuilder;
class LChunk: public ZoneObject {
public:
@@ -2175,7 +2342,6 @@ class LChunkBuilder BASE_EMBEDDED {
};
#undef DECLARE_HYDROGEN_ACCESSOR
-#undef DECLARE_INSTRUCTION
#undef DECLARE_CONCRETE_INSTRUCTION
} } // namespace v8::internal
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 3dcd4278..d25ca495 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -46,7 +46,7 @@ class SafepointGenerator : public CallWrapper {
deoptimization_index_(deoptimization_index) { }
virtual ~SafepointGenerator() { }
- virtual void BeforeCall(int call_size) {
+ virtual void BeforeCall(int call_size) const {
ASSERT(call_size >= 0);
// Ensure that we have enough space after the previous safepoint position
// for the generated code there.
@@ -63,7 +63,7 @@ class SafepointGenerator : public CallWrapper {
}
}
- virtual void AfterCall() {
+ virtual void AfterCall() const {
codegen_->RecordSafepoint(pointers_, deoptimization_index_);
}
@@ -85,13 +85,14 @@ bool LCodeGen::GenerateCode() {
return GeneratePrologue() &&
GenerateBody() &&
GenerateDeferredCode() &&
+ GenerateDeoptJumpTable() &&
GenerateSafepointTable();
}
void LCodeGen::FinishCode(Handle<Code> code) {
ASSERT(is_done());
- code->set_stack_slots(StackSlotCount());
+ code->set_stack_slots(GetStackSlotCount());
code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
PopulateDeoptimizationData(code);
Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
@@ -145,11 +146,25 @@ bool LCodeGen::GeneratePrologue() {
// fp: Caller's frame pointer.
// lr: Caller's pc.
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). r5 is zero for method calls and non-zero for function
+ // calls.
+ if (info_->is_strict_mode()) {
+ Label ok;
+ __ cmp(r5, Operand(0));
+ __ b(eq, &ok);
+ int receiver_offset = scope()->num_parameters() * kPointerSize;
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ str(r2, MemOperand(sp, receiver_offset));
+ __ bind(&ok);
+ }
+
__ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
__ add(fp, sp, Operand(2 * kPointerSize)); // Adjust FP to point to saved FP.
// Reserve space for the stack slots needed by the code.
- int slots = StackSlotCount();
+ int slots = GetStackSlotCount();
if (slots > 0) {
if (FLAG_debug_code) {
__ mov(r0, Operand(slots));
@@ -249,13 +264,43 @@ bool LCodeGen::GenerateDeferredCode() {
__ jmp(code->exit());
}
- // Force constant pool emission at the end of deferred code to make
- // sure that no constant pools are emitted after the official end of
- // the instruction sequence.
+ // Force constant pool emission at the end of the deferred code to make
+ // sure that no constant pools are emitted after.
masm()->CheckConstPool(true, false);
- // Deferred code is the last part of the instruction sequence. Mark
- // the generated code as done unless we bailed out.
+ return !is_aborted();
+}
+
+
+bool LCodeGen::GenerateDeoptJumpTable() {
+ // Check that the jump table is accessible from everywhere in the function
+ // code, ie that offsets to the table can be encoded in the 24bit signed
+ // immediate of a branch instruction.
+ // To simplify we consider the code size from the first instruction to the
+ // end of the jump table. We also don't consider the pc load delta.
+ // Each entry in the jump table generates one instruction and inlines one
+ // 32bit data after it.
+ if (!is_int24((masm()->pc_offset() / Assembler::kInstrSize) +
+ deopt_jump_table_.length() * 2)) {
+ Abort("Generated code is too large");
+ }
+
+ // Block the constant pool emission during the jump table emission.
+ __ BlockConstPoolFor(deopt_jump_table_.length());
+ __ RecordComment("[ Deoptimisation jump table");
+ Label table_start;
+ __ bind(&table_start);
+ for (int i = 0; i < deopt_jump_table_.length(); i++) {
+ __ bind(&deopt_jump_table_[i].label);
+ __ ldr(pc, MemOperand(pc, Assembler::kInstrSize - Assembler::kPcLoadDelta));
+ __ dd(reinterpret_cast<uint32_t>(deopt_jump_table_[i].address));
+ }
+ ASSERT(masm()->InstructionsGeneratedSince(&table_start) ==
+ deopt_jump_table_.length() * 2);
+ __ RecordComment("]");
+
+ // The deoptimization jump table is the last part of the instruction
+ // sequence. Mark the generated code as done unless we bailed out.
if (!is_aborted()) status_ = DONE;
return !is_aborted();
}
@@ -263,7 +308,7 @@ bool LCodeGen::GenerateDeferredCode() {
bool LCodeGen::GenerateSafepointTable() {
ASSERT(is_done());
- safepoints_.Emit(masm(), StackSlotCount());
+ safepoints_.Emit(masm(), GetStackSlotCount());
return !is_aborted();
}
@@ -459,7 +504,7 @@ void LCodeGen::AddToTranslation(Translation* translation,
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsArgument()) {
ASSERT(is_tagged);
- int src_index = StackSlotCount() + op->index();
+ int src_index = GetStackSlotCount() + op->index();
translation->StoreStackSlot(src_index);
} else if (op->IsRegister()) {
Register reg = ToRegister(op);
@@ -500,7 +545,7 @@ void LCodeGen::CallCodeGeneric(Handle<Code> code,
// Signal that we don't inline smi code before these stubs in the
// optimizing code generator.
- if (code->kind() == Code::TYPE_RECORDING_BINARY_OP_IC ||
+ if (code->kind() == Code::BINARY_OP_IC ||
code->kind() == Code::COMPARE_IC) {
__ nop();
}
@@ -602,19 +647,18 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) {
return;
}
+ if (FLAG_trap_on_deopt) __ stop("trap_on_deopt", cc);
+
if (cc == al) {
- if (FLAG_trap_on_deopt) __ stop("trap_on_deopt");
__ Jump(entry, RelocInfo::RUNTIME_ENTRY);
} else {
- if (FLAG_trap_on_deopt) {
- Label done;
- __ b(&done, NegateCondition(cc));
- __ stop("trap_on_deopt");
- __ Jump(entry, RelocInfo::RUNTIME_ENTRY);
- __ bind(&done);
- } else {
- __ Jump(entry, RelocInfo::RUNTIME_ENTRY, cc);
+ // We often have several deopts to the same entry, reuse the last
+ // jump entry if this is the case.
+ if (deopt_jump_table_.is_empty() ||
+ (deopt_jump_table_.last().address != entry)) {
+ deopt_jump_table_.Add(JumpTableEntry(entry));
}
+ __ b(cc, &deopt_jump_table_.last().label);
}
}
@@ -746,7 +790,7 @@ void LCodeGen::DoLabel(LLabel* label) {
}
__ bind(label->label());
current_block_ = label->block_id();
- LCodeGen::DoGap(label);
+ DoGap(label);
}
@@ -772,6 +816,11 @@ void LCodeGen::DoGap(LGap* gap) {
}
+void LCodeGen::DoInstructionGap(LInstructionGap* instr) {
+ DoGap(instr);
+}
+
+
void LCodeGen::DoParameter(LParameter* instr) {
// Nothing to do.
}
@@ -1067,7 +1116,7 @@ void LCodeGen::DoDeferredBinaryOpStub(LTemplateInstruction<1, 2, T>* instr,
__ mov(r0, right);
__ mov(r1, left);
}
- TypeRecordingBinaryOpStub stub(op, OVERWRITE_LEFT);
+ BinaryOpStub stub(op, OVERWRITE_LEFT);
__ CallStub(&stub);
RecordSafepointWithRegistersAndDoubles(instr->pointer_map(),
0,
@@ -1349,11 +1398,11 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
// Save r0-r3 on the stack.
__ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit() | r3.bit());
- __ PrepareCallCFunction(4, scratch0());
- __ vmov(r0, r1, left);
- __ vmov(r2, r3, right);
+ __ PrepareCallCFunction(0, 2, scratch0());
+ __ SetCallCDoubleArguments(left, right);
__ CallCFunction(
- ExternalReference::double_fp_operation(Token::MOD, isolate()), 4);
+ ExternalReference::double_fp_operation(Token::MOD, isolate()),
+ 0, 2);
// Move the result in the double result register.
__ GetCFunctionDoubleResult(ToDoubleRegister(instr->result()));
@@ -1373,7 +1422,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
ASSERT(ToRegister(instr->InputAt(1)).is(r0));
ASSERT(ToRegister(instr->result()).is(r0));
- TypeRecordingBinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ nop(); // Signals no inlined code.
}
@@ -1625,6 +1674,28 @@ void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
}
+void LCodeGen::DoCmpSymbolEq(LCmpSymbolEq* instr) {
+ Register left = ToRegister(instr->InputAt(0));
+ Register right = ToRegister(instr->InputAt(1));
+ Register result = ToRegister(instr->result());
+
+ __ cmp(left, Operand(right));
+ __ LoadRoot(result, Heap::kTrueValueRootIndex, eq);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex, ne);
+}
+
+
+void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
+ Register left = ToRegister(instr->InputAt(0));
+ Register right = ToRegister(instr->InputAt(1));
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+
+ __ cmp(left, Operand(right));
+ EmitBranch(true_block, false_block, eq);
+}
+
+
void LCodeGen::DoIsNull(LIsNull* instr) {
Register reg = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
@@ -1779,6 +1850,40 @@ void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
}
+void LCodeGen::DoIsUndetectable(LIsUndetectable* instr) {
+ Register input = ToRegister(instr->InputAt(0));
+ Register result = ToRegister(instr->result());
+
+ ASSERT(instr->hydrogen()->value()->representation().IsTagged());
+ Label false_label, done;
+ __ JumpIfSmi(input, &false_label);
+ __ ldr(result, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ ldrb(result, FieldMemOperand(result, Map::kBitFieldOffset));
+ __ tst(result, Operand(1 << Map::kIsUndetectable));
+ __ b(eq, &false_label);
+ __ LoadRoot(result, Heap::kTrueValueRootIndex);
+ __ jmp(&done);
+ __ bind(&false_label);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->InputAt(0));
+ Register temp = ToRegister(instr->TempAt(0));
+
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+
+ __ JumpIfSmi(input, chunk_->GetAssemblyLabel(false_block));
+ __ ldr(temp, FieldMemOperand(input, HeapObject::kMapOffset));
+ __ ldrb(temp, FieldMemOperand(temp, Map::kBitFieldOffset));
+ __ tst(temp, Operand(1 << Map::kIsUndetectable));
+ EmitBranch(true_block, false_block, ne);
+}
+
+
static InstanceType TestType(HHasInstanceType* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
@@ -2190,7 +2295,7 @@ void LCodeGen::DoReturn(LReturn* instr) {
__ push(r0);
__ CallRuntime(Runtime::kTraceExit, 1);
}
- int32_t sp_delta = (ParameterCount() + 1) * kPointerSize;
+ int32_t sp_delta = (GetParameterCount() + 1) * kPointerSize;
__ mov(sp, fp);
__ ldm(ia_w, sp, fp.bit() | lr.bit());
__ add(sp, sp, Operand(sp_delta));
@@ -2289,23 +2394,29 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
}
-void LCodeGen::EmitLoadField(Register result,
- Register object,
- Handle<Map> type,
- Handle<String> name) {
+void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
+ Register object,
+ Handle<Map> type,
+ Handle<String> name) {
LookupResult lookup;
type->LookupInDescriptors(NULL, *name, &lookup);
- ASSERT(lookup.IsProperty() && lookup.type() == FIELD);
- int index = lookup.GetLocalFieldIndexFromMap(*type);
- int offset = index * kPointerSize;
- if (index < 0) {
- // Negative property indices are in-object properties, indexed
- // from the end of the fixed part of the object.
- __ ldr(result, FieldMemOperand(object, offset + type->instance_size()));
+ ASSERT(lookup.IsProperty() &&
+ (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION));
+ if (lookup.type() == FIELD) {
+ int index = lookup.GetLocalFieldIndexFromMap(*type);
+ int offset = index * kPointerSize;
+ if (index < 0) {
+ // Negative property indices are in-object properties, indexed
+ // from the end of the fixed part of the object.
+ __ ldr(result, FieldMemOperand(object, offset + type->instance_size()));
+ } else {
+ // Non-negative property indices are in the properties array.
+ __ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
+ __ ldr(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize));
+ }
} else {
- // Non-negative property indices are in the properties array.
- __ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
- __ ldr(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize));
+ Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
+ LoadHeapObject(result, Handle<HeapObject>::cast(function));
}
}
@@ -2329,7 +2440,7 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Label next;
__ cmp(scratch, Operand(map));
__ b(ne, &next);
- EmitLoadField(result, object, map, name);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
__ b(&done);
__ bind(&next);
}
@@ -2338,7 +2449,7 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
if (instr->hydrogen()->need_generic()) {
Label generic;
__ b(ne, &generic);
- EmitLoadField(result, object, map, name);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
__ b(&done);
__ bind(&generic);
__ mov(r2, Operand(name));
@@ -2346,7 +2457,7 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
DeoptimizeIf(ne, instr->environment());
- EmitLoadField(result, object, map, name);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
}
__ bind(&done);
}
@@ -2472,44 +2583,67 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
__ ldr(result, FieldMemOperand(scratch, FixedArray::kHeaderSize));
// Check for the hole value.
- __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
- __ cmp(result, scratch);
- DeoptimizeIf(eq, instr->environment());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
+ __ cmp(result, scratch);
+ DeoptimizeIf(eq, instr->environment());
+ }
}
void LCodeGen::DoLoadKeyedSpecializedArrayElement(
LLoadKeyedSpecializedArrayElement* instr) {
Register external_pointer = ToRegister(instr->external_pointer());
- Register key = ToRegister(instr->key());
+ Register key = no_reg;
ExternalArrayType array_type = instr->array_type();
- if (array_type == kExternalFloatArray) {
+ bool key_is_constant = instr->key()->IsConstantOperand();
+ int constant_key = 0;
+ if (key_is_constant) {
+ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
+ if (constant_key & 0xF0000000) {
+ Abort("array index constant value too big.");
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int shift_size = ExternalArrayTypeToShiftSize(array_type);
+
+ if (array_type == kExternalFloatArray || array_type == kExternalDoubleArray) {
CpuFeatures::Scope scope(VFP3);
DwVfpRegister result(ToDoubleRegister(instr->result()));
- __ add(scratch0(), external_pointer, Operand(key, LSL, 2));
- __ vldr(result.low(), scratch0(), 0);
- __ vcvt_f64_f32(result, result.low());
+ Operand operand(key_is_constant ? Operand(constant_key * (1 << shift_size))
+ : Operand(key, LSL, shift_size));
+ __ add(scratch0(), external_pointer, operand);
+ if (array_type == kExternalFloatArray) {
+ __ vldr(result.low(), scratch0(), 0);
+ __ vcvt_f64_f32(result, result.low());
+ } else { // i.e. array_type == kExternalDoubleArray
+ __ vldr(result, scratch0(), 0);
+ }
} else {
Register result(ToRegister(instr->result()));
+ MemOperand mem_operand(key_is_constant
+ ? MemOperand(external_pointer, constant_key * (1 << shift_size))
+ : MemOperand(external_pointer, key, LSL, shift_size));
switch (array_type) {
case kExternalByteArray:
- __ ldrsb(result, MemOperand(external_pointer, key));
+ __ ldrsb(result, mem_operand);
break;
case kExternalUnsignedByteArray:
case kExternalPixelArray:
- __ ldrb(result, MemOperand(external_pointer, key));
+ __ ldrb(result, mem_operand);
break;
case kExternalShortArray:
- __ ldrsh(result, MemOperand(external_pointer, key, LSL, 1));
+ __ ldrsh(result, mem_operand);
break;
case kExternalUnsignedShortArray:
- __ ldrh(result, MemOperand(external_pointer, key, LSL, 1));
+ __ ldrh(result, mem_operand);
break;
case kExternalIntArray:
- __ ldr(result, MemOperand(external_pointer, key, LSL, 2));
+ __ ldr(result, mem_operand);
break;
case kExternalUnsignedIntArray:
- __ ldr(result, MemOperand(external_pointer, key, LSL, 2));
+ __ ldr(result, mem_operand);
__ cmp(result, Operand(0x80000000));
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
@@ -2517,6 +2651,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
DeoptimizeIf(cs, instr->environment());
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
UNREACHABLE();
break;
}
@@ -2582,6 +2717,9 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
ASSERT(function.is(r1)); // Required by InvokeFunction.
ASSERT(ToRegister(instr->result()).is(r0));
+ // TODO(1412): This is not correct if the called function is a
+ // strict mode function or a native.
+ //
// If the receiver is null or undefined, we have to pass the global object
// as a receiver.
Label global_object, receiver_ok;
@@ -2601,6 +2739,8 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
__ bind(&global_object);
__ ldr(receiver, GlobalObjectOperand());
+ __ ldr(receiver,
+ FieldMemOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
__ bind(&receiver_ok);
// Copy the arguments to this function possibly from the
@@ -2640,7 +2780,8 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
// The number of arguments is stored in receiver which is r0, as expected
// by InvokeFunction.
v8::internal::ParameterCount actual(receiver);
- __ InvokeFunction(function, actual, CALL_FUNCTION, &safepoint_generator);
+ __ InvokeFunction(function, actual, CALL_FUNCTION,
+ safepoint_generator, CALL_AS_METHOD);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
@@ -2687,7 +2828,8 @@ void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
int arity,
- LInstruction* instr) {
+ LInstruction* instr,
+ CallKind call_kind) {
// Change context if needed.
bool change_context =
(info()->closure()->context() != function->context()) ||
@@ -2707,6 +2849,7 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
RecordPosition(pointers->position());
// Invoke function.
+ __ SetCallKind(r5, call_kind);
__ ldr(ip, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
__ Call(ip);
@@ -2721,7 +2864,10 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
__ mov(r1, Operand(instr->function()));
- CallKnownFunction(instr->function(), instr->arity(), instr);
+ CallKnownFunction(instr->function(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD);
}
@@ -2871,9 +3017,49 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
- Register scratch1 = scratch0();
- Register scratch2 = result;
- __ EmitVFPTruncate(kRoundToNearest,
+ Register scratch1 = result;
+ Register scratch2 = scratch0();
+ Label done, check_sign_on_zero;
+
+ // Extract exponent bits.
+ __ vmov(scratch1, input.high());
+ __ ubfx(scratch2,
+ scratch1,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // If the number is in ]-0.5, +0.5[, the result is +/- 0.
+ __ cmp(scratch2, Operand(HeapNumber::kExponentBias - 2));
+ __ mov(result, Operand(0), LeaveCC, le);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ __ b(le, &check_sign_on_zero);
+ } else {
+ __ b(le, &done);
+ }
+
+ // The following conversion will not work with numbers
+ // outside of ]-2^32, 2^32[.
+ __ cmp(scratch2, Operand(HeapNumber::kExponentBias + 32));
+ DeoptimizeIf(ge, instr->environment());
+
+ // Save the original sign for later comparison.
+ __ and_(scratch2, scratch1, Operand(HeapNumber::kSignMask));
+
+ __ vmov(double_scratch0(), 0.5);
+ __ vadd(input, input, double_scratch0());
+
+ // Check sign of the result: if the sign changed, the input
+ // value was in ]0.5, 0[ and the result should be -0.
+ __ vmov(scratch1, input.high());
+ __ eor(scratch1, scratch1, Operand(scratch2), SetCC);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(mi, instr->environment());
+ } else {
+ __ mov(result, Operand(0), LeaveCC, mi);
+ __ b(mi, &done);
+ }
+
+ __ EmitVFPTruncate(kRoundToMinusInf,
double_scratch0().low(),
input,
scratch1,
@@ -2883,14 +3069,14 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// Test for -0.
- Label done;
__ cmp(result, Operand(0));
__ b(ne, &done);
+ __ bind(&check_sign_on_zero);
__ vmov(scratch1, input.high());
__ tst(scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment());
- __ bind(&done);
}
+ __ bind(&done);
}
@@ -2925,19 +3111,18 @@ void LCodeGen::DoPower(LPower* instr) {
Representation exponent_type = instr->hydrogen()->right()->representation();
if (exponent_type.IsDouble()) {
// Prepare arguments and call C function.
- __ PrepareCallCFunction(4, scratch);
- __ vmov(r0, r1, ToDoubleRegister(left));
- __ vmov(r2, r3, ToDoubleRegister(right));
+ __ PrepareCallCFunction(0, 2, scratch);
+ __ SetCallCDoubleArguments(ToDoubleRegister(left),
+ ToDoubleRegister(right));
__ CallCFunction(
- ExternalReference::power_double_double_function(isolate()), 4);
+ ExternalReference::power_double_double_function(isolate()), 0, 2);
} else if (exponent_type.IsInteger32()) {
ASSERT(ToRegister(right).is(r0));
// Prepare arguments and call C function.
- __ PrepareCallCFunction(4, scratch);
- __ mov(r2, ToRegister(right));
- __ vmov(r0, r1, ToDoubleRegister(left));
+ __ PrepareCallCFunction(1, 1, scratch);
+ __ SetCallCDoubleArguments(ToDoubleRegister(left), ToRegister(right));
__ CallCFunction(
- ExternalReference::power_double_int_function(isolate()), 4);
+ ExternalReference::power_double_int_function(isolate()), 1, 1);
} else {
ASSERT(exponent_type.IsTagged());
ASSERT(instr->hydrogen()->left()->representation().IsDouble());
@@ -2967,11 +3152,10 @@ void LCodeGen::DoPower(LPower* instr) {
// Prepare arguments and call C function.
__ bind(&call);
- __ PrepareCallCFunction(4, scratch);
- __ vmov(r0, r1, ToDoubleRegister(left));
- __ vmov(r2, r3, result_reg);
+ __ PrepareCallCFunction(0, 2, scratch);
+ __ SetCallCDoubleArguments(ToDoubleRegister(left), result_reg);
__ CallCFunction(
- ExternalReference::power_double_double_function(isolate()), 4);
+ ExternalReference::power_double_double_function(isolate()), 0, 2);
}
// Store the result in the result register.
__ GetCFunctionDoubleResult(result_reg);
@@ -3035,6 +3219,21 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
}
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(r1));
+ ASSERT(instr->HasPointerMap());
+ ASSERT(instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator generator(this, pointers, env->deoptimization_index());
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(r1, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+}
+
+
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
@@ -3050,10 +3249,11 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(
- arity, NOT_IN_LOOP);
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
__ mov(r2, Operand(instr->name()));
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, mode, instr);
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
@@ -3063,7 +3263,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
int arity = instr->arity();
- CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_IMPLICIT);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ Drop(1);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
@@ -3074,10 +3274,11 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
int arity = instr->arity();
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
__ mov(r2, Operand(instr->name()));
- CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
+ CallCode(ic, mode, instr);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
@@ -3085,7 +3286,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
__ mov(r1, Operand(instr->target()));
- CallKnownFunction(instr->target(), instr->arity(), instr);
+ CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION);
}
@@ -3185,35 +3386,53 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
LStoreKeyedSpecializedArrayElement* instr) {
Register external_pointer = ToRegister(instr->external_pointer());
- Register key = ToRegister(instr->key());
+ Register key = no_reg;
ExternalArrayType array_type = instr->array_type();
- if (array_type == kExternalFloatArray) {
+ bool key_is_constant = instr->key()->IsConstantOperand();
+ int constant_key = 0;
+ if (key_is_constant) {
+ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
+ if (constant_key & 0xF0000000) {
+ Abort("array index constant value too big.");
+ }
+ } else {
+ key = ToRegister(instr->key());
+ }
+ int shift_size = ExternalArrayTypeToShiftSize(array_type);
+
+ if (array_type == kExternalFloatArray || array_type == kExternalDoubleArray) {
CpuFeatures::Scope scope(VFP3);
DwVfpRegister value(ToDoubleRegister(instr->value()));
- __ add(scratch0(), external_pointer, Operand(key, LSL, 2));
- __ vcvt_f32_f64(double_scratch0().low(), value);
- __ vstr(double_scratch0().low(), scratch0(), 0);
+ Operand operand(key_is_constant ? Operand(constant_key * (1 << shift_size))
+ : Operand(key, LSL, shift_size));
+ __ add(scratch0(), external_pointer, operand);
+ if (array_type == kExternalFloatArray) {
+ __ vcvt_f32_f64(double_scratch0().low(), value);
+ __ vstr(double_scratch0().low(), scratch0(), 0);
+ } else { // i.e. array_type == kExternalDoubleArray
+ __ vstr(value, scratch0(), 0);
+ }
} else {
Register value(ToRegister(instr->value()));
+ MemOperand mem_operand(key_is_constant
+ ? MemOperand(external_pointer, constant_key * (1 << shift_size))
+ : MemOperand(external_pointer, key, LSL, shift_size));
switch (array_type) {
case kExternalPixelArray:
- // Clamp the value to [0..255].
- __ Usat(value, 8, Operand(value));
- __ strb(value, MemOperand(external_pointer, key));
- break;
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ strb(value, MemOperand(external_pointer, key));
+ __ strb(value, mem_operand);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ strh(value, MemOperand(external_pointer, key, LSL, 1));
+ __ strh(value, mem_operand);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ str(value, MemOperand(external_pointer, key, LSL, 2));
+ __ str(value, mem_operand);
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
UNREACHABLE();
break;
}
@@ -3233,6 +3452,14 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
}
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ __ push(ToRegister(instr->left()));
+ __ push(ToRegister(instr->right()));
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
@@ -3561,10 +3788,13 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() && input->Equals(instr->result()));
if (instr->needs_check()) {
- __ tst(ToRegister(input), Operand(kSmiTagMask));
- DeoptimizeIf(ne, instr->environment());
+ ASSERT(kHeapObjectTag == 1);
+ // If the input is a HeapObject, SmiUntag will set the carry flag.
+ __ SmiUntag(ToRegister(input), SetCC);
+ DeoptimizeIf(cs, instr->environment());
+ } else {
+ __ SmiUntag(ToRegister(input));
}
- __ SmiUntag(ToRegister(input));
}
@@ -3641,6 +3871,12 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
Label done;
+ // The input was optimistically untagged; revert it.
+ // The carry flag is set when we reach this deferred code as we just executed
+ // SmiUntag(heap_object, SetCC)
+ ASSERT(kHeapObjectTag == 1);
+ __ adc(input_reg, input_reg, Operand(input_reg));
+
// Heap number map check.
__ ldr(scratch1, FieldMemOperand(input_reg, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
@@ -3713,13 +3949,12 @@ void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
DeferredTaggedToI* deferred = new DeferredTaggedToI(this, instr);
- // Smi check.
- __ tst(input_reg, Operand(kSmiTagMask));
- __ b(ne, deferred->entry());
-
- // Smi to int32 conversion
- __ SmiUntag(input_reg); // Untag smi.
-
+ // Optimistically untag the input.
+ // If the input is a HeapObject, SmiUntag will set the carry flag.
+ __ SmiUntag(input_reg, SetCC);
+ // Branch to deferred code if the input was tagged.
+ // The deferred code will take care of restoring the tag.
+ __ b(cs, deferred->entry());
__ bind(deferred->exit());
}
@@ -3792,22 +4027,41 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0));
Register scratch = scratch0();
- InstanceType first = instr->hydrogen()->first();
- InstanceType last = instr->hydrogen()->last();
__ ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
- __ cmp(scratch, Operand(first));
- // If there is only one type in the interval check for equality.
- if (first == last) {
- DeoptimizeIf(ne, instr->environment());
+ if (instr->hydrogen()->is_interval_check()) {
+ InstanceType first;
+ InstanceType last;
+ instr->hydrogen()->GetCheckInterval(&first, &last);
+
+ __ cmp(scratch, Operand(first));
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(ne, instr->environment());
+ } else {
+ DeoptimizeIf(lo, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmp(scratch, Operand(last));
+ DeoptimizeIf(hi, instr->environment());
+ }
+ }
} else {
- DeoptimizeIf(lo, instr->environment());
- // Omit check for the last type.
- if (last != LAST_TYPE) {
- __ cmp(scratch, Operand(last));
- DeoptimizeIf(hi, instr->environment());
+ uint8_t mask;
+ uint8_t tag;
+ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
+
+ if (IsPowerOf2(mask)) {
+ ASSERT(tag == 0 || IsPowerOf2(tag));
+ __ tst(scratch, Operand(mask));
+ DeoptimizeIf(tag == 0 ? ne : eq, instr->environment());
+ } else {
+ __ and_(scratch, scratch, Operand(mask));
+ __ cmp(scratch, Operand(tag));
+ DeoptimizeIf(ne, instr->environment());
}
}
}
@@ -3832,6 +4086,59 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
}
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ DoubleRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ DoubleRegister temp_reg = ToDoubleRegister(instr->TempAt(0));
+ __ ClampDoubleToUint8(result_reg, value_reg, temp_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+ Register unclamped_reg = ToRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ __ ClampUint8(result_reg, unclamped_reg);
+}
+
+
+void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
+ Register scratch = scratch0();
+ Register input_reg = ToRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ DoubleRegister temp_reg = ToDoubleRegister(instr->TempAt(0));
+ Label is_smi, done, heap_number;
+
+ // Both smi and heap number cases are handled.
+ __ JumpIfSmi(input_reg, &is_smi);
+
+ // Check for heap number
+ __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
+ __ cmp(scratch, Operand(factory()->heap_number_map()));
+ __ b(eq, &heap_number);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ cmp(input_reg, Operand(factory()->undefined_value()));
+ DeoptimizeIf(ne, instr->environment());
+ __ movt(input_reg, 0);
+ __ jmp(&done);
+
+ // Heap number
+ __ bind(&heap_number);
+ __ vldr(double_scratch0(), FieldMemOperand(input_reg,
+ HeapNumber::kValueOffset));
+ __ ClampDoubleToUint8(result_reg, double_scratch0(), temp_reg);
+ __ jmp(&done);
+
+ // smi
+ __ bind(&is_smi);
+ __ SmiUntag(result_reg, input_reg);
+ __ ClampUint8(result_reg, result_reg);
+
+ __ bind(&done);
+}
+
+
void LCodeGen::LoadHeapObject(Register result,
Handle<HeapObject> object) {
if (heap()->InNewSpace(*object)) {
@@ -4187,7 +4494,23 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
SafepointGenerator safepoint_generator(this,
pointers,
env->deoptimization_index());
- __ InvokeBuiltin(Builtins::DELETE, CALL_JS, &safepoint_generator);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator);
+}
+
+
+void LCodeGen::DoIn(LIn* instr) {
+ Register obj = ToRegister(instr->object());
+ Register key = ToRegister(instr->key());
+ __ Push(key, obj);
+ ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator safepoint_generator(this,
+ pointers,
+ env->deoptimization_index());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator);
}
@@ -4220,6 +4543,8 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
}
+
+
#undef __
} } // namespace v8::internal
diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h
index 092e7b77..8253c176 100644
--- a/src/arm/lithium-codegen-arm.h
+++ b/src/arm/lithium-codegen-arm.h
@@ -51,6 +51,7 @@ class LCodeGen BASE_EMBEDDED {
current_instruction_(-1),
instructions_(chunk->instructions()),
deoptimizations_(4),
+ deopt_jump_table_(4),
deoptimization_literals_(8),
inlined_function_count_(0),
scope_(info->scope()),
@@ -115,6 +116,7 @@ class LCodeGen BASE_EMBEDDED {
// Parallel move support.
void DoParallelMove(LParallelMove* move);
+ void DoGap(LGap* instr);
// Emit frame translation commands for an environment.
void WriteTranslation(LEnvironment* environment, Translation* translation);
@@ -158,8 +160,8 @@ class LCodeGen BASE_EMBEDDED {
Register temporary,
Register temporary2);
- int StackSlotCount() const { return chunk()->spill_slot_count(); }
- int ParameterCount() const { return scope()->num_parameters(); }
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+ int GetParameterCount() const { return scope()->num_parameters(); }
void Abort(const char* format, ...);
void Comment(const char* format, ...);
@@ -171,6 +173,7 @@ class LCodeGen BASE_EMBEDDED {
bool GeneratePrologue();
bool GenerateBody();
bool GenerateDeferredCode();
+ bool GenerateDeoptJumpTable();
bool GenerateSafepointTable();
enum SafepointMode {
@@ -206,7 +209,8 @@ class LCodeGen BASE_EMBEDDED {
// to be in edi.
void CallKnownFunction(Handle<JSFunction> function,
int arity,
- LInstruction* instr);
+ LInstruction* instr,
+ CallKind call_kind);
void LoadHeapObject(Register result, Handle<HeapObject> object);
@@ -284,10 +288,18 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp1, Register temp2);
- void EmitLoadField(Register result,
- Register object,
- Handle<Map> type,
- Handle<String> name);
+ void EmitLoadFieldOrConstantFunction(Register result,
+ Register object,
+ Handle<Map> type,
+ Handle<String> name);
+
+ struct JumpTableEntry {
+ explicit inline JumpTableEntry(Address entry)
+ : label(),
+ address(entry) { }
+ Label label;
+ Address address;
+ };
LChunk* const chunk_;
MacroAssembler* const masm_;
@@ -297,6 +309,7 @@ class LCodeGen BASE_EMBEDDED {
int current_instruction_;
const ZoneList<LInstruction*>* instructions_;
ZoneList<LEnvironment*> deoptimizations_;
+ ZoneList<JumpTableEntry> deopt_jump_table_;
ZoneList<Handle<Object> > deoptimization_literals_;
int inlined_function_count_;
Scope* const scope_;
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 6a095d3c..c227b13d 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -83,7 +83,7 @@ void MacroAssembler::Jump(Register target, Condition cond) {
void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
Condition cond) {
#if USE_BX
- mov(ip, Operand(target, rmode), LeaveCC, cond);
+ mov(ip, Operand(target, rmode));
bx(ip, cond);
#else
mov(pc, Operand(target, rmode), LeaveCC, cond);
@@ -148,8 +148,9 @@ int MacroAssembler::CallSize(
}
-void MacroAssembler::Call(
- intptr_t target, RelocInfo::Mode rmode, Condition cond) {
+void MacroAssembler::Call(intptr_t target,
+ RelocInfo::Mode rmode,
+ Condition cond) {
// Block constant pool for the call instruction sequence.
BlockConstPoolScope block_const_pool(this);
#ifdef DEBUG
@@ -167,7 +168,7 @@ void MacroAssembler::Call(
// we have to do it explicitly.
positions_recorder()->WriteRecordedPositions();
- mov(ip, Operand(target, rmode), LeaveCC, cond);
+ mov(ip, Operand(target, rmode));
blx(ip, cond);
ASSERT(kCallTargetAddressOffset == 2 * kInstrSize);
@@ -214,8 +215,31 @@ int MacroAssembler::CallSize(
}
-void MacroAssembler::Call(
- Handle<Code> code, RelocInfo::Mode rmode, Condition cond) {
+void MacroAssembler::CallWithAstId(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ unsigned ast_id,
+ Condition cond) {
+#ifdef DEBUG
+ int pre_position = pc_offset();
+#endif
+
+ ASSERT(rmode == RelocInfo::CODE_TARGET_WITH_ID);
+ ASSERT(ast_id != kNoASTId);
+ ASSERT(ast_id_for_reloc_info_ == kNoASTId);
+ ast_id_for_reloc_info_ = ast_id;
+ // 'code' is always generated ARM code, never THUMB code
+ Call(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
+
+#ifdef DEBUG
+ int post_position = pc_offset();
+ CHECK_EQ(pre_position + CallSize(code, rmode, cond), post_position);
+#endif
+}
+
+
+void MacroAssembler::Call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ Condition cond) {
#ifdef DEBUG
int pre_position = pc_offset();
#endif
@@ -286,6 +310,15 @@ void MacroAssembler::Move(Register dst, Register src) {
}
+void MacroAssembler::Move(DoubleRegister dst, DoubleRegister src) {
+ ASSERT(CpuFeatures::IsSupported(VFP3));
+ CpuFeatures::Scope scope(VFP3);
+ if (!dst.is(src)) {
+ vmov(dst, src);
+ }
+}
+
+
void MacroAssembler::And(Register dst, Register src1, const Operand& src2,
Condition cond) {
if (!src2.is_reg() &&
@@ -839,7 +872,25 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles,
}
void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
- vmov(dst, r0, r1);
+ if (use_eabi_hardfloat()) {
+ Move(dst, d0);
+ } else {
+ vmov(dst, r0, r1);
+ }
+}
+
+
+void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) {
+ // This macro takes the dst register to make the code more readable
+ // at the call sites. However, the dst register has to be r5 to
+ // follow the calling convention which requires the call type to be
+ // in r5.
+ ASSERT(dst.is(r5));
+ if (call_kind == CALL_AS_FUNCTION) {
+ mov(dst, Operand(Smi::FromInt(1)));
+ } else {
+ mov(dst, Operand(Smi::FromInt(0)));
+ }
}
@@ -849,7 +900,8 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Register code_reg,
Label* done,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
bool definitely_matches = false;
Label regular_invoke;
@@ -904,13 +956,13 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Handle<Code> adaptor =
isolate()->builtins()->ArgumentsAdaptorTrampoline();
if (flag == CALL_FUNCTION) {
- if (call_wrapper != NULL) {
- call_wrapper->BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
- }
+ call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
+ SetCallKind(r5, call_kind);
Call(adaptor, RelocInfo::CODE_TARGET);
- if (call_wrapper != NULL) call_wrapper->AfterCall();
+ call_wrapper.AfterCall();
b(done);
} else {
+ SetCallKind(r5, call_kind);
Jump(adaptor, RelocInfo::CODE_TARGET);
}
bind(&regular_invoke);
@@ -922,17 +974,20 @@ void MacroAssembler::InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
Label done;
InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag,
- call_wrapper);
+ call_wrapper, call_kind);
if (flag == CALL_FUNCTION) {
- if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
+ call_wrapper.BeforeCall(CallSize(code));
+ SetCallKind(r5, call_kind);
Call(code);
- if (call_wrapper != NULL) call_wrapper->AfterCall();
+ call_wrapper.AfterCall();
} else {
ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(r5, call_kind);
Jump(code);
}
@@ -946,13 +1001,17 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
const ParameterCount& expected,
const ParameterCount& actual,
RelocInfo::Mode rmode,
- InvokeFlag flag) {
+ InvokeFlag flag,
+ CallKind call_kind) {
Label done;
- InvokePrologue(expected, actual, code, no_reg, &done, flag);
+ InvokePrologue(expected, actual, code, no_reg, &done, flag,
+ NullCallWrapper(), call_kind);
if (flag == CALL_FUNCTION) {
+ SetCallKind(r5, call_kind);
Call(code, rmode);
} else {
+ SetCallKind(r5, call_kind);
Jump(code, rmode);
}
@@ -965,7 +1024,8 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
void MacroAssembler::InvokeFunction(Register fun,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
// Contract with called JS functions requires that function is passed in r1.
ASSERT(fun.is(r1));
@@ -982,13 +1042,14 @@ void MacroAssembler::InvokeFunction(Register fun,
FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
- InvokeCode(code_reg, expected, actual, flag, call_wrapper);
+ InvokeCode(code_reg, expected, actual, flag, call_wrapper, call_kind);
}
void MacroAssembler::InvokeFunction(JSFunction* function,
const ParameterCount& actual,
- InvokeFlag flag) {
+ InvokeFlag flag,
+ CallKind call_kind) {
ASSERT(function->is_compiled());
// Get the function and setup the context.
@@ -1003,9 +1064,9 @@ void MacroAssembler::InvokeFunction(JSFunction* function,
// code field in the function to allow recompilation to take effect
// without changing any of the call sites.
ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
- InvokeCode(r3, expected, actual, flag);
+ InvokeCode(r3, expected, actual, flag, NullCallWrapper(), call_kind);
} else {
- InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag);
+ InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag, call_kind);
}
}
@@ -1620,8 +1681,8 @@ void MacroAssembler::CheckMap(Register obj,
Register scratch,
Handle<Map> map,
Label* fail,
- bool is_heap_object) {
- if (!is_heap_object) {
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
JumpIfSmi(obj, fail);
}
ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
@@ -1635,8 +1696,8 @@ void MacroAssembler::CheckMap(Register obj,
Register scratch,
Heap::RootListIndex index,
Label* fail,
- bool is_heap_object) {
- if (!is_heap_object) {
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
JumpIfSmi(obj, fail);
}
ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
@@ -1646,6 +1707,23 @@ void MacroAssembler::CheckMap(Register obj,
}
+void MacroAssembler::DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ mov(ip, Operand(map));
+ cmp(scratch, ip);
+ Jump(success, RelocInfo::CODE_TARGET, eq);
+ bind(&fail);
+}
+
+
void MacroAssembler::TryGetFunctionPrototype(Register function,
Register result,
Register scratch,
@@ -1699,6 +1777,17 @@ void MacroAssembler::CallStub(CodeStub* stub, Condition cond) {
}
+MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond) {
+ ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs.
+ Object* result;
+ { MaybeObject* maybe_result = stub->TryGetCode();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Call(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond);
+ return result;
+}
+
+
void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) {
ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs.
Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond);
@@ -1711,7 +1800,7 @@ MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub, Condition cond) {
{ MaybeObject* maybe_result = stub->TryGetCode();
if (!maybe_result->ToObject(&result)) return maybe_result;
}
- Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond);
+ Jump(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond);
return result;
}
@@ -2276,15 +2365,17 @@ MaybeObject* MacroAssembler::TryJumpToExternalReference(
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags,
- CallWrapper* call_wrapper) {
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper) {
GetBuiltinEntry(r2, id);
- if (flags == CALL_JS) {
- if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(r2));
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(r2));
+ SetCallKind(r5, CALL_AS_METHOD);
Call(r2);
- if (call_wrapper != NULL) call_wrapper->AfterCall();
+ call_wrapper.AfterCall();
} else {
- ASSERT(flags == JUMP_JS);
+ ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(r5, CALL_AS_METHOD);
Jump(r2);
}
}
@@ -2475,7 +2566,7 @@ void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
ldr(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
if (emit_debug_code()) {
Label ok, fail;
- CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, false);
+ CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK);
b(&ok);
bind(&fail);
Abort("Global functions must have initial map");
@@ -2794,12 +2885,36 @@ void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type,
static const int kRegisterPassedArguments = 4;
-void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
- int frame_alignment = ActivationFrameAlignment();
+int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments,
+ int num_double_arguments) {
+ int stack_passed_words = 0;
+ if (use_eabi_hardfloat()) {
+ // In the hard floating point calling convention, we can use
+ // all double registers to pass doubles.
+ if (num_double_arguments > DoubleRegister::kNumRegisters) {
+ stack_passed_words +=
+ 2 * (num_double_arguments - DoubleRegister::kNumRegisters);
+ }
+ } else {
+ // In the soft floating point calling convention, every double
+ // argument is passed using two registers.
+ num_reg_arguments += 2 * num_double_arguments;
+ }
// Up to four simple arguments are passed in registers r0..r3.
- int stack_passed_arguments = (num_arguments <= kRegisterPassedArguments) ?
- 0 : num_arguments - kRegisterPassedArguments;
+ if (num_reg_arguments > kRegisterPassedArguments) {
+ stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
+ }
+ return stack_passed_words;
+}
+
+
+void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
+ int num_double_arguments,
+ Register scratch) {
+ int frame_alignment = ActivationFrameAlignment();
+ int stack_passed_arguments = CalculateStackPassedWords(
+ num_reg_arguments, num_double_arguments);
if (frame_alignment > kPointerSize) {
// Make stack end at alignment and make room for num_arguments - 4 words
// and the original value of sp.
@@ -2814,25 +2929,92 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
}
+void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
+ Register scratch) {
+ PrepareCallCFunction(num_reg_arguments, 0, scratch);
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) {
+ if (use_eabi_hardfloat()) {
+ Move(d0, dreg);
+ } else {
+ vmov(r0, r1, dreg);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1,
+ DoubleRegister dreg2) {
+ if (use_eabi_hardfloat()) {
+ if (dreg2.is(d0)) {
+ ASSERT(!dreg1.is(d1));
+ Move(d1, dreg2);
+ Move(d0, dreg1);
+ } else {
+ Move(d0, dreg1);
+ Move(d1, dreg2);
+ }
+ } else {
+ vmov(r0, r1, dreg1);
+ vmov(r2, r3, dreg2);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg,
+ Register reg) {
+ if (use_eabi_hardfloat()) {
+ Move(d0, dreg);
+ Move(r0, reg);
+ } else {
+ Move(r2, reg);
+ vmov(r0, r1, dreg);
+ }
+}
+
+
void MacroAssembler::CallCFunction(ExternalReference function,
- int num_arguments) {
- CallCFunctionHelper(no_reg, function, ip, num_arguments);
+ int num_reg_arguments,
+ int num_double_arguments) {
+ CallCFunctionHelper(no_reg,
+ function,
+ ip,
+ num_reg_arguments,
+ num_double_arguments);
}
+
void MacroAssembler::CallCFunction(Register function,
- Register scratch,
- int num_arguments) {
+ Register scratch,
+ int num_reg_arguments,
+ int num_double_arguments) {
CallCFunctionHelper(function,
ExternalReference::the_hole_value_location(isolate()),
scratch,
- num_arguments);
+ num_reg_arguments,
+ num_double_arguments);
+}
+
+
+void MacroAssembler::CallCFunction(ExternalReference function,
+ int num_arguments) {
+ CallCFunction(function, num_arguments, 0);
+}
+
+
+void MacroAssembler::CallCFunction(Register function,
+ Register scratch,
+ int num_arguments) {
+ CallCFunction(function, scratch, num_arguments, 0);
}
void MacroAssembler::CallCFunctionHelper(Register function,
ExternalReference function_reference,
Register scratch,
- int num_arguments) {
+ int num_reg_arguments,
+ int num_double_arguments) {
// Make sure that the stack is aligned before calling a C function unless
// running in the simulator. The simulator has its own alignment check which
// provides more information.
@@ -2861,9 +3043,9 @@ void MacroAssembler::CallCFunctionHelper(Register function,
function = scratch;
}
Call(function);
- int stack_passed_arguments = (num_arguments <= kRegisterPassedArguments) ?
- 0 : num_arguments - kRegisterPassedArguments;
- if (OS::ActivationFrameAlignment() > kPointerSize) {
+ int stack_passed_arguments = CalculateStackPassedWords(
+ num_reg_arguments, num_double_arguments);
+ if (ActivationFrameAlignment() > kPointerSize) {
ldr(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
} else {
add(sp, sp, Operand(stack_passed_arguments * sizeof(kPointerSize)));
@@ -2891,6 +3073,55 @@ void MacroAssembler::GetRelocatedValueLocation(Register ldr_location,
}
+void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) {
+ Usat(output_reg, 8, Operand(input_reg));
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(Register result_reg,
+ DoubleRegister input_reg,
+ DoubleRegister temp_double_reg) {
+ Label above_zero;
+ Label done;
+ Label in_bounds;
+
+ vmov(temp_double_reg, 0.0);
+ VFPCompareAndSetFlags(input_reg, temp_double_reg);
+ b(gt, &above_zero);
+
+ // Double value is less than zero, NaN or Inf, return 0.
+ mov(result_reg, Operand(0));
+ b(al, &done);
+
+ // Double value is >= 255, return 255.
+ bind(&above_zero);
+ vmov(temp_double_reg, 255.0);
+ VFPCompareAndSetFlags(input_reg, temp_double_reg);
+ b(le, &in_bounds);
+ mov(result_reg, Operand(255));
+ b(al, &done);
+
+ // In 0-255 range, round and truncate.
+ bind(&in_bounds);
+ vmov(temp_double_reg, 0.5);
+ vadd(temp_double_reg, input_reg, temp_double_reg);
+ vcvt_u32_f64(s0, temp_double_reg);
+ vmov(result_reg, s0);
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ ldr(descriptors,
+ FieldMemOperand(map, Map::kInstanceDescriptorsOrBitField3Offset));
+ Label not_smi;
+ JumpIfNotSmi(descriptors, &not_smi);
+ mov(descriptors, Operand(FACTORY->empty_descriptor_array()));
+ bind(&not_smi);
+}
+
+
CodePatcher::CodePatcher(byte* address, int instructions)
: address_(address),
instructions_(instructions),
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 8d817c07..1e2c9f4a 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -29,13 +29,11 @@
#define V8_ARM_MACRO_ASSEMBLER_ARM_H_
#include "assembler.h"
+#include "v8globals.h"
namespace v8 {
namespace internal {
-// Forward declaration.
-class CallWrapper;
-
// ----------------------------------------------------------------------------
// Static helper functions
@@ -55,12 +53,6 @@ static inline Operand SmiUntagOperand(Register object) {
const Register cp = { 8 }; // JavaScript context pointer
const Register roots = { 10 }; // Roots array pointer.
-enum InvokeJSFlags {
- CALL_JS,
- JUMP_JS
-};
-
-
// Flags used for the AllocateInNewSpace functions.
enum AllocationFlags {
// No special flags.
@@ -107,7 +99,13 @@ class MacroAssembler: public Assembler {
static int CallSize(Handle<Code> code,
RelocInfo::Mode rmode,
Condition cond = al);
- void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
+ void Call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ Condition cond = al);
+ void CallWithAstId(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ unsigned ast_id,
+ Condition cond = al);
void Ret(Condition cond = al);
// Emit code to discard a non-negative number of pointer-sized elements
@@ -144,9 +142,12 @@ class MacroAssembler: public Assembler {
Condition cond = al);
void Call(Label* target);
+
+ // Register move. May do nothing if the registers are identical.
void Move(Register dst, Handle<Object> value);
- // May do nothing if the registers are identical.
void Move(Register dst, Register src);
+ void Move(DoubleRegister dst, DoubleRegister src);
+
// Jumps to the label at the index given by the Smi in "index".
void SmiJumpTable(Register index, Vector<Label*> targets);
// Load an object from the root table.
@@ -347,29 +348,38 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// JavaScript invokes
+ // Setup call kind marking in ecx. The method takes ecx as an
+ // explicit first parameter to make the code more readable at the
+ // call sites.
+ void SetCallKind(Register dst, CallKind kind);
+
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
void InvokeCode(Handle<Code> code,
const ParameterCount& expected,
const ParameterCount& actual,
RelocInfo::Mode rmode,
- InvokeFlag flag);
+ InvokeFlag flag,
+ CallKind call_kind);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
void InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
void InvokeFunction(JSFunction* function,
const ParameterCount& actual,
- InvokeFlag flag);
+ InvokeFlag flag,
+ CallKind call_kind);
void IsObjectJSObjectType(Register heap_object,
Register map,
@@ -577,13 +587,24 @@ class MacroAssembler: public Assembler {
Register scratch,
Handle<Map> map,
Label* fail,
- bool is_heap_object);
+ SmiCheckType smi_check_type);
+
void CheckMap(Register obj,
Register scratch,
Heap::RootListIndex index,
Label* fail,
- bool is_heap_object);
+ SmiCheckType smi_check_type);
+
+
+ // Check if the map of an object is equal to a specified map and branch to a
+ // specified target if equal. Skip the smi check if not required (object is
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
// Compare the object in a register to a value from the root list.
@@ -704,6 +725,11 @@ class MacroAssembler: public Assembler {
// Call a code stub.
void CallStub(CodeStub* stub, Condition cond = al);
+ // Call a code stub and return the code object called. Try to generate
+ // the code if necessary. Do not perform a GC but instead return a retry
+ // after GC failure.
+ MUST_USE_RESULT MaybeObject* TryCallStub(CodeStub* stub, Condition cond = al);
+
// Call a code stub.
void TailCallStub(CodeStub* stub, Condition cond = al);
@@ -742,15 +768,32 @@ class MacroAssembler: public Assembler {
int num_arguments,
int result_size);
+ int CalculateStackPassedWords(int num_reg_arguments,
+ int num_double_arguments);
+
// Before calling a C-function from generated code, align arguments on stack.
// After aligning the frame, non-register arguments must be stored in
// sp[0], sp[4], etc., not pushed. The argument count assumes all arguments
- // are word sized.
+ // are word sized. If double arguments are used, this function assumes that
+ // all double arguments are stored before core registers; otherwise the
+ // correct alignment of the double values is not guaranteed.
// Some compilers/platforms require the stack to be aligned when calling
// C++ code.
// Needs a scratch register to do some arithmetic. This register will be
// trashed.
- void PrepareCallCFunction(int num_arguments, Register scratch);
+ void PrepareCallCFunction(int num_reg_arguments,
+ int num_double_registers,
+ Register scratch);
+ void PrepareCallCFunction(int num_reg_arguments,
+ Register scratch);
+
+ // There are two ways of passing double arguments on ARM, depending on
+ // whether soft or hard floating point ABI is used. These functions
+ // abstract parameter passing for the three different ways we call
+ // C functions from generated code.
+ void SetCallCDoubleArguments(DoubleRegister dreg);
+ void SetCallCDoubleArguments(DoubleRegister dreg1, DoubleRegister dreg2);
+ void SetCallCDoubleArguments(DoubleRegister dreg, Register reg);
// Calls a C function and cleans up the space for arguments allocated
// by PrepareCallCFunction. The called function is not allowed to trigger a
@@ -759,6 +802,12 @@ class MacroAssembler: public Assembler {
// function).
void CallCFunction(ExternalReference function, int num_arguments);
void CallCFunction(Register function, Register scratch, int num_arguments);
+ void CallCFunction(ExternalReference function,
+ int num_reg_arguments,
+ int num_double_arguments);
+ void CallCFunction(Register function, Register scratch,
+ int num_reg_arguments,
+ int num_double_arguments);
void GetCFunctionDoubleResult(const DoubleRegister dst);
@@ -777,8 +826,8 @@ class MacroAssembler: public Assembler {
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags,
- CallWrapper* call_wrapper = NULL);
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper = NullCallWrapper());
// Store the code object for the given builtin in the target register and
// setup the function in r1.
@@ -825,6 +874,15 @@ class MacroAssembler: public Assembler {
void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; }
bool allow_stub_calls() { return allow_stub_calls_; }
+ // EABI variant for double arguments in use.
+ bool use_eabi_hardfloat() {
+#if USE_EABI_HARDFLOAT
+ return true;
+#else
+ return false;
+#endif
+ }
+
// ---------------------------------------------------------------------------
// Number utilities
@@ -952,17 +1010,29 @@ class MacroAssembler: public Assembler {
Register result);
+ void ClampUint8(Register output_reg, Register input_reg);
+
+ void ClampDoubleToUint8(Register result_reg,
+ DoubleRegister input_reg,
+ DoubleRegister temp_double_reg);
+
+
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+
private:
void CallCFunctionHelper(Register function,
ExternalReference function_reference,
Register scratch,
- int num_arguments);
+ int num_reg_arguments,
+ int num_double_arguments);
void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
static int CallSize(intptr_t target,
RelocInfo::Mode rmode,
Condition cond = al);
- void Call(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
+ void Call(intptr_t target,
+ RelocInfo::Mode rmode,
+ Condition cond = al);
// Helper functions for generating invokes.
void InvokePrologue(const ParameterCount& expected,
@@ -971,7 +1041,8 @@ class MacroAssembler: public Assembler {
Register code_reg,
Label* done,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
// Activation support.
void EnterFrame(StackFrame::Type type);
@@ -1032,21 +1103,6 @@ class CodePatcher {
#endif // ENABLE_DEBUGGER_SUPPORT
-// Helper class for generating code or data associated with the code
-// right after a call instruction. As an example this can be used to
-// generate safepoint data after calls for crankshaft.
-class CallWrapper {
- public:
- CallWrapper() { }
- virtual ~CallWrapper() { }
- // Called just before emitting a call. Argument is the size of the generated
- // call code.
- virtual void BeforeCall(int call_size) = 0;
- // Called just after emitting a call, i.e., at the return site for the call.
- virtual void AfterCall() = 0;
-};
-
-
// -----------------------------------------------------------------------------
// Static helper functions.
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
index da554c29..6af53555 100644
--- a/src/arm/simulator-arm.cc
+++ b/src/arm/simulator-arm.cc
@@ -719,20 +719,21 @@ void Simulator::CheckICache(v8::internal::HashMap* i_cache,
}
-void Simulator::Initialize() {
- if (Isolate::Current()->simulator_initialized()) return;
- Isolate::Current()->set_simulator_initialized(true);
- ::v8::internal::ExternalReference::set_redirector(&RedirectExternalReference);
+void Simulator::Initialize(Isolate* isolate) {
+ if (isolate->simulator_initialized()) return;
+ isolate->set_simulator_initialized(true);
+ ::v8::internal::ExternalReference::set_redirector(isolate,
+ &RedirectExternalReference);
}
-Simulator::Simulator() : isolate_(Isolate::Current()) {
+Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
i_cache_ = isolate_->simulator_i_cache();
if (i_cache_ == NULL) {
i_cache_ = new v8::internal::HashMap(&ICacheMatch);
isolate_->set_simulator_i_cache(i_cache_);
}
- Initialize();
+ Initialize(isolate);
// Setup simulator support first. Some of this information is needed to
// setup the architecture state.
size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack
@@ -848,17 +849,13 @@ void* Simulator::RedirectExternalReference(void* external_function,
// Get the active Simulator for the current thread.
Simulator* Simulator::current(Isolate* isolate) {
v8::internal::Isolate::PerIsolateThreadData* isolate_data =
- Isolate::CurrentPerIsolateThreadData();
- if (isolate_data == NULL) {
- Isolate::EnterDefaultIsolate();
- isolate_data = Isolate::CurrentPerIsolateThreadData();
- }
+ isolate->FindOrAllocatePerThreadDataForThisThread();
ASSERT(isolate_data != NULL);
Simulator* sim = isolate_data->simulator();
if (sim == NULL) {
// TODO(146): delete the simulator object when a thread/isolate goes away.
- sim = new Simulator();
+ sim = new Simulator(isolate);
isolate_data->set_simulator(sim);
}
return sim;
@@ -1009,26 +1006,74 @@ double Simulator::get_double_from_d_register(int dreg) {
}
-// For use in calls that take two double values, constructed from r0, r1, r2
-// and r3.
+// For use in calls that take two double values, constructed either
+// from r0-r3 or d0 and d1.
void Simulator::GetFpArgs(double* x, double* y) {
- // We use a char buffer to get around the strict-aliasing rules which
- // otherwise allow the compiler to optimize away the copy.
- char buffer[2 * sizeof(registers_[0])];
- // Registers 0 and 1 -> x.
- memcpy(buffer, registers_, sizeof(buffer));
- memcpy(x, buffer, sizeof(buffer));
- // Registers 2 and 3 -> y.
- memcpy(buffer, registers_ + 2, sizeof(buffer));
- memcpy(y, buffer, sizeof(buffer));
+ if (use_eabi_hardfloat()) {
+ *x = vfp_register[0];
+ *y = vfp_register[1];
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ // Registers 0 and 1 -> x.
+ memcpy(buffer, registers_, sizeof(*x));
+ memcpy(x, buffer, sizeof(*x));
+ // Registers 2 and 3 -> y.
+ memcpy(buffer, registers_ + 2, sizeof(*y));
+ memcpy(y, buffer, sizeof(*y));
+ }
+}
+
+// For use in calls that take one double value, constructed either
+// from r0 and r1 or d0.
+void Simulator::GetFpArgs(double* x) {
+ if (use_eabi_hardfloat()) {
+ *x = vfp_register[0];
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ // Registers 0 and 1 -> x.
+ memcpy(buffer, registers_, sizeof(*x));
+ memcpy(x, buffer, sizeof(*x));
+ }
+}
+
+
+// For use in calls that take one double value constructed either
+// from r0 and r1 or d0 and one integer value.
+void Simulator::GetFpArgs(double* x, int32_t* y) {
+ if (use_eabi_hardfloat()) {
+ *x = vfp_register[0];
+ *y = registers_[1];
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ // Registers 0 and 1 -> x.
+ memcpy(buffer, registers_, sizeof(*x));
+ memcpy(x, buffer, sizeof(*x));
+ // Register 2 -> y.
+ memcpy(buffer, registers_ + 2, sizeof(*y));
+ memcpy(y, buffer, sizeof(*y));
+ }
}
+// The return value is either in r0/r1 or d0.
void Simulator::SetFpResult(const double& result) {
- char buffer[2 * sizeof(registers_[0])];
- memcpy(buffer, &result, sizeof(buffer));
- // result -> registers 0 and 1.
- memcpy(registers_, buffer, sizeof(buffer));
+ if (use_eabi_hardfloat()) {
+ char buffer[2 * sizeof(vfp_register[0])];
+ memcpy(buffer, &result, sizeof(buffer));
+ // Copy result to d0.
+ memcpy(vfp_register, buffer, sizeof(buffer));
+ } else {
+ char buffer[2 * sizeof(registers_[0])];
+ memcpy(buffer, &result, sizeof(buffer));
+ // Copy result to r0 and r1.
+ memcpy(registers_, buffer, sizeof(buffer));
+ }
}
@@ -1282,12 +1327,13 @@ void Simulator::SetVFlag(bool val) {
// Calculate C flag value for additions.
-bool Simulator::CarryFrom(int32_t left, int32_t right) {
+bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) {
uint32_t uleft = static_cast<uint32_t>(left);
uint32_t uright = static_cast<uint32_t>(right);
uint32_t urest = 0xffffffffU - uleft;
- return (uright > urest);
+ return (uright > urest) ||
+ (carry && (((uright + 1) > urest) || (uright > (urest - 1))));
}
@@ -1684,27 +1730,92 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
int32_t* stack_pointer = reinterpret_cast<int32_t*>(get_register(sp));
int32_t arg4 = stack_pointer[0];
int32_t arg5 = stack_pointer[1];
+ bool fp_call =
+ (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL);
+ if (use_eabi_hardfloat()) {
+ // With the hard floating point calling convention, double
+ // arguments are passed in VFP registers. Fetch the arguments
+ // from there and call the builtin using soft floating point
+ // convention.
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ arg0 = vfp_register[0];
+ arg1 = vfp_register[1];
+ arg2 = vfp_register[2];
+ arg3 = vfp_register[3];
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ arg0 = vfp_register[0];
+ arg1 = vfp_register[1];
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ arg0 = vfp_register[0];
+ arg1 = vfp_register[1];
+ arg2 = get_register(0);
+ break;
+ default:
+ break;
+ }
+ }
// This is dodgy but it works because the C entry stubs are never moved.
// See comment in codegen-arm.cc and bug 1242173.
int32_t saved_lr = get_register(lr);
intptr_t external =
reinterpret_cast<intptr_t>(redirection->external_function());
- if (redirection->type() == ExternalReference::FP_RETURN_CALL) {
- SimulatorRuntimeFPCall target =
- reinterpret_cast<SimulatorRuntimeFPCall>(external);
+ if (fp_call) {
if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
- double x, y;
- GetFpArgs(&x, &y);
- PrintF("Call to host function at %p with args %f, %f",
- FUNCTION_ADDR(target), x, y);
+ SimulatorRuntimeFPCall target =
+ reinterpret_cast<SimulatorRuntimeFPCall>(external);
+ double dval0, dval1;
+ int32_t ival;
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ GetFpArgs(&dval0, &dval1);
+ PrintF("Call to host function at %p with args %f, %f",
+ FUNCTION_ADDR(target), dval0, dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ GetFpArgs(&dval0);
+ PrintF("Call to host function at %p with arg %f",
+ FUNCTION_ADDR(target), dval0);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ GetFpArgs(&dval0, &ival);
+ PrintF("Call to host function at %p with args %f, %d",
+ FUNCTION_ADDR(target), dval0, ival);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
if (!stack_aligned) {
PrintF(" with unaligned stack %08x\n", get_register(sp));
}
PrintF("\n");
}
CHECK(stack_aligned);
- double result = target(arg0, arg1, arg2, arg3);
- SetFpResult(result);
+ if (redirection->type() != ExternalReference::BUILTIN_COMPARE_CALL) {
+ SimulatorRuntimeFPCall target =
+ reinterpret_cast<SimulatorRuntimeFPCall>(external);
+ double result = target(arg0, arg1, arg2, arg3);
+ SetFpResult(result);
+ } else {
+ SimulatorRuntimeCall target =
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
+ int32_t lo_res = static_cast<int32_t>(result);
+ int32_t hi_res = static_cast<int32_t>(result >> 32);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %08x\n", lo_res);
+ }
+ set_register(r0, lo_res);
+ set_register(r1, hi_res);
+ }
} else if (redirection->type() == ExternalReference::DIRECT_API_CALL) {
SimulatorRuntimeDirectApiCall target =
reinterpret_cast<SimulatorRuntimeDirectApiCall>(external);
@@ -2209,8 +2320,15 @@ void Simulator::DecodeType01(Instruction* instr) {
}
case ADC: {
- Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm");
- Format(instr, "adc'cond's 'rd, 'rn, 'imm");
+ // Format(instr, "adc'cond's 'rd, 'rn, 'shift_rm");
+ // Format(instr, "adc'cond's 'rd, 'rn, 'imm");
+ alu_out = rn_val + shifter_operand + GetCarry();
+ set_register(rd, alu_out);
+ if (instr->HasS()) {
+ SetNZFlags(alu_out);
+ SetCFlag(CarryFrom(rn_val, shifter_operand, GetCarry()));
+ SetVFlag(OverflowFrom(alu_out, rn_val, shifter_operand, true));
+ }
break;
}
diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h
index a16cae5a..391ef69f 100644
--- a/src/arm/simulator-arm.h
+++ b/src/arm/simulator-arm.h
@@ -68,7 +68,9 @@ typedef int (*arm_regexp_matcher)(String*, int, const byte*, const byte*,
// just use the C stack limit.
class SimulatorStack : public v8::internal::AllStatic {
public:
- static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+ static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
+ uintptr_t c_limit) {
+ USE(isolate);
return c_limit;
}
@@ -143,7 +145,7 @@ class Simulator {
num_d_registers = 16
};
- Simulator();
+ explicit Simulator(Isolate* isolate);
~Simulator();
// The currently executing Simulator instance. Potentially there can be one
@@ -179,7 +181,7 @@ class Simulator {
void Execute();
// Call on program start.
- static void Initialize();
+ static void Initialize(Isolate* isolate);
// V8 generally calls into generated JS code with 5 parameters and into
// generated RegExp code with 7 parameters. This is a convenience function,
@@ -200,6 +202,15 @@ class Simulator {
// below (bad_lr, end_sim_pc).
bool has_bad_pc() const;
+ // EABI variant for double arguments in use.
+ bool use_eabi_hardfloat() {
+#if USE_EABI_HARDFLOAT
+ return true;
+#else
+ return false;
+#endif
+ }
+
private:
enum special_values {
// Known bad pc value to ensure that the simulator does not execute
@@ -223,13 +234,17 @@ class Simulator {
void SetNZFlags(int32_t val);
void SetCFlag(bool val);
void SetVFlag(bool val);
- bool CarryFrom(int32_t left, int32_t right);
+ bool CarryFrom(int32_t left, int32_t right, int32_t carry = 0);
bool BorrowFrom(int32_t left, int32_t right);
bool OverflowFrom(int32_t alu_out,
int32_t left,
int32_t right,
bool addition);
+ inline int GetCarry() {
+ return c_flag_ ? 1 : 0;
+ };
+
// Support for VFP.
void Compute_FPSCR_Flags(double val1, double val2);
void Copy_FPSCR_to_APSR();
@@ -306,9 +321,10 @@ class Simulator {
void* external_function,
v8::internal::ExternalReference::Type type);
- // For use in calls that take two double values, constructed from r0, r1, r2
- // and r3.
+ // For use in calls that take double value arguments.
void GetFpArgs(double* x, double* y);
+ void GetFpArgs(double* x);
+ void GetFpArgs(double* x, int32_t* y);
void SetFpResult(const double& result);
void TrashCallerSaveRegisters();
@@ -394,8 +410,9 @@ class Simulator {
// trouble down the line.
class SimulatorStack : public v8::internal::AllStatic {
public:
- static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
- return Simulator::current(Isolate::Current())->StackLimit();
+ static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
+ uintptr_t c_limit) {
+ return Simulator::current(isolate)->StackLimit();
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index 47d675b0..be8b7d6f 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -95,12 +95,13 @@ static void ProbeTable(Isolate* isolate,
// must always call a backup property check that is complete.
// This function is safe to call if the receiver has fast properties.
// Name must be a symbol and receiver must be a heap object.
-static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
- Label* miss_label,
- Register receiver,
- String* name,
- Register scratch0,
- Register scratch1) {
+MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ String* name,
+ Register scratch0,
+ Register scratch1) {
ASSERT(name->IsSymbol());
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
@@ -136,71 +137,21 @@ static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
// Restore the temporarily used register.
__ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
- // Compute the capacity mask.
- const int kCapacityOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kCapacityIndex * kPointerSize;
-
- // Generate an unrolled loop that performs a few probes before
- // giving up.
- static const int kProbes = 4;
- const int kElementsStartOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kElementsStartIndex * kPointerSize;
-
- // If names of slots in range from 1 to kProbes - 1 for the hash value are
- // not equal to the name and kProbes-th slot is not used (its name is the
- // undefined value), it guarantees the hash table doesn't contain the
- // property. It's true even if some slots represent deleted properties
- // (their names are the null value).
- for (int i = 0; i < kProbes; i++) {
- // scratch0 points to properties hash.
- // Compute the masked index: (hash + i + i * i) & mask.
- Register index = scratch1;
- // Capacity is smi 2^n.
- __ ldr(index, FieldMemOperand(properties, kCapacityOffset));
- __ sub(index, index, Operand(1));
- __ and_(index, index, Operand(
- Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i))));
-
- // Scale the index by multiplying by the entry size.
- ASSERT(StringDictionary::kEntrySize == 3);
- __ add(index, index, Operand(index, LSL, 1)); // index *= 3.
-
- Register entity_name = scratch1;
- // Having undefined at this place means the name is not contained.
- ASSERT_EQ(kSmiTagSize, 1);
- Register tmp = properties;
- __ add(tmp, properties, Operand(index, LSL, 1));
- __ ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset));
-
- ASSERT(!tmp.is(entity_name));
- __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
- __ cmp(entity_name, tmp);
- if (i != kProbes - 1) {
- __ b(eq, &done);
-
- // Stop if found the property.
- __ cmp(entity_name, Operand(Handle<String>(name)));
- __ b(eq, miss_label);
-
- // Check if the entry name is not a symbol.
- __ ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset));
- __ ldrb(entity_name,
- FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
- __ tst(entity_name, Operand(kIsSymbolMask));
- __ b(eq, miss_label);
-
- // Restore the properties.
- __ ldr(properties,
- FieldMemOperand(receiver, JSObject::kPropertiesOffset));
- } else {
- // Give up probing if still not found the undefined value.
- __ b(ne, miss_label);
- }
- }
+
+ MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup(
+ masm,
+ miss_label,
+ &done,
+ receiver,
+ properties,
+ name,
+ scratch1);
+ if (result->IsFailure()) return result;
+
__ bind(&done);
__ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+
+ return result;
}
@@ -525,7 +476,8 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
static void GenerateCallFunction(MacroAssembler* masm,
Object* object,
const ParameterCount& arguments,
- Label* miss) {
+ Label* miss,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- r0: receiver
// -- r1: function to call
@@ -544,7 +496,10 @@ static void GenerateCallFunction(MacroAssembler* masm,
}
// Invoke the function.
- __ InvokeFunction(r1, arguments, JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(r1, arguments, JUMP_FUNCTION, NullCallWrapper(), call_kind);
}
@@ -674,10 +629,12 @@ class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
const ParameterCount& arguments,
- Register name)
+ Register name,
+ Code::ExtraICState extra_ic_state)
: stub_compiler_(stub_compiler),
arguments_(arguments),
- name_(name) {}
+ name_(name),
+ extra_ic_state_(extra_ic_state) {}
MaybeObject* Compile(MacroAssembler* masm,
JSObject* object,
@@ -805,8 +762,11 @@ class CallInterceptorCompiler BASE_EMBEDDED {
arguments_.immediate());
if (result->IsFailure()) return result;
} else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
__ InvokeFunction(optimization.constant_function(), arguments_,
- JUMP_FUNCTION);
+ JUMP_FUNCTION, call_kind);
}
// Deferred code for fast API call case---clean preallocated space.
@@ -888,6 +848,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
StubCompiler* stub_compiler_;
const ParameterCount& arguments_;
Register name_;
+ Code::ExtraICState extra_ic_state_;
};
@@ -1102,12 +1063,17 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
ASSERT(current->property_dictionary()->FindEntry(name) ==
StringDictionary::kNotFound);
- GenerateDictionaryNegativeLookup(masm(),
- miss,
- reg,
- name,
- scratch1,
- scratch2);
+ MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch1,
+ scratch2);
+ if (negative_lookup->IsFailure()) {
+ set_failure(Failure::cast(negative_lookup));
+ return reg;
+ }
+
__ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // from now the object is in holder_reg
__ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
@@ -1501,8 +1467,10 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
MaybeObject* CallStubCompiler::GenerateMissBranch() {
- MaybeObject* maybe_obj = masm()->isolate()->stub_cache()->ComputeCallMiss(
- arguments().immediate(), kind_);
+ MaybeObject* maybe_obj =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_ic_state_);
Object* obj;
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
__ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
@@ -1534,7 +1502,7 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object,
Register reg = CheckPrototypes(object, r0, holder, r1, r3, r4, name, &miss);
GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
- GenerateCallFunction(masm(), object, arguments(), &miss);
+ GenerateCallFunction(masm(), object, arguments(), &miss, extra_ic_state_);
// Handle call cache miss.
__ bind(&miss);
@@ -1594,8 +1562,11 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
__ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
// Check that the elements are in fast mode and writable.
- __ CheckMap(elements, r0,
- Heap::kFixedArrayMapRootIndex, &call_builtin, true);
+ __ CheckMap(elements,
+ r0,
+ Heap::kFixedArrayMapRootIndex,
+ &call_builtin,
+ DONT_DO_SMI_CHECK);
if (argc == 1) { // Otherwise fall through to call the builtin.
Label exit, with_write_barrier, attempt_to_grow_elements;
@@ -1744,7 +1715,11 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object,
__ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
// Check that the elements are in fast mode and writable.
- __ CheckMap(elements, r0, Heap::kFixedArrayMapRootIndex, &call_builtin, true);
+ __ CheckMap(elements,
+ r0,
+ Heap::kFixedArrayMapRootIndex,
+ &call_builtin,
+ DONT_DO_SMI_CHECK);
// Get the array's length into r4 and calculate new length.
__ ldr(r4, FieldMemOperand(receiver, JSArray::kLengthOffset));
@@ -1815,7 +1790,9 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
Label index_out_of_range;
Label* index_out_of_range_label = &index_out_of_range;
- if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
index_out_of_range_label = &miss;
}
@@ -1899,7 +1876,9 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
Label index_out_of_range;
Label* index_out_of_range_label = &index_out_of_range;
- if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
index_out_of_range_label = &miss;
}
@@ -2023,7 +2002,7 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall(
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD);
__ bind(&miss);
// r2: function name.
@@ -2086,7 +2065,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
__ Drop(argc + 1, eq);
__ Ret(eq);
- __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, true);
+ __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
Label wont_fit_smi, no_vfp_exception, restore_fpscr_and_return;
@@ -2171,7 +2150,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
__ bind(&slow);
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD);
__ bind(&miss);
// r2: function name.
@@ -2247,7 +2226,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
// Check if the argument is a heap number and load its exponent and
// sign.
__ bind(&not_smi);
- __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, true);
+ __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
__ ldr(r1, FieldMemOperand(r0, HeapNumber::kExponentOffset));
// Check the sign of the argument. If the argument is positive,
@@ -2273,7 +2252,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION, CALL_AS_METHOD);
__ bind(&miss);
// r2: function name.
@@ -2299,6 +2278,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall(
// repatch it to global receiver.
if (object->IsGlobalObject()) return heap()->undefined_value();
if (cell != NULL) return heap()->undefined_value();
+ if (!object->IsJSObject()) return heap()->undefined_value();
int depth = optimization.GetPrototypeDepthOfExpectedType(
JSObject::cast(object), holder);
if (depth == kInvalidProtoDepth) return heap()->undefined_value();
@@ -2460,7 +2440,10 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
UNREACHABLE();
}
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION, call_kind);
// Handle call cache miss.
__ bind(&miss);
@@ -2493,7 +2476,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Get the receiver from the stack.
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
- CallInterceptorCompiler compiler(this, arguments(), r2);
+ CallInterceptorCompiler compiler(this, arguments(), r2, extra_ic_state_);
MaybeObject* result = compiler.Compile(masm(),
object,
holder,
@@ -2513,7 +2496,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Restore receiver.
__ ldr(r0, MemOperand(sp, argc * kPointerSize));
- GenerateCallFunction(masm(), object, arguments(), &miss);
+ GenerateCallFunction(masm(), object, arguments(), &miss, extra_ic_state_);
// Handle call cache miss.
__ bind(&miss);
@@ -2571,15 +2554,19 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object,
ASSERT(function->is_compiled());
Handle<Code> code(function->code());
ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
if (V8::UseCrankshaft()) {
// TODO(kasperl): For now, we always call indirectly through the
// code field in the function to allow recompilation to take effect
// without changing any of the call sites.
__ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
- __ InvokeCode(r3, expected, arguments(), JUMP_FUNCTION);
+ __ InvokeCode(r3, expected, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
} else {
- __ InvokeCode(code, expected, arguments(),
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ __ InvokeCode(code, expected, arguments(), RelocInfo::CODE_TARGET,
+ JUMP_FUNCTION, call_kind);
}
// Handle call cache miss.
@@ -3128,52 +3115,56 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadFastElement(Map* receiver_map) {
// ----------- S t a t e -------------
// -- lr : return address
// -- r0 : key
// -- r1 : receiver
// -----------------------------------
- Label miss;
-
- // Check that the receiver isn't a smi.
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &miss);
-
- // Check that the map matches.
- __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ cmp(r2, Operand(Handle<Map>(receiver->map())));
- __ b(ne, &miss);
+ MaybeObject* maybe_stub = KeyedLoadFastElementStub().TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(r1,
+ r2,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
- // Check that the key is a smi.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(ne, &miss);
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
- // Get the elements array.
- __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
- __ AssertFastElements(r2);
- // Check that the key is within bounds.
- __ ldr(r3, FieldMemOperand(r2, FixedArray::kLengthOffset));
- __ cmp(r0, Operand(r3));
- __ b(hs, &miss);
+MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(r1, &miss);
- // Load the result and make sure it's not the hole.
- __ add(r3, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
- __ ldr(r4,
- MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ cmp(r4, ip);
- __ b(eq, &miss);
- __ mov(r0, r4);
- __ Ret();
+ int receiver_count = receiver_maps->length();
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map(receiver_maps->at(current));
+ Handle<Code> code(handler_ics->at(current));
+ __ mov(ip, Operand(map));
+ __ cmp(r2, ip);
+ __ Jump(code, RelocInfo::CODE_TARGET, eq);
+ }
__ bind(&miss);
- GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+ Handle<Code> miss_ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ Jump(miss_ic, RelocInfo::CODE_TARGET, al);
// Return the generated code.
- return GetCode(NORMAL, NULL);
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -3215,73 +3206,63 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
}
-MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
- JSObject* receiver) {
+MaybeObject* KeyedStoreStubCompiler::CompileStoreFastElement(
+ Map* receiver_map) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
// -- r2 : receiver
// -- lr : return address
// -- r3 : scratch
- // -- r4 : scratch (elements)
// -----------------------------------
- Label miss;
-
- Register value_reg = r0;
- Register key_reg = r1;
- Register receiver_reg = r2;
- Register scratch = r3;
- Register elements_reg = r4;
-
- // Check that the receiver isn't a smi.
- __ tst(receiver_reg, Operand(kSmiTagMask));
- __ b(eq, &miss);
-
- // Check that the map matches.
- __ ldr(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
- __ cmp(scratch, Operand(Handle<Map>(receiver->map())));
- __ b(ne, &miss);
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ MaybeObject* maybe_stub =
+ KeyedStoreFastElementStub(is_js_array).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(r2,
+ r3,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
- // Check that the key is a smi.
- __ tst(key_reg, Operand(kSmiTagMask));
- __ b(ne, &miss);
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
- // Get the elements array and make sure it is a fast element array, not 'cow'.
- __ ldr(elements_reg,
- FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
- __ ldr(scratch, FieldMemOperand(elements_reg, HeapObject::kMapOffset));
- __ cmp(scratch, Operand(Handle<Map>(factory()->fixed_array_map())));
- __ b(ne, &miss);
- // Check that the key is within bounds.
- if (receiver->IsJSArray()) {
- __ ldr(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
- } else {
- __ ldr(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -- r3 : scratch
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(r2, &miss);
+
+ int receiver_count = receiver_maps->length();
+ __ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map(receiver_maps->at(current));
+ Handle<Code> code(handler_ics->at(current));
+ __ mov(ip, Operand(map));
+ __ cmp(r3, ip);
+ __ Jump(code, RelocInfo::CODE_TARGET, eq);
}
- // Compare smis.
- __ cmp(key_reg, scratch);
- __ b(hs, &miss);
-
- __ add(scratch,
- elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
- __ str(value_reg,
- MemOperand(scratch, key_reg, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ RecordWrite(scratch,
- Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize),
- receiver_reg , elements_reg);
-
- // value_reg (r0) is preserved.
- // Done.
- __ Ret();
__ bind(&miss);
- Handle<Code> ic = masm()->isolate()->builtins()->KeyedStoreIC_Miss();
- __ Jump(ic, RelocInfo::CODE_TARGET);
+ Handle<Code> miss_ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(miss_ic, RelocInfo::CODE_TARGET, al);
// Return the generated code.
- return GetCode(NORMAL, NULL);
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -3429,6 +3410,60 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
}
+MaybeObject* ExternalArrayLoadStubCompiler::CompileLoad(
+ JSObject*receiver, ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ MaybeObject* maybe_stub =
+ KeyedLoadExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(r1,
+ r2,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode();
+}
+
+
+MaybeObject* ExternalArrayStoreStubCompiler::CompileStore(
+ JSObject* receiver, ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : name
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+ MaybeObject* maybe_stub =
+ KeyedStoreExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(r2,
+ r3,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ return GetCode();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
static bool IsElementTypeSigned(ExternalArrayType array_type) {
switch (array_type) {
case kExternalByteArray:
@@ -3448,30 +3483,24 @@ static bool IsElementTypeSigned(ExternalArrayType array_type) {
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
- JSObject* receiver_object,
- ExternalArrayType array_type,
- Code::Flags flags) {
+void KeyedLoadStubCompiler::GenerateLoadExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
// ---------- S t a t e --------------
// -- lr : return address
// -- r0 : key
// -- r1 : receiver
// -----------------------------------
- Label slow, failed_allocation;
+ Label miss_force_generic, slow, failed_allocation;
Register key = r0;
Register receiver = r1;
- // Check that the object isn't a smi
- __ JumpIfSmi(receiver, &slow);
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
// Check that the key is a smi.
- __ JumpIfNotSmi(key, &slow);
-
- // Make sure that we've got the right map.
- __ ldr(r2, FieldMemOperand(receiver, HeapObject::kMapOffset));
- __ cmp(r2, Operand(Handle<Map>(receiver_object->map())));
- __ b(ne, &slow);
+ __ JumpIfNotSmi(key, &miss_force_generic);
__ ldr(r3, FieldMemOperand(receiver, JSObject::kElementsOffset));
// r3: elements array
@@ -3480,7 +3509,7 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ ldr(ip, FieldMemOperand(r3, ExternalArray::kLengthOffset));
__ cmp(ip, Operand(key, ASR, kSmiTagSize));
// Unsigned comparison catches both negative and too-large values.
- __ b(lo, &slow);
+ __ b(lo, &miss_force_generic);
__ ldr(r3, FieldMemOperand(r3, ExternalArray::kExternalPointerOffset));
// r3: base pointer of external storage
@@ -3517,6 +3546,18 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ ldr(value, MemOperand(r3, key, LSL, 1));
}
break;
+ case kExternalDoubleArray:
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ __ add(r2, r3, Operand(key, LSL, 2));
+ __ vldr(d0, r2, 0);
+ } else {
+ __ add(r4, r3, Operand(key, LSL, 2));
+ // r4: pointer to the beginning of the double we want to load.
+ __ ldr(r2, MemOperand(r4, 0));
+ __ ldr(r3, MemOperand(r4, Register::kSizeInBytes));
+ }
+ break;
default:
UNREACHABLE();
break;
@@ -3524,9 +3565,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
// For integer array types:
// r2: value
- // For floating-point array type
+ // For float array type:
// s0: value (if VFP3 is supported)
// r2: value (if VFP3 is not supported)
+ // For double array type:
+ // d0: value (if VFP3 is supported)
+ // r2/r3: value (if VFP3 is not supported)
if (array_type == kExternalIntArray) {
// For the Int and UnsignedInt array types, we need to see whether
@@ -3556,8 +3600,21 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ vstr(d0, r3, HeapNumber::kValueOffset);
__ Ret();
} else {
- WriteInt32ToHeapNumberStub stub(value, r0, r3);
- __ TailCallStub(&stub);
+ Register dst1 = r1;
+ Register dst2 = r3;
+ FloatingPointHelper::Destination dest =
+ FloatingPointHelper::kCoreRegisters;
+ FloatingPointHelper::ConvertIntToDouble(masm,
+ value,
+ dest,
+ d0,
+ dst1,
+ dst2,
+ r9,
+ s0);
+ __ str(dst1, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
+ __ str(dst2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
+ __ Ret();
}
} else if (array_type == kExternalUnsignedIntArray) {
// The test is different for unsigned int values. Since we need
@@ -3602,12 +3659,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ bind(&box_int_0);
// Integer does not have leading zeros.
- GenerateUInt2Double(masm(), hiword, loword, r4, 0);
+ GenerateUInt2Double(masm, hiword, loword, r4, 0);
__ b(&done);
__ bind(&box_int_1);
// Integer has one leading zero.
- GenerateUInt2Double(masm(), hiword, loword, r4, 1);
+ GenerateUInt2Double(masm, hiword, loword, r4, 1);
__ bind(&done);
@@ -3694,6 +3751,31 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ mov(r0, r3);
__ Ret();
}
+ } else if (array_type == kExternalDoubleArray) {
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ // Allocate a HeapNumber for the result. Don't use r0 and r1 as
+ // AllocateHeapNumber clobbers all registers - also when jumping due to
+ // exhausted young space.
+ __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r2, r3, r4, r6, &slow);
+ __ sub(r1, r2, Operand(kHeapObjectTag));
+ __ vstr(d0, r1, HeapNumber::kValueOffset);
+
+ __ mov(r0, r2);
+ __ Ret();
+ } else {
+ // Allocate a HeapNumber for the result. Don't use r0 and r1 as
+ // AllocateHeapNumber clobbers all registers - also when jumping due to
+ // exhausted young space.
+ __ LoadRoot(r7, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(r4, r5, r6, r7, &slow);
+
+ __ str(r2, FieldMemOperand(r4, HeapNumber::kMantissaOffset));
+ __ str(r3, FieldMemOperand(r4, HeapNumber::kExponentOffset));
+ __ mov(r0, r4);
+ __ Ret();
+ }
} else {
// Tag integer as smi and return it.
@@ -3704,7 +3786,7 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
// Slow case, key and receiver still in r0 and r1.
__ bind(&slow);
__ IncrementCounter(
- masm()->isolate()->counters()->keyed_load_external_array_slow(),
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
1, r2, r3);
// ---------- S t a t e --------------
@@ -3717,21 +3799,23 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
- return GetCode(flags);
+ __ bind(&miss_force_generic);
+ Code* stub = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET);
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
- JSObject* receiver_object,
- ExternalArrayType array_type,
- Code::Flags flags) {
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
// ---------- S t a t e --------------
// -- r0 : value
// -- r1 : key
// -- r2 : receiver
// -- lr : return address
// -----------------------------------
- Label slow, check_heap_number;
+ Label slow, check_heap_number, miss_force_generic;
// Register usage.
Register value = r0;
@@ -3739,25 +3823,20 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
Register receiver = r2;
// r3 mostly holds the elements array or the destination external array.
- // Check that the object isn't a smi.
- __ JumpIfSmi(receiver, &slow);
-
- // Make sure that we've got the right map.
- __ ldr(r3, FieldMemOperand(receiver, HeapObject::kMapOffset));
- __ cmp(r3, Operand(Handle<Map>(receiver_object->map())));
- __ b(ne, &slow);
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
__ ldr(r3, FieldMemOperand(receiver, JSObject::kElementsOffset));
// Check that the key is a smi.
- __ JumpIfNotSmi(key, &slow);
+ __ JumpIfNotSmi(key, &miss_force_generic);
// Check that the index is in range
__ SmiUntag(r4, key);
__ ldr(ip, FieldMemOperand(r3, ExternalArray::kLengthOffset));
__ cmp(r4, ip);
// Unsigned comparison catches both negative and too-large values.
- __ b(hs, &slow);
+ __ b(hs, &miss_force_generic);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
@@ -3795,7 +3874,28 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
break;
case kExternalFloatArray:
// Perform int-to-float conversion and store to memory.
- StoreIntAsFloat(masm(), r3, r4, r5, r6, r7, r9);
+ StoreIntAsFloat(masm, r3, r4, r5, r6, r7, r9);
+ break;
+ case kExternalDoubleArray:
+ __ add(r3, r3, Operand(r4, LSL, 3));
+ // r3: effective address of the double element
+ FloatingPointHelper::Destination destination;
+ if (CpuFeatures::IsSupported(VFP3)) {
+ destination = FloatingPointHelper::kVFPRegisters;
+ } else {
+ destination = FloatingPointHelper::kCoreRegisters;
+ }
+ FloatingPointHelper::ConvertIntToDouble(
+ masm, r5, destination,
+ d0, r6, r7, // These are: double_dst, dst1, dst2.
+ r4, s2); // These are: scratch2, single_scratch.
+ if (destination == FloatingPointHelper::kVFPRegisters) {
+ CpuFeatures::Scope scope(VFP3);
+ __ vstr(d0, r3, 0);
+ } else {
+ __ str(r6, MemOperand(r3, 0));
+ __ str(r7, MemOperand(r3, Register::kSizeInBytes));
+ }
break;
default:
UNREACHABLE();
@@ -3831,6 +3931,11 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
__ add(r5, r3, Operand(r4, LSL, 2));
__ vcvt_f32_f64(s0, d0);
__ vstr(s0, r5, 0);
+ } else if (array_type == kExternalDoubleArray) {
+ __ sub(r5, r0, Operand(kHeapObjectTag));
+ __ vldr(d0, r5, HeapNumber::kValueOffset);
+ __ add(r5, r3, Operand(r4, LSL, 3));
+ __ vstr(d0, r5, 0);
} else {
// Need to perform float-to-int conversion.
// Test for NaN or infinity (both give zero).
@@ -3933,6 +4038,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
__ orr(r9, r9, Operand(r5, LSL, kMantissaInHiWordShift));
__ orr(r5, r9, Operand(r6, LSR, kMantissaInLoWordShift));
__ b(&done);
+ } else if (array_type == kExternalDoubleArray) {
+ __ add(r7, r3, Operand(r4, LSL, 3));
+ // r7: effective address of destination element.
+ __ str(r6, MemOperand(r7, 0));
+ __ str(r5, MemOperand(r7, Register::kSizeInBytes));
+ __ Ret();
} else {
bool is_signed_type = IsElementTypeSigned(array_type);
int meaningfull_bits = is_signed_type ? (kBitsPerInt - 1) : kBitsPerInt;
@@ -4002,28 +4113,137 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
}
}
- // Slow case: call runtime.
+ // Slow case, key and receiver still in r0 and r1.
__ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, r2, r3);
- // Entry registers are intact.
// ---------- S t a t e --------------
- // -- r0 : value
- // -- r1 : key
- // -- r2 : receiver
// -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
// -----------------------------------
+ Handle<Code> slow_ic =
+ masm->isolate()->builtins()->KeyedStoreIC_Slow();
+ __ Jump(slow_ic, RelocInfo::CODE_TARGET);
- // Push receiver, key and value for runtime call.
- __ Push(r2, r1, r0);
+ // Miss case, call the runtime.
+ __ bind(&miss_force_generic);
- __ mov(r1, Operand(Smi::FromInt(NONE))); // PropertyAttributes
- __ mov(r0, Operand(Smi::FromInt(
- Code::ExtractExtraICStateFromFlags(flags) & kStrictMode)));
- __ Push(r1, r0);
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- lr : return address
+ // -- r0 : key
+ // -- r1 : receiver
+ // -----------------------------------
+ Label miss_force_generic;
- __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
- return GetCode(flags);
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(r0, &miss_force_generic);
+
+ // Get the elements array.
+ __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
+ __ AssertFastElements(r2);
+
+ // Check that the key is within bounds.
+ __ ldr(r3, FieldMemOperand(r2, FixedArray::kLengthOffset));
+ __ cmp(r0, Operand(r3));
+ __ b(hs, &miss_force_generic);
+
+ // Load the result and make sure it's not the hole.
+ __ add(r3, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ ldr(r4,
+ MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r4, ip);
+ __ b(eq, &miss_force_generic);
+ __ mov(r0, r4);
+ __ Ret();
+
+ __ bind(&miss_force_generic);
+ Code* stub = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
+ bool is_js_array) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : key
+ // -- r2 : receiver
+ // -- lr : return address
+ // -- r3 : scratch
+ // -- r4 : scratch (elements)
+ // -----------------------------------
+ Label miss_force_generic;
+
+ Register value_reg = r0;
+ Register key_reg = r1;
+ Register receiver_reg = r2;
+ Register scratch = r3;
+ Register elements_reg = r4;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(r0, &miss_force_generic);
+
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ ldr(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ CheckMap(elements_reg,
+ scratch,
+ Heap::kFixedArrayMapRootIndex,
+ &miss_force_generic,
+ DONT_DO_SMI_CHECK);
+
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ ldr(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ } else {
+ __ ldr(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ }
+ // Compare smis.
+ __ cmp(key_reg, scratch);
+ __ b(hs, &miss_force_generic);
+
+ __ add(scratch,
+ elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ str(value_reg,
+ MemOperand(scratch, key_reg, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ RecordWrite(scratch,
+ Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize),
+ receiver_reg , elements_reg);
+
+ // value_reg (r0) is preserved.
+ // Done.
+ __ Ret();
+
+ __ bind(&miss_force_generic);
+ Handle<Code> ic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
}
diff --git a/src/array.js b/src/array.js
index 6ed14760..df080a76 100644
--- a/src/array.js
+++ b/src/array.js
@@ -67,6 +67,25 @@ function GetSortedArrayKeys(array, intervals) {
}
+function SparseJoinWithSeparator(array, len, convert, separator) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var totalLength = 0;
+ var elements = new InternalArray(keys.length * 2);
+ var previousKey = -1;
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ if (key != previousKey) { // keys may contain duplicates.
+ var e = array[key];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[i * 2] = key;
+ elements[i * 2 + 1] = e;
+ previousKey = key;
+ }
+ }
+ return %SparseJoinWithSeparator(elements, len, separator);
+}
+
+
// Optimized for sparse arrays if separator is ''.
function SparseJoin(array, len, convert) {
var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
@@ -110,8 +129,12 @@ function Join(array, length, separator, convert) {
// Attempt to convert the elements.
try {
- if (UseSparseVariant(array, length, is_array) && (separator.length == 0)) {
- return SparseJoin(array, length, convert);
+ if (UseSparseVariant(array, length, is_array)) {
+ if (separator.length == 0) {
+ return SparseJoin(array, length, convert);
+ } else {
+ return SparseJoinWithSeparator(array, length, convert, separator);
+ }
}
// Fast case for one-element arrays.
@@ -129,10 +152,8 @@ function Join(array, length, separator, convert) {
var elements_length = 0;
for (var i = 0; i < length; i++) {
var e = array[i];
- if (!IS_UNDEFINED(e)) {
- if (!IS_STRING(e)) e = convert(e);
- elements[elements_length++] = e;
- }
+ if (!IS_STRING(e)) e = convert(e);
+ elements[elements_length++] = e;
}
elements.length = elements_length;
var result = %_FastAsciiArrayJoin(elements, '');
@@ -151,11 +172,12 @@ function Join(array, length, separator, convert) {
} else {
for (var i = 0; i < length; i++) {
var e = array[i];
- if (IS_NUMBER(e)) elements[i] = %_NumberToString(e);
- else {
- if (!IS_STRING(e)) e = convert(e);
+ if (IS_NUMBER(e)) {
+ e = %_NumberToString(e);
+ } else if (!IS_STRING(e)) {
+ e = convert(e);
+ }
elements[i] = e;
- }
}
}
var result = %_FastAsciiArrayJoin(elements, separator);
@@ -375,6 +397,11 @@ function ArrayToLocaleString() {
function ArrayJoin(separator) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.join"]);
+ }
+
if (IS_UNDEFINED(separator)) {
separator = ',';
} else if (!IS_STRING(separator)) {
@@ -391,6 +418,11 @@ function ArrayJoin(separator) {
// Removes the last element from the array and returns it. See
// ECMA-262, section 15.4.4.6.
function ArrayPop() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.pop"]);
+ }
+
var n = TO_UINT32(this.length);
if (n == 0) {
this.length = n;
@@ -407,6 +439,11 @@ function ArrayPop() {
// Appends the arguments to the end of the array and returns the new
// length of the array. See ECMA-262, section 15.4.4.7.
function ArrayPush() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.push"]);
+ }
+
var n = TO_UINT32(this.length);
var m = %_ArgumentsLength();
for (var i = 0; i < m; i++) {
@@ -418,6 +455,11 @@ function ArrayPush() {
function ArrayConcat(arg1) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.concat"]);
+ }
+
var arg_count = %_ArgumentsLength();
var arrays = new InternalArray(1 + arg_count);
arrays[0] = this;
@@ -474,6 +516,11 @@ function SparseReverse(array, len) {
function ArrayReverse() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.reverse"]);
+ }
+
var j = TO_UINT32(this.length) - 1;
if (UseSparseVariant(this, j, IS_ARRAY(this))) {
@@ -505,6 +552,11 @@ function ArrayReverse() {
function ArrayShift() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.shift"]);
+ }
+
var len = TO_UINT32(this.length);
if (len === 0) {
@@ -526,6 +578,11 @@ function ArrayShift() {
function ArrayUnshift(arg1) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.unshift"]);
+ }
+
var len = TO_UINT32(this.length);
var num_arguments = %_ArgumentsLength();
@@ -545,6 +602,11 @@ function ArrayUnshift(arg1) { // length == 1
function ArraySlice(start, end) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.slice"]);
+ }
+
var len = TO_UINT32(this.length);
var start_i = TO_INTEGER(start);
var end_i = len;
@@ -582,6 +644,11 @@ function ArraySlice(start, end) {
function ArraySplice(start, delete_count) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.splice"]);
+ }
+
var num_arguments = %_ArgumentsLength();
var len = TO_UINT32(this.length);
@@ -653,6 +720,11 @@ function ArraySplice(start, delete_count) {
function ArraySort(comparefn) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.sort"]);
+ }
+
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
@@ -914,6 +986,11 @@ function ArraySort(comparefn) {
// preserving the semantics, since the calls to the receiver function can add
// or delete elements from the array.
function ArrayFilter(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.filter"]);
+ }
+
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
@@ -935,6 +1012,11 @@ function ArrayFilter(f, receiver) {
function ArrayForEach(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.forEach"]);
+ }
+
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
@@ -953,6 +1035,11 @@ function ArrayForEach(f, receiver) {
// Executes the function once for each element present in the
// array until it finds one where callback returns true.
function ArraySome(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.some"]);
+ }
+
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
@@ -970,6 +1057,11 @@ function ArraySome(f, receiver) {
function ArrayEvery(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.every"]);
+ }
+
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
@@ -986,6 +1078,11 @@ function ArrayEvery(f, receiver) {
}
function ArrayMap(f, receiver) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.map"]);
+ }
+
if (!IS_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
@@ -1006,6 +1103,11 @@ function ArrayMap(f, receiver) {
function ArrayIndexOf(element, index) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.indexOf"]);
+ }
+
var length = TO_UINT32(this.length);
if (length == 0) return -1;
if (IS_UNDEFINED(index)) {
@@ -1063,6 +1165,11 @@ function ArrayIndexOf(element, index) {
function ArrayLastIndexOf(element, index) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.lastIndexOf"]);
+ }
+
var length = TO_UINT32(this.length);
if (length == 0) return -1;
if (%_ArgumentsLength() < 2) {
@@ -1116,6 +1223,11 @@ function ArrayLastIndexOf(element, index) {
function ArrayReduce(callback, current) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.reduce"]);
+ }
+
if (!IS_FUNCTION(callback)) {
throw MakeTypeError('called_non_callable', [callback]);
}
@@ -1145,6 +1257,11 @@ function ArrayReduce(callback, current) {
}
function ArrayReduceRight(callback, current) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Array.prototype.reduceRight"]);
+ }
+
if (!IS_FUNCTION(callback)) {
throw MakeTypeError('called_non_callable', [callback]);
}
diff --git a/src/assembler.cc b/src/assembler.cc
index bfecc774..3c7fc1c6 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -30,7 +30,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#include "v8.h"
@@ -69,6 +69,8 @@ namespace internal {
const double DoubleConstant::min_int = kMinInt;
const double DoubleConstant::one_half = 0.5;
const double DoubleConstant::minus_zero = -0.0;
+const double DoubleConstant::uint8_max_value = 255;
+const double DoubleConstant::zero = 0.0;
const double DoubleConstant::nan = OS::nan_value();
const double DoubleConstant::negative_infinity = -V8_INFINITY;
const char* RelocInfo::kFillerCommentString = "DEOPTIMIZATION PADDING";
@@ -99,58 +101,85 @@ int Label::pos() const {
// -----------------------------------------------------------------------------
// Implementation of RelocInfoWriter and RelocIterator
//
+// Relocation information is written backwards in memory, from high addresses
+// towards low addresses, byte by byte. Therefore, in the encodings listed
+// below, the first byte listed it at the highest address, and successive
+// bytes in the record are at progressively lower addresses.
+//
// Encoding
//
// The most common modes are given single-byte encodings. Also, it is
// easy to identify the type of reloc info and skip unwanted modes in
// an iteration.
//
-// The encoding relies on the fact that there are less than 14
-// different relocation modes.
-//
-// embedded_object: [6 bits pc delta] 00
-//
-// code_taget: [6 bits pc delta] 01
+// The encoding relies on the fact that there are fewer than 14
+// different non-compactly encoded relocation modes.
//
-// position: [6 bits pc delta] 10,
-// [7 bits signed data delta] 0
+// The first byte of a relocation record has a tag in its low 2 bits:
+// Here are the record schemes, depending on the low tag and optional higher
+// tags.
//
-// statement_position: [6 bits pc delta] 10,
-// [7 bits signed data delta] 1
+// Low tag:
+// 00: embedded_object: [6-bit pc delta] 00
//
-// any nondata mode: 00 [4 bits rmode] 11, // rmode: 0..13 only
-// 00 [6 bits pc delta]
+// 01: code_target: [6-bit pc delta] 01
//
-// pc-jump: 00 1111 11,
-// 00 [6 bits pc delta]
+// 10: short_data_record: [6-bit pc delta] 10 followed by
+// [6-bit data delta] [2-bit data type tag]
//
-// pc-jump: 01 1111 11,
-// (variable length) 7 - 26 bit pc delta, written in chunks of 7
-// bits, the lowest 7 bits written first.
+// 11: long_record [2-bit high tag][4 bit middle_tag] 11
+// followed by variable data depending on type.
//
-// data-jump + pos: 00 1110 11,
-// signed intptr_t, lowest byte written first
+// 2-bit data type tags, used in short_data_record and data_jump long_record:
+// code_target_with_id: 00
+// position: 01
+// statement_position: 10
+// comment: 11 (not used in short_data_record)
//
-// data-jump + st.pos: 01 1110 11,
-// signed intptr_t, lowest byte written first
+// Long record format:
+// 4-bit middle_tag:
+// 0000 - 1100 : Short record for RelocInfo::Mode middle_tag + 2
+// (The middle_tag encodes rmode - RelocInfo::LAST_COMPACT_ENUM,
+// and is between 0000 and 1100)
+// The format is:
+// 00 [4 bit middle_tag] 11 followed by
+// 00 [6 bit pc delta]
//
-// data-jump + comm.: 10 1110 11,
-// signed intptr_t, lowest byte written first
+// 1101: not used (would allow one more relocation mode to be added)
+// 1110: long_data_record
+// The format is: [2-bit data_type_tag] 1110 11
+// signed intptr_t, lowest byte written first
+// (except data_type code_target_with_id, which
+// is followed by a signed int, not intptr_t.)
//
+// 1111: long_pc_jump
+// The format is:
+// pc-jump: 00 1111 11,
+// 00 [6 bits pc delta]
+// or
+// pc-jump (variable length):
+// 01 1111 11,
+// [7 bits data] 0
+// ...
+// [7 bits data] 1
+// (Bits 6..31 of pc delta, with leading zeroes
+// dropped, and last non-zero chunk tagged with 1.)
+
+
const int kMaxRelocModes = 14;
const int kTagBits = 2;
const int kTagMask = (1 << kTagBits) - 1;
const int kExtraTagBits = 4;
-const int kPositionTypeTagBits = 1;
-const int kSmallDataBits = kBitsPerByte - kPositionTypeTagBits;
+const int kLocatableTypeTagBits = 2;
+const int kSmallDataBits = kBitsPerByte - kLocatableTypeTagBits;
const int kEmbeddedObjectTag = 0;
const int kCodeTargetTag = 1;
-const int kPositionTag = 2;
+const int kLocatableTag = 2;
const int kDefaultTag = 3;
-const int kPCJumpTag = (1 << kExtraTagBits) - 1;
+const int kPCJumpExtraTag = (1 << kExtraTagBits) - 1;
const int kSmallPCDeltaBits = kBitsPerByte - kTagBits;
const int kSmallPCDeltaMask = (1 << kSmallPCDeltaBits) - 1;
@@ -164,11 +193,12 @@ const int kLastChunkTagMask = 1;
const int kLastChunkTag = 1;
-const int kDataJumpTag = kPCJumpTag - 1;
+const int kDataJumpExtraTag = kPCJumpExtraTag - 1;
-const int kNonstatementPositionTag = 0;
-const int kStatementPositionTag = 1;
-const int kCommentTag = 2;
+const int kCodeWithIdTag = 0;
+const int kNonstatementPositionTag = 1;
+const int kStatementPositionTag = 2;
+const int kCommentTag = 3;
uint32_t RelocInfoWriter::WriteVariableLengthPCJump(uint32_t pc_delta) {
@@ -176,7 +206,7 @@ uint32_t RelocInfoWriter::WriteVariableLengthPCJump(uint32_t pc_delta) {
// Otherwise write a variable length PC jump for the bits that do
// not fit in the kSmallPCDeltaBits bits.
if (is_uintn(pc_delta, kSmallPCDeltaBits)) return pc_delta;
- WriteExtraTag(kPCJumpTag, kVariableLengthPCJumpTopTag);
+ WriteExtraTag(kPCJumpExtraTag, kVariableLengthPCJumpTopTag);
uint32_t pc_jump = pc_delta >> kSmallPCDeltaBits;
ASSERT(pc_jump > 0);
// Write kChunkBits size chunks of the pc_jump.
@@ -199,7 +229,7 @@ void RelocInfoWriter::WriteTaggedPC(uint32_t pc_delta, int tag) {
void RelocInfoWriter::WriteTaggedData(intptr_t data_delta, int tag) {
- *--pos_ = static_cast<byte>(data_delta << kPositionTypeTagBits | tag);
+ *--pos_ = static_cast<byte>(data_delta << kLocatableTypeTagBits | tag);
}
@@ -218,11 +248,20 @@ void RelocInfoWriter::WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag) {
}
+void RelocInfoWriter::WriteExtraTaggedIntData(int data_delta, int top_tag) {
+ WriteExtraTag(kDataJumpExtraTag, top_tag);
+ for (int i = 0; i < kIntSize; i++) {
+ *--pos_ = static_cast<byte>(data_delta);
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ data_delta = data_delta >> kBitsPerByte;
+ }
+}
+
void RelocInfoWriter::WriteExtraTaggedData(intptr_t data_delta, int top_tag) {
- WriteExtraTag(kDataJumpTag, top_tag);
+ WriteExtraTag(kDataJumpExtraTag, top_tag);
for (int i = 0; i < kIntptrSize; i++) {
*--pos_ = static_cast<byte>(data_delta);
- // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
data_delta = data_delta >> kBitsPerByte;
}
}
@@ -233,7 +272,8 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) {
byte* begin_pos = pos_;
#endif
ASSERT(rinfo->pc() - last_pc_ >= 0);
- ASSERT(RelocInfo::NUMBER_OF_MODES <= kMaxRelocModes);
+ ASSERT(RelocInfo::NUMBER_OF_MODES - RelocInfo::LAST_COMPACT_ENUM <=
+ kMaxRelocModes);
// Use unsigned delta-encoding for pc.
uint32_t pc_delta = static_cast<uint32_t>(rinfo->pc() - last_pc_);
RelocInfo::Mode rmode = rinfo->rmode();
@@ -244,35 +284,48 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) {
} else if (rmode == RelocInfo::CODE_TARGET) {
WriteTaggedPC(pc_delta, kCodeTargetTag);
ASSERT(begin_pos - pos_ <= RelocInfo::kMaxCallSize);
+ } else if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
+ // Use signed delta-encoding for id.
+ ASSERT(static_cast<int>(rinfo->data()) == rinfo->data());
+ int id_delta = static_cast<int>(rinfo->data()) - last_id_;
+ // Check if delta is small enough to fit in a tagged byte.
+ if (is_intn(id_delta, kSmallDataBits)) {
+ WriteTaggedPC(pc_delta, kLocatableTag);
+ WriteTaggedData(id_delta, kCodeWithIdTag);
+ } else {
+ // Otherwise, use costly encoding.
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedIntData(id_delta, kCodeWithIdTag);
+ }
+ last_id_ = static_cast<int>(rinfo->data());
} else if (RelocInfo::IsPosition(rmode)) {
- // Use signed delta-encoding for data.
- intptr_t data_delta = rinfo->data() - last_data_;
- int pos_type_tag = rmode == RelocInfo::POSITION ? kNonstatementPositionTag
- : kStatementPositionTag;
- // Check if data is small enough to fit in a tagged byte.
- // We cannot use is_intn because data_delta is not an int32_t.
- if (data_delta >= -(1 << (kSmallDataBits-1)) &&
- data_delta < 1 << (kSmallDataBits-1)) {
- WriteTaggedPC(pc_delta, kPositionTag);
- WriteTaggedData(data_delta, pos_type_tag);
- last_data_ = rinfo->data();
+ // Use signed delta-encoding for position.
+ ASSERT(static_cast<int>(rinfo->data()) == rinfo->data());
+ int pos_delta = static_cast<int>(rinfo->data()) - last_position_;
+ int pos_type_tag = (rmode == RelocInfo::POSITION) ? kNonstatementPositionTag
+ : kStatementPositionTag;
+ // Check if delta is small enough to fit in a tagged byte.
+ if (is_intn(pos_delta, kSmallDataBits)) {
+ WriteTaggedPC(pc_delta, kLocatableTag);
+ WriteTaggedData(pos_delta, pos_type_tag);
} else {
// Otherwise, use costly encoding.
- WriteExtraTaggedPC(pc_delta, kPCJumpTag);
- WriteExtraTaggedData(data_delta, pos_type_tag);
- last_data_ = rinfo->data();
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedIntData(pos_delta, pos_type_tag);
}
+ last_position_ = static_cast<int>(rinfo->data());
} else if (RelocInfo::IsComment(rmode)) {
// Comments are normally not generated, so we use the costly encoding.
- WriteExtraTaggedPC(pc_delta, kPCJumpTag);
- WriteExtraTaggedData(rinfo->data() - last_data_, kCommentTag);
- last_data_ = rinfo->data();
+ WriteExtraTaggedPC(pc_delta, kPCJumpExtraTag);
+ WriteExtraTaggedData(rinfo->data(), kCommentTag);
ASSERT(begin_pos - pos_ >= RelocInfo::kMinRelocCommentSize);
} else {
+ ASSERT(rmode > RelocInfo::LAST_COMPACT_ENUM);
+ int saved_mode = rmode - RelocInfo::LAST_COMPACT_ENUM;
// For all other modes we simply use the mode as the extra tag.
// None of these modes need a data component.
- ASSERT(rmode < kPCJumpTag && rmode < kDataJumpTag);
- WriteExtraTaggedPC(pc_delta, rmode);
+ ASSERT(saved_mode < kPCJumpExtraTag && saved_mode < kDataJumpExtraTag);
+ WriteExtraTaggedPC(pc_delta, saved_mode);
}
last_pc_ = rinfo->pc();
#ifdef DEBUG
@@ -306,12 +359,32 @@ inline void RelocIterator::AdvanceReadPC() {
}
+void RelocIterator::AdvanceReadId() {
+ int x = 0;
+ for (int i = 0; i < kIntSize; i++) {
+ x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
+ }
+ last_id_ += x;
+ rinfo_.data_ = last_id_;
+}
+
+
+void RelocIterator::AdvanceReadPosition() {
+ int x = 0;
+ for (int i = 0; i < kIntSize; i++) {
+ x |= static_cast<int>(*--pos_) << i * kBitsPerByte;
+ }
+ last_position_ += x;
+ rinfo_.data_ = last_position_;
+}
+
+
void RelocIterator::AdvanceReadData() {
intptr_t x = 0;
for (int i = 0; i < kIntptrSize; i++) {
x |= static_cast<intptr_t>(*--pos_) << i * kBitsPerByte;
}
- rinfo_.data_ += x;
+ rinfo_.data_ = x;
}
@@ -331,27 +404,33 @@ void RelocIterator::AdvanceReadVariableLengthPCJump() {
}
-inline int RelocIterator::GetPositionTypeTag() {
- return *pos_ & ((1 << kPositionTypeTagBits) - 1);
+inline int RelocIterator::GetLocatableTypeTag() {
+ return *pos_ & ((1 << kLocatableTypeTagBits) - 1);
}
-inline void RelocIterator::ReadTaggedData() {
+inline void RelocIterator::ReadTaggedId() {
int8_t signed_b = *pos_;
// Signed right shift is arithmetic shift. Tested in test-utils.cc.
- rinfo_.data_ += signed_b >> kPositionTypeTagBits;
+ last_id_ += signed_b >> kLocatableTypeTagBits;
+ rinfo_.data_ = last_id_;
}
-inline RelocInfo::Mode RelocIterator::DebugInfoModeFromTag(int tag) {
- if (tag == kStatementPositionTag) {
- return RelocInfo::STATEMENT_POSITION;
- } else if (tag == kNonstatementPositionTag) {
- return RelocInfo::POSITION;
- } else {
- ASSERT(tag == kCommentTag);
- return RelocInfo::COMMENT;
- }
+inline void RelocIterator::ReadTaggedPosition() {
+ int8_t signed_b = *pos_;
+ // Signed right shift is arithmetic shift. Tested in test-utils.cc.
+ last_position_ += signed_b >> kLocatableTypeTagBits;
+ rinfo_.data_ = last_position_;
+}
+
+
+static inline RelocInfo::Mode GetPositionModeFromTag(int tag) {
+ ASSERT(tag == kNonstatementPositionTag ||
+ tag == kStatementPositionTag);
+ return (tag == kNonstatementPositionTag) ?
+ RelocInfo::POSITION :
+ RelocInfo::STATEMENT_POSITION;
}
@@ -370,37 +449,64 @@ void RelocIterator::next() {
} else if (tag == kCodeTargetTag) {
ReadTaggedPC();
if (SetMode(RelocInfo::CODE_TARGET)) return;
- } else if (tag == kPositionTag) {
+ } else if (tag == kLocatableTag) {
ReadTaggedPC();
Advance();
- // Check if we want source positions.
- if (mode_mask_ & RelocInfo::kPositionMask) {
- ReadTaggedData();
- if (SetMode(DebugInfoModeFromTag(GetPositionTypeTag()))) return;
+ int locatable_tag = GetLocatableTypeTag();
+ if (locatable_tag == kCodeWithIdTag) {
+ if (SetMode(RelocInfo::CODE_TARGET_WITH_ID)) {
+ ReadTaggedId();
+ return;
+ }
+ } else {
+ // Compact encoding is never used for comments,
+ // so it must be a position.
+ ASSERT(locatable_tag == kNonstatementPositionTag ||
+ locatable_tag == kStatementPositionTag);
+ if (mode_mask_ & RelocInfo::kPositionMask) {
+ ReadTaggedPosition();
+ if (SetMode(GetPositionModeFromTag(locatable_tag))) return;
+ }
}
} else {
ASSERT(tag == kDefaultTag);
int extra_tag = GetExtraTag();
- if (extra_tag == kPCJumpTag) {
+ if (extra_tag == kPCJumpExtraTag) {
int top_tag = GetTopTag();
if (top_tag == kVariableLengthPCJumpTopTag) {
AdvanceReadVariableLengthPCJump();
} else {
AdvanceReadPC();
}
- } else if (extra_tag == kDataJumpTag) {
- // Check if we want debug modes (the only ones with data).
- if (mode_mask_ & RelocInfo::kDebugMask) {
- int top_tag = GetTopTag();
- AdvanceReadData();
- if (SetMode(DebugInfoModeFromTag(top_tag))) return;
+ } else if (extra_tag == kDataJumpExtraTag) {
+ int locatable_tag = GetTopTag();
+ if (locatable_tag == kCodeWithIdTag) {
+ if (SetMode(RelocInfo::CODE_TARGET_WITH_ID)) {
+ AdvanceReadId();
+ return;
+ }
+ Advance(kIntSize);
+ } else if (locatable_tag != kCommentTag) {
+ ASSERT(locatable_tag == kNonstatementPositionTag ||
+ locatable_tag == kStatementPositionTag);
+ if (mode_mask_ & RelocInfo::kPositionMask) {
+ AdvanceReadPosition();
+ if (SetMode(GetPositionModeFromTag(locatable_tag))) return;
+ } else {
+ Advance(kIntSize);
+ }
} else {
- // Otherwise, just skip over the data.
+ ASSERT(locatable_tag == kCommentTag);
+ if (SetMode(RelocInfo::COMMENT)) {
+ AdvanceReadData();
+ return;
+ }
Advance(kIntptrSize);
}
} else {
AdvanceReadPC();
- if (SetMode(static_cast<RelocInfo::Mode>(extra_tag))) return;
+ int rmode = extra_tag + RelocInfo::LAST_COMPACT_ENUM;
+ if (SetMode(static_cast<RelocInfo::Mode>(rmode))) return;
}
}
}
@@ -416,6 +522,8 @@ RelocIterator::RelocIterator(Code* code, int mode_mask) {
end_ = code->relocation_start();
done_ = false;
mode_mask_ = mode_mask;
+ last_id_ = 0;
+ last_position_ = 0;
if (mode_mask_ == 0) pos_ = end_;
next();
}
@@ -429,6 +537,8 @@ RelocIterator::RelocIterator(const CodeDesc& desc, int mode_mask) {
end_ = pos_ - desc.reloc_size;
done_ = false;
mode_mask_ = mode_mask;
+ last_id_ = 0;
+ last_position_ = 0;
if (mode_mask_ == 0) pos_ = end_;
next();
}
@@ -456,6 +566,8 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
return "debug break";
case RelocInfo::CODE_TARGET:
return "code target";
+ case RelocInfo::CODE_TARGET_WITH_ID:
+ return "code target with id";
case RelocInfo::GLOBAL_PROPERTY_CELL:
return "global property cell";
case RelocInfo::RUNTIME_ENTRY:
@@ -502,6 +614,9 @@ void RelocInfo::Print(FILE* out) {
Code* code = Code::GetCodeFromTargetAddress(target_address());
PrintF(out, " (%s) (%p)", Code::Kind2String(code->kind()),
target_address());
+ if (rmode_ == CODE_TARGET_WITH_ID) {
+ PrintF(" (id=%d)", static_cast<int>(data_));
+ }
} else if (IsPosition(rmode_)) {
PrintF(out, " (%" V8_PTR_PREFIX "d)", data());
} else if (rmode_ == RelocInfo::RUNTIME_ENTRY &&
@@ -535,6 +650,7 @@ void RelocInfo::Verify() {
#endif
case CONSTRUCT_CALL:
case CODE_TARGET_CONTEXT:
+ case CODE_TARGET_WITH_ID:
case CODE_TARGET: {
// convert inline target address to code object
Address addr = target_address();
@@ -787,6 +903,18 @@ ExternalReference ExternalReference::address_of_minus_zero() {
}
+ExternalReference ExternalReference::address_of_zero() {
+ return ExternalReference(reinterpret_cast<void*>(
+ const_cast<double*>(&DoubleConstant::zero)));
+}
+
+
+ExternalReference ExternalReference::address_of_uint8_max_value() {
+ return ExternalReference(reinterpret_cast<void*>(
+ const_cast<double*>(&DoubleConstant::uint8_max_value)));
+}
+
+
ExternalReference ExternalReference::address_of_negative_infinity() {
return ExternalReference(reinterpret_cast<void*>(
const_cast<double*>(&DoubleConstant::negative_infinity)));
@@ -899,7 +1027,7 @@ ExternalReference ExternalReference::math_sin_double_function(
Isolate* isolate) {
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(math_sin_double),
- FP_RETURN_CALL));
+ BUILTIN_FP_CALL));
}
@@ -907,7 +1035,7 @@ ExternalReference ExternalReference::math_cos_double_function(
Isolate* isolate) {
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(math_cos_double),
- FP_RETURN_CALL));
+ BUILTIN_FP_CALL));
}
@@ -915,7 +1043,7 @@ ExternalReference ExternalReference::math_log_double_function(
Isolate* isolate) {
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(math_log_double),
- FP_RETURN_CALL));
+ BUILTIN_FP_CALL));
}
@@ -958,7 +1086,7 @@ ExternalReference ExternalReference::power_double_double_function(
Isolate* isolate) {
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(power_double_double),
- FP_RETURN_CALL));
+ BUILTIN_FP_FP_CALL));
}
@@ -966,7 +1094,7 @@ ExternalReference ExternalReference::power_double_int_function(
Isolate* isolate) {
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(power_double_int),
- FP_RETURN_CALL));
+ BUILTIN_FP_INT_CALL));
}
@@ -999,17 +1127,16 @@ ExternalReference ExternalReference::double_fp_operation(
default:
UNREACHABLE();
}
- // Passing true as 2nd parameter indicates that they return an fp value.
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(function),
- FP_RETURN_CALL));
+ BUILTIN_FP_FP_CALL));
}
ExternalReference ExternalReference::compare_doubles(Isolate* isolate) {
return ExternalReference(Redirect(isolate,
FUNCTION_ADDR(native_compare_doubles),
- BUILTIN_CALL));
+ BUILTIN_COMPARE_CALL));
}
diff --git a/src/assembler.h b/src/assembler.h
index 395bbd54..29f1ea9f 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -35,6 +35,7 @@
#ifndef V8_ASSEMBLER_H_
#define V8_ASSEMBLER_H_
+#include "allocation.h"
#include "gdb-jit.h"
#include "runtime.h"
#include "token.h"
@@ -42,7 +43,7 @@
namespace v8 {
namespace internal {
-
+const unsigned kNoASTId = -1;
// -----------------------------------------------------------------------------
// Platform independent assembler base class.
@@ -66,6 +67,8 @@ class DoubleConstant: public AllStatic {
static const double min_int;
static const double one_half;
static const double minus_zero;
+ static const double zero;
+ static const double uint8_max_value;
static const double negative_infinity;
static const double nan;
};
@@ -79,18 +82,28 @@ class DoubleConstant: public AllStatic {
class Label BASE_EMBEDDED {
public:
- INLINE(Label()) { Unuse(); }
+ enum Distance {
+ kNear, kFar
+ };
+
+ INLINE(Label()) {
+ Unuse();
+ UnuseNear();
+ }
INLINE(~Label()) { ASSERT(!is_linked()); }
INLINE(void Unuse()) { pos_ = 0; }
+ INLINE(void UnuseNear()) { near_link_pos_ = 0; }
INLINE(bool is_bound() const) { return pos_ < 0; }
- INLINE(bool is_unused() const) { return pos_ == 0; }
+ INLINE(bool is_unused() const) { return pos_ == 0 && near_link_pos_ == 0; }
INLINE(bool is_linked() const) { return pos_ > 0; }
+ INLINE(bool is_near_linked() const) { return near_link_pos_ > 0; }
// Returns the position of bound or linked labels. Cannot be used
// for unused labels.
int pos() const;
+ int near_link_pos() const { return near_link_pos_ - 1; }
private:
// pos_ encodes both the binding state (via its sign)
@@ -101,13 +114,21 @@ class Label BASE_EMBEDDED {
// pos_ > 0 linked label, pos() returns the last reference position
int pos_;
+ // Behaves like |pos_| in the "> 0" case, but for near jumps to this label.
+ int near_link_pos_;
+
void bind_to(int pos) {
pos_ = -pos - 1;
ASSERT(is_bound());
}
- void link_to(int pos) {
- pos_ = pos + 1;
- ASSERT(is_linked());
+ void link_to(int pos, Distance distance = kFar) {
+ if (distance == kNear) {
+ near_link_pos_ = pos + 1;
+ ASSERT(is_near_linked());
+ } else {
+ pos_ = pos + 1;
+ ASSERT(is_linked());
+ }
}
friend class Assembler;
@@ -118,57 +139,6 @@ class Label BASE_EMBEDDED {
// -----------------------------------------------------------------------------
-// NearLabels are labels used for short jumps (in Intel jargon).
-// NearLabels should be used if it can be guaranteed that the jump range is
-// within -128 to +127. We already use short jumps when jumping backwards,
-// so using a NearLabel will only have performance impact if used for forward
-// jumps.
-class NearLabel BASE_EMBEDDED {
- public:
- NearLabel() { Unuse(); }
- ~NearLabel() { ASSERT(!is_linked()); }
-
- void Unuse() {
- pos_ = -1;
- unresolved_branches_ = 0;
-#ifdef DEBUG
- for (int i = 0; i < kMaxUnresolvedBranches; i++) {
- unresolved_positions_[i] = -1;
- }
-#endif
- }
-
- int pos() {
- ASSERT(is_bound());
- return pos_;
- }
-
- bool is_bound() { return pos_ >= 0; }
- bool is_linked() { return !is_bound() && unresolved_branches_ > 0; }
- bool is_unused() { return !is_bound() && unresolved_branches_ == 0; }
-
- void bind_to(int position) {
- ASSERT(!is_bound());
- pos_ = position;
- }
-
- void link_to(int position) {
- ASSERT(!is_bound());
- ASSERT(unresolved_branches_ < kMaxUnresolvedBranches);
- unresolved_positions_[unresolved_branches_++] = position;
- }
-
- private:
- static const int kMaxUnresolvedBranches = 8;
- int pos_;
- int unresolved_branches_;
- int unresolved_positions_[kMaxUnresolvedBranches];
-
- friend class Assembler;
-};
-
-
-// -----------------------------------------------------------------------------
// Relocation information
@@ -211,10 +181,11 @@ class RelocInfo BASE_EMBEDDED {
enum Mode {
// Please note the order is important (see IsCodeTarget, IsGCRelocMode).
+ CODE_TARGET, // Code target which is not any of the above.
+ CODE_TARGET_WITH_ID,
CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor.
CODE_TARGET_CONTEXT, // Code target used for contextual loads and stores.
DEBUG_BREAK, // Code target for the debugger statement.
- CODE_TARGET, // Code target which is not any of the above.
EMBEDDED_OBJECT,
GLOBAL_PROPERTY_CELL,
@@ -230,10 +201,12 @@ class RelocInfo BASE_EMBEDDED {
// add more as needed
// Pseudo-types
- NUMBER_OF_MODES, // must be no greater than 14 - see RelocInfoWriter
+ NUMBER_OF_MODES, // There are at most 14 modes with noncompact encoding.
NONE, // never recorded
- LAST_CODE_ENUM = CODE_TARGET,
- LAST_GCED_ENUM = GLOBAL_PROPERTY_CELL
+ LAST_CODE_ENUM = DEBUG_BREAK,
+ LAST_GCED_ENUM = GLOBAL_PROPERTY_CELL,
+ // Modes <= LAST_COMPACT_ENUM are guaranteed to have compact encoding.
+ LAST_COMPACT_ENUM = CODE_TARGET_WITH_ID
};
@@ -363,7 +336,8 @@ class RelocInfo BASE_EMBEDDED {
static const int kCodeTargetMask = (1 << (LAST_CODE_ENUM + 1)) - 1;
static const int kPositionMask = 1 << POSITION | 1 << STATEMENT_POSITION;
- static const int kDebugMask = kPositionMask | 1 << COMMENT;
+ static const int kDataMask =
+ (1 << CODE_TARGET_WITH_ID) | kPositionMask | (1 << COMMENT);
static const int kApplyMask; // Modes affected by apply. Depends on arch.
private:
@@ -374,6 +348,19 @@ class RelocInfo BASE_EMBEDDED {
byte* pc_;
Mode rmode_;
intptr_t data_;
+#ifdef V8_TARGET_ARCH_MIPS
+ // Code and Embedded Object pointers in mips are stored split
+ // across two consecutive 32-bit instructions. Heap management
+ // routines expect to access these pointers indirectly. The following
+ // location provides a place for these pointers to exist natually
+ // when accessed via the Iterator.
+ Object *reconstructed_obj_ptr_;
+ // External-reference pointers are also split across instruction-pairs
+ // in mips, but are accessed via indirect pointers. This location
+ // provides a place for that pointer to exist naturally. Its address
+ // is returned by RelocInfo::target_reference_address().
+ Address reconstructed_adr_ptr_;
+#endif // V8_TARGET_ARCH_MIPS
friend class RelocIterator;
};
@@ -382,9 +369,14 @@ class RelocInfo BASE_EMBEDDED {
// lower addresses.
class RelocInfoWriter BASE_EMBEDDED {
public:
- RelocInfoWriter() : pos_(NULL), last_pc_(NULL), last_data_(0) {}
- RelocInfoWriter(byte* pos, byte* pc) : pos_(pos), last_pc_(pc),
- last_data_(0) {}
+ RelocInfoWriter() : pos_(NULL),
+ last_pc_(NULL),
+ last_id_(0),
+ last_position_(0) {}
+ RelocInfoWriter(byte* pos, byte* pc) : pos_(pos),
+ last_pc_(pc),
+ last_id_(0),
+ last_position_(0) {}
byte* pos() const { return pos_; }
byte* last_pc() const { return last_pc_; }
@@ -409,13 +401,15 @@ class RelocInfoWriter BASE_EMBEDDED {
inline uint32_t WriteVariableLengthPCJump(uint32_t pc_delta);
inline void WriteTaggedPC(uint32_t pc_delta, int tag);
inline void WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag);
+ inline void WriteExtraTaggedIntData(int data_delta, int top_tag);
inline void WriteExtraTaggedData(intptr_t data_delta, int top_tag);
inline void WriteTaggedData(intptr_t data_delta, int tag);
inline void WriteExtraTag(int extra_tag, int top_tag);
byte* pos_;
byte* last_pc_;
- intptr_t last_data_;
+ int last_id_;
+ int last_position_;
DISALLOW_COPY_AND_ASSIGN(RelocInfoWriter);
};
@@ -457,12 +451,13 @@ class RelocIterator: public Malloced {
int GetTopTag();
void ReadTaggedPC();
void AdvanceReadPC();
+ void AdvanceReadId();
+ void AdvanceReadPosition();
void AdvanceReadData();
void AdvanceReadVariableLengthPCJump();
- int GetPositionTypeTag();
- void ReadTaggedData();
-
- static RelocInfo::Mode DebugInfoModeFromTag(int tag);
+ int GetLocatableTypeTag();
+ void ReadTaggedId();
+ void ReadTaggedPosition();
// If the given mode is wanted, set it in rinfo_ and return true.
// Else return false. Used for efficiently skipping unwanted modes.
@@ -475,6 +470,8 @@ class RelocIterator: public Malloced {
RelocInfo rinfo_;
bool done_;
int mode_mask_;
+ int last_id_;
+ int last_position_;
DISALLOW_COPY_AND_ASSIGN(RelocIterator);
};
@@ -503,9 +500,21 @@ class ExternalReference BASE_EMBEDDED {
// MaybeObject* f(v8::internal::Arguments).
BUILTIN_CALL, // default
+ // Builtin that takes float arguments and returns an int.
+ // int f(double, double).
+ BUILTIN_COMPARE_CALL,
+
// Builtin call that returns floating point.
// double f(double, double).
- FP_RETURN_CALL,
+ BUILTIN_FP_FP_CALL,
+
+ // Builtin call that returns floating point.
+ // double f(double).
+ BUILTIN_FP_CALL,
+
+ // Builtin call that returns floating point.
+ // double f(double, int).
+ BUILTIN_FP_INT_CALL,
// Direct call to API function callback.
// Handle<Value> f(v8::Arguments&)
@@ -613,6 +622,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference address_of_min_int();
static ExternalReference address_of_one_half();
static ExternalReference address_of_minus_zero();
+ static ExternalReference address_of_zero();
+ static ExternalReference address_of_uint8_max_value();
static ExternalReference address_of_negative_infinity();
static ExternalReference address_of_nan();
@@ -649,10 +660,11 @@ class ExternalReference BASE_EMBEDDED {
// This lets you register a function that rewrites all external references.
// Used by the ARM simulator to catch calls to external references.
- static void set_redirector(ExternalReferenceRedirector* redirector) {
+ static void set_redirector(Isolate* isolate,
+ ExternalReferenceRedirector* redirector) {
// We can't stack them.
- ASSERT(Isolate::Current()->external_reference_redirector() == NULL);
- Isolate::Current()->set_external_reference_redirector(
+ ASSERT(isolate->external_reference_redirector() == NULL);
+ isolate->set_external_reference_redirector(
reinterpret_cast<ExternalReferenceRedirectorPointer*>(redirector));
}
@@ -819,6 +831,28 @@ static inline int NumberOfBitsSet(uint32_t x) {
double power_double_int(double x, int y);
double power_double_double(double x, double y);
+// Helper class for generating code or data associated with the code
+// right after a call instruction. As an example this can be used to
+// generate safepoint data after calls for crankshaft.
+class CallWrapper {
+ public:
+ CallWrapper() { }
+ virtual ~CallWrapper() { }
+ // Called just before emitting a call. Argument is the size of the generated
+ // call code.
+ virtual void BeforeCall(int call_size) const = 0;
+ // Called just after emitting a call, i.e., at the return site for the call.
+ virtual void AfterCall() const = 0;
+};
+
+class NullCallWrapper : public CallWrapper {
+ public:
+ NullCallWrapper() { }
+ virtual ~NullCallWrapper() { }
+ virtual void BeforeCall(int call_size) const { }
+ virtual void AfterCall() const { }
+};
+
} } // namespace v8::internal
#endif // V8_ASSEMBLER_H_
diff --git a/src/ast-inl.h b/src/ast-inl.h
index d80684a4..c2bd6134 100644
--- a/src/ast-inl.h
+++ b/src/ast-inl.h
@@ -31,6 +31,7 @@
#include "v8.h"
#include "ast.h"
+#include "scopes.h"
namespace v8 {
namespace internal {
diff --git a/src/ast.cc b/src/ast.cc
index 8ab09b38..b4abf541 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -337,15 +337,6 @@ bool BinaryOperation::ResultOverwriteAllowed() {
}
-BinaryOperation::BinaryOperation(Assignment* assignment) {
- ASSERT(assignment->is_compound());
- op_ = assignment->binary_op();
- left_ = assignment->target();
- right_ = assignment->value();
- pos_ = assignment->position();
-}
-
-
// ----------------------------------------------------------------------------
// Inlining support
@@ -413,8 +404,7 @@ bool DebuggerStatement::IsInlineable() const {
bool Throw::IsInlineable() const {
- // TODO(1143): Make functions containing throw inlineable.
- return false;
+ return exception()->IsInlineable();
}
@@ -733,14 +723,15 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
}
-void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
+void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle,
+ CallKind call_kind) {
Property* property = expression()->AsProperty();
ASSERT(property != NULL);
// Specialize for the receiver types seen at runtime.
Literal* key = property->key()->AsLiteral();
ASSERT(key != NULL && key->handle()->IsString());
Handle<String> name = Handle<String>::cast(key->handle());
- receiver_types_ = oracle->CallReceiverTypes(this, name);
+ receiver_types_ = oracle->CallReceiverTypes(this, name, call_kind);
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
if (receiver_types_ != NULL) {
@@ -1159,6 +1150,7 @@ CaseClause::CaseClause(Expression* label,
statements_(statements),
position_(pos),
compare_type_(NONE),
+ compare_id_(AstNode::GetNextId()),
entry_id_(AstNode::GetNextId()) {
}
diff --git a/src/ast.h b/src/ast.h
index 65a25a9b..0ac1644f 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -28,6 +28,7 @@
#ifndef V8_AST_H_
#define V8_AST_H_
+#include "allocation.h"
#include "execution.h"
#include "factory.h"
#include "jsregexp.h"
@@ -661,6 +662,7 @@ class CaseClause: public ZoneObject {
void set_position(int pos) { position_ = pos; }
int EntryId() { return entry_id_; }
+ int CompareId() { return compare_id_; }
// Type feedback information.
void RecordTypeFeedback(TypeFeedbackOracle* oracle);
@@ -674,6 +676,7 @@ class CaseClause: public ZoneObject {
int position_;
enum CompareTypeFeedback { NONE, SMI_ONLY, OBJECT_ONLY };
CompareTypeFeedback compare_type_;
+ int compare_id_;
int entry_id_;
};
@@ -1276,7 +1279,8 @@ class Call: public Expression {
ZoneList<Expression*>* arguments() const { return arguments_; }
virtual int position() const { return pos_; }
- void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+ void RecordTypeFeedback(TypeFeedbackOracle* oracle,
+ CallKind call_kind);
virtual ZoneMapList* GetReceiverTypes() { return receiver_types_; }
virtual bool IsMonomorphic() { return is_monomorphic_; }
CheckType check_type() const { return check_type_; }
@@ -1390,8 +1394,8 @@ class CallRuntime: public Expression {
class UnaryOperation: public Expression {
public:
- UnaryOperation(Token::Value op, Expression* expression)
- : op_(op), expression_(expression) {
+ UnaryOperation(Token::Value op, Expression* expression, int pos)
+ : op_(op), expression_(expression), pos_(pos) {
ASSERT(Token::IsUnaryOp(op));
}
@@ -1403,10 +1407,12 @@ class UnaryOperation: public Expression {
Token::Value op() const { return op_; }
Expression* expression() const { return expression_; }
+ virtual int position() const { return pos_; }
private:
Token::Value op_;
Expression* expression_;
+ int pos_;
};
@@ -1423,9 +1429,6 @@ class BinaryOperation: public Expression {
: AstNode::kNoNumber;
}
- // Create the binary operation corresponding to a compound assignment.
- explicit BinaryOperation(Assignment* assignment);
-
DECLARE_NODE_TYPE(BinaryOperation)
virtual bool IsInlineable() const;
diff --git a/src/atomicops_internals_x86_gcc.cc b/src/atomicops_internals_x86_gcc.cc
index a5725647..181c2024 100644
--- a/src/atomicops_internals_x86_gcc.cc
+++ b/src/atomicops_internals_x86_gcc.cc
@@ -57,6 +57,9 @@
#if defined(cpuid) // initialize the struct only on x86
+namespace v8 {
+namespace internal {
+
// Set the flags so that code will run correctly and conservatively, so even
// if we haven't been initialized yet, we're probably single threaded, and our
// default values should hopefully be pretty safe.
@@ -65,8 +68,14 @@ struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
false, // no SSE2
};
+} } // namespace v8::internal
+
+namespace {
+
// Initialize the AtomicOps_Internalx86CPUFeatures struct.
-static void AtomicOps_Internalx86CPUFeaturesInit() {
+void AtomicOps_Internalx86CPUFeaturesInit() {
+ using v8::internal::AtomicOps_Internalx86CPUFeatures;
+
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
@@ -107,8 +116,6 @@ static void AtomicOps_Internalx86CPUFeaturesInit() {
AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1);
}
-namespace {
-
class AtomicOpsx86Initializer {
public:
AtomicOpsx86Initializer() {
diff --git a/src/atomicops_internals_x86_gcc.h b/src/atomicops_internals_x86_gcc.h
index 3f17fa0d..6e55b501 100644
--- a/src/atomicops_internals_x86_gcc.h
+++ b/src/atomicops_internals_x86_gcc.h
@@ -30,6 +30,9 @@
#ifndef V8_ATOMICOPS_INTERNALS_X86_GCC_H_
#define V8_ATOMICOPS_INTERNALS_X86_GCC_H_
+namespace v8 {
+namespace internal {
+
// This struct is not part of the public API of this module; clients may not
// use it.
// Features of this x86. Values may not be correct before main() is run,
@@ -43,9 +46,6 @@ extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
-namespace v8 {
-namespace internal {
-
// 32-bit low-level operations on any platform.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 5b876405..d32ac805 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -141,7 +141,8 @@ void Bootstrapper::TearDown() {
class Genesis BASE_EMBEDDED {
public:
- Genesis(Handle<Object> global_object,
+ Genesis(Isolate* isolate,
+ Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions);
~Genesis() { }
@@ -150,8 +151,13 @@ class Genesis BASE_EMBEDDED {
Genesis* previous() { return previous_; }
+ Isolate* isolate() const { return isolate_; }
+ Factory* factory() const { return isolate_->factory(); }
+ Heap* heap() const { return isolate_->heap(); }
+
private:
Handle<Context> global_context_;
+ Isolate* isolate_;
// There may be more than one active genesis object: When GC is
// triggered during environment creation there may be weak handle
@@ -163,9 +169,9 @@ class Genesis BASE_EMBEDDED {
// Creates some basic objects. Used for creating a context from scratch.
void CreateRoots();
// Creates the empty function. Used for creating a context from scratch.
- Handle<JSFunction> CreateEmptyFunction();
+ Handle<JSFunction> CreateEmptyFunction(Isolate* isolate);
// Creates the ThrowTypeError function. ECMA 5th Ed. 13.2.3
- Handle<JSFunction> CreateThrowTypeErrorFunction(Builtins::Name builtin);
+ Handle<JSFunction> GetThrowTypeErrorFunction();
void CreateStrictModeFunctionMaps(Handle<JSFunction> empty);
// Creates the global objects using the global and the template passed in
@@ -193,7 +199,9 @@ class Genesis BASE_EMBEDDED {
// Installs the contents of the native .js files on the global objects.
// Used for creating a context from scratch.
void InstallNativeFunctions();
+ void InstallExperimentalNativeFunctions();
bool InstallNatives();
+ bool InstallExperimentalNatives();
void InstallBuiltinFunctionIds();
void InstallJSFunctionResultCaches();
void InitializeNormalizedMapCaches();
@@ -239,7 +247,8 @@ class Genesis BASE_EMBEDDED {
Handle<FixedArray> arguments,
Handle<FixedArray> caller);
- static bool CompileBuiltin(int index);
+ static bool CompileBuiltin(Isolate* isolate, int index);
+ static bool CompileExperimentalBuiltin(Isolate* isolate, int index);
static bool CompileNative(Vector<const char> name, Handle<String> source);
static bool CompileScriptCached(Vector<const char> name,
Handle<String> source,
@@ -256,6 +265,7 @@ class Genesis BASE_EMBEDDED {
// These are the final, writable prototype, maps.
Handle<Map> function_instance_map_writable_prototype_;
Handle<Map> strict_mode_function_instance_map_writable_prototype_;
+ Handle<JSFunction> throw_type_error_function;
BootstrapperActive active_;
friend class Bootstrapper;
@@ -269,12 +279,13 @@ void Bootstrapper::Iterate(ObjectVisitor* v) {
Handle<Context> Bootstrapper::CreateEnvironment(
+ Isolate* isolate,
Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions) {
HandleScope scope;
Handle<Context> env;
- Genesis genesis(global_object, global_template, extensions);
+ Genesis genesis(isolate, global_object, global_template, extensions);
env = genesis.result();
if (!env.is_null()) {
if (InstallExtensions(env, extensions)) {
@@ -287,15 +298,16 @@ Handle<Context> Bootstrapper::CreateEnvironment(
static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) {
// object.__proto__ = proto;
+ Factory* factory = object->GetIsolate()->factory();
Handle<Map> old_to_map = Handle<Map>(object->map());
- Handle<Map> new_to_map = FACTORY->CopyMapDropTransitions(old_to_map);
+ Handle<Map> new_to_map = factory->CopyMapDropTransitions(old_to_map);
new_to_map->set_prototype(*proto);
object->set_map(*new_to_map);
}
void Bootstrapper::DetachGlobal(Handle<Context> env) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = env->GetIsolate()->factory();
JSGlobalProxy::cast(env->global_proxy())->set_context(*factory->null_value());
SetObjectPrototype(Handle<JSObject>(env->global_proxy()),
factory->null_value());
@@ -322,7 +334,7 @@ static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
Handle<JSObject> prototype,
Builtins::Name call,
bool is_ecma_native) {
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = target->GetIsolate();
Factory* factory = isolate->factory();
Handle<String> symbol = factory->LookupAsciiSymbol(name);
Handle<Code> call_code = Handle<Code>(isolate->builtins()->builtin(call));
@@ -344,30 +356,32 @@ static Handle<JSFunction> InstallFunction(Handle<JSObject> target,
Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
PrototypePropertyMode prototypeMode) {
- Factory* factory = Isolate::Current()->factory();
Handle<DescriptorArray> descriptors =
- factory->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE ? 4 : 5);
+ factory()->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE
+ ? 4
+ : 5);
PropertyAttributes attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
{ // Add length.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionLength);
- CallbacksDescriptor d(*factory->length_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionLength);
+ CallbacksDescriptor d(*factory()->length_symbol(), *foreign, attributes);
descriptors->Set(0, &d);
}
{ // Add name.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionName);
- CallbacksDescriptor d(*factory->name_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionName);
+ CallbacksDescriptor d(*factory()->name_symbol(), *foreign, attributes);
descriptors->Set(1, &d);
}
{ // Add arguments.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionArguments);
- CallbacksDescriptor d(*factory->arguments_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign =
+ factory()->NewForeign(&Accessors::FunctionArguments);
+ CallbacksDescriptor d(*factory()->arguments_symbol(), *foreign, attributes);
descriptors->Set(2, &d);
}
{ // Add caller.
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionCaller);
- CallbacksDescriptor d(*factory->caller_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionCaller);
+ CallbacksDescriptor d(*factory()->caller_symbol(), *foreign, attributes);
descriptors->Set(3, &d);
}
if (prototypeMode != DONT_ADD_PROTOTYPE) {
@@ -375,8 +389,9 @@ Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY);
}
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionPrototype);
- CallbacksDescriptor d(*factory->prototype_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign =
+ factory()->NewForeign(&Accessors::FunctionPrototype);
+ CallbacksDescriptor d(*factory()->prototype_symbol(), *foreign, attributes);
descriptors->Set(4, &d);
}
descriptors->Sort();
@@ -385,7 +400,7 @@ Handle<DescriptorArray> Genesis::ComputeFunctionInstanceDescriptor(
Handle<Map> Genesis::CreateFunctionMap(PrototypePropertyMode prototype_mode) {
- Handle<Map> map = FACTORY->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
Handle<DescriptorArray> descriptors =
ComputeFunctionInstanceDescriptor(prototype_mode);
map->set_instance_descriptors(*descriptors);
@@ -394,7 +409,7 @@ Handle<Map> Genesis::CreateFunctionMap(PrototypePropertyMode prototype_mode) {
}
-Handle<JSFunction> Genesis::CreateEmptyFunction() {
+Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
// Allocate the map for function instances. Maps are allocated first and their
// prototypes patched later, once empty function is created.
@@ -422,7 +437,6 @@ Handle<JSFunction> Genesis::CreateEmptyFunction() {
function_instance_map_writable_prototype_ =
CreateFunctionMap(ADD_WRITEABLE_PROTOTYPE);
- Isolate* isolate = Isolate::Current();
Factory* factory = isolate->factory();
Heap* heap = isolate->heap();
@@ -491,28 +505,31 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
PrototypePropertyMode prototypeMode,
Handle<FixedArray> arguments,
Handle<FixedArray> caller) {
- Factory* factory = Isolate::Current()->factory();
Handle<DescriptorArray> descriptors =
- factory->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE ? 4 : 5);
+ factory()->NewDescriptorArray(prototypeMode == DONT_ADD_PROTOTYPE
+ ? 4
+ : 5);
PropertyAttributes attributes = static_cast<PropertyAttributes>(
DONT_ENUM | DONT_DELETE | READ_ONLY);
{ // length
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionLength);
- CallbacksDescriptor d(*factory->length_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionLength);
+ CallbacksDescriptor d(*factory()->length_symbol(), *foreign, attributes);
descriptors->Set(0, &d);
}
{ // name
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionName);
- CallbacksDescriptor d(*factory->name_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign = factory()->NewForeign(&Accessors::FunctionName);
+ CallbacksDescriptor d(*factory()->name_symbol(), *foreign, attributes);
descriptors->Set(1, &d);
}
{ // arguments
- CallbacksDescriptor d(*factory->arguments_symbol(), *arguments, attributes);
+ CallbacksDescriptor d(*factory()->arguments_symbol(),
+ *arguments,
+ attributes);
descriptors->Set(2, &d);
}
{ // caller
- CallbacksDescriptor d(*factory->caller_symbol(), *caller, attributes);
+ CallbacksDescriptor d(*factory()->caller_symbol(), *caller, attributes);
descriptors->Set(3, &d);
}
@@ -521,8 +538,9 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
if (prototypeMode == ADD_WRITEABLE_PROTOTYPE) {
attributes = static_cast<PropertyAttributes>(attributes & ~READ_ONLY);
}
- Handle<Proxy> proxy = factory->NewProxy(&Accessors::FunctionPrototype);
- CallbacksDescriptor d(*factory->prototype_symbol(), *proxy, attributes);
+ Handle<Foreign> foreign =
+ factory()->NewForeign(&Accessors::FunctionPrototype);
+ CallbacksDescriptor d(*factory()->prototype_symbol(), *foreign, attributes);
descriptors->Set(4, &d);
}
@@ -532,25 +550,22 @@ Handle<DescriptorArray> Genesis::ComputeStrictFunctionInstanceDescriptor(
// ECMAScript 5th Edition, 13.2.3
-Handle<JSFunction> Genesis::CreateThrowTypeErrorFunction(
- Builtins::Name builtin) {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- Handle<String> name = factory->LookupAsciiSymbol("ThrowTypeError");
- Handle<JSFunction> throw_type_error =
- factory->NewFunctionWithoutPrototype(name, kStrictMode);
- Handle<Code> code = Handle<Code>(
- isolate->builtins()->builtin(builtin));
-
- throw_type_error->set_map(global_context()->strict_mode_function_map());
- throw_type_error->set_code(*code);
- throw_type_error->shared()->set_code(*code);
- throw_type_error->shared()->DontAdaptArguments();
-
- PreventExtensions(throw_type_error);
-
- return throw_type_error;
+Handle<JSFunction> Genesis::GetThrowTypeErrorFunction() {
+ if (throw_type_error_function.is_null()) {
+ Handle<String> name = factory()->LookupAsciiSymbol("ThrowTypeError");
+ throw_type_error_function =
+ factory()->NewFunctionWithoutPrototype(name, kNonStrictMode);
+ Handle<Code> code(isolate()->builtins()->builtin(
+ Builtins::kStrictModePoisonPill));
+ throw_type_error_function->set_map(
+ global_context()->function_map());
+ throw_type_error_function->set_code(*code);
+ throw_type_error_function->shared()->set_code(*code);
+ throw_type_error_function->shared()->DontAdaptArguments();
+
+ PreventExtensions(throw_type_error_function);
+ }
+ return throw_type_error_function;
}
@@ -559,7 +574,7 @@ Handle<Map> Genesis::CreateStrictModeFunctionMap(
Handle<JSFunction> empty_function,
Handle<FixedArray> arguments_callbacks,
Handle<FixedArray> caller_callbacks) {
- Handle<Map> map = FACTORY->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
+ Handle<Map> map = factory()->NewMap(JS_FUNCTION_TYPE, JSFunction::kSize);
Handle<DescriptorArray> descriptors =
ComputeStrictFunctionInstanceDescriptor(prototype_mode,
arguments_callbacks,
@@ -574,7 +589,7 @@ Handle<Map> Genesis::CreateStrictModeFunctionMap(
void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) {
// Create the callbacks arrays for ThrowTypeError functions.
// The get/set callacks are filled in after the maps are created below.
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = empty->GetIsolate()->factory();
Handle<FixedArray> arguments = factory->NewFixedArray(2, TENURED);
Handle<FixedArray> caller = factory->NewFixedArray(2, TENURED);
@@ -607,23 +622,21 @@ void Genesis::CreateStrictModeFunctionMaps(Handle<JSFunction> empty) {
CreateStrictModeFunctionMap(
ADD_WRITEABLE_PROTOTYPE, empty, arguments, caller);
- // Create the ThrowTypeError function instances.
- Handle<JSFunction> arguments_throw =
- CreateThrowTypeErrorFunction(Builtins::kStrictFunctionArguments);
- Handle<JSFunction> caller_throw =
- CreateThrowTypeErrorFunction(Builtins::kStrictFunctionCaller);
+ // Create the ThrowTypeError function instance.
+ Handle<JSFunction> throw_function =
+ GetThrowTypeErrorFunction();
// Complete the callback fixed arrays.
- arguments->set(0, *arguments_throw);
- arguments->set(1, *arguments_throw);
- caller->set(0, *caller_throw);
- caller->set(1, *caller_throw);
+ arguments->set(0, *throw_function);
+ arguments->set(1, *throw_function);
+ caller->set(0, *throw_function);
+ caller->set(1, *throw_function);
}
static void AddToWeakGlobalContextList(Context* context) {
ASSERT(context->IsGlobalContext());
- Heap* heap = Isolate::Current()->heap();
+ Heap* heap = context->GetIsolate()->heap();
#ifdef DEBUG
{ // NOLINT
ASSERT(context->get(Context::NEXT_CONTEXT_LINK)->IsUndefined());
@@ -641,15 +654,14 @@ static void AddToWeakGlobalContextList(Context* context) {
void Genesis::CreateRoots() {
- Isolate* isolate = Isolate::Current();
// Allocate the global context FixedArray first and then patch the
// closure and extension object later (we need the empty function
// and the global object, but in order to create those, we need the
// global context).
- global_context_ = Handle<Context>::cast(isolate->global_handles()->Create(
- *isolate->factory()->NewGlobalContext()));
+ global_context_ = Handle<Context>::cast(isolate()->global_handles()->Create(
+ *factory()->NewGlobalContext()));
AddToWeakGlobalContextList(*global_context_);
- isolate->set_context(*global_context());
+ isolate()->set_context(*global_context());
// Allocate the message listeners object.
{
@@ -692,17 +704,13 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals(
}
}
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
-
if (js_global_template.is_null()) {
- Handle<String> name = Handle<String>(heap->empty_symbol());
- Handle<Code> code = Handle<Code>(isolate->builtins()->builtin(
+ Handle<String> name = Handle<String>(heap()->empty_symbol());
+ Handle<Code> code = Handle<Code>(isolate()->builtins()->builtin(
Builtins::kIllegal));
js_global_function =
- factory->NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
- JSGlobalObject::kSize, code, true);
+ factory()->NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
+ JSGlobalObject::kSize, code, true);
// Change the constructor property of the prototype of the
// hidden global function to refer to the Object function.
Handle<JSObject> prototype =
@@ -710,20 +718,20 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals(
JSObject::cast(js_global_function->instance_prototype()));
SetLocalPropertyNoThrow(
prototype,
- factory->constructor_symbol(),
- isolate->object_function(),
+ factory()->constructor_symbol(),
+ isolate()->object_function(),
NONE);
} else {
Handle<FunctionTemplateInfo> js_global_constructor(
FunctionTemplateInfo::cast(js_global_template->constructor()));
js_global_function =
- factory->CreateApiFunction(js_global_constructor,
- factory->InnerGlobalObject);
+ factory()->CreateApiFunction(js_global_constructor,
+ factory()->InnerGlobalObject);
}
js_global_function->initial_map()->set_is_hidden_prototype();
Handle<GlobalObject> inner_global =
- factory->NewGlobalObject(js_global_function);
+ factory()->NewGlobalObject(js_global_function);
if (inner_global_out != NULL) {
*inner_global_out = inner_global;
}
@@ -731,23 +739,23 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals(
// Step 2: create or re-initialize the global proxy object.
Handle<JSFunction> global_proxy_function;
if (global_template.IsEmpty()) {
- Handle<String> name = Handle<String>(heap->empty_symbol());
- Handle<Code> code = Handle<Code>(isolate->builtins()->builtin(
+ Handle<String> name = Handle<String>(heap()->empty_symbol());
+ Handle<Code> code = Handle<Code>(isolate()->builtins()->builtin(
Builtins::kIllegal));
global_proxy_function =
- factory->NewFunction(name, JS_GLOBAL_PROXY_TYPE,
- JSGlobalProxy::kSize, code, true);
+ factory()->NewFunction(name, JS_GLOBAL_PROXY_TYPE,
+ JSGlobalProxy::kSize, code, true);
} else {
Handle<ObjectTemplateInfo> data =
v8::Utils::OpenHandle(*global_template);
Handle<FunctionTemplateInfo> global_constructor(
FunctionTemplateInfo::cast(data->constructor()));
global_proxy_function =
- factory->CreateApiFunction(global_constructor,
- factory->OuterGlobalObject);
+ factory()->CreateApiFunction(global_constructor,
+ factory()->OuterGlobalObject);
}
- Handle<String> global_name = factory->LookupAsciiSymbol("global");
+ Handle<String> global_name = factory()->LookupAsciiSymbol("global");
global_proxy_function->shared()->set_instance_class_name(*global_name);
global_proxy_function->initial_map()->set_is_access_check_needed(true);
@@ -761,7 +769,7 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals(
Handle<JSGlobalProxy>::cast(global_object));
} else {
return Handle<JSGlobalProxy>::cast(
- factory->NewJSObject(global_proxy_function, TENURED));
+ factory()->NewJSObject(global_proxy_function, TENURED));
}
}
@@ -786,7 +794,7 @@ void Genesis::HookUpInnerGlobal(Handle<GlobalObject> inner_global) {
static const PropertyAttributes attributes =
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
ForceSetProperty(builtins_global,
- FACTORY->LookupAsciiSymbol("global"),
+ factory()->LookupAsciiSymbol("global"),
inner_global,
attributes);
// Setup the reference from the global object to the builtins object.
@@ -814,7 +822,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
// object reinitialization.
global_context()->set_security_token(*inner_global);
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = inner_global->GetIsolate();
Factory* factory = isolate->factory();
Heap* heap = isolate->heap();
@@ -841,10 +849,10 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
// is 1.
array_function->shared()->set_length(1);
Handle<DescriptorArray> array_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory->CopyAppendForeignDescriptor(
factory->empty_descriptor_array(),
factory->length_symbol(),
- factory->NewProxy(&Accessors::ArrayLength),
+ factory->NewForeign(&Accessors::ArrayLength),
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE));
// Cache the fast JavaScript array map
@@ -884,10 +892,10 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
global_context()->set_string_function(*string_fun);
// Add 'length' property to strings.
Handle<DescriptorArray> string_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory->CopyAppendForeignDescriptor(
factory->empty_descriptor_array(),
factory->length_symbol(),
- factory->NewProxy(&Accessors::StringLength),
+ factory->NewForeign(&Accessors::StringLength),
static_cast<PropertyAttributes>(DONT_ENUM |
DONT_DELETE |
READ_ONLY));
@@ -1052,16 +1060,14 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
Handle<FixedArray> callee = factory->NewFixedArray(2, TENURED);
Handle<FixedArray> caller = factory->NewFixedArray(2, TENURED);
- Handle<JSFunction> callee_throw =
- CreateThrowTypeErrorFunction(Builtins::kStrictArgumentsCallee);
- Handle<JSFunction> caller_throw =
- CreateThrowTypeErrorFunction(Builtins::kStrictArgumentsCaller);
+ Handle<JSFunction> throw_function =
+ GetThrowTypeErrorFunction();
// Install the ThrowTypeError functions.
- callee->set(0, *callee_throw);
- callee->set(1, *callee_throw);
- caller->set(0, *caller_throw);
- caller->set(1, *caller_throw);
+ callee->set(0, *throw_function);
+ callee->set(1, *throw_function);
+ caller->set(0, *throw_function);
+ caller->set(1, *throw_function);
// Create the descriptor array for the arguments object.
Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(3);
@@ -1164,17 +1170,26 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
}
-bool Genesis::CompileBuiltin(int index) {
+bool Genesis::CompileBuiltin(Isolate* isolate, int index) {
Vector<const char> name = Natives::GetScriptName(index);
Handle<String> source_code =
- Isolate::Current()->bootstrapper()->NativesSourceLookup(index);
+ isolate->bootstrapper()->NativesSourceLookup(index);
+ return CompileNative(name, source_code);
+}
+
+
+bool Genesis::CompileExperimentalBuiltin(Isolate* isolate, int index) {
+ Vector<const char> name = ExperimentalNatives::GetScriptName(index);
+ Factory* factory = isolate->factory();
+ Handle<String> source_code =
+ factory->NewStringFromAscii(ExperimentalNatives::GetScriptSource(index));
return CompileNative(name, source_code);
}
bool Genesis::CompileNative(Vector<const char> name, Handle<String> source) {
HandleScope scope;
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = source->GetIsolate();
#ifdef ENABLE_DEBUGGER_SUPPORT
isolate->debugger()->set_compiling_natives(true);
#endif
@@ -1199,7 +1214,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name,
v8::Extension* extension,
Handle<Context> top_context,
bool use_runtime_context) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = source->GetIsolate()->factory();
HandleScope scope;
Handle<SharedFunctionInfo> function_info;
@@ -1239,22 +1254,21 @@ bool Genesis::CompileScriptCached(Vector<const char> name,
? top_context->builtins()
: top_context->global());
bool has_pending_exception;
- Handle<Object> result =
- Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
+ Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
if (has_pending_exception) return false;
return true;
}
-#define INSTALL_NATIVE(Type, name, var) \
- Handle<String> var##_name = factory->LookupAsciiSymbol(name); \
- Object* var##_native = \
- global_context()->builtins()->GetPropertyNoExceptionThrown(*var##_name); \
+#define INSTALL_NATIVE(Type, name, var) \
+ Handle<String> var##_name = factory()->LookupAsciiSymbol(name); \
+ Object* var##_native = \
+ global_context()->builtins()->GetPropertyNoExceptionThrown( \
+ *var##_name); \
global_context()->set_##var(Type::cast(var##_native));
void Genesis::InstallNativeFunctions() {
- Factory* factory = Isolate::Current()->factory();
HandleScope scope;
INSTALL_NATIVE(JSFunction, "CreateDate", create_date_fun);
INSTALL_NATIVE(JSFunction, "ToNumber", to_number_fun);
@@ -1272,30 +1286,34 @@ void Genesis::InstallNativeFunctions() {
INSTALL_NATIVE(JSObject, "functionCache", function_cache);
}
+void Genesis::InstallExperimentalNativeFunctions() {
+ if (FLAG_harmony_proxies) {
+ INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap);
+ }
+}
+
#undef INSTALL_NATIVE
bool Genesis::InstallNatives() {
HandleScope scope;
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
// Create a function for the builtins object. Allocate space for the
// JavaScript builtins, a reference to the builtins object
// (itself) and a reference to the global_context directly in the object.
Handle<Code> code = Handle<Code>(
- isolate->builtins()->builtin(Builtins::kIllegal));
+ isolate()->builtins()->builtin(Builtins::kIllegal));
Handle<JSFunction> builtins_fun =
- factory->NewFunction(factory->empty_symbol(), JS_BUILTINS_OBJECT_TYPE,
- JSBuiltinsObject::kSize, code, true);
+ factory()->NewFunction(factory()->empty_symbol(),
+ JS_BUILTINS_OBJECT_TYPE,
+ JSBuiltinsObject::kSize, code, true);
- Handle<String> name = factory->LookupAsciiSymbol("builtins");
+ Handle<String> name = factory()->LookupAsciiSymbol("builtins");
builtins_fun->shared()->set_instance_class_name(*name);
// Allocate the builtins object.
Handle<JSBuiltinsObject> builtins =
- Handle<JSBuiltinsObject>::cast(factory->NewGlobalObject(builtins_fun));
+ Handle<JSBuiltinsObject>::cast(factory()->NewGlobalObject(builtins_fun));
builtins->set_builtins(*builtins);
builtins->set_global_context(*global_context());
builtins->set_global_receiver(*builtins);
@@ -1306,7 +1324,7 @@ bool Genesis::InstallNatives() {
// global object.
static const PropertyAttributes attributes =
static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
- Handle<String> global_symbol = factory->LookupAsciiSymbol("global");
+ Handle<String> global_symbol = factory()->LookupAsciiSymbol("global");
Handle<Object> global_obj(global_context()->global());
SetLocalPropertyNoThrow(builtins, global_symbol, global_obj, attributes);
@@ -1315,12 +1333,13 @@ bool Genesis::InstallNatives() {
// Create a bridge function that has context in the global context.
Handle<JSFunction> bridge =
- factory->NewFunction(factory->empty_symbol(), factory->undefined_value());
- ASSERT(bridge->context() == *isolate->global_context());
+ factory()->NewFunction(factory()->empty_symbol(),
+ factory()->undefined_value());
+ ASSERT(bridge->context() == *isolate()->global_context());
// Allocate the builtins context.
Handle<Context> context =
- factory->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, bridge);
+ factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, bridge);
context->set_global(*builtins); // override builtins global object
global_context()->set_runtime_context(*context);
@@ -1329,123 +1348,127 @@ bool Genesis::InstallNatives() {
// Builtin functions for Script.
Handle<JSFunction> script_fun =
InstallFunction(builtins, "Script", JS_VALUE_TYPE, JSValue::kSize,
- isolate->initial_object_prototype(),
+ isolate()->initial_object_prototype(),
Builtins::kIllegal, false);
Handle<JSObject> prototype =
- factory->NewJSObject(isolate->object_function(), TENURED);
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
SetPrototype(script_fun, prototype);
global_context()->set_script_function(*script_fun);
// Add 'source' and 'data' property to scripts.
PropertyAttributes common_attributes =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
- Handle<Proxy> proxy_source = factory->NewProxy(&Accessors::ScriptSource);
+ Handle<Foreign> foreign_source =
+ factory()->NewForeign(&Accessors::ScriptSource);
Handle<DescriptorArray> script_descriptors =
- factory->CopyAppendProxyDescriptor(
- factory->empty_descriptor_array(),
- factory->LookupAsciiSymbol("source"),
- proxy_source,
+ factory()->CopyAppendForeignDescriptor(
+ factory()->empty_descriptor_array(),
+ factory()->LookupAsciiSymbol("source"),
+ foreign_source,
common_attributes);
- Handle<Proxy> proxy_name = factory->NewProxy(&Accessors::ScriptName);
+ Handle<Foreign> foreign_name =
+ factory()->NewForeign(&Accessors::ScriptName);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("name"),
- proxy_name,
+ factory()->LookupAsciiSymbol("name"),
+ foreign_name,
common_attributes);
- Handle<Proxy> proxy_id = factory->NewProxy(&Accessors::ScriptId);
+ Handle<Foreign> foreign_id = factory()->NewForeign(&Accessors::ScriptId);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("id"),
- proxy_id,
+ factory()->LookupAsciiSymbol("id"),
+ foreign_id,
common_attributes);
- Handle<Proxy> proxy_line_offset =
- factory->NewProxy(&Accessors::ScriptLineOffset);
+ Handle<Foreign> foreign_line_offset =
+ factory()->NewForeign(&Accessors::ScriptLineOffset);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("line_offset"),
- proxy_line_offset,
+ factory()->LookupAsciiSymbol("line_offset"),
+ foreign_line_offset,
common_attributes);
- Handle<Proxy> proxy_column_offset =
- factory->NewProxy(&Accessors::ScriptColumnOffset);
+ Handle<Foreign> foreign_column_offset =
+ factory()->NewForeign(&Accessors::ScriptColumnOffset);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("column_offset"),
- proxy_column_offset,
+ factory()->LookupAsciiSymbol("column_offset"),
+ foreign_column_offset,
common_attributes);
- Handle<Proxy> proxy_data = factory->NewProxy(&Accessors::ScriptData);
+ Handle<Foreign> foreign_data =
+ factory()->NewForeign(&Accessors::ScriptData);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("data"),
- proxy_data,
+ factory()->LookupAsciiSymbol("data"),
+ foreign_data,
common_attributes);
- Handle<Proxy> proxy_type = factory->NewProxy(&Accessors::ScriptType);
+ Handle<Foreign> foreign_type =
+ factory()->NewForeign(&Accessors::ScriptType);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("type"),
- proxy_type,
+ factory()->LookupAsciiSymbol("type"),
+ foreign_type,
common_attributes);
- Handle<Proxy> proxy_compilation_type =
- factory->NewProxy(&Accessors::ScriptCompilationType);
+ Handle<Foreign> foreign_compilation_type =
+ factory()->NewForeign(&Accessors::ScriptCompilationType);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("compilation_type"),
- proxy_compilation_type,
+ factory()->LookupAsciiSymbol("compilation_type"),
+ foreign_compilation_type,
common_attributes);
- Handle<Proxy> proxy_line_ends =
- factory->NewProxy(&Accessors::ScriptLineEnds);
+ Handle<Foreign> foreign_line_ends =
+ factory()->NewForeign(&Accessors::ScriptLineEnds);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("line_ends"),
- proxy_line_ends,
+ factory()->LookupAsciiSymbol("line_ends"),
+ foreign_line_ends,
common_attributes);
- Handle<Proxy> proxy_context_data =
- factory->NewProxy(&Accessors::ScriptContextData);
+ Handle<Foreign> foreign_context_data =
+ factory()->NewForeign(&Accessors::ScriptContextData);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("context_data"),
- proxy_context_data,
+ factory()->LookupAsciiSymbol("context_data"),
+ foreign_context_data,
common_attributes);
- Handle<Proxy> proxy_eval_from_script =
- factory->NewProxy(&Accessors::ScriptEvalFromScript);
+ Handle<Foreign> foreign_eval_from_script =
+ factory()->NewForeign(&Accessors::ScriptEvalFromScript);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("eval_from_script"),
- proxy_eval_from_script,
+ factory()->LookupAsciiSymbol("eval_from_script"),
+ foreign_eval_from_script,
common_attributes);
- Handle<Proxy> proxy_eval_from_script_position =
- factory->NewProxy(&Accessors::ScriptEvalFromScriptPosition);
+ Handle<Foreign> foreign_eval_from_script_position =
+ factory()->NewForeign(&Accessors::ScriptEvalFromScriptPosition);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("eval_from_script_position"),
- proxy_eval_from_script_position,
+ factory()->LookupAsciiSymbol("eval_from_script_position"),
+ foreign_eval_from_script_position,
common_attributes);
- Handle<Proxy> proxy_eval_from_function_name =
- factory->NewProxy(&Accessors::ScriptEvalFromFunctionName);
+ Handle<Foreign> foreign_eval_from_function_name =
+ factory()->NewForeign(&Accessors::ScriptEvalFromFunctionName);
script_descriptors =
- factory->CopyAppendProxyDescriptor(
+ factory()->CopyAppendForeignDescriptor(
script_descriptors,
- factory->LookupAsciiSymbol("eval_from_function_name"),
- proxy_eval_from_function_name,
+ factory()->LookupAsciiSymbol("eval_from_function_name"),
+ foreign_eval_from_function_name,
common_attributes);
Handle<Map> script_map = Handle<Map>(script_fun->initial_map());
script_map->set_instance_descriptors(*script_descriptors);
// Allocate the empty script.
- Handle<Script> script = factory->NewScript(factory->empty_string());
+ Handle<Script> script = factory()->NewScript(factory()->empty_string());
script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
- heap->public_set_empty_script(*script);
+ heap()->public_set_empty_script(*script);
}
{
// Builtin function for OpaqueReference -- a JSValue-based object,
@@ -1454,10 +1477,10 @@ bool Genesis::InstallNatives() {
Handle<JSFunction> opaque_reference_fun =
InstallFunction(builtins, "OpaqueReference", JS_VALUE_TYPE,
JSValue::kSize,
- isolate->initial_object_prototype(),
+ isolate()->initial_object_prototype(),
Builtins::kIllegal, false);
Handle<JSObject> prototype =
- factory->NewJSObject(isolate->object_function(), TENURED);
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
SetPrototype(opaque_reference_fun, prototype);
global_context()->set_opaque_reference_function(*opaque_reference_fun);
}
@@ -1476,23 +1499,23 @@ bool Genesis::InstallNatives() {
"InternalArray",
JS_ARRAY_TYPE,
JSArray::kSize,
- isolate->initial_object_prototype(),
+ isolate()->initial_object_prototype(),
Builtins::kArrayCode,
true);
Handle<JSObject> prototype =
- factory->NewJSObject(isolate->object_function(), TENURED);
+ factory()->NewJSObject(isolate()->object_function(), TENURED);
SetPrototype(array_function, prototype);
array_function->shared()->set_construct_stub(
- isolate->builtins()->builtin(Builtins::kArrayConstructCode));
+ isolate()->builtins()->builtin(Builtins::kArrayConstructCode));
array_function->shared()->DontAdaptArguments();
// Make "length" magic on instances.
Handle<DescriptorArray> array_descriptors =
- factory->CopyAppendProxyDescriptor(
- factory->empty_descriptor_array(),
- factory->length_symbol(),
- factory->NewProxy(&Accessors::ArrayLength),
+ factory()->CopyAppendForeignDescriptor(
+ factory()->empty_descriptor_array(),
+ factory()->length_symbol(),
+ factory()->NewForeign(&Accessors::ArrayLength),
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE));
array_function->initial_map()->set_instance_descriptors(
@@ -1508,8 +1531,7 @@ bool Genesis::InstallNatives() {
for (int i = Natives::GetDebuggerCount();
i < Natives::GetBuiltinsCount();
i++) {
- Vector<const char> name = Natives::GetScriptName(i);
- if (!CompileBuiltin(i)) return false;
+ if (!CompileBuiltin(isolate(), i)) return false;
// TODO(ager): We really only need to install the JS builtin
// functions on the builtins object after compiling and running
// runtime.js.
@@ -1527,9 +1549,9 @@ bool Genesis::InstallNatives() {
HeapObject::cast(string_function->initial_map()->prototype())->map());
// Install Function.prototype.call and apply.
- { Handle<String> key = factory->function_class_symbol();
+ { Handle<String> key = factory()->function_class_symbol();
Handle<JSFunction> function =
- Handle<JSFunction>::cast(GetProperty(isolate->global(), key));
+ Handle<JSFunction>::cast(GetProperty(isolate()->global(), key));
Handle<JSObject> proto =
Handle<JSObject>(JSObject::cast(function->instance_prototype()));
@@ -1573,7 +1595,7 @@ bool Genesis::InstallNatives() {
// Add initial map.
Handle<Map> initial_map =
- factory->NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
+ factory()->NewMap(JS_ARRAY_TYPE, JSRegExpResult::kSize);
initial_map->set_constructor(*array_constructor);
// Set prototype on map.
@@ -1587,13 +1609,13 @@ bool Genesis::InstallNatives() {
ASSERT_EQ(1, array_descriptors->number_of_descriptors());
Handle<DescriptorArray> reresult_descriptors =
- factory->NewDescriptorArray(3);
+ factory()->NewDescriptorArray(3);
reresult_descriptors->CopyFrom(0, *array_descriptors, 0);
int enum_index = 0;
{
- FieldDescriptor index_field(heap->index_symbol(),
+ FieldDescriptor index_field(heap()->index_symbol(),
JSRegExpResult::kIndexIndex,
NONE,
enum_index++);
@@ -1601,7 +1623,7 @@ bool Genesis::InstallNatives() {
}
{
- FieldDescriptor input_field(heap->input_symbol(),
+ FieldDescriptor input_field(heap()->input_symbol(),
JSRegExpResult::kInputIndex,
NONE,
enum_index++);
@@ -1626,10 +1648,27 @@ bool Genesis::InstallNatives() {
}
+bool Genesis::InstallExperimentalNatives() {
+ for (int i = ExperimentalNatives::GetDebuggerCount();
+ i < ExperimentalNatives::GetBuiltinsCount();
+ i++) {
+ if (FLAG_harmony_proxies &&
+ strcmp(ExperimentalNatives::GetScriptName(i).start(),
+ "native proxy.js") == 0) {
+ if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+ }
+ }
+
+ InstallExperimentalNativeFunctions();
+
+ return true;
+}
+
+
static Handle<JSObject> ResolveBuiltinIdHolder(
Handle<Context> global_context,
const char* holder_expr) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = global_context->GetIsolate()->factory();
Handle<GlobalObject> global(global_context->global());
const char* period_pos = strchr(holder_expr, '.');
if (period_pos == NULL) {
@@ -1648,7 +1687,8 @@ static Handle<JSObject> ResolveBuiltinIdHolder(
static void InstallBuiltinFunctionId(Handle<JSObject> holder,
const char* function_name,
BuiltinFunctionId id) {
- Handle<String> name = FACTORY->LookupAsciiSymbol(function_name);
+ Factory* factory = holder->GetIsolate()->factory();
+ Handle<String> name = factory->LookupAsciiSymbol(function_name);
Object* function_object = holder->GetProperty(*name)->ToObjectUnchecked();
Handle<JSFunction> function(JSFunction::cast(function_object));
function->shared()->set_function_data(Smi::FromInt(id));
@@ -1675,13 +1715,14 @@ void Genesis::InstallBuiltinFunctionIds() {
F(16, global_context()->regexp_function())
-static FixedArray* CreateCache(int size, JSFunction* factory) {
+static FixedArray* CreateCache(int size, Handle<JSFunction> factory_function) {
+ Factory* factory = factory_function->GetIsolate()->factory();
// Caches are supposed to live for a long time, allocate in old space.
int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
// Cannot use cast as object is not fully initialized yet.
JSFunctionResultCache* cache = reinterpret_cast<JSFunctionResultCache*>(
- *FACTORY->NewFixedArrayWithHoles(array_size, TENURED));
- cache->set(JSFunctionResultCache::kFactoryIndex, factory);
+ *factory->NewFixedArrayWithHoles(array_size, TENURED));
+ cache->set(JSFunctionResultCache::kFactoryIndex, *factory_function);
cache->MakeZeroSize();
return cache;
}
@@ -1698,9 +1739,9 @@ void Genesis::InstallJSFunctionResultCaches() {
int index = 0;
-#define F(size, func) do { \
- FixedArray* cache = CreateCache((size), (func)); \
- caches->set(index++, cache); \
+#define F(size, func) do { \
+ FixedArray* cache = CreateCache((size), Handle<JSFunction>(func)); \
+ caches->set(index++, cache); \
} while (false)
JSFUNCTION_RESULT_CACHE_LIST(F);
@@ -1720,7 +1761,7 @@ void Genesis::InitializeNormalizedMapCaches() {
bool Bootstrapper::InstallExtensions(Handle<Context> global_context,
v8::ExtensionConfiguration* extensions) {
- Isolate* isolate = Isolate::Current();
+ Isolate* isolate = global_context->GetIsolate();
BootstrapperActive active;
SaveContext saved_context(isolate);
isolate->set_context(*global_context);
@@ -1731,7 +1772,7 @@ bool Bootstrapper::InstallExtensions(Handle<Context> global_context,
void Genesis::InstallSpecialObjects(Handle<Context> global_context) {
- Factory* factory = Isolate::Current()->factory();
+ Factory* factory = global_context->GetIsolate()->factory();
HandleScope scope;
Handle<JSGlobalObject> js_global(
JSGlobalObject::cast(global_context->global()));
@@ -1867,9 +1908,10 @@ bool Genesis::InstallExtension(v8::RegisteredExtension* current) {
bool Genesis::InstallJSBuiltins(Handle<JSBuiltinsObject> builtins) {
HandleScope scope;
+ Factory* factory = builtins->GetIsolate()->factory();
for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) {
Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i);
- Handle<String> name = FACTORY->LookupAsciiSymbol(Builtins::GetName(id));
+ Handle<String> name = factory->LookupAsciiSymbol(Builtins::GetName(id));
Object* function_object = builtins->GetPropertyNoExceptionThrown(*name);
Handle<JSFunction> function
= Handle<JSFunction>(JSFunction::cast(function_object));
@@ -1918,13 +1960,12 @@ bool Genesis::ConfigureApiObject(Handle<JSObject> object,
ASSERT(object->IsInstanceOf(
FunctionTemplateInfo::cast(object_template->constructor())));
- Isolate* isolate = Isolate::Current();
bool pending_exception = false;
Handle<JSObject> obj =
Execution::InstantiateObject(object_template, &pending_exception);
if (pending_exception) {
- ASSERT(isolate->has_pending_exception());
- isolate->clear_pending_exception();
+ ASSERT(isolate()->has_pending_exception());
+ isolate()->clear_pending_exception();
return false;
}
TransferObject(obj, object);
@@ -1979,8 +2020,9 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
break;
case NORMAL:
// Do not occur since the from object has fast properties.
+ case HANDLER:
case INTERCEPTOR:
- // No element in instance descriptors have interceptor type.
+ // No element in instance descriptors have proxy or interceptor type.
UNREACHABLE();
break;
}
@@ -2023,6 +2065,7 @@ void Genesis::TransferIndexedProperties(Handle<JSObject> from,
void Genesis::TransferObject(Handle<JSObject> from, Handle<JSObject> to) {
HandleScope outer;
+ Factory* factory = from->GetIsolate()->factory();
ASSERT(!from->IsJSArray());
ASSERT(!to->IsJSArray());
@@ -2032,7 +2075,7 @@ void Genesis::TransferObject(Handle<JSObject> from, Handle<JSObject> to) {
// Transfer the prototype (new map is needed).
Handle<Map> old_to_map = Handle<Map>(to->map());
- Handle<Map> new_to_map = FACTORY->CopyMapDropTransitions(old_to_map);
+ Handle<Map> new_to_map = factory->CopyMapDropTransitions(old_to_map);
new_to_map->set_prototype(from->map()->prototype());
to->set_map(*new_to_map);
}
@@ -2053,10 +2096,10 @@ void Genesis::MakeFunctionInstancePrototypeWritable() {
}
-Genesis::Genesis(Handle<Object> global_object,
+Genesis::Genesis(Isolate* isolate,
+ Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
- v8::ExtensionConfiguration* extensions) {
- Isolate* isolate = Isolate::Current();
+ v8::ExtensionConfiguration* extensions) : isolate_(isolate) {
result_ = Handle<Context>::null();
// If V8 isn't running and cannot be initialized, just return.
if (!V8::IsRunning() && !V8::Initialize(NULL)) return;
@@ -2086,7 +2129,7 @@ Genesis::Genesis(Handle<Object> global_object,
} else {
// We get here if there was no context snapshot.
CreateRoots();
- Handle<JSFunction> empty_function = CreateEmptyFunction();
+ Handle<JSFunction> empty_function = CreateEmptyFunction(isolate);
CreateStrictModeFunctionMaps(empty_function);
Handle<GlobalObject> inner_global;
Handle<JSGlobalProxy> global_proxy =
@@ -2103,6 +2146,9 @@ Genesis::Genesis(Handle<Object> global_object,
isolate->counters()->contexts_created_from_scratch()->Increment();
}
+ // Install experimental natives.
+ if (!InstallExperimentalNatives()) return;
+
result_ = global_context_;
}
diff --git a/src/bootstrapper.h b/src/bootstrapper.h
index 3e158d66..2e05452a 100644
--- a/src/bootstrapper.h
+++ b/src/bootstrapper.h
@@ -29,6 +29,8 @@
#ifndef V8_BOOTSTRAPPER_H_
#define V8_BOOTSTRAPPER_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
@@ -93,6 +95,7 @@ class Bootstrapper {
// Creates a JavaScript Global Context with initial object graph.
// The returned value is a global handle casted to V8Environment*.
Handle<Context> CreateEnvironment(
+ Isolate* isolate,
Handle<Object> global_object,
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions);
@@ -113,7 +116,7 @@ class Bootstrapper {
bool IsActive() const { return nesting_ != 0; }
// Support for thread preemption.
- RLYSTC int ArchiveSpacePerThread();
+ static int ArchiveSpacePerThread();
char* ArchiveState(char* to);
char* RestoreState(char* from);
void FreeThreadResources();
diff --git a/src/builtins.cc b/src/builtins.cc
index 18465903..c34b0747 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -982,34 +982,12 @@ BUILTIN(ArrayConcat) {
// Strict mode poison pills
-BUILTIN(StrictArgumentsCallee) {
+BUILTIN(StrictModePoisonPill) {
HandleScope scope;
return isolate->Throw(*isolate->factory()->NewTypeError(
- "strict_arguments_callee", HandleVector<Object>(NULL, 0)));
+ "strict_poison_pill", HandleVector<Object>(NULL, 0)));
}
-
-BUILTIN(StrictArgumentsCaller) {
- HandleScope scope;
- return isolate->Throw(*isolate->factory()->NewTypeError(
- "strict_arguments_caller", HandleVector<Object>(NULL, 0)));
-}
-
-
-BUILTIN(StrictFunctionCaller) {
- HandleScope scope;
- return isolate->Throw(*isolate->factory()->NewTypeError(
- "strict_function_caller", HandleVector<Object>(NULL, 0)));
-}
-
-
-BUILTIN(StrictFunctionArguments) {
- HandleScope scope;
- return isolate->Throw(*isolate->factory()->NewTypeError(
- "strict_function_arguments", HandleVector<Object>(NULL, 0)));
-}
-
-
// -----------------------------------------------------------------------------
//
@@ -1025,6 +1003,8 @@ static inline Object* TypeCheck(Heap* heap,
Object** argv,
FunctionTemplateInfo* info) {
Object* recv = argv[0];
+ // API calls are only supported with JSObject receivers.
+ if (!recv->IsJSObject()) return heap->null_value();
Object* sig_obj = info->signature();
if (sig_obj->IsUndefined()) return recv;
SignatureInfo* sig = SignatureInfo::cast(sig_obj);
@@ -1338,8 +1318,18 @@ static void Generate_KeyedLoadIC_Initialize(MacroAssembler* masm) {
}
+static void Generate_KeyedLoadIC_Slow(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateRuntimeGetProperty(masm);
+}
+
+
static void Generate_KeyedLoadIC_Miss(MacroAssembler* masm) {
- KeyedLoadIC::GenerateMiss(masm);
+ KeyedLoadIC::GenerateMiss(masm, false);
+}
+
+
+static void Generate_KeyedLoadIC_MissForceGeneric(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateMiss(masm, true);
}
@@ -1428,7 +1418,17 @@ static void Generate_KeyedStoreIC_Generic_Strict(MacroAssembler* masm) {
static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
- KeyedStoreIC::GenerateMiss(masm);
+ KeyedStoreIC::GenerateMiss(masm, false);
+}
+
+
+static void Generate_KeyedStoreIC_MissForceGeneric(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateMiss(masm, true);
+}
+
+
+static void Generate_KeyedStoreIC_Slow(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateSlow(masm);
}
diff --git a/src/builtins.h b/src/builtins.h
index bc0facb4..eca998cf 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -60,122 +60,125 @@ enum BuiltinExtraArguments {
V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \
V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS) \
\
- V(StrictArgumentsCallee, NO_EXTRA_ARGUMENTS) \
- V(StrictArgumentsCaller, NO_EXTRA_ARGUMENTS) \
- V(StrictFunctionCaller, NO_EXTRA_ARGUMENTS) \
- V(StrictFunctionArguments, NO_EXTRA_ARGUMENTS)
-
+ V(StrictModePoisonPill, NO_EXTRA_ARGUMENTS)
// Define list of builtins implemented in assembly.
-#define BUILTIN_LIST_A(V) \
- V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(JSConstructCall, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(JSConstructStubApi, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(LazyCompile, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(LazyRecompile, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(NotifyDeoptimized, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(NotifyLazyDeoptimized, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(NotifyOSR, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- \
- V(LoadIC_Miss, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(StoreIC_Miss, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- \
- V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC, \
- Code::kNoExtraICState) \
- V(LoadIC_Normal, LOAD_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(LoadIC_ArrayLength, LOAD_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(LoadIC_StringWrapperLength, LOAD_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- \
- V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC, \
- Code::kNoExtraICState) \
- V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- \
- V(StoreIC_Initialize, STORE_IC, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(StoreIC_Normal, STORE_IC, MONOMORPHIC, \
- Code::kNoExtraICState) \
- V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- V(StoreIC_GlobalProxy, STORE_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- V(StoreIC_Initialize_Strict, STORE_IC, UNINITIALIZED, \
- kStrictMode) \
- V(StoreIC_ArrayLength_Strict, STORE_IC, MONOMORPHIC, \
- kStrictMode) \
- V(StoreIC_Normal_Strict, STORE_IC, MONOMORPHIC, \
- kStrictMode) \
- V(StoreIC_Megamorphic_Strict, STORE_IC, MEGAMORPHIC, \
- kStrictMode) \
- V(StoreIC_GlobalProxy_Strict, STORE_IC, MEGAMORPHIC, \
- kStrictMode) \
- \
- V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC, \
- Code::kNoExtraICState) \
- \
- V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \
- kStrictMode) \
- V(KeyedStoreIC_Generic_Strict, KEYED_STORE_IC, MEGAMORPHIC, \
- kStrictMode) \
- \
- /* Uses KeyedLoadIC_Initialize; must be after in list. */ \
- V(FunctionCall, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(FunctionApply, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- \
- V(ArrayCode, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- V(ArrayConstructCode, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- \
- V(StringConstructCode, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
- \
- V(OnStackReplacement, BUILTIN, UNINITIALIZED, \
+#define BUILTIN_LIST_A(V) \
+ V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructCall, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructStubApi, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LazyCompile, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LazyRecompile, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyDeoptimized, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyLazyDeoptimized, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(NotifyOSR, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(LoadIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_MissForceGeneric, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_Slow, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_MissForceGeneric, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_Slow, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Normal, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_ArrayLength, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_StringWrapperLength, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ \
+ V(KeyedLoadIC_Initialize, KEYED_LOAD_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ \
+ V(StoreIC_Initialize, STORE_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Normal, STORE_IC, MONOMORPHIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_GlobalProxy, STORE_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ V(StoreIC_Initialize_Strict, STORE_IC, UNINITIALIZED, \
+ kStrictMode) \
+ V(StoreIC_ArrayLength_Strict, STORE_IC, MONOMORPHIC, \
+ kStrictMode) \
+ V(StoreIC_Normal_Strict, STORE_IC, MONOMORPHIC, \
+ kStrictMode) \
+ V(StoreIC_Megamorphic_Strict, STORE_IC, MEGAMORPHIC, \
+ kStrictMode) \
+ V(StoreIC_GlobalProxy_Strict, STORE_IC, MEGAMORPHIC, \
+ kStrictMode) \
+ \
+ V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC, \
+ Code::kNoExtraICState) \
+ \
+ V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \
+ kStrictMode) \
+ V(KeyedStoreIC_Generic_Strict, KEYED_STORE_IC, MEGAMORPHIC, \
+ kStrictMode) \
+ \
+ /* Uses KeyedLoadIC_Initialize; must be after in list. */ \
+ V(FunctionCall, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(FunctionApply, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(ArrayCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(ArrayConstructCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(StringConstructCode, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ \
+ V(OnStackReplacement, BUILTIN, UNINITIALIZED, \
Code::kNoExtraICState)
diff --git a/src/char-predicates.h b/src/char-predicates.h
index dac1eb8f..5a901a26 100644
--- a/src/char-predicates.h
+++ b/src/char-predicates.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,6 +28,8 @@
#ifndef V8_CHAR_PREDICATES_H_
#define V8_CHAR_PREDICATES_H_
+#include "unicode.h"
+
namespace v8 {
namespace internal {
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index f680c600..d12def85 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -29,6 +29,7 @@
#include "bootstrapper.h"
#include "code-stubs.h"
+#include "stub-cache.h"
#include "factory.h"
#include "gdb-jit.h"
#include "macro-assembler.h"
@@ -197,6 +198,12 @@ void ICCompareStub::Generate(MacroAssembler* masm) {
case CompareIC::HEAP_NUMBERS:
GenerateHeapNumbers(masm);
break;
+ case CompareIC::STRINGS:
+ GenerateStrings(masm);
+ break;
+ case CompareIC::SYMBOLS:
+ GenerateSymbols(masm);
+ break;
case CompareIC::OBJECTS:
GenerateObjects(masm);
break;
@@ -237,4 +244,24 @@ const char* InstanceofStub::GetName() {
}
+void KeyedLoadFastElementStub::Generate(MacroAssembler* masm) {
+ KeyedLoadStubCompiler::GenerateLoadFastElement(masm);
+}
+
+
+void KeyedStoreFastElementStub::Generate(MacroAssembler* masm) {
+ KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_);
+}
+
+
+void KeyedLoadExternalArrayStub::Generate(MacroAssembler* masm) {
+ KeyedLoadStubCompiler::GenerateLoadExternalArray(masm, array_type_);
+}
+
+
+void KeyedStoreExternalArrayStub::Generate(MacroAssembler* masm) {
+ KeyedStoreStubCompiler::GenerateStoreExternalArray(masm, array_type_);
+}
+
+
} } // namespace v8::internal
diff --git a/src/code-stubs.h b/src/code-stubs.h
index 56ef0726..7ab0b7c5 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -28,6 +28,7 @@
#ifndef V8_CODE_STUBS_H_
#define V8_CODE_STUBS_H_
+#include "allocation.h"
#include "globals.h"
namespace v8 {
@@ -37,7 +38,8 @@ namespace internal {
// as only the stubs up to and including Instanceof allows nested stub calls.
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
- V(TypeRecordingBinaryOp) \
+ V(UnaryOp) \
+ V(BinaryOp) \
V(StringAdd) \
V(SubString) \
V(StringCompare) \
@@ -53,7 +55,6 @@ namespace internal {
V(FastNewClosure) \
V(FastNewContext) \
V(FastCloneShallowArray) \
- V(GenericUnaryOp) \
V(RevertToNumber) \
V(ToBoolean) \
V(ToNumber) \
@@ -64,7 +65,12 @@ namespace internal {
V(NumberToString) \
V(CEntry) \
V(JSEntry) \
- V(DebuggerStatement)
+ V(KeyedLoadFastElement) \
+ V(KeyedStoreFastElement) \
+ V(KeyedLoadExternalArray) \
+ V(KeyedStoreExternalArray) \
+ V(DebuggerStatement) \
+ V(StringDictionaryNegativeLookup)
// List of code stubs only used on ARM platforms.
#ifdef V8_TARGET_ARCH_ARM
@@ -81,7 +87,8 @@ namespace internal {
// List of code stubs only used on MIPS platforms.
#ifdef V8_TARGET_ARCH_MIPS
#define CODE_STUB_LIST_MIPS(V) \
- V(RegExpCEntry)
+ V(RegExpCEntry) \
+ V(DirectCEntry)
#else
#define CODE_STUB_LIST_MIPS(V)
#endif
@@ -162,10 +169,10 @@ class CodeStub BASE_EMBEDDED {
// lazily generated function should be fully optimized or not.
virtual InLoopFlag InLoop() { return NOT_IN_LOOP; }
- // TypeRecordingBinaryOpStub needs to override this.
+ // BinaryOpStub needs to override this.
virtual int GetCodeKind();
- // TypeRecordingBinaryOpStub needs to override this.
+ // BinaryOpStub needs to override this.
virtual InlineCacheState GetICState() {
return UNINITIALIZED;
}
@@ -388,54 +395,6 @@ class InstanceofStub: public CodeStub {
};
-enum NegativeZeroHandling {
- kStrictNegativeZero,
- kIgnoreNegativeZero
-};
-
-
-enum UnaryOpFlags {
- NO_UNARY_FLAGS = 0,
- NO_UNARY_SMI_CODE_IN_STUB = 1 << 0
-};
-
-
-class GenericUnaryOpStub : public CodeStub {
- public:
- GenericUnaryOpStub(Token::Value op,
- UnaryOverwriteMode overwrite,
- UnaryOpFlags flags,
- NegativeZeroHandling negative_zero = kStrictNegativeZero)
- : op_(op),
- overwrite_(overwrite),
- include_smi_code_((flags & NO_UNARY_SMI_CODE_IN_STUB) == 0),
- negative_zero_(negative_zero) { }
-
- private:
- Token::Value op_;
- UnaryOverwriteMode overwrite_;
- bool include_smi_code_;
- NegativeZeroHandling negative_zero_;
-
- class OverwriteField: public BitField<UnaryOverwriteMode, 0, 1> {};
- class IncludeSmiCodeField: public BitField<bool, 1, 1> {};
- class NegativeZeroField: public BitField<NegativeZeroHandling, 2, 1> {};
- class OpField: public BitField<Token::Value, 3, kMinorBits - 3> {};
-
- Major MajorKey() { return GenericUnaryOp; }
- int MinorKey() {
- return OpField::encode(op_) |
- OverwriteField::encode(overwrite_) |
- IncludeSmiCodeField::encode(include_smi_code_) |
- NegativeZeroField::encode(negative_zero_);
- }
-
- void Generate(MacroAssembler* masm);
-
- const char* GetName();
-};
-
-
class MathPowStub: public CodeStub {
public:
MathPowStub() {}
@@ -471,6 +430,8 @@ class ICCompareStub: public CodeStub {
void GenerateSmis(MacroAssembler* masm);
void GenerateHeapNumbers(MacroAssembler* masm);
+ void GenerateSymbols(MacroAssembler* masm);
+ void GenerateStrings(MacroAssembler* masm);
void GenerateObjects(MacroAssembler* masm);
void GenerateMiss(MacroAssembler* masm);
@@ -784,8 +745,9 @@ class CallFunctionStub: public CodeStub {
}
InLoopFlag InLoop() { return in_loop_; }
- bool ReceiverMightBeValue() {
- return (flags_ & RECEIVER_MIGHT_BE_VALUE) != 0;
+
+ bool ReceiverMightBeImplicit() {
+ return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
}
};
@@ -964,6 +926,86 @@ class AllowStubCallsScope {
DISALLOW_COPY_AND_ASSIGN(AllowStubCallsScope);
};
+#ifdef DEBUG
+#define DECLARE_ARRAY_STUB_PRINT(name) void Print() { PrintF(#name); }
+#else
+#define DECLARE_ARRAY_STUB_PRINT(name)
+#endif
+
+
+class KeyedLoadFastElementStub : public CodeStub {
+ public:
+ explicit KeyedLoadFastElementStub() {
+ }
+
+ Major MajorKey() { return KeyedLoadFastElement; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+
+ const char* GetName() { return "KeyedLoadFastElementStub"; }
+
+ DECLARE_ARRAY_STUB_PRINT(KeyedLoadFastElementStub)
+};
+
+
+class KeyedStoreFastElementStub : public CodeStub {
+ public:
+ explicit KeyedStoreFastElementStub(bool is_js_array)
+ : is_js_array_(is_js_array) { }
+
+ Major MajorKey() { return KeyedStoreFastElement; }
+ int MinorKey() { return is_js_array_ ? 1 : 0; }
+
+ void Generate(MacroAssembler* masm);
+
+ const char* GetName() { return "KeyedStoreFastElementStub"; }
+
+ DECLARE_ARRAY_STUB_PRINT(KeyedStoreFastElementStub)
+
+ private:
+ bool is_js_array_;
+};
+
+
+class KeyedLoadExternalArrayStub : public CodeStub {
+ public:
+ explicit KeyedLoadExternalArrayStub(ExternalArrayType array_type)
+ : array_type_(array_type) { }
+
+ Major MajorKey() { return KeyedLoadExternalArray; }
+ int MinorKey() { return array_type_; }
+
+ void Generate(MacroAssembler* masm);
+
+ const char* GetName() { return "KeyedLoadExternalArrayStub"; }
+
+ DECLARE_ARRAY_STUB_PRINT(KeyedLoadExternalArrayStub)
+
+ protected:
+ ExternalArrayType array_type_;
+};
+
+
+class KeyedStoreExternalArrayStub : public CodeStub {
+ public:
+ explicit KeyedStoreExternalArrayStub(ExternalArrayType array_type)
+ : array_type_(array_type) { }
+
+ Major MajorKey() { return KeyedStoreExternalArray; }
+ int MinorKey() { return array_type_; }
+
+ void Generate(MacroAssembler* masm);
+
+ const char* GetName() { return "KeyedStoreExternalArrayStub"; }
+
+ DECLARE_ARRAY_STUB_PRINT(KeyedStoreExternalArrayStub)
+
+ protected:
+ ExternalArrayType array_type_;
+};
+
+
} } // namespace v8::internal
#endif // V8_CODE_STUBS_H_
diff --git a/src/code.h b/src/code.h
index 072344b6..766c932e 100644
--- a/src/code.h
+++ b/src/code.h
@@ -28,6 +28,8 @@
#ifndef V8_CODE_H_
#define V8_CODE_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/codegen.cc b/src/codegen.cc
index 4bbe6ae2..ad3cf1bf 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -34,7 +34,6 @@
#include "prettyprinter.h"
#include "rewriter.h"
#include "runtime.h"
-#include "scopeinfo.h"
#include "stub-cache.h"
namespace v8 {
@@ -176,7 +175,10 @@ static Vector<const char> kRegexp = CStrVector("regexp");
bool CodeGenerator::ShouldGenerateLog(Expression* type) {
ASSERT(type != NULL);
- if (!LOGGER->is_logging() && !CpuProfiler::is_profiling()) return false;
+ Isolate* isolate = Isolate::Current();
+ if (!isolate->logger()->is_logging() && !CpuProfiler::is_profiling(isolate)) {
+ return false;
+ }
Handle<String> name = Handle<String>::cast(type->AsLiteral()->handle());
if (FLAG_log_regexp) {
if (name->IsEqualTo(kRegexp))
@@ -202,29 +204,6 @@ bool CodeGenerator::RecordPositions(MacroAssembler* masm,
}
-const char* GenericUnaryOpStub::GetName() {
- switch (op_) {
- case Token::SUB:
- if (negative_zero_ == kStrictNegativeZero) {
- return overwrite_ == UNARY_OVERWRITE
- ? "GenericUnaryOpStub_SUB_Overwrite_Strict0"
- : "GenericUnaryOpStub_SUB_Alloc_Strict0";
- } else {
- return overwrite_ == UNARY_OVERWRITE
- ? "GenericUnaryOpStub_SUB_Overwrite_Ignore0"
- : "GenericUnaryOpStub_SUB_Alloc_Ignore0";
- }
- case Token::BIT_NOT:
- return overwrite_ == UNARY_OVERWRITE
- ? "GenericUnaryOpStub_BIT_NOT_Overwrite"
- : "GenericUnaryOpStub_BIT_NOT_Alloc";
- default:
- UNREACHABLE();
- return "<unknown>";
- }
-}
-
-
void ArgumentsAccessStub::Generate(MacroAssembler* masm) {
switch (type_) {
case READ_ELEMENT:
diff --git a/src/compiler.cc b/src/compiler.cc
index 6a9bc277..d82bcd0c 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -32,7 +32,6 @@
#include "bootstrapper.h"
#include "codegen.h"
#include "compilation-cache.h"
-#include "data-flow.h"
#include "debug.h"
#include "full-codegen.h"
#include "gdb-jit.h"
@@ -95,22 +94,23 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure)
}
+// Disable optimization for the rest of the compilation pipeline.
void CompilationInfo::DisableOptimization() {
- if (FLAG_optimize_closures) {
- // If we allow closures optimizations and it's an optimizable closure
- // mark it correspondingly.
- bool is_closure = closure_.is_null() && !scope_->HasTrivialOuterContext();
- if (is_closure) {
- bool is_optimizable_closure =
- !scope_->outer_scope_calls_eval() && !scope_->inside_with();
- if (is_optimizable_closure) {
- SetMode(BASE);
- return;
- }
- }
- }
+ bool is_optimizable_closure =
+ FLAG_optimize_closures &&
+ closure_.is_null() &&
+ !scope_->HasTrivialOuterContext() &&
+ !scope_->outer_scope_calls_non_strict_eval() &&
+ !scope_->inside_with();
+ SetMode(is_optimizable_closure ? BASE : NONOPT);
+}
+
- SetMode(NONOPT);
+void CompilationInfo::AbortOptimization() {
+ Handle<Code> code(shared_info()->code());
+ SetCode(code);
+ Isolate* isolate = code->GetIsolate();
+ isolate->compilation_cache()->MarkForLazyOptimizing(closure());
}
@@ -122,20 +122,23 @@ void CompilationInfo::DisableOptimization() {
// all. However crankshaft support recompilation of functions, so in this case
// the full compiler need not be be used if a debugger is attached, but only if
// break points has actually been set.
-static bool AlwaysFullCompiler() {
+static bool is_debugging_active() {
#ifdef ENABLE_DEBUGGER_SUPPORT
Isolate* isolate = Isolate::Current();
- if (V8::UseCrankshaft()) {
- return FLAG_always_full_compiler || isolate->debug()->has_break_points();
- } else {
- return FLAG_always_full_compiler || isolate->debugger()->IsDebuggerActive();
- }
+ return V8::UseCrankshaft() ?
+ isolate->debug()->has_break_points() :
+ isolate->debugger()->IsDebuggerActive();
#else
- return FLAG_always_full_compiler;
+ return false;
#endif
}
+static bool AlwaysFullCompiler() {
+ return FLAG_always_full_compiler || is_debugging_active();
+}
+
+
static void FinishOptimization(Handle<JSFunction> function, int64_t start) {
int opt_count = function->shared()->opt_count();
function->shared()->set_opt_count(opt_count + 1);
@@ -162,31 +165,6 @@ static void FinishOptimization(Handle<JSFunction> function, int64_t start) {
}
-static void AbortAndDisable(CompilationInfo* info) {
- // Disable optimization for the shared function info and mark the
- // code as non-optimizable. The marker on the shared function info
- // is there because we flush non-optimized code thereby loosing the
- // non-optimizable information for the code. When the code is
- // regenerated and set on the shared function info it is marked as
- // non-optimizable if optimization is disabled for the shared
- // function info.
- Handle<SharedFunctionInfo> shared = info->shared_info();
- shared->set_optimization_disabled(true);
- Handle<Code> code = Handle<Code>(shared->code());
- ASSERT(code->kind() == Code::FUNCTION);
- code->set_optimizable(false);
- info->SetCode(code);
- Isolate* isolate = code->GetIsolate();
- isolate->compilation_cache()->MarkForLazyOptimizing(info->closure());
- if (FLAG_trace_opt) {
- PrintF("[disabled optimization for: ");
- info->closure()->PrintName();
- PrintF(" / %" V8PRIxPTR "]\n",
- reinterpret_cast<intptr_t>(*info->closure()));
- }
-}
-
-
static bool MakeCrankshaftCode(CompilationInfo* info) {
// Test if we can optimize this function when asked to. We can only
// do this after the scopes are computed.
@@ -220,7 +198,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
const int kMaxOptCount =
FLAG_deopt_every_n_times == 0 ? Compiler::kDefaultMaxOptCount : 1000;
if (info->shared_info()->opt_count() > kMaxOptCount) {
- AbortAndDisable(info);
+ info->AbortOptimization();
+ Handle<JSFunction> closure = info->closure();
+ info->shared_info()->DisableOptimization(*closure);
// True indicates the compilation pipeline is still going, not
// necessarily that we optimized the code.
return true;
@@ -239,7 +219,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
if ((scope->num_parameters() + 1) > parameter_limit ||
(info->osr_ast_id() != AstNode::kNoNumber &&
scope->num_parameters() + 1 + scope->num_stack_slots() > locals_limit)) {
- AbortAndDisable(info);
+ info->AbortOptimization();
+ Handle<JSFunction> closure = info->closure();
+ info->shared_info()->DisableOptimization(*closure);
// True indicates the compilation pipeline is still going, not
// necessarily that we optimized the code.
return true;
@@ -312,28 +294,32 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
}
}
- // Compilation with the Hydrogen compiler failed. Keep using the
- // shared code but mark it as unoptimizable.
- AbortAndDisable(info);
+ // Keep using the shared code.
+ info->AbortOptimization();
+ if (!builder.inline_bailout()) {
+ // Mark the shared code as unoptimizable unless it was an inlined
+ // function that bailed out.
+ Handle<JSFunction> closure = info->closure();
+ info->shared_info()->DisableOptimization(*closure);
+ }
// True indicates the compilation pipeline is still going, not necessarily
// that we optimized the code.
return true;
}
+static bool GenerateCode(CompilationInfo* info) {
+ return V8::UseCrankshaft() ?
+ MakeCrankshaftCode(info) :
+ FullCodeGenerator::MakeCode(info);
+}
+
+
static bool MakeCode(CompilationInfo* info) {
// Precondition: code has been parsed. Postcondition: the code field in
// the compilation info is set if compilation succeeded.
ASSERT(info->function() != NULL);
-
- if (Rewriter::Rewrite(info) && Scope::Analyze(info)) {
- if (V8::UseCrankshaft()) return MakeCrankshaftCode(info);
- // If crankshaft is not supported fall back to full code generator
- // for all compilation.
- return FullCodeGenerator::MakeCode(info);
- }
-
- return false;
+ return Rewriter::Rewrite(info) && Scope::Analyze(info) && GenerateCode(info);
}
@@ -353,9 +339,8 @@ bool Compiler::MakeCodeForLiveEdit(CompilationInfo* info) {
static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
- CompilationZoneScope zone_scope(DELETE_ON_EXIT);
-
Isolate* isolate = info->isolate();
+ CompilationZoneScope zone_scope(isolate, DELETE_ON_EXIT);
PostponeInterruptsScope postpone(isolate);
ASSERT(!isolate->global_context().is_null());
@@ -586,12 +571,13 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
bool Compiler::CompileLazy(CompilationInfo* info) {
- CompilationZoneScope zone_scope(DELETE_ON_EXIT);
+ Isolate* isolate = info->isolate();
+
+ CompilationZoneScope zone_scope(isolate, DELETE_ON_EXIT);
// The VM is in the COMPILER state until exiting this function.
- VMState state(info->isolate(), COMPILER);
+ VMState state(isolate, COMPILER);
- Isolate* isolate = info->isolate();
PostponeInterruptsScope postpone(isolate);
Handle<SharedFunctionInfo> shared = info->shared_info();
@@ -665,7 +651,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
// version of the function right away - unless the debugger is
// active as it makes no sense to compile optimized code then.
if (FLAG_always_opt &&
- !Isolate::Current()->debug()->has_break_points()) {
+ !Isolate::Current()->DebuggerHasBreakPoints()) {
CompilationInfo optimized(function);
optimized.SetOptimizing(AstNode::kNoNumber);
return CompileLazy(&optimized);
@@ -691,6 +677,7 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
CompilationInfo info(script);
info.SetFunction(literal);
info.SetScope(literal->scope());
+ if (literal->scope()->is_strict_mode()) info.MarkAsStrictMode();
LiveEditFunctionTracker live_edit_tracker(info.isolate(), literal);
// Determine if the function can be lazily compiled. This is necessary to
@@ -768,7 +755,8 @@ void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
// Log the code generation. If source information is available include
// script name and line number. Check explicitly whether logging is
// enabled as finding the line number is not free.
- if (info->isolate()->logger()->is_logging() || CpuProfiler::is_profiling()) {
+ if (info->isolate()->logger()->is_logging() ||
+ CpuProfiler::is_profiling(info->isolate())) {
Handle<Script> script = info->script();
Handle<Code> code = info->code();
if (*code == info->isolate()->builtins()->builtin(Builtins::kLazyCompile))
diff --git a/src/compiler.h b/src/compiler.h
index a6a0d903..ea74d60d 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -28,8 +28,8 @@
#ifndef V8_COMPILER_H_
#define V8_COMPILER_H_
+#include "allocation.h"
#include "ast.h"
-#include "frame-element.h"
#include "zone.h"
namespace v8 {
@@ -144,6 +144,10 @@ class CompilationInfo BASE_EMBEDDED {
return V8::UseCrankshaft() && !closure_.is_null();
}
+ // Disable all optimization attempts of this info for the rest of the
+ // current compilation pipeline.
+ void AbortOptimization();
+
private:
Isolate* isolate_;
@@ -293,7 +297,9 @@ class Compiler : public AllStatic {
// clear this list of handles as well.
class CompilationZoneScope : public ZoneScope {
public:
- explicit CompilationZoneScope(ZoneScopeMode mode) : ZoneScope(mode) { }
+ CompilationZoneScope(Isolate* isolate, ZoneScopeMode mode)
+ : ZoneScope(isolate, mode) {}
+
virtual ~CompilationZoneScope() {
if (ShouldDeleteOnExit()) {
Isolate* isolate = Isolate::Current();
diff --git a/src/contexts.cc b/src/contexts.cc
index 520f3dde..f6031f1d 100644
--- a/src/contexts.cc
+++ b/src/contexts.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -162,7 +162,6 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
ASSERT(index >= 0); // arguments must exist and be in the heap context
Handle<JSObject> arguments(JSObject::cast(context->get(index)),
isolate);
- ASSERT(arguments->HasLocalProperty(isolate->heap()->length_symbol()));
if (FLAG_trace_contexts) {
PrintF("=> found parameter %d in arguments object\n", param_index);
}
@@ -243,6 +242,27 @@ bool Context::GlobalIfNotShadowedByEval(Handle<String> name) {
}
+void Context::ComputeEvalScopeInfo(bool* outer_scope_calls_eval,
+ bool* outer_scope_calls_non_strict_eval) {
+ Context* context = this;
+ while (true) {
+ Handle<SerializedScopeInfo> scope_info(
+ context->closure()->shared()->scope_info());
+ if (scope_info->CallsEval()) {
+ *outer_scope_calls_eval = true;
+ if (!scope_info->IsStrictMode()) {
+ // No need to go further since the answers will not change
+ // from here.
+ *outer_scope_calls_non_strict_eval = true;
+ return;
+ }
+ }
+ if (context->IsGlobalContext()) break;
+ context = Context::cast(context->closure()->context());
+ }
+}
+
+
void Context::AddOptimizedFunction(JSFunction* function) {
ASSERT(IsGlobalContext());
#ifdef DEBUG
diff --git a/src/contexts.h b/src/contexts.h
index e46619ec..b0d3ae4c 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -106,7 +106,9 @@ enum ContextLookupFlags {
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
V(OUT_OF_MEMORY_INDEX, Object, out_of_memory) \
V(MAP_CACHE_INDEX, Object, map_cache) \
- V(CONTEXT_DATA_INDEX, Object, data)
+ V(CONTEXT_DATA_INDEX, Object, data) \
+ V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
+ V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap)
// JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and
@@ -236,6 +238,8 @@ class Context: public FixedArray {
OUT_OF_MEMORY_INDEX,
MAP_CACHE_INDEX,
CONTEXT_DATA_INDEX,
+ ALLOW_CODE_GEN_FROM_STRINGS_INDEX,
+ DERIVED_GET_TRAP_INDEX,
// Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references.
@@ -349,6 +353,11 @@ class Context: public FixedArray {
// eval.
bool GlobalIfNotShadowedByEval(Handle<String> name);
+ // Determine if any function scope in the context call eval and if
+ // any of those calls are in non-strict mode.
+ void ComputeEvalScopeInfo(bool* outer_scope_calls_eval,
+ bool* outer_scope_calls_non_strict_eval);
+
// Code generation support.
static int SlotOffset(int index) {
return kHeaderSize + index * kPointerSize - kHeapObjectTag;
diff --git a/src/conversions.cc b/src/conversions.cc
index 14585845..353b6810 100644
--- a/src/conversions.cc
+++ b/src/conversions.cc
@@ -254,12 +254,12 @@ static double InternalStringToInt(UnicodeCache* unicode_cache,
if (*current == '+') {
// Ignore leading sign; skip following spaces.
++current;
- if (!AdvanceToNonspace(unicode_cache, &current, end)) {
+ if (current == end) {
return JUNK_STRING_VALUE;
}
} else if (*current == '-') {
++current;
- if (!AdvanceToNonspace(unicode_cache, &current, end)) {
+ if (current == end) {
return JUNK_STRING_VALUE;
}
negative = true;
@@ -734,6 +734,15 @@ double StringToDouble(UnicodeCache* unicode_cache,
empty_string_val);
}
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const uc16> str,
+ int flags,
+ double empty_string_val) {
+ const uc16* end = str.start() + str.length();
+ return InternalStringToDouble(unicode_cache, str.start(), end, flags,
+ empty_string_val);
+}
+
const char* DoubleToCString(double v, Vector<char> buffer) {
switch (fpclassify(v)) {
diff --git a/src/conversions.h b/src/conversions.h
index a14dc9ae..4cbeeca8 100644
--- a/src/conversions.h
+++ b/src/conversions.h
@@ -101,6 +101,10 @@ double StringToDouble(UnicodeCache* unicode_cache,
Vector<const char> str,
int flags,
double empty_string_val = 0);
+double StringToDouble(UnicodeCache* unicode_cache,
+ Vector<const uc16> str,
+ int flags,
+ double empty_string_val = 0);
// This version expects a zero-terminated character array.
double StringToDouble(UnicodeCache* unicode_cache,
const char* str,
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index 10a33606..f54e3e88 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -288,14 +288,16 @@ void CpuProfiler::StartProfiling(String* title) {
CpuProfile* CpuProfiler::StopProfiling(const char* title) {
- return is_profiling() ?
- Isolate::Current()->cpu_profiler()->StopCollectingProfile(title) : NULL;
+ Isolate* isolate = Isolate::Current();
+ return is_profiling(isolate) ?
+ isolate->cpu_profiler()->StopCollectingProfile(title) : NULL;
}
CpuProfile* CpuProfiler::StopProfiling(Object* security_token, String* title) {
- return is_profiling() ?
- Isolate::Current()->cpu_profiler()->StopCollectingProfile(
+ Isolate* isolate = Isolate::Current();
+ return is_profiling(isolate) ?
+ isolate->cpu_profiler()->StopCollectingProfile(
security_token, title) : NULL;
}
@@ -336,8 +338,9 @@ TickSample* CpuProfiler::TickSampleEvent(Isolate* isolate) {
void CpuProfiler::DeleteAllProfiles() {
Isolate* isolate = Isolate::Current();
ASSERT(isolate->cpu_profiler() != NULL);
- if (is_profiling())
+ if (is_profiling(isolate)) {
isolate->cpu_profiler()->StopProcessor();
+ }
isolate->cpu_profiler()->ResetProfiles();
}
diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h
index e04cf855..f9f61675 100644
--- a/src/cpu-profiler.h
+++ b/src/cpu-profiler.h
@@ -30,6 +30,7 @@
#ifdef ENABLE_LOGGING_AND_PROFILING
+#include "allocation.h"
#include "atomicops.h"
#include "circular-queue.h"
#include "unbound-queue.h"
@@ -133,8 +134,8 @@ class TickSampleEventRecord BASE_EMBEDDED {
// methods called by event producers: VM and stack sampler threads.
class ProfilerEventsProcessor : public Thread {
public:
- explicit ProfilerEventsProcessor(Isolate* isolate,
- ProfileGenerator* generator);
+ ProfilerEventsProcessor(Isolate* isolate,
+ ProfileGenerator* generator);
virtual ~ProfilerEventsProcessor() {}
// Thread control.
@@ -197,12 +198,12 @@ class ProfilerEventsProcessor : public Thread {
} } // namespace v8::internal
-#define PROFILE(isolate, Call) \
- LOG(isolate, Call); \
- do { \
- if (v8::internal::CpuProfiler::is_profiling()) { \
- v8::internal::CpuProfiler::Call; \
- } \
+#define PROFILE(isolate, Call) \
+ LOG(isolate, Call); \
+ do { \
+ if (v8::internal::CpuProfiler::is_profiling(isolate)) { \
+ v8::internal::CpuProfiler::Call; \
+ } \
} while (false)
#else
#define PROFILE(isolate, Call) LOG(isolate, Call)
@@ -261,10 +262,6 @@ class CpuProfiler {
// TODO(isolates): this doesn't have to use atomics anymore.
- static INLINE(bool is_profiling()) {
- return is_profiling(Isolate::Current());
- }
-
static INLINE(bool is_profiling(Isolate* isolate)) {
CpuProfiler* profiler = isolate->cpu_profiler();
return profiler != NULL && NoBarrier_Load(&profiler->is_profiling_);
@@ -292,7 +289,7 @@ class CpuProfiler {
Atomic32 is_profiling_;
#else
- static INLINE(bool is_profiling()) { return false; }
+ static INLINE(bool is_profiling(Isolate* isolate)) { return false; }
#endif // ENABLE_LOGGING_AND_PROFILING
private:
diff --git a/src/cpu.h b/src/cpu.h
index e3073027..2525484a 100644
--- a/src/cpu.h
+++ b/src/cpu.h
@@ -36,6 +36,8 @@
#ifndef V8_CPU_H_
#define V8_CPU_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/d8-readline.cc b/src/d8-readline.cc
index 67fc9eff..08395e53 100644
--- a/src/d8-readline.cc
+++ b/src/d8-readline.cc
@@ -30,6 +30,8 @@
#include <readline/readline.h> // NOLINT
#include <readline/history.h> // NOLINT
+// The readline includes leaves RETURN defined which breaks V8 compilation.
+#undef RETURN
#include "d8.h"
diff --git a/src/d8.cc b/src/d8.cc
index 7de82b75..f1068cb4 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -266,6 +266,11 @@ void Shell::ReportException(v8::TryCatch* try_catch) {
printf("^");
}
printf("\n");
+ v8::String::Utf8Value stack_trace(try_catch->StackTrace());
+ if (stack_trace.length() > 0) {
+ const char* stack_trace_string = ToCString(stack_trace);
+ printf("%s\n", stack_trace_string);
+ }
}
}
@@ -791,6 +796,8 @@ int Shell::Main(int argc, char* argv[]) {
} // namespace v8
+#ifndef GOOGLE3
int main(int argc, char* argv[]) {
return v8::Shell::Main(argc, argv);
}
+#endif
diff --git a/src/d8.gyp b/src/d8.gyp
index 901fd651..8b52ed9a 100644
--- a/src/d8.gyp
+++ b/src/d8.gyp
@@ -52,6 +52,9 @@
[ 'OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', {
'sources': [ 'd8-posix.cc', ]
}],
+ [ 'OS=="win"', {
+ 'sources': [ 'd8-windows.cc', ]
+ }],
],
},
{
@@ -61,6 +64,7 @@
'variables': {
'js_files': [
'd8.js',
+ 'macros.py',
],
},
'actions': [
@@ -72,7 +76,6 @@
],
'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/d8-js.cc',
- '<(SHARED_INTERMEDIATE_DIR)/d8-js-empty.cc',
],
'action': [
'python',
diff --git a/src/d8.h b/src/d8.h
index de1fe0de..dc023229 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -28,9 +28,13 @@
#ifndef V8_D8_H_
#define V8_D8_H_
+#include "allocation.h"
#include "v8.h"
#include "hashmap.h"
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#error Using compressed startup data is not supported for D8
+#endif
namespace v8 {
diff --git a/src/data-flow.cc b/src/data-flow.cc
index 79339eda..6a3b05cc 100644
--- a/src/data-flow.cc
+++ b/src/data-flow.cc
@@ -63,477 +63,4 @@ void BitVector::Iterator::Advance() {
current_value_ = val >> 1;
}
-
-bool AssignedVariablesAnalyzer::Analyze(CompilationInfo* info) {
- Scope* scope = info->scope();
- int size = scope->num_parameters() + scope->num_stack_slots();
- if (size == 0) return true;
- AssignedVariablesAnalyzer analyzer(info, size);
- return analyzer.Analyze();
-}
-
-
-AssignedVariablesAnalyzer::AssignedVariablesAnalyzer(CompilationInfo* info,
- int size)
- : info_(info), av_(size) {
-}
-
-
-bool AssignedVariablesAnalyzer::Analyze() {
- ASSERT(av_.length() > 0);
- VisitStatements(info_->function()->body());
- return !HasStackOverflow();
-}
-
-
-Variable* AssignedVariablesAnalyzer::FindSmiLoopVariable(ForStatement* stmt) {
- // The loop must have all necessary parts.
- if (stmt->init() == NULL || stmt->cond() == NULL || stmt->next() == NULL) {
- return NULL;
- }
- // The initialization statement has to be a simple assignment.
- Assignment* init = stmt->init()->StatementAsSimpleAssignment();
- if (init == NULL) return NULL;
-
- // We only deal with local variables.
- Variable* loop_var = init->target()->AsVariableProxy()->AsVariable();
- if (loop_var == NULL || !loop_var->IsStackAllocated()) return NULL;
-
- // Don't try to get clever with const or dynamic variables.
- if (loop_var->mode() != Variable::VAR) return NULL;
-
- // The initial value has to be a smi.
- Literal* init_lit = init->value()->AsLiteral();
- if (init_lit == NULL || !init_lit->handle()->IsSmi()) return NULL;
- int init_value = Smi::cast(*init_lit->handle())->value();
-
- // The condition must be a compare of variable with <, <=, >, or >=.
- CompareOperation* cond = stmt->cond()->AsCompareOperation();
- if (cond == NULL) return NULL;
- if (cond->op() != Token::LT
- && cond->op() != Token::LTE
- && cond->op() != Token::GT
- && cond->op() != Token::GTE) return NULL;
-
- // The lhs must be the same variable as in the init expression.
- if (cond->left()->AsVariableProxy()->AsVariable() != loop_var) return NULL;
-
- // The rhs must be a smi.
- Literal* term_lit = cond->right()->AsLiteral();
- if (term_lit == NULL || !term_lit->handle()->IsSmi()) return NULL;
- int term_value = Smi::cast(*term_lit->handle())->value();
-
- // The count operation updates the same variable as in the init expression.
- CountOperation* update = stmt->next()->StatementAsCountOperation();
- if (update == NULL) return NULL;
- if (update->expression()->AsVariableProxy()->AsVariable() != loop_var) {
- return NULL;
- }
-
- // The direction of the count operation must agree with the start and the end
- // value. We currently do not allow the initial value to be the same as the
- // terminal value. This _would_ be ok as long as the loop body never executes
- // or executes exactly one time.
- if (init_value == term_value) return NULL;
- if (init_value < term_value && update->op() != Token::INC) return NULL;
- if (init_value > term_value && update->op() != Token::DEC) return NULL;
-
- // Check that the update operation cannot overflow the smi range. This can
- // occur in the two cases where the loop bound is equal to the largest or
- // smallest smi.
- if (update->op() == Token::INC && term_value == Smi::kMaxValue) return NULL;
- if (update->op() == Token::DEC && term_value == Smi::kMinValue) return NULL;
-
- // Found a smi loop variable.
- return loop_var;
-}
-
-int AssignedVariablesAnalyzer::BitIndex(Variable* var) {
- ASSERT(var != NULL);
- ASSERT(var->IsStackAllocated());
- Slot* slot = var->AsSlot();
- if (slot->type() == Slot::PARAMETER) {
- return slot->index();
- } else {
- return info_->scope()->num_parameters() + slot->index();
- }
-}
-
-
-void AssignedVariablesAnalyzer::RecordAssignedVar(Variable* var) {
- ASSERT(var != NULL);
- if (var->IsStackAllocated()) {
- av_.Add(BitIndex(var));
- }
-}
-
-
-void AssignedVariablesAnalyzer::MarkIfTrivial(Expression* expr) {
- Variable* var = expr->AsVariableProxy()->AsVariable();
- if (var != NULL &&
- var->IsStackAllocated() &&
- !var->is_arguments() &&
- var->mode() != Variable::CONST &&
- (var->is_this() || !av_.Contains(BitIndex(var)))) {
- expr->AsVariableProxy()->MarkAsTrivial();
- }
-}
-
-
-void AssignedVariablesAnalyzer::ProcessExpression(Expression* expr) {
- BitVector saved_av(av_);
- av_.Clear();
- Visit(expr);
- av_.Union(saved_av);
-}
-
-void AssignedVariablesAnalyzer::VisitBlock(Block* stmt) {
- VisitStatements(stmt->statements());
-}
-
-
-void AssignedVariablesAnalyzer::VisitExpressionStatement(
- ExpressionStatement* stmt) {
- ProcessExpression(stmt->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) {
- // Do nothing.
-}
-
-
-void AssignedVariablesAnalyzer::VisitIfStatement(IfStatement* stmt) {
- ProcessExpression(stmt->condition());
- Visit(stmt->then_statement());
- Visit(stmt->else_statement());
-}
-
-
-void AssignedVariablesAnalyzer::VisitContinueStatement(
- ContinueStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitBreakStatement(BreakStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitReturnStatement(ReturnStatement* stmt) {
- ProcessExpression(stmt->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitWithEnterStatement(
- WithEnterStatement* stmt) {
- ProcessExpression(stmt->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitWithExitStatement(
- WithExitStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) {
- BitVector result(av_);
- av_.Clear();
- Visit(stmt->tag());
- result.Union(av_);
- for (int i = 0; i < stmt->cases()->length(); i++) {
- CaseClause* clause = stmt->cases()->at(i);
- if (!clause->is_default()) {
- av_.Clear();
- Visit(clause->label());
- result.Union(av_);
- }
- VisitStatements(clause->statements());
- }
- av_.Union(result);
-}
-
-
-void AssignedVariablesAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) {
- ProcessExpression(stmt->cond());
- Visit(stmt->body());
-}
-
-
-void AssignedVariablesAnalyzer::VisitWhileStatement(WhileStatement* stmt) {
- ProcessExpression(stmt->cond());
- Visit(stmt->body());
-}
-
-
-void AssignedVariablesAnalyzer::VisitForStatement(ForStatement* stmt) {
- if (stmt->init() != NULL) Visit(stmt->init());
- if (stmt->cond() != NULL) ProcessExpression(stmt->cond());
- if (stmt->next() != NULL) Visit(stmt->next());
-
- // Process loop body. After visiting the loop body av_ contains
- // the assigned variables of the loop body.
- BitVector saved_av(av_);
- av_.Clear();
- Visit(stmt->body());
-
- Variable* var = FindSmiLoopVariable(stmt);
- if (var != NULL && !av_.Contains(BitIndex(var))) {
- stmt->set_loop_variable(var);
- }
- av_.Union(saved_av);
-}
-
-
-void AssignedVariablesAnalyzer::VisitForInStatement(ForInStatement* stmt) {
- ProcessExpression(stmt->each());
- ProcessExpression(stmt->enumerable());
- Visit(stmt->body());
-}
-
-
-void AssignedVariablesAnalyzer::VisitTryCatchStatement(
- TryCatchStatement* stmt) {
- Visit(stmt->try_block());
- Visit(stmt->catch_block());
-}
-
-
-void AssignedVariablesAnalyzer::VisitTryFinallyStatement(
- TryFinallyStatement* stmt) {
- Visit(stmt->try_block());
- Visit(stmt->finally_block());
-}
-
-
-void AssignedVariablesAnalyzer::VisitDebuggerStatement(
- DebuggerStatement* stmt) {
- // Nothing to do.
-}
-
-
-void AssignedVariablesAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitConditional(Conditional* expr) {
- ASSERT(av_.IsEmpty());
-
- Visit(expr->condition());
-
- BitVector result(av_);
- av_.Clear();
- Visit(expr->then_expression());
- result.Union(av_);
-
- av_.Clear();
- Visit(expr->else_expression());
- av_.Union(result);
-}
-
-
-void AssignedVariablesAnalyzer::VisitVariableProxy(VariableProxy* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitLiteral(Literal* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) {
- ASSERT(av_.IsEmpty());
- BitVector result(av_.length());
- for (int i = 0; i < expr->properties()->length(); i++) {
- Visit(expr->properties()->at(i)->value());
- result.Union(av_);
- av_.Clear();
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) {
- ASSERT(av_.IsEmpty());
- BitVector result(av_.length());
- for (int i = 0; i < expr->values()->length(); i++) {
- Visit(expr->values()->at(i));
- result.Union(av_);
- av_.Clear();
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitCatchExtensionObject(
- CatchExtensionObject* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->key());
- ProcessExpression(expr->value());
-}
-
-
-void AssignedVariablesAnalyzer::VisitAssignment(Assignment* expr) {
- ASSERT(av_.IsEmpty());
-
- // There are three kinds of assignments: variable assignments, property
- // assignments, and reference errors (invalid left-hand sides).
- Variable* var = expr->target()->AsVariableProxy()->AsVariable();
- Property* prop = expr->target()->AsProperty();
- ASSERT(var == NULL || prop == NULL);
-
- if (var != NULL) {
- MarkIfTrivial(expr->value());
- Visit(expr->value());
- if (expr->is_compound()) {
- // Left-hand side occurs also as an rvalue.
- MarkIfTrivial(expr->target());
- ProcessExpression(expr->target());
- }
- RecordAssignedVar(var);
-
- } else if (prop != NULL) {
- MarkIfTrivial(expr->value());
- Visit(expr->value());
- if (!prop->key()->IsPropertyName()) {
- MarkIfTrivial(prop->key());
- ProcessExpression(prop->key());
- }
- MarkIfTrivial(prop->obj());
- ProcessExpression(prop->obj());
-
- } else {
- Visit(expr->target());
- }
-}
-
-
-void AssignedVariablesAnalyzer::VisitThrow(Throw* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->exception());
-}
-
-
-void AssignedVariablesAnalyzer::VisitProperty(Property* expr) {
- ASSERT(av_.IsEmpty());
- if (!expr->key()->IsPropertyName()) {
- MarkIfTrivial(expr->key());
- Visit(expr->key());
- }
- MarkIfTrivial(expr->obj());
- ProcessExpression(expr->obj());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCall(Call* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->expression());
- BitVector result(av_);
- for (int i = 0; i < expr->arguments()->length(); i++) {
- av_.Clear();
- Visit(expr->arguments()->at(i));
- result.Union(av_);
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitCallNew(CallNew* expr) {
- ASSERT(av_.IsEmpty());
- Visit(expr->expression());
- BitVector result(av_);
- for (int i = 0; i < expr->arguments()->length(); i++) {
- av_.Clear();
- Visit(expr->arguments()->at(i));
- result.Union(av_);
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitCallRuntime(CallRuntime* expr) {
- ASSERT(av_.IsEmpty());
- BitVector result(av_);
- for (int i = 0; i < expr->arguments()->length(); i++) {
- av_.Clear();
- Visit(expr->arguments()->at(i));
- result.Union(av_);
- }
- av_ = result;
-}
-
-
-void AssignedVariablesAnalyzer::VisitUnaryOperation(UnaryOperation* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->expression());
- Visit(expr->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCountOperation(CountOperation* expr) {
- ASSERT(av_.IsEmpty());
- if (expr->is_prefix()) MarkIfTrivial(expr->expression());
- Visit(expr->expression());
-
- Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
- if (var != NULL) RecordAssignedVar(var);
-}
-
-
-void AssignedVariablesAnalyzer::VisitBinaryOperation(BinaryOperation* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->right());
- Visit(expr->right());
- MarkIfTrivial(expr->left());
- ProcessExpression(expr->left());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCompareOperation(CompareOperation* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->right());
- Visit(expr->right());
- MarkIfTrivial(expr->left());
- ProcessExpression(expr->left());
-}
-
-
-void AssignedVariablesAnalyzer::VisitCompareToNull(CompareToNull* expr) {
- ASSERT(av_.IsEmpty());
- MarkIfTrivial(expr->expression());
- Visit(expr->expression());
-}
-
-
-void AssignedVariablesAnalyzer::VisitThisFunction(ThisFunction* expr) {
- // Nothing to do.
- ASSERT(av_.IsEmpty());
-}
-
-
-void AssignedVariablesAnalyzer::VisitDeclaration(Declaration* decl) {
- UNREACHABLE();
-}
-
-
} } // namespace v8::internal
diff --git a/src/data-flow.h b/src/data-flow.h
index 573d7d80..d69d6c7a 100644
--- a/src/data-flow.h
+++ b/src/data-flow.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,6 +30,7 @@
#include "v8.h"
+#include "allocation.h"
#include "ast.h"
#include "compiler.h"
#include "zone-inl.h"
@@ -37,9 +38,6 @@
namespace v8 {
namespace internal {
-// Forward declarations.
-class Node;
-
class BitVector: public ZoneObject {
public:
// Iterator for the elements of this BitVector.
@@ -201,178 +199,6 @@ class BitVector: public ZoneObject {
uint32_t* data_;
};
-
-// An implementation of a sparse set whose elements are drawn from integers
-// in the range [0..universe_size[. It supports constant-time Contains,
-// destructive Add, and destructuve Remove operations and linear-time (in
-// the number of elements) destructive Union.
-class SparseSet: public ZoneObject {
- public:
- // Iterator for sparse set elements. Elements should not be added or
- // removed during iteration.
- class Iterator BASE_EMBEDDED {
- public:
- explicit Iterator(SparseSet* target) : target_(target), current_(0) {
- ASSERT(++target->iterator_count_ > 0);
- }
- ~Iterator() {
- ASSERT(target_->iterator_count_-- > 0);
- }
- bool Done() const { return current_ >= target_->dense_.length(); }
- void Advance() {
- ASSERT(!Done());
- ++current_;
- }
- int Current() {
- ASSERT(!Done());
- return target_->dense_[current_];
- }
-
- private:
- SparseSet* target_;
- int current_;
-
- friend class SparseSet;
- };
-
- explicit SparseSet(int universe_size)
- : dense_(4),
- sparse_(ZONE->NewArray<int>(universe_size)) {
-#ifdef DEBUG
- size_ = universe_size;
- iterator_count_ = 0;
-#endif
- }
-
- bool Contains(int n) const {
- ASSERT(0 <= n && n < size_);
- int dense_index = sparse_[n];
- return (0 <= dense_index) &&
- (dense_index < dense_.length()) &&
- (dense_[dense_index] == n);
- }
-
- void Add(int n) {
- ASSERT(0 <= n && n < size_);
- ASSERT(iterator_count_ == 0);
- if (!Contains(n)) {
- sparse_[n] = dense_.length();
- dense_.Add(n);
- }
- }
-
- void Remove(int n) {
- ASSERT(0 <= n && n < size_);
- ASSERT(iterator_count_ == 0);
- if (Contains(n)) {
- int dense_index = sparse_[n];
- int last = dense_.RemoveLast();
- if (dense_index < dense_.length()) {
- dense_[dense_index] = last;
- sparse_[last] = dense_index;
- }
- }
- }
-
- void Union(const SparseSet& other) {
- for (int i = 0; i < other.dense_.length(); ++i) {
- Add(other.dense_[i]);
- }
- }
-
- private:
- // The set is implemented as a pair of a growable dense list and an
- // uninitialized sparse array.
- ZoneList<int> dense_;
- int* sparse_;
-#ifdef DEBUG
- int size_;
- int iterator_count_;
-#endif
-};
-
-
-// Simple fixed-capacity list-based worklist (managed as a queue) of
-// pointers to T.
-template<typename T>
-class WorkList BASE_EMBEDDED {
- public:
- // The worklist cannot grow bigger than size. We keep one item empty to
- // distinguish between empty and full.
- explicit WorkList(int size)
- : capacity_(size + 1), head_(0), tail_(0), queue_(capacity_) {
- for (int i = 0; i < capacity_; i++) queue_.Add(NULL);
- }
-
- bool is_empty() { return head_ == tail_; }
-
- bool is_full() {
- // The worklist is full if head is at 0 and tail is at capacity - 1:
- // head == 0 && tail == capacity-1 ==> tail - head == capacity - 1
- // or if tail is immediately to the left of head:
- // tail+1 == head ==> tail - head == -1
- int diff = tail_ - head_;
- return (diff == -1 || diff == capacity_ - 1);
- }
-
- void Insert(T* item) {
- ASSERT(!is_full());
- queue_[tail_++] = item;
- if (tail_ == capacity_) tail_ = 0;
- }
-
- T* Remove() {
- ASSERT(!is_empty());
- T* item = queue_[head_++];
- if (head_ == capacity_) head_ = 0;
- return item;
- }
-
- private:
- int capacity_; // Including one empty slot.
- int head_; // Where the first item is.
- int tail_; // Where the next inserted item will go.
- List<T*> queue_;
-};
-
-
-// Computes the set of assigned variables and annotates variables proxies
-// that are trivial sub-expressions and for-loops where the loop variable
-// is guaranteed to be a smi.
-class AssignedVariablesAnalyzer : public AstVisitor {
- public:
- static bool Analyze(CompilationInfo* info);
-
- private:
- AssignedVariablesAnalyzer(CompilationInfo* info, int bits);
- bool Analyze();
-
- Variable* FindSmiLoopVariable(ForStatement* stmt);
-
- int BitIndex(Variable* var);
-
- void RecordAssignedVar(Variable* var);
-
- void MarkIfTrivial(Expression* expr);
-
- // Visits an expression saving the accumulator before, clearing
- // it before visting and restoring it after visiting.
- void ProcessExpression(Expression* expr);
-
- // AST node visit functions.
-#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
- AST_NODE_LIST(DECLARE_VISIT)
-#undef DECLARE_VISIT
-
- CompilationInfo* info_;
-
- // Accumulator for assigned variables set.
- BitVector av_;
-
- DISALLOW_COPY_AND_ASSIGN(AssignedVariablesAnalyzer);
-};
-
-
} } // namespace v8::internal
diff --git a/src/date.js b/src/date.js
index 242ab7bb..5a2e9a23 100644
--- a/src/date.js
+++ b/src/date.js
@@ -684,7 +684,7 @@ function DateGetUTCDate() {
// ECMA 262 - 15.9.5.16
function DateGetDay() {
- var t = %_ValueOf(this);
+ var t = DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return t;
return WeekDay(LocalTimeNoCheck(t));
}
@@ -692,7 +692,7 @@ function DateGetDay() {
// ECMA 262 - 15.9.5.17
function DateGetUTCDay() {
- var t = %_ValueOf(this);
+ var t = DATE_VALUE(this);
if (NUMBER_IS_NAN(t)) return t;
return WeekDay(t);
}
diff --git a/src/dateparser.h b/src/dateparser.h
index 9d297155..6e87c341 100644
--- a/src/dateparser.h
+++ b/src/dateparser.h
@@ -28,6 +28,7 @@
#ifndef V8_DATEPARSER_H_
#define V8_DATEPARSER_H_
+#include "allocation.h"
#include "char-predicates-inl.h"
#include "scanner-base.h"
diff --git a/src/debug-debugger.js b/src/debug-debugger.js
index bc0f966f..908fcd21 100644
--- a/src/debug-debugger.js
+++ b/src/debug-debugger.js
@@ -1335,7 +1335,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
try {
try {
// Convert the JSON string to an object.
- request = %CompileString('(' + json_request + ')')();
+ request = JSON.parse(json_request);
// Create an initial response.
response = this.createResponse(request);
diff --git a/src/debug.cc b/src/debug.cc
index 6f0431c9..85c4b5ef 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -92,7 +92,7 @@ static Handle<Code> ComputeCallDebugBreak(int argc, Code::Kind kind) {
}
-static Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
+static Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
Isolate* isolate = Isolate::Current();
CALL_HEAP_FUNCTION(
isolate,
@@ -167,7 +167,8 @@ void BreakLocationIterator::Next() {
Address target = original_rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
if ((code->is_inline_cache_stub() &&
- !code->is_type_recording_binary_op_stub() &&
+ !code->is_binary_op_stub() &&
+ !code->is_unary_op_stub() &&
!code->is_compare_ic_stub()) ||
RelocInfo::IsConstructCall(rmode())) {
break_point_++;
@@ -477,21 +478,6 @@ void BreakLocationIterator::SetDebugBreakAtIC() {
// calling convention used by the call site.
Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode));
rinfo()->set_target_address(dbgbrk_code->entry());
-
- // For stubs that refer back to an inlined version clear the cached map for
- // the inlined case to always go through the IC. As long as the break point
- // is set the patching performed by the runtime system will take place in
- // the code copy and will therefore have no effect on the running code
- // keeping it from using the inlined code.
- if (code->is_keyed_load_stub()) {
- KeyedLoadIC::ClearInlinedVersion(pc());
- } else if (code->is_keyed_store_stub()) {
- KeyedStoreIC::ClearInlinedVersion(pc());
- } else if (code->is_load_stub()) {
- LoadIC::ClearInlinedVersion(pc());
- } else if (code->is_store_stub()) {
- StoreIC::ClearInlinedVersion(pc());
- }
}
}
@@ -499,20 +485,6 @@ void BreakLocationIterator::SetDebugBreakAtIC() {
void BreakLocationIterator::ClearDebugBreakAtIC() {
// Patch the code to the original invoke.
rinfo()->set_target_address(original_rinfo()->target_address());
-
- RelocInfo::Mode mode = rmode();
- if (RelocInfo::IsCodeTarget(mode)) {
- AssertNoAllocation nogc;
- Address target = original_rinfo()->target_address();
- Code* code = Code::GetCodeFromTargetAddress(target);
-
- // Restore the inlined version of keyed stores to get back to the
- // fast case. We need to patch back the keyed store because no
- // patching happens when running normally. For keyed loads, the
- // map check will get patched back when running normally after ICs
- // have been cleared at GC.
- if (code->is_keyed_store_stub()) KeyedStoreIC::RestoreInlinedVersion(pc());
- }
}
@@ -843,6 +815,7 @@ bool Debug::Load() {
HandleScope scope(isolate_);
Handle<Context> context =
isolate_->bootstrapper()->CreateEnvironment(
+ isolate_,
Handle<Object>::null(),
v8::Handle<ObjectTemplate>(),
NULL);
@@ -1017,6 +990,11 @@ Object* Debug::Break(Arguments args) {
} else if (thread_local_.frame_drop_mode_ ==
FRAME_DROPPED_IN_DIRECT_CALL) {
// Nothing to do, after_break_target is not used here.
+ } else if (thread_local_.frame_drop_mode_ ==
+ FRAME_DROPPED_IN_RETURN_CALL) {
+ Code* plain_return = isolate_->builtins()->builtin(
+ Builtins::kFrameDropper_LiveEdit);
+ thread_local_.after_break_target_ = plain_return->entry();
} else {
UNREACHABLE();
}
@@ -1986,8 +1964,8 @@ void Debug::AfterGarbageCollection() {
}
-Debugger::Debugger(Isolate* isolate)
- : debugger_access_(isolate->debugger_access()),
+Debugger::Debugger()
+ : debugger_access_(OS::CreateMutex()),
event_listener_(Handle<Object>()),
event_listener_data_(Handle<Object>()),
compiling_natives_(false),
@@ -2003,12 +1981,13 @@ Debugger::Debugger(Isolate* isolate)
agent_(NULL),
command_queue_(kQueueInitialSize),
command_received_(OS::CreateSemaphore(0)),
- event_command_queue_(kQueueInitialSize),
- isolate_(isolate) {
+ event_command_queue_(kQueueInitialSize) {
}
Debugger::~Debugger() {
+ delete debugger_access_;
+ debugger_access_ = 0;
delete dispatch_handler_access_;
dispatch_handler_access_ = 0;
delete command_received_;
@@ -2387,7 +2366,7 @@ void Debugger::CallEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
v8::Debug::ClientData* client_data) {
- if (event_listener_->IsProxy()) {
+ if (event_listener_->IsForeign()) {
CallCEventCallback(event, exec_state, event_data, client_data);
} else {
CallJSEventCallback(event, exec_state, event_data);
@@ -2399,9 +2378,9 @@ void Debugger::CallCEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data,
v8::Debug::ClientData* client_data) {
- Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
+ Handle<Foreign> callback_obj(Handle<Foreign>::cast(event_listener_));
v8::Debug::EventCallback2 callback =
- FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->proxy());
+ FUNCTION_CAST<v8::Debug::EventCallback2>(callback_obj->address());
EventDetailsImpl event_details(
event,
Handle<JSObject>::cast(exec_state),
diff --git a/src/debug.h b/src/debug.h
index 6be33a66..95dca729 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -28,6 +28,7 @@
#ifndef V8_DEBUG_H_
#define V8_DEBUG_H_
+#include "allocation.h"
#include "arguments.h"
#include "assembler.h"
#include "debug-agent.h"
@@ -422,7 +423,8 @@ class Debug {
FRAME_DROPPED_IN_DEBUG_SLOT_CALL,
// The top JS frame had been calling some C++ function. The return address
// gets patched automatically.
- FRAME_DROPPED_IN_DIRECT_CALL
+ FRAME_DROPPED_IN_DIRECT_CALL,
+ FRAME_DROPPED_IN_RETURN_CALL
};
void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
@@ -809,7 +811,7 @@ class Debugger {
bool IsDebuggerActive();
private:
- explicit Debugger(Isolate* isolate);
+ Debugger();
void CallEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
diff --git a/src/deoptimizer.h b/src/deoptimizer.h
index cb82f446..7c5dfb87 100644
--- a/src/deoptimizer.h
+++ b/src/deoptimizer.h
@@ -30,6 +30,7 @@
#include "v8.h"
+#include "allocation.h"
#include "macro-assembler.h"
#include "zone-inl.h"
diff --git a/src/disassembler.cc b/src/disassembler.cc
index 65e16681..368c3a89 100644
--- a/src/disassembler.cc
+++ b/src/disassembler.cc
@@ -282,6 +282,9 @@ static int DecodeIt(FILE* f,
} else {
out.AddFormatted(" %s", Code::Kind2String(kind));
}
+ if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
+ out.AddFormatted(" (id = %d)", static_cast<int>(relocinfo.data()));
+ }
} else if (rmode == RelocInfo::RUNTIME_ENTRY &&
Isolate::Current()->deoptimizer_data() != NULL) {
// A runtime entry reloinfo might be a deoptimization bailout.
diff --git a/src/disassembler.h b/src/disassembler.h
index 68a338d1..4a87dca6 100644
--- a/src/disassembler.h
+++ b/src/disassembler.h
@@ -28,6 +28,8 @@
#ifndef V8_DISASSEMBLER_H_
#define V8_DISASSEMBLER_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/execution.cc b/src/execution.cc
index 7a2bbc67..e84ab9e8 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -132,7 +132,7 @@ static Handle<Object> Invoke(bool construct,
if (*has_pending_exception) {
isolate->ReportPendingMessages();
if (isolate->pending_exception() == Failure::OutOfMemoryException()) {
- if (!isolate->ignore_out_of_memory()) {
+ if (!isolate->handle_scope_implementer()->ignore_out_of_memory()) {
V8::FatalProcessOutOfMemory("JS", true);
}
}
@@ -145,11 +145,16 @@ static Handle<Object> Invoke(bool construct,
}
-Handle<Object> Execution::Call(Handle<JSFunction> func,
+Handle<Object> Execution::Call(Handle<Object> callable,
Handle<Object> receiver,
int argc,
Object*** args,
bool* pending_exception) {
+ if (!callable->IsJSFunction()) {
+ callable = TryGetFunctionDelegate(callable, pending_exception);
+ if (*pending_exception) return callable;
+ }
+ Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
return Invoke(false, func, receiver, argc, args, pending_exception);
}
@@ -234,6 +239,30 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) {
}
+Handle<Object> Execution::TryGetFunctionDelegate(Handle<Object> object,
+ bool* has_pending_exception) {
+ ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+
+ // Objects created through the API can have an instance-call handler
+ // that should be used when calling the object as a function.
+ if (object->IsHeapObject() &&
+ HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+ return Handle<JSFunction>(
+ isolate->global_context()->call_as_function_delegate());
+ }
+
+ // If the Object doesn't have an instance-call handler we should
+ // throw a non-callable exception.
+ i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError(
+ "called_non_callable", i::HandleVector<i::Object>(&object, 1));
+ isolate->Throw(*error_obj);
+ *has_pending_exception = true;
+
+ return isolate->factory()->undefined_value();
+}
+
+
Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
ASSERT(!object->IsJSFunction());
Isolate* isolate = Isolate::Current();
@@ -253,6 +282,34 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
}
+Handle<Object> Execution::TryGetConstructorDelegate(
+ Handle<Object> object,
+ bool* has_pending_exception) {
+ ASSERT(!object->IsJSFunction());
+ Isolate* isolate = Isolate::Current();
+
+ // If you return a function from here, it will be called when an
+ // attempt is made to call the given object as a constructor.
+
+ // Objects created through the API can have an instance-call handler
+ // that should be used when calling the object as a function.
+ if (object->IsHeapObject() &&
+ HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+ return Handle<JSFunction>(
+ isolate->global_context()->call_as_constructor_delegate());
+ }
+
+ // If the Object doesn't have an instance-call handler we should
+ // throw a non-callable exception.
+ i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError(
+ "called_non_callable", i::HandleVector<i::Object>(&object, 1));
+ isolate->Throw(*error_obj);
+ *has_pending_exception = true;
+
+ return isolate->factory()->undefined_value();
+}
+
+
bool StackGuard::IsStackOverflow() {
ExecutionAccess access(isolate_);
return (thread_local_.jslimit_ != kInterruptLimit &&
@@ -272,7 +329,7 @@ void StackGuard::SetStackLimit(uintptr_t limit) {
ExecutionAccess access(isolate_);
// If the current limits are special (eg due to a pending interrupt) then
// leave them alone.
- uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(limit);
+ uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(isolate_, limit);
if (thread_local_.jslimit_ == thread_local_.real_jslimit_) {
thread_local_.jslimit_ = jslimit;
}
@@ -428,7 +485,7 @@ void StackGuard::ThreadLocal::Clear() {
}
-bool StackGuard::ThreadLocal::Initialize() {
+bool StackGuard::ThreadLocal::Initialize(Isolate* isolate) {
bool should_set_stack_limits = false;
if (real_climit_ == kIllegalLimit) {
// Takes the address of the limit variable in order to find out where
@@ -436,8 +493,8 @@ bool StackGuard::ThreadLocal::Initialize() {
const uintptr_t kLimitSize = FLAG_stack_size * KB;
uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize;
ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
- real_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
- jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
+ real_jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
+ jslimit_ = SimulatorStack::JsLimitFromCLimit(isolate, limit);
real_climit_ = limit;
climit_ = limit;
should_set_stack_limits = true;
@@ -456,9 +513,10 @@ void StackGuard::ClearThread(const ExecutionAccess& lock) {
void StackGuard::InitThread(const ExecutionAccess& lock) {
- if (thread_local_.Initialize()) isolate_->heap()->SetStackLimits();
- uintptr_t stored_limit =
- Isolate::CurrentPerIsolateThreadData()->stack_limit();
+ if (thread_local_.Initialize(isolate_)) isolate_->heap()->SetStackLimits();
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindOrAllocatePerThreadDataForThisThread();
+ uintptr_t stored_limit = per_thread->stack_limit();
// You should hold the ExecutionAccess lock when you call this.
if (stored_limit != 0) {
StackGuard::SetStackLimit(stored_limit);
@@ -681,13 +739,13 @@ static Object* RuntimePreempt() {
isolate->debug()->PreemptionWhileInDebugger();
} else {
// Perform preemption.
- v8::Unlocker unlocker;
+ v8::Unlocker unlocker(reinterpret_cast<v8::Isolate*>(isolate));
Thread::YieldCPU();
}
#else
{ // NOLINT
// Perform preemption.
- v8::Unlocker unlocker;
+ v8::Unlocker unlocker(reinterpret_cast<v8::Isolate*>(isolate));
Thread::YieldCPU();
}
#endif
diff --git a/src/execution.h b/src/execution.h
index d4b80d27..bb5f8045 100644
--- a/src/execution.h
+++ b/src/execution.h
@@ -28,6 +28,8 @@
#ifndef V8_EXECUTION_H_
#define V8_EXECUTION_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
@@ -51,7 +53,7 @@ class Execution : public AllStatic {
// *pending_exception tells whether the invoke resulted in
// a pending exception.
//
- static Handle<Object> Call(Handle<JSFunction> func,
+ static Handle<Object> Call(Handle<Object> callable,
Handle<Object> receiver,
int argc,
Object*** args,
@@ -138,10 +140,14 @@ class Execution : public AllStatic {
// Get a function delegate (or undefined) for the given non-function
// object. Used for support calling objects as functions.
static Handle<Object> GetFunctionDelegate(Handle<Object> object);
+ static Handle<Object> TryGetFunctionDelegate(Handle<Object> object,
+ bool* has_pending_exception);
// Get a function delegate (or undefined) for the given non-function
// object. Used for support calling objects as constructors.
static Handle<Object> GetConstructorDelegate(Handle<Object> object);
+ static Handle<Object> TryGetConstructorDelegate(Handle<Object> object,
+ bool* has_pending_exception);
};
@@ -252,7 +258,7 @@ class StackGuard {
void Clear();
// Returns true if the heap's stack limits should be set, false if not.
- bool Initialize();
+ bool Initialize(Isolate* isolate);
// The stack limit is split into a JavaScript and a C++ stack limit. These
// two are the same except when running on a simulator where the C++ and
diff --git a/src/extensions/experimental/collator.cc b/src/extensions/experimental/collator.cc
new file mode 100644
index 00000000..7d1a21dc
--- /dev/null
+++ b/src/extensions/experimental/collator.cc
@@ -0,0 +1,218 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "collator.h"
+
+#include "unicode/coll.h"
+#include "unicode/locid.h"
+#include "unicode/ucol.h"
+
+namespace v8 {
+namespace internal {
+
+v8::Persistent<v8::FunctionTemplate> Collator::collator_template_;
+
+icu::Collator* Collator::UnpackCollator(v8::Handle<v8::Object> obj) {
+ if (collator_template_->HasInstance(obj)) {
+ return static_cast<icu::Collator*>(obj->GetPointerFromInternalField(0));
+ }
+
+ return NULL;
+}
+
+void Collator::DeleteCollator(v8::Persistent<v8::Value> object, void* param) {
+ v8::Persistent<v8::Object> persistent_object =
+ v8::Persistent<v8::Object>::Cast(object);
+
+ // First delete the hidden C++ object.
+ // Unpacking should never return NULL here. That would only happen if
+ // this method is used as the weak callback for persistent handles not
+ // pointing to a collator.
+ delete UnpackCollator(persistent_object);
+
+ // Then dispose of the persistent handle to JS object.
+ persistent_object.Dispose();
+}
+
+// Throws a JavaScript exception.
+static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
+ // Returns undefined, and schedules an exception to be thrown.
+ return v8::ThrowException(v8::Exception::Error(
+ v8::String::New("Collator method called on an object "
+ "that is not a Collator.")));
+}
+
+// Extract a boolean option named in |option| and set it to |result|.
+// Return true if it's specified. Otherwise, return false.
+static bool ExtractBooleanOption(const v8::Local<v8::Object>& options,
+ const char* option,
+ bool* result) {
+ v8::HandleScope handle_scope;
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> value = options->Get(v8::String::New(option));
+ if (try_catch.HasCaught()) {
+ return false;
+ }
+ // No need to check if |value| is empty because it's taken care of
+ // by TryCatch above.
+ if (!value->IsUndefined() && !value->IsNull()) {
+ if (value->IsBoolean()) {
+ *result = value->BooleanValue();
+ return true;
+ }
+ }
+ return false;
+}
+
+// When there's an ICU error, throw a JavaScript error with |message|.
+static v8::Handle<v8::Value> ThrowExceptionForICUError(const char* message) {
+ return v8::ThrowException(v8::Exception::Error(v8::String::New(message)));
+}
+
+v8::Handle<v8::Value> Collator::CollatorCompare(const v8::Arguments& args) {
+ if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
+ return v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Two string arguments are required.")));
+ }
+
+ icu::Collator* collator = UnpackCollator(args.Holder());
+ if (!collator) {
+ return ThrowUnexpectedObjectError();
+ }
+
+ v8::String::Value string_value1(args[0]);
+ v8::String::Value string_value2(args[1]);
+ const UChar* string1 = reinterpret_cast<const UChar*>(*string_value1);
+ const UChar* string2 = reinterpret_cast<const UChar*>(*string_value2);
+ UErrorCode status = U_ZERO_ERROR;
+ UCollationResult result = collator->compare(
+ string1, string_value1.length(), string2, string_value2.length(), status);
+
+ if (U_FAILURE(status)) {
+ return ThrowExceptionForICUError(
+ "Unexpected failure in Collator.compare.");
+ }
+
+ return v8::Int32::New(result);
+}
+
+v8::Handle<v8::Value> Collator::JSCollator(const v8::Arguments& args) {
+ v8::HandleScope handle_scope;
+
+ if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsObject()) {
+ return v8::ThrowException(v8::Exception::SyntaxError(
+ v8::String::New("Locale and collation options are required.")));
+ }
+
+ v8::String::AsciiValue locale(args[0]);
+ icu::Locale icu_locale(*locale);
+
+ icu::Collator* collator = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ collator = icu::Collator::createInstance(icu_locale, status);
+
+ if (U_FAILURE(status)) {
+ delete collator;
+ return ThrowExceptionForICUError("Failed to create collator.");
+ }
+
+ v8::Local<v8::Object> options(args[1]->ToObject());
+
+ // Below, we change collation options that are explicitly specified
+ // by a caller in JavaScript. Otherwise, we don't touch because
+ // we don't want to change the locale-dependent default value.
+ // The three options below are very likely to have the same default
+ // across locales, but I haven't checked them all. Others we may add
+ // in the future have certainly locale-dependent default (e.g.
+ // caseFirst is upperFirst for Danish while is off for most other locales).
+
+ bool ignore_case, ignore_accents, numeric;
+
+ if (ExtractBooleanOption(options, "ignoreCase", &ignore_case)) {
+ collator->setAttribute(UCOL_CASE_LEVEL, ignore_case ? UCOL_OFF : UCOL_ON,
+ status);
+ if (U_FAILURE(status)) {
+ delete collator;
+ return ThrowExceptionForICUError("Failed to set ignoreCase.");
+ }
+ }
+
+ // Accents are taken into account with strength secondary or higher.
+ if (ExtractBooleanOption(options, "ignoreAccents", &ignore_accents)) {
+ if (!ignore_accents) {
+ collator->setStrength(icu::Collator::SECONDARY);
+ } else {
+ collator->setStrength(icu::Collator::PRIMARY);
+ }
+ }
+
+ if (ExtractBooleanOption(options, "numeric", &numeric)) {
+ collator->setAttribute(UCOL_NUMERIC_COLLATION,
+ numeric ? UCOL_ON : UCOL_OFF, status);
+ if (U_FAILURE(status)) {
+ delete collator;
+ return ThrowExceptionForICUError("Failed to set numeric sort option.");
+ }
+ }
+
+ if (collator_template_.IsEmpty()) {
+ v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New());
+ raw_template->SetClassName(v8::String::New("v8Locale.Collator"));
+
+ // Define internal field count on instance template.
+ v8::Local<v8::ObjectTemplate> object_template =
+ raw_template->InstanceTemplate();
+
+ // Set aside internal fields for icu collator.
+ object_template->SetInternalFieldCount(1);
+
+ // Define all of the prototype methods on prototype template.
+ v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate();
+ proto->Set(v8::String::New("compare"),
+ v8::FunctionTemplate::New(CollatorCompare));
+
+ collator_template_ =
+ v8::Persistent<v8::FunctionTemplate>::New(raw_template);
+ }
+
+ // Create an empty object wrapper.
+ v8::Local<v8::Object> local_object =
+ collator_template_->GetFunction()->NewInstance();
+ v8::Persistent<v8::Object> wrapper =
+ v8::Persistent<v8::Object>::New(local_object);
+
+ // Set collator as internal field of the resulting JS object.
+ wrapper->SetPointerInInternalField(0, collator);
+
+ // Make object handle weak so we can delete iterator once GC kicks in.
+ wrapper.MakeWeak(NULL, DeleteCollator);
+
+ return wrapper;
+}
+
+} } // namespace v8::internal
+
diff --git a/src/extensions/experimental/collator.h b/src/extensions/experimental/collator.h
new file mode 100644
index 00000000..10d6ffb5
--- /dev/null
+++ b/src/extensions/experimental/collator.h
@@ -0,0 +1,69 @@
+// Copyright 2011 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_EXTENSIONS_EXPERIMENTAL_COLLATOR_H
+#define V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H_
+
+#include <v8.h>
+
+#include "unicode/uversion.h"
+
+namespace U_ICU_NAMESPACE {
+class Collator;
+class UnicodeString;
+}
+
+namespace v8 {
+namespace internal {
+
+class Collator {
+ public:
+ static v8::Handle<v8::Value> JSCollator(const v8::Arguments& args);
+
+ // Helper methods for various bindings.
+
+ // Unpacks collator object from corresponding JavaScript object.
+ static icu::Collator* UnpackCollator(v8::Handle<v8::Object> obj);
+
+ // Release memory we allocated for the Collator once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteCollator(v8::Persistent<v8::Value> object, void* param);
+
+ // Compare two strings and returns -1, 0 and 1 depending on
+ // whether string1 is smaller than, equal to or larger than string2.
+ static v8::Handle<v8::Value> CollatorCompare(const v8::Arguments& args);
+
+ private:
+ Collator() {}
+
+ static v8::Persistent<v8::FunctionTemplate> collator_template_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EXTENSIONS_EXPERIMENTAL_COLLATOR
+
diff --git a/src/extensions/experimental/experimental.gyp b/src/extensions/experimental/experimental.gyp
index a8585fd7..2a7775ea 100644
--- a/src/extensions/experimental/experimental.gyp
+++ b/src/extensions/experimental/experimental.gyp
@@ -39,9 +39,17 @@
'sources': [
'break-iterator.cc',
'break-iterator.h',
+ 'collator.cc',
+ 'collator.h',
'i18n-extension.cc',
'i18n-extension.h',
- '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc',
+ 'i18n-locale.cc',
+ 'i18n-locale.h',
+ 'i18n-utils.cc',
+ 'i18n-utils.h',
+ 'language-matcher.cc',
+ 'language-matcher.h',
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc',
],
'include_dirs': [
'<(icu_src_dir)/public/common',
@@ -49,7 +57,7 @@
],
'dependencies': [
'<(icu_src_dir)/icu.gyp:*',
- 'js2c_i18n#host',
+ 'js2c_i18n#host',
'../../../tools/gyp/v8.gyp:v8',
],
},
@@ -59,28 +67,27 @@
'toolsets': ['host'],
'variables': {
'library_files': [
- 'i18n.js'
- ],
+ 'i18n.js'
+ ],
},
'actions': [
{
- 'action_name': 'js2c_i18n',
- 'inputs': [
- '../../../tools/js2c.py',
- '<@(library_files)',
- ],
- 'outputs': [
- '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc',
- '<(SHARED_INTERMEDIATE_DIR)/i18n-js-empty.cc'
- ],
- 'action': [
- 'python',
- '../../../tools/js2c.py',
- '<@(_outputs)',
- 'I18N',
- '<@(library_files)'
- ],
- },
+ 'action_name': 'js2c_i18n',
+ 'inputs': [
+ '../../../tools/js2c.py',
+ '<@(library_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../../tools/js2c.py',
+ '<@(_outputs)',
+ 'I18N',
+ '<@(library_files)'
+ ],
+ },
],
},
], # targets
diff --git a/src/extensions/experimental/i18n-extension.cc b/src/extensions/experimental/i18n-extension.cc
index 6e3ab15f..88c609ea 100644
--- a/src/extensions/experimental/i18n-extension.cc
+++ b/src/extensions/experimental/i18n-extension.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -27,13 +27,10 @@
#include "i18n-extension.h"
-#include <algorithm>
-#include <string>
-
#include "break-iterator.h"
+#include "collator.h"
+#include "i18n-locale.h"
#include "natives.h"
-#include "unicode/locid.h"
-#include "unicode/uloc.h"
namespace v8 {
namespace internal {
@@ -57,166 +54,16 @@ I18NExtension::I18NExtension()
v8::Handle<v8::FunctionTemplate> I18NExtension::GetNativeFunction(
v8::Handle<v8::String> name) {
if (name->Equals(v8::String::New("NativeJSLocale"))) {
- return v8::FunctionTemplate::New(JSLocale);
- } else if (name->Equals(v8::String::New("NativeJSAvailableLocales"))) {
- return v8::FunctionTemplate::New(JSAvailableLocales);
- } else if (name->Equals(v8::String::New("NativeJSMaximizedLocale"))) {
- return v8::FunctionTemplate::New(JSMaximizedLocale);
- } else if (name->Equals(v8::String::New("NativeJSMinimizedLocale"))) {
- return v8::FunctionTemplate::New(JSMinimizedLocale);
- } else if (name->Equals(v8::String::New("NativeJSDisplayLanguage"))) {
- return v8::FunctionTemplate::New(JSDisplayLanguage);
- } else if (name->Equals(v8::String::New("NativeJSDisplayScript"))) {
- return v8::FunctionTemplate::New(JSDisplayScript);
- } else if (name->Equals(v8::String::New("NativeJSDisplayRegion"))) {
- return v8::FunctionTemplate::New(JSDisplayRegion);
- } else if (name->Equals(v8::String::New("NativeJSDisplayName"))) {
- return v8::FunctionTemplate::New(JSDisplayName);
+ return v8::FunctionTemplate::New(I18NLocale::JSLocale);
} else if (name->Equals(v8::String::New("NativeJSBreakIterator"))) {
return v8::FunctionTemplate::New(BreakIterator::JSBreakIterator);
+ } else if (name->Equals(v8::String::New("NativeJSCollator"))) {
+ return v8::FunctionTemplate::New(Collator::JSCollator);
}
return v8::Handle<v8::FunctionTemplate>();
}
-v8::Handle<v8::Value> I18NExtension::JSLocale(const v8::Arguments& args) {
- // TODO(cira): Fetch browser locale. Accept en-US as good default for now.
- // We could possibly pass browser locale as a parameter in the constructor.
- std::string locale_name("en-US");
- if (args.Length() == 1 && args[0]->IsString()) {
- locale_name = *v8::String::Utf8Value(args[0]->ToString());
- }
-
- v8::Local<v8::Object> locale = v8::Object::New();
- locale->Set(v8::String::New("locale"), v8::String::New(locale_name.c_str()));
-
- icu::Locale icu_locale(locale_name.c_str());
-
- const char* language = icu_locale.getLanguage();
- locale->Set(v8::String::New("language"), v8::String::New(language));
-
- const char* script = icu_locale.getScript();
- if (strlen(script)) {
- locale->Set(v8::String::New("script"), v8::String::New(script));
- }
-
- const char* region = icu_locale.getCountry();
- if (strlen(region)) {
- locale->Set(v8::String::New("region"), v8::String::New(region));
- }
-
- return locale;
-}
-
-// TODO(cira): Filter out locales that Chrome doesn't support.
-v8::Handle<v8::Value> I18NExtension::JSAvailableLocales(
- const v8::Arguments& args) {
- v8::Local<v8::Array> all_locales = v8::Array::New();
-
- int count = 0;
- const icu::Locale* icu_locales = icu::Locale::getAvailableLocales(count);
- for (int i = 0; i < count; ++i) {
- all_locales->Set(i, v8::String::New(icu_locales[i].getName()));
- }
-
- return all_locales;
-}
-
-// Use - as tag separator, not _ that ICU uses.
-static std::string NormalizeLocale(const std::string& locale) {
- std::string result(locale);
- // TODO(cira): remove STL dependency.
- std::replace(result.begin(), result.end(), '_', '-');
- return result;
-}
-
-v8::Handle<v8::Value> I18NExtension::JSMaximizedLocale(
- const v8::Arguments& args) {
- if (!args.Length() || !args[0]->IsString()) {
- return v8::Undefined();
- }
-
- UErrorCode status = U_ZERO_ERROR;
- std::string locale_name = *v8::String::Utf8Value(args[0]->ToString());
- char max_locale[ULOC_FULLNAME_CAPACITY];
- uloc_addLikelySubtags(locale_name.c_str(), max_locale,
- sizeof(max_locale), &status);
- if (U_FAILURE(status)) {
- return v8::Undefined();
- }
-
- return v8::String::New(NormalizeLocale(max_locale).c_str());
-}
-
-v8::Handle<v8::Value> I18NExtension::JSMinimizedLocale(
- const v8::Arguments& args) {
- if (!args.Length() || !args[0]->IsString()) {
- return v8::Undefined();
- }
-
- UErrorCode status = U_ZERO_ERROR;
- std::string locale_name = *v8::String::Utf8Value(args[0]->ToString());
- char min_locale[ULOC_FULLNAME_CAPACITY];
- uloc_minimizeSubtags(locale_name.c_str(), min_locale,
- sizeof(min_locale), &status);
- if (U_FAILURE(status)) {
- return v8::Undefined();
- }
-
- return v8::String::New(NormalizeLocale(min_locale).c_str());
-}
-
-// Common code for JSDisplayXXX methods.
-static v8::Handle<v8::Value> GetDisplayItem(const v8::Arguments& args,
- const std::string& item) {
- if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
- return v8::Undefined();
- }
-
- std::string base_locale = *v8::String::Utf8Value(args[0]->ToString());
- icu::Locale icu_locale(base_locale.c_str());
- icu::Locale display_locale =
- icu::Locale(*v8::String::Utf8Value(args[1]->ToString()));
- icu::UnicodeString result;
- if (item == "language") {
- icu_locale.getDisplayLanguage(display_locale, result);
- } else if (item == "script") {
- icu_locale.getDisplayScript(display_locale, result);
- } else if (item == "region") {
- icu_locale.getDisplayCountry(display_locale, result);
- } else if (item == "name") {
- icu_locale.getDisplayName(display_locale, result);
- } else {
- return v8::Undefined();
- }
-
- if (result.length()) {
- return v8::String::New(
- reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length());
- }
-
- return v8::Undefined();
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayLanguage(
- const v8::Arguments& args) {
- return GetDisplayItem(args, "language");
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayScript(
- const v8::Arguments& args) {
- return GetDisplayItem(args, "script");
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayRegion(
- const v8::Arguments& args) {
- return GetDisplayItem(args, "region");
-}
-
-v8::Handle<v8::Value> I18NExtension::JSDisplayName(const v8::Arguments& args) {
- return GetDisplayItem(args, "name");
-}
-
I18NExtension* I18NExtension::get() {
if (!extension_) {
extension_ = new I18NExtension();
diff --git a/src/extensions/experimental/i18n-extension.h b/src/extensions/experimental/i18n-extension.h
index 54c973f7..b4dc7c35 100644
--- a/src/extensions/experimental/i18n-extension.h
+++ b/src/extensions/experimental/i18n-extension.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -41,16 +41,6 @@ class I18NExtension : public v8::Extension {
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
v8::Handle<v8::String> name);
- // Implementations of window.Locale methods.
- static v8::Handle<v8::Value> JSLocale(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSAvailableLocales(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSMaximizedLocale(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSMinimizedLocale(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayLanguage(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayScript(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayRegion(const v8::Arguments& args);
- static v8::Handle<v8::Value> JSDisplayName(const v8::Arguments& args);
-
// V8 code prefers Register, while Chrome and WebKit use get kind of methods.
static void Register();
static I18NExtension* get();
diff --git a/src/extensions/experimental/i18n-locale.cc b/src/extensions/experimental/i18n-locale.cc
new file mode 100644
index 00000000..cf178120
--- /dev/null
+++ b/src/extensions/experimental/i18n-locale.cc
@@ -0,0 +1,112 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "i18n-locale.h"
+
+#include "i18n-utils.h"
+#include "language-matcher.h"
+#include "unicode/locid.h"
+#include "unicode/uloc.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+const char* const I18NLocale::kLocaleID = "localeID";
+const char* const I18NLocale::kRegionID = "regionID";
+const char* const I18NLocale::kICULocaleID = "icuLocaleID";
+
+v8::Handle<v8::Value> I18NLocale::JSLocale(const v8::Arguments& args) {
+ v8::HandleScope handle_scope;
+
+ if (args.Length() != 1 || !args[0]->IsObject()) {
+ return v8::Undefined();
+ }
+
+ v8::Local<v8::Object> settings = args[0]->ToObject();
+
+ // Get best match for locale.
+ v8::TryCatch try_catch;
+ v8::Handle<v8::Value> locale_id = settings->Get(v8::String::New(kLocaleID));
+ if (try_catch.HasCaught()) {
+ return v8::Undefined();
+ }
+
+ LocaleIDMatch result;
+ if (locale_id->IsArray()) {
+ LanguageMatcher::GetBestMatchForPriorityList(
+ v8::Handle<v8::Array>::Cast(locale_id), &result);
+ } else if (locale_id->IsString()) {
+ LanguageMatcher::GetBestMatchForString(locale_id->ToString(), &result);
+ } else {
+ LanguageMatcher::GetBestMatchForString(v8::String::New(""), &result);
+ }
+
+ // Get best match for region.
+ char region_id[ULOC_COUNTRY_CAPACITY];
+ I18NUtils::StrNCopy(region_id, ULOC_COUNTRY_CAPACITY, "");
+
+ v8::Handle<v8::Value> region = settings->Get(v8::String::New(kRegionID));
+ if (try_catch.HasCaught()) {
+ return v8::Undefined();
+ }
+
+ if (!GetBestMatchForRegionID(result.icu_id, region, region_id)) {
+ // Set region id to empty string because region couldn't be inferred.
+ I18NUtils::StrNCopy(region_id, ULOC_COUNTRY_CAPACITY, "");
+ }
+
+ // Build JavaScript object that contains bcp and icu locale ID and region ID.
+ v8::Handle<v8::Object> locale = v8::Object::New();
+ locale->Set(v8::String::New(kLocaleID), v8::String::New(result.bcp47_id));
+ locale->Set(v8::String::New(kICULocaleID), v8::String::New(result.icu_id));
+ locale->Set(v8::String::New(kRegionID), v8::String::New(region_id));
+
+ return handle_scope.Close(locale);
+}
+
+bool I18NLocale::GetBestMatchForRegionID(
+ const char* locale_id, v8::Handle<v8::Value> region_id, char* result) {
+ if (region_id->IsString() && region_id->ToString()->Length() != 0) {
+ icu::Locale user_locale(
+ icu::Locale("und", *v8::String::Utf8Value(region_id->ToString())));
+ I18NUtils::StrNCopy(
+ result, ULOC_COUNTRY_CAPACITY, user_locale.getCountry());
+ return true;
+ }
+ // Maximize locale_id to infer the region (e.g. expand "de" to "de-Latn-DE"
+ // and grab "DE" from the result).
+ UErrorCode status = U_ZERO_ERROR;
+ char maximized_locale[ULOC_FULLNAME_CAPACITY];
+ uloc_addLikelySubtags(
+ locale_id, maximized_locale, ULOC_FULLNAME_CAPACITY, &status);
+ uloc_getCountry(maximized_locale, result, ULOC_COUNTRY_CAPACITY, &status);
+
+ return !U_FAILURE(status);
+}
+
+} } // namespace v8::internal
diff --git a/src/mips/register-allocator-mips.cc b/src/extensions/experimental/i18n-locale.h
index 2c5d61be..053886b3 100644
--- a/src/mips/register-allocator-mips.cc
+++ b/src/extensions/experimental/i18n-locale.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,39 +25,36 @@
// (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 "v8.h"
+#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_
+#define V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_
-#if defined(V8_TARGET_ARCH_MIPS)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
+#include <v8.h>
namespace v8 {
namespace internal {
-// -------------------------------------------------------------------------
-// Result implementation.
-
-void Result::ToRegister() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void Result::ToRegister(Register target) {
- UNIMPLEMENTED_MIPS();
-}
-
+class I18NLocale {
+ public:
+ I18NLocale() {}
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
+ // Implementations of window.Locale methods.
+ static v8::Handle<v8::Value> JSLocale(const v8::Arguments& args);
-Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() {
- // No byte registers on MIPS.
- UNREACHABLE();
- return Result();
-}
+ // Infers region id given the locale id, or uses user specified region id.
+ // Result is canonicalized.
+ // Returns status of ICU operation (maximizing locale or get region call).
+ static bool GetBestMatchForRegionID(
+ const char* locale_id, v8::Handle<v8::Value> regions, char* result);
+ private:
+ // Key name for localeID parameter.
+ static const char* const kLocaleID;
+ // Key name for regionID parameter.
+ static const char* const kRegionID;
+ // Key name for the icuLocaleID result.
+ static const char* const kICULocaleID;
+};
} } // namespace v8::internal
-#endif // V8_TARGET_ARCH_MIPS
+#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_LOCALE_H_
diff --git a/src/frame-element.cc b/src/extensions/experimental/i18n-utils.cc
index f6299007..a82c8eb9 100644
--- a/src/frame-element.cc
+++ b/src/extensions/experimental/i18n-utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,13 +25,19 @@
// (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 "v8.h"
+#include "i18n-utils.h"
-#include "frame-element.h"
-#include "zone-inl.h"
+#include <string.h>
namespace v8 {
namespace internal {
+// static
+void I18NUtils::StrNCopy(char* dest, int length, const char* src) {
+ if (!dest || !src) return;
+
+ strncpy(dest, src, length);
+ dest[length - 1] = '\0';
+}
} } // namespace v8::internal
diff --git a/src/mips/codegen-mips-inl.h b/src/extensions/experimental/i18n-utils.h
index be9ae9ed..77027086 100644
--- a/src/mips/codegen-mips-inl.h
+++ b/src/extensions/experimental/i18n-utils.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,40 +25,25 @@
// (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_MIPS_CODEGEN_MIPS_INL_H_
-#define V8_MIPS_CODEGEN_MIPS_INL_H_
-
-#include "virtual-frame-mips.h"
+#ifndef V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_
+#define V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm_)
-
-// Platform-specific inline functions.
-
-void DeferredCode::Jump() {
- __ b(&entry_label_);
- __ nop();
-}
-
+class I18NUtils {
+ public:
+ // Safe string copy. Null terminates the destination. Copies at most
+ // (length - 1) bytes.
+ // We can't use snprintf since it's not supported on all relevant platforms.
+ // We can't use OS::SNPrintF, it's only for internal code.
+ // TODO(cira): Find a way to use OS::SNPrintF instead.
+ static void StrNCopy(char* dest, int length, const char* src);
-// Note: this has been hacked for submisson. Mips branches require two
-// additional operands: Register src1, const Operand& src2.
-void DeferredCode::Branch(Condition cond) {
- __ Branch(&entry_label_, cond, zero_reg, Operand(0));
-}
-
-
-void Reference::GetValueAndSpill() {
- GetValue();
-}
-
-
-#undef __
+ private:
+ I18NUtils() {}
+};
} } // namespace v8::internal
-#endif // V8_MIPS_CODEGEN_MIPS_INL_H_
-
+#endif // V8_EXTENSIONS_EXPERIMENTAL_I18N_UTILS_H_
diff --git a/src/extensions/experimental/i18n.js b/src/extensions/experimental/i18n.js
index baf38591..0fa7ae72 100644
--- a/src/extensions/experimental/i18n.js
+++ b/src/extensions/experimental/i18n.js
@@ -25,70 +25,71 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// TODO(cira): Remove v8 prefix from v8Locale once we have stable API.
-v8Locale = function(optLocale) {
+// TODO(cira): Rename v8Locale into LocaleInfo once we have stable API.
+/**
+ * LocaleInfo class is an aggregate class of all i18n API calls.
+ * @param {Object} settings - localeID and regionID to create LocaleInfo from.
+ * {Array.<string>|string} settings.localeID -
+ * Unicode identifier of the locale.
+ * See http://unicode.org/reports/tr35/#BCP_47_Conformance
+ * {string} settings.regionID - ISO3166 region ID with addition of
+ * invalid, undefined and reserved region codes.
+ * @constructor
+ */
+v8Locale = function(settings) {
native function NativeJSLocale();
- var properties = NativeJSLocale(optLocale);
- this.locale = properties.locale;
- this.language = properties.language;
- this.script = properties.script;
- this.region = properties.region;
-};
-
-v8Locale.availableLocales = function() {
- native function NativeJSAvailableLocales();
- return NativeJSAvailableLocales();
-};
-
-v8Locale.prototype.maximizedLocale = function() {
- native function NativeJSMaximizedLocale();
- return new v8Locale(NativeJSMaximizedLocale(this.locale));
-};
-
-v8Locale.prototype.minimizedLocale = function() {
- native function NativeJSMinimizedLocale();
- return new v8Locale(NativeJSMinimizedLocale(this.locale));
-};
-v8Locale.prototype.displayLocale_ = function(displayLocale) {
- var result = this.locale;
- if (displayLocale !== undefined) {
- result = displayLocale.locale;
+ // Assume user wanted to do v8Locale("sr");
+ if (typeof(settings) === "string") {
+ settings = {'localeID': settings};
}
- return result;
-};
-
-v8Locale.prototype.displayLanguage = function(optDisplayLocale) {
- var displayLocale = this.displayLocale_(optDisplayLocale);
- native function NativeJSDisplayLanguage();
- return NativeJSDisplayLanguage(this.locale, displayLocale);
-};
-v8Locale.prototype.displayScript = function(optDisplayLocale) {
- var displayLocale = this.displayLocale_(optDisplayLocale);
- native function NativeJSDisplayScript();
- return NativeJSDisplayScript(this.locale, displayLocale);
-};
+ var properties = NativeJSLocale(
+ v8Locale.createSettingsOrDefault_(settings, {'localeID': 'root'}));
-v8Locale.prototype.displayRegion = function(optDisplayLocale) {
- var displayLocale = this.displayLocale_(optDisplayLocale);
- native function NativeJSDisplayRegion();
- return NativeJSDisplayRegion(this.locale, displayLocale);
+ // Keep the resolved ICU locale ID around to avoid resolving localeID to
+ // ICU locale ID every time BreakIterator, Collator and so forth are called.
+ this.__icuLocaleID__ = properties.icuLocaleID;
+ this.options = {'localeID': properties.localeID,
+ 'regionID': properties.regionID};
};
-v8Locale.prototype.displayName = function(optDisplayLocale) {
- var displayLocale = this.displayLocale_(optDisplayLocale);
- native function NativeJSDisplayName();
- return NativeJSDisplayName(this.locale, displayLocale);
+/**
+ * Clones existing locale with possible overrides for some of the options.
+ * @param {!Object} settings - overrides for current locale settings.
+ * @returns {Object} - new LocaleInfo object.
+ */
+v8Locale.prototype.derive = function(settings) {
+ return new v8Locale(
+ v8Locale.createSettingsOrDefault_(settings, this.options));
};
+/**
+ * v8BreakIterator class implements locale aware segmenatation.
+ * It is not part of EcmaScript proposal.
+ * @param {Object} locale - locale object to pass to break
+ * iterator implementation.
+ * @param {string} type - type of segmenatation:
+ * - character
+ * - word
+ * - sentence
+ * - line
+ * @constructor
+ */
v8Locale.v8BreakIterator = function(locale, type) {
native function NativeJSBreakIterator();
- var iterator = NativeJSBreakIterator(locale, type);
+
+ locale = v8Locale.createLocaleOrDefault_(locale);
+ // BCP47 ID would work in this case, but we use ICU locale for consistency.
+ var iterator = NativeJSBreakIterator(locale.__icuLocaleID__, type);
iterator.type = type;
return iterator;
};
+/**
+ * Type of the break we encountered during previous iteration.
+ * @type{Enum}
+ */
v8Locale.v8BreakIterator.BreakType = {
'unknown': -1,
'none': 0,
@@ -98,6 +99,82 @@ v8Locale.v8BreakIterator.BreakType = {
'ideo': 400
};
+/**
+ * Creates new v8BreakIterator based on current locale.
+ * @param {string} - type of segmentation. See constructor.
+ * @returns {Object} - new v8BreakIterator object.
+ */
v8Locale.prototype.v8CreateBreakIterator = function(type) {
- return new v8Locale.v8BreakIterator(this.locale, type);
+ return new v8Locale.v8BreakIterator(this, type);
+};
+
+// TODO(jungshik): Set |collator.options| to actually recognized / resolved
+// values.
+/**
+ * Collator class implements locale-aware sort.
+ * @param {Object} locale - locale object to pass to collator implementation.
+ * @param {Object} settings - collation flags:
+ * - ignoreCase
+ * - ignoreAccents
+ * - numeric
+ * @constructor
+ */
+v8Locale.Collator = function(locale, settings) {
+ native function NativeJSCollator();
+
+ locale = v8Locale.createLocaleOrDefault_(locale);
+ var collator = NativeJSCollator(
+ locale.__icuLocaleID__, v8Locale.createSettingsOrDefault_(settings, {}));
+ return collator;
+};
+
+/**
+ * Creates new Collator based on current locale.
+ * @param {Object} - collation flags. See constructor.
+ * @returns {Object} - new v8BreakIterator object.
+ */
+v8Locale.prototype.createCollator = function(settings) {
+ return new v8Locale.Collator(this, settings);
+};
+
+/**
+ * Merges user settings and defaults.
+ * Settings that are not of object type are rejected.
+ * Actual property values are not validated, but whitespace is trimmed if they
+ * are strings.
+ * @param {!Object} settings - user provided settings.
+ * @param {!Object} defaults - default values for this type of settings.
+ * @returns {Object} - valid settings object.
+ */
+v8Locale.createSettingsOrDefault_ = function(settings, defaults) {
+ if (!settings || typeof(settings) !== 'object' ) {
+ return defaults;
+ }
+ for (var key in defaults) {
+ if (!settings.hasOwnProperty(key)) {
+ settings[key] = defaults[key];
+ }
+ }
+ // Clean up values, like trimming whitespace.
+ for (var key in settings) {
+ if (typeof(settings[key]) === "string") {
+ settings[key] = settings[key].trim();
+ }
+ }
+
+ return settings;
+};
+
+/**
+ * If locale is valid (defined and of v8Locale type) we return it. If not
+ * we create default locale and return it.
+ * @param {!Object} locale - user provided locale.
+ * @returns {Object} - v8Locale object.
+ */
+v8Locale.createLocaleOrDefault_ = function(locale) {
+ if (!locale || !(locale instanceof v8Locale)) {
+ return new v8Locale();
+ } else {
+ return locale;
+ }
};
diff --git a/src/extensions/experimental/language-matcher.cc b/src/extensions/experimental/language-matcher.cc
new file mode 100644
index 00000000..385ebfff
--- /dev/null
+++ b/src/extensions/experimental/language-matcher.cc
@@ -0,0 +1,251 @@
+// Copyright 2011 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.
+
+// TODO(cira): Remove LanguageMatcher from v8 when ICU implements
+// language matching API.
+
+#include "language-matcher.h"
+
+#include "i18n-utils.h"
+#include "unicode/datefmt.h" // For getAvailableLocales
+#include "unicode/locid.h"
+#include "unicode/uloc.h"
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+const unsigned int LanguageMatcher::kLanguageWeight = 75;
+const unsigned int LanguageMatcher::kScriptWeight = 20;
+const unsigned int LanguageMatcher::kRegionWeight = 5;
+const unsigned int LanguageMatcher::kThreshold = 50;
+const unsigned int LanguageMatcher::kPositionBonus = 1;
+const char* const LanguageMatcher::kDefaultLocale = "root";
+
+static const char* GetLanguageException(const char*);
+static bool BCP47ToICUFormat(const char*, char*);
+static int CompareLocaleSubtags(const char*, const char*);
+static bool BuildLocaleName(const char*, const char*, LocaleIDMatch*);
+
+LocaleIDMatch::LocaleIDMatch()
+ : score(-1) {
+ I18NUtils::StrNCopy(
+ bcp47_id, ULOC_FULLNAME_CAPACITY, LanguageMatcher::kDefaultLocale);
+
+ I18NUtils::StrNCopy(
+ icu_id, ULOC_FULLNAME_CAPACITY, LanguageMatcher::kDefaultLocale);
+}
+
+LocaleIDMatch& LocaleIDMatch::operator=(const LocaleIDMatch& rhs) {
+ I18NUtils::StrNCopy(this->bcp47_id, ULOC_FULLNAME_CAPACITY, rhs.bcp47_id);
+ I18NUtils::StrNCopy(this->icu_id, ULOC_FULLNAME_CAPACITY, rhs.icu_id);
+ this->score = rhs.score;
+
+ return *this;
+}
+
+// static
+void LanguageMatcher::GetBestMatchForPriorityList(
+ v8::Handle<v8::Array> locales, LocaleIDMatch* result) {
+ v8::HandleScope handle_scope;
+
+ unsigned int position_bonus = locales->Length() * kPositionBonus;
+
+ int max_score = 0;
+ LocaleIDMatch match;
+ for (unsigned int i = 0; i < locales->Length(); ++i) {
+ position_bonus -= kPositionBonus;
+
+ v8::TryCatch try_catch;
+ v8::Local<v8::Value> locale_id = locales->Get(v8::Integer::New(i));
+
+ // Return default if exception is raised when reading parameter.
+ if (try_catch.HasCaught()) break;
+
+ // JavaScript arrays can be heterogenous so check each item
+ // if it's a string.
+ if (!locale_id->IsString()) continue;
+
+ if (!CompareToSupportedLocaleIDList(locale_id->ToString(), &match)) {
+ continue;
+ }
+
+ // Skip items under threshold.
+ if (match.score < kThreshold) continue;
+
+ match.score += position_bonus;
+ if (match.score > max_score) {
+ *result = match;
+
+ max_score = match.score;
+ }
+ }
+}
+
+// static
+void LanguageMatcher::GetBestMatchForString(
+ v8::Handle<v8::String> locale, LocaleIDMatch* result) {
+ LocaleIDMatch match;
+
+ if (CompareToSupportedLocaleIDList(locale, &match) &&
+ match.score >= kThreshold) {
+ *result = match;
+ }
+}
+
+// static
+bool LanguageMatcher::CompareToSupportedLocaleIDList(
+ v8::Handle<v8::String> locale_id, LocaleIDMatch* result) {
+ static int32_t available_count = 0;
+ // Depending on how ICU data is built, locales returned by
+ // Locale::getAvailableLocale() are not guaranteed to support DateFormat,
+ // Collation and other services. We can call getAvailableLocale() of all the
+ // services we want to support and take the intersection of them all, but
+ // using DateFormat::getAvailableLocales() should suffice.
+ // TODO(cira): Maybe make this thread-safe?
+ static const icu::Locale* available_locales =
+ icu::DateFormat::getAvailableLocales(available_count);
+
+ // Skip this locale_id if it's not in ASCII.
+ static LocaleIDMatch default_match;
+ v8::String::AsciiValue ascii_value(locale_id);
+ if (*ascii_value == NULL) return false;
+
+ char locale[ULOC_FULLNAME_CAPACITY];
+ if (!BCP47ToICUFormat(*ascii_value, locale)) return false;
+
+ icu::Locale input_locale(locale);
+
+ // Position of the best match locale in list of available locales.
+ int position = -1;
+ const char* language = GetLanguageException(input_locale.getLanguage());
+ const char* script = input_locale.getScript();
+ const char* region = input_locale.getCountry();
+ for (int32_t i = 0; i < available_count; ++i) {
+ int current_score = 0;
+ int sign =
+ CompareLocaleSubtags(language, available_locales[i].getLanguage());
+ current_score += sign * kLanguageWeight;
+
+ sign = CompareLocaleSubtags(script, available_locales[i].getScript());
+ current_score += sign * kScriptWeight;
+
+ sign = CompareLocaleSubtags(region, available_locales[i].getCountry());
+ current_score += sign * kRegionWeight;
+
+ if (current_score >= kThreshold && current_score > result->score) {
+ result->score = current_score;
+ position = i;
+ }
+ }
+
+ // Didn't find any good matches so use defaults.
+ if (position == -1) return false;
+
+ return BuildLocaleName(available_locales[position].getBaseName(),
+ input_locale.getName(), result);
+}
+
+// For some unsupported language subtags it is better to fallback to related
+// language that is supported than to default.
+static const char* GetLanguageException(const char* language) {
+ // Serbo-croatian to Serbian.
+ if (!strcmp(language, "sh")) return "sr";
+
+ // Norweigan to Norweiaan to Norwegian Bokmal.
+ if (!strcmp(language, "no")) return "nb";
+
+ // Moldavian to Romanian.
+ if (!strcmp(language, "mo")) return "ro";
+
+ // Tagalog to Filipino.
+ if (!strcmp(language, "tl")) return "fil";
+
+ return language;
+}
+
+// Converts user input from BCP47 locale id format to ICU compatible format.
+// Returns false if uloc_forLanguageTag call fails or if extension is too long.
+static bool BCP47ToICUFormat(const char* locale_id, char* result) {
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t locale_size = 0;
+
+ char locale[ULOC_FULLNAME_CAPACITY];
+ I18NUtils::StrNCopy(locale, ULOC_FULLNAME_CAPACITY, locale_id);
+
+ // uloc_forLanguageTag has a bug where long extension can crash the code.
+ // We need to check if extension part of language id conforms to the length.
+ // ICU bug: http://bugs.icu-project.org/trac/ticket/8519
+ const char* extension = strstr(locale_id, "-u-");
+ if (extension != NULL &&
+ strlen(extension) > ULOC_KEYWORD_AND_VALUES_CAPACITY) {
+ // Truncate to get non-crashing string, but still preserve base language.
+ int base_length = strlen(locale_id) - strlen(extension);
+ locale[base_length] = '\0';
+ }
+
+ uloc_forLanguageTag(locale, result, ULOC_FULLNAME_CAPACITY,
+ &locale_size, &status);
+ return !U_FAILURE(status);
+}
+
+// Compares locale id subtags.
+// Returns 1 for match or -1 for mismatch.
+static int CompareLocaleSubtags(const char* lsubtag, const char* rsubtag) {
+ return strcmp(lsubtag, rsubtag) == 0 ? 1 : -1;
+}
+
+// Builds a BCP47 compliant locale id from base name of matched locale and
+// full user specified locale.
+// Returns false if uloc_toLanguageTag failed to convert locale id.
+// Example:
+// base_name of matched locale (ICU ID): de_DE
+// input_locale_name (ICU ID): de_AT@collation=phonebk
+// result (ICU ID): de_DE@collation=phonebk
+// result (BCP47 ID): de-DE-u-co-phonebk
+static bool BuildLocaleName(const char* base_name,
+ const char* input_locale_name,
+ LocaleIDMatch* result) {
+ I18NUtils::StrNCopy(result->icu_id, ULOC_LANG_CAPACITY, base_name);
+
+ // Get extensions (if any) from the original locale.
+ const char* extension = strchr(input_locale_name, ULOC_KEYWORD_SEPARATOR);
+ if (extension != NULL) {
+ I18NUtils::StrNCopy(result->icu_id + strlen(base_name),
+ ULOC_KEYWORD_AND_VALUES_CAPACITY, extension);
+ } else {
+ I18NUtils::StrNCopy(result->icu_id, ULOC_LANG_CAPACITY, base_name);
+ }
+
+ // Convert ICU locale name into BCP47 format.
+ UErrorCode status = U_ZERO_ERROR;
+ uloc_toLanguageTag(result->icu_id, result->bcp47_id,
+ ULOC_FULLNAME_CAPACITY, false, &status);
+ return !U_FAILURE(status);
+}
+
+} } // namespace v8::internal
diff --git a/src/extensions/experimental/language-matcher.h b/src/extensions/experimental/language-matcher.h
new file mode 100644
index 00000000..b5336a2f
--- /dev/null
+++ b/src/extensions/experimental/language-matcher.h
@@ -0,0 +1,95 @@
+// Copyright 2011 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_EXTENSIONS_EXPERIMENTAL_LANGUAGE_MATCHER_H_
+#define V8_EXTENSIONS_EXPERIMENTAL_LANGUAGE_MATCHER_H_
+
+#include <v8.h>
+
+#include "unicode/uloc.h"
+
+namespace v8 {
+namespace internal {
+
+struct LocaleIDMatch {
+ LocaleIDMatch();
+
+ LocaleIDMatch& operator=(const LocaleIDMatch& rhs);
+
+ // Bcp47 locale id - "de-Latn-DE-u-co-phonebk".
+ char bcp47_id[ULOC_FULLNAME_CAPACITY];
+
+ // ICU locale id - "de_Latn_DE@collation=phonebk".
+ char icu_id[ULOC_FULLNAME_CAPACITY];
+
+ // Score for this locale.
+ int score;
+};
+
+class LanguageMatcher {
+ public:
+ // Default locale.
+ static const char* const kDefaultLocale;
+
+ // Finds best supported locale for a given a list of locale identifiers.
+ // It preserves the extension for the locale id.
+ static void GetBestMatchForPriorityList(
+ v8::Handle<v8::Array> locale_list, LocaleIDMatch* result);
+
+ // Finds best supported locale for a single locale identifier.
+ // It preserves the extension for the locale id.
+ static void GetBestMatchForString(
+ v8::Handle<v8::String> locale_id, LocaleIDMatch* result);
+
+ private:
+ // If langauge subtags match add this amount to the score.
+ static const unsigned int kLanguageWeight;
+
+ // If script subtags match add this amount to the score.
+ static const unsigned int kScriptWeight;
+
+ // If region subtags match add this amount to the score.
+ static const unsigned int kRegionWeight;
+
+ // LocaleID match score has to be over this number to accept the match.
+ static const unsigned int kThreshold;
+
+ // For breaking ties in priority queue.
+ static const unsigned int kPositionBonus;
+
+ LanguageMatcher();
+
+ // Compares locale_id to the supported list of locales and returns best
+ // match.
+ // Returns false if it fails to convert locale id from ICU to BCP47 format.
+ static bool CompareToSupportedLocaleIDList(v8::Handle<v8::String> locale_id,
+ LocaleIDMatch* result);
+};
+
+} } // namespace v8::internal
+
+#endif // V8_EXTENSIONS_EXPERIMENTAL_LANGUAGE_MATCHER_H_
diff --git a/src/factory.cc b/src/factory.cc
index 7dee66f6..06d1655f 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -111,12 +111,31 @@ Handle<String> Factory::LookupSymbol(Vector<const char> string) {
String);
}
+// Symbols are created in the old generation (data space).
+Handle<String> Factory::LookupSymbol(Handle<String> string) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->LookupSymbol(*string),
+ String);
+}
+
Handle<String> Factory::LookupAsciiSymbol(Vector<const char> string) {
CALL_HEAP_FUNCTION(isolate(),
isolate()->heap()->LookupAsciiSymbol(string),
String);
}
+
+Handle<String> Factory::LookupAsciiSymbol(Handle<SeqAsciiString> string,
+ int from,
+ int length) {
+ CALL_HEAP_FUNCTION(isolate(),
+ isolate()->heap()->LookupAsciiSymbol(string,
+ from,
+ length),
+ String);
+}
+
+
Handle<String> Factory::LookupTwoByteSymbol(Vector<const uc16> string) {
CALL_HEAP_FUNCTION(isolate(),
isolate()->heap()->LookupTwoByteSymbol(string),
@@ -266,7 +285,7 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
heap->SetLastScriptId(Smi::FromInt(id));
// Create and initialize script object.
- Handle<Proxy> wrapper = NewProxy(0, TENURED);
+ Handle<Foreign> wrapper = NewForeign(0, TENURED);
Handle<Script> script = Handle<Script>::cast(NewStruct(SCRIPT_TYPE));
script->set_source(*source);
script->set_name(heap->undefined_value());
@@ -286,15 +305,15 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
}
-Handle<Proxy> Factory::NewProxy(Address addr, PretenureFlag pretenure) {
+Handle<Foreign> Factory::NewForeign(Address addr, PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(isolate(),
- isolate()->heap()->AllocateProxy(addr, pretenure),
- Proxy);
+ isolate()->heap()->AllocateForeign(addr, pretenure),
+ Foreign);
}
-Handle<Proxy> Factory::NewProxy(const AccessorDescriptor* desc) {
- return NewProxy((Address) desc, TENURED);
+Handle<Foreign> Factory::NewForeign(const AccessorDescriptor* desc) {
+ return NewForeign((Address) desc, TENURED);
}
@@ -712,7 +731,7 @@ MUST_USE_RESULT static inline MaybeObject* DoCopyInsert(
// Allocate the new array.
-Handle<DescriptorArray> Factory::CopyAppendProxyDescriptor(
+Handle<DescriptorArray> Factory::CopyAppendForeignDescriptor(
Handle<DescriptorArray> array,
Handle<String> key,
Handle<Object> value,
@@ -832,6 +851,15 @@ Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements,
}
+Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
+ Handle<Object> prototype) {
+ CALL_HEAP_FUNCTION(
+ isolate(),
+ isolate()->heap()->AllocateJSProxy(*handler, *prototype),
+ JSProxy);
+}
+
+
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
Handle<String> name,
int number_of_literals,
@@ -1161,12 +1189,14 @@ void Factory::SetRegExpIrregexpData(Handle<JSRegExp> regexp,
JSRegExp::Flags flags,
int capture_count) {
Handle<FixedArray> store = NewFixedArray(JSRegExp::kIrregexpDataSize);
-
+ Smi* uninitialized = Smi::FromInt(JSRegExp::kUninitializedValue);
store->set(JSRegExp::kTagIndex, Smi::FromInt(type));
store->set(JSRegExp::kSourceIndex, *source);
store->set(JSRegExp::kFlagsIndex, Smi::FromInt(flags.value()));
- store->set(JSRegExp::kIrregexpASCIICodeIndex, HEAP->the_hole_value());
- store->set(JSRegExp::kIrregexpUC16CodeIndex, HEAP->the_hole_value());
+ store->set(JSRegExp::kIrregexpASCIICodeIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpUC16CodeIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpASCIICodeSavedIndex, uninitialized);
+ store->set(JSRegExp::kIrregexpUC16CodeSavedIndex, uninitialized);
store->set(JSRegExp::kIrregexpMaxRegisterCountIndex, Smi::FromInt(0));
store->set(JSRegExp::kIrregexpCaptureCountIndex,
Smi::FromInt(capture_count));
diff --git a/src/factory.h b/src/factory.h
index 71bfdc4c..55d1e9a1 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -62,7 +62,11 @@ class Factory {
PretenureFlag pretenure);
Handle<String> LookupSymbol(Vector<const char> str);
+ Handle<String> LookupSymbol(Handle<String> str);
Handle<String> LookupAsciiSymbol(Vector<const char> str);
+ Handle<String> LookupAsciiSymbol(Handle<SeqAsciiString>,
+ int from,
+ int length);
Handle<String> LookupTwoByteSymbol(Vector<const uc16> str);
Handle<String> LookupAsciiSymbol(const char* str) {
return LookupSymbol(CStrVector(str));
@@ -156,13 +160,13 @@ class Factory {
Handle<Script> NewScript(Handle<String> source);
- // Proxies are pretenured when allocated by the bootstrapper.
- Handle<Proxy> NewProxy(Address addr,
- PretenureFlag pretenure = NOT_TENURED);
+ // Foreign objects are pretenured when allocated by the bootstrapper.
+ Handle<Foreign> NewForeign(Address addr,
+ PretenureFlag pretenure = NOT_TENURED);
- // Allocate a new proxy. The proxy is pretenured (allocated directly in
- // the old generation).
- Handle<Proxy> NewProxy(const AccessorDescriptor* proxy);
+ // Allocate a new foreign object. The foreign is pretenured (allocated
+ // directly in the old generation).
+ Handle<Foreign> NewForeign(const AccessorDescriptor* foreign);
Handle<ByteArray> NewByteArray(int length,
PretenureFlag pretenure = NOT_TENURED);
@@ -231,6 +235,8 @@ class Factory {
Handle<FixedArray> elements,
PretenureFlag pretenure = NOT_TENURED);
+ Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
+
Handle<JSFunction> NewFunction(Handle<String> name,
Handle<Object> prototype);
@@ -314,7 +320,7 @@ class Factory {
Handle<JSFunction> NewFunctionWithoutPrototype(Handle<String> name,
Handle<Code> code);
- Handle<DescriptorArray> CopyAppendProxyDescriptor(
+ Handle<DescriptorArray> CopyAppendForeignDescriptor(
Handle<DescriptorArray> array,
Handle<String> key,
Handle<Object> value,
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 17e2015d..a85f5fe8 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -96,6 +96,9 @@ private:
//
#define FLAG FLAG_FULL
+// Flags for experimental language features.
+DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
+
// Flags for Crankshaft.
#ifdef V8_TARGET_ARCH_MIPS
DEFINE_bool(crankshaft, false, "use crankshaft")
@@ -144,7 +147,6 @@ DEFINE_bool(optimize_closures, true, "optimize closures")
DEFINE_bool(debug_code, false,
"generate extra code (assertions) for debugging")
DEFINE_bool(code_comments, false, "emit comments in code disassembly")
-DEFINE_bool(emit_branch_hints, false, "emit branch hints")
DEFINE_bool(peephole_optimization, true,
"perform peephole optimizations in assembly code")
DEFINE_bool(print_peephole_optimization, false,
@@ -285,10 +287,9 @@ DEFINE_bool(native_code_counters, false,
DEFINE_bool(always_compact, false, "Perform compaction on every full GC")
DEFINE_bool(never_compact, false,
"Never perform compaction on full GC - testing only")
-DEFINE_bool(cleanup_ics_at_gc, true,
- "Flush inline caches prior to mark compact collection.")
-DEFINE_bool(cleanup_caches_in_maps_at_gc, true,
- "Flush code caches in maps during mark compact cycle.")
+DEFINE_bool(cleanup_code_caches_at_gc, true,
+ "Flush inline caches prior to mark compact collection and "
+ "flush code caches in maps during mark compact cycle.")
DEFINE_int(random_seed, 0,
"Default seed for initializing random generator "
"(0, the default, means to use system random).")
@@ -325,7 +326,7 @@ DEFINE_int(stop_sim_at, 0, "Simulator stop after x number of instructions")
DEFINE_int(sim_stack_alignment, 8,
"Stack alingment in bytes in simulator (4 or 8, 8 is default)")
-// top.cc
+// isolate.cc
DEFINE_bool(trace_exception, false,
"print stack trace when throwing exceptions")
DEFINE_bool(preallocate_message_memory, false,
diff --git a/src/frame-element.h b/src/frame-element.h
deleted file mode 100644
index 0c7d0103..00000000
--- a/src/frame-element.h
+++ /dev/null
@@ -1,269 +0,0 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_FRAME_ELEMENT_H_
-#define V8_FRAME_ELEMENT_H_
-
-#include "type-info.h"
-#include "macro-assembler.h"
-#include "zone.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// Virtual frame elements
-//
-// The internal elements of the virtual frames. There are several kinds of
-// elements:
-// * Invalid: elements that are uninitialized or not actually part
-// of the virtual frame. They should not be read.
-// * Memory: an element that resides in the actual frame. Its address is
-// given by its position in the virtual frame.
-// * Register: an element that resides in a register.
-// * Constant: an element whose value is known at compile time.
-
-class FrameElement BASE_EMBEDDED {
- public:
- enum SyncFlag {
- NOT_SYNCED,
- SYNCED
- };
-
- inline TypeInfo type_info() {
- // Copied elements do not have type info. Instead
- // we have to inspect their backing element in the frame.
- ASSERT(!is_copy());
- return TypeInfo::FromInt(TypeInfoField::decode(value_));
- }
-
- inline void set_type_info(TypeInfo info) {
- // Copied elements do not have type info. Instead
- // we have to inspect their backing element in the frame.
- ASSERT(!is_copy());
- value_ = value_ & ~TypeInfoField::mask();
- value_ = value_ | TypeInfoField::encode(info.ToInt());
- }
-
- // The default constructor creates an invalid frame element.
- FrameElement() {
- value_ = TypeField::encode(INVALID)
- | CopiedField::encode(false)
- | SyncedField::encode(false)
- | TypeInfoField::encode(TypeInfo::Uninitialized().ToInt())
- | DataField::encode(0);
- }
-
- // Factory function to construct an invalid frame element.
- static FrameElement InvalidElement() {
- FrameElement result;
- return result;
- }
-
- // Factory function to construct an in-memory frame element.
- static FrameElement MemoryElement(TypeInfo info) {
- FrameElement result(MEMORY, no_reg, SYNCED, info);
- return result;
- }
-
- // Factory function to construct an in-register frame element.
- static FrameElement RegisterElement(Register reg,
- SyncFlag is_synced,
- TypeInfo info) {
- return FrameElement(REGISTER, reg, is_synced, info);
- }
-
- // Factory function to construct a frame element whose value is known at
- // compile time.
- static FrameElement ConstantElement(Handle<Object> value,
- SyncFlag is_synced) {
- TypeInfo info = TypeInfo::TypeFromValue(value);
- FrameElement result(value, is_synced, info);
- return result;
- }
-
- static bool ConstantPoolOverflowed() {
- return !DataField::is_valid(
- Isolate::Current()->frame_element_constant_list()->length());
- }
-
- bool is_synced() const { return SyncedField::decode(value_); }
-
- void set_sync() {
- ASSERT(type() != MEMORY);
- value_ = value_ | SyncedField::encode(true);
- }
-
- void clear_sync() {
- ASSERT(type() != MEMORY);
- value_ = value_ & ~SyncedField::mask();
- }
-
- bool is_valid() const { return type() != INVALID; }
- bool is_memory() const { return type() == MEMORY; }
- bool is_register() const { return type() == REGISTER; }
- bool is_constant() const { return type() == CONSTANT; }
- bool is_copy() const { return type() == COPY; }
-
- bool is_copied() const { return CopiedField::decode(value_); }
- void set_copied() { value_ = value_ | CopiedField::encode(true); }
- void clear_copied() { value_ = value_ & ~CopiedField::mask(); }
-
- // An untagged int32 FrameElement represents a signed int32
- // on the stack. These are only allowed in a side-effect-free
- // int32 calculation, and if a non-int32 input shows up or an overflow
- // occurs, we bail out and drop all the int32 values.
- void set_untagged_int32(bool value) {
- value_ &= ~UntaggedInt32Field::mask();
- value_ |= UntaggedInt32Field::encode(value);
- }
- bool is_untagged_int32() const { return UntaggedInt32Field::decode(value_); }
-
- Register reg() const {
- ASSERT(is_register());
- uint32_t reg = DataField::decode(value_);
- Register result;
- result.code_ = reg;
- return result;
- }
-
- Handle<Object> handle() const {
- ASSERT(is_constant());
- return Isolate::Current()->frame_element_constant_list()->
- at(DataField::decode(value_));
- }
-
- int index() const {
- ASSERT(is_copy());
- return DataField::decode(value_);
- }
-
- bool Equals(FrameElement other) {
- uint32_t masked_difference = (value_ ^ other.value_) & ~CopiedField::mask();
- if (!masked_difference) {
- // The elements are equal if they agree exactly except on copied field.
- return true;
- } else {
- // If two constants have the same value, and agree otherwise, return true.
- return !(masked_difference & ~DataField::mask()) &&
- is_constant() &&
- handle().is_identical_to(other.handle());
- }
- }
-
- // Test if two FrameElements refer to the same memory or register location.
- bool SameLocation(FrameElement* other) {
- if (type() == other->type()) {
- if (value_ == other->value_) return true;
- if (is_constant() && handle().is_identical_to(other->handle())) {
- return true;
- }
- }
- return false;
- }
-
- // Given a pair of non-null frame element pointers, return one of them
- // as an entry frame candidate or null if they are incompatible.
- FrameElement* Combine(FrameElement* other) {
- // If either is invalid, the result is.
- if (!is_valid()) return this;
- if (!other->is_valid()) return other;
-
- if (!SameLocation(other)) return NULL;
- // If either is unsynced, the result is.
- FrameElement* result = is_synced() ? other : this;
- return result;
- }
-
- private:
- enum Type {
- INVALID,
- MEMORY,
- REGISTER,
- CONSTANT,
- COPY
- };
-
- // Used to construct memory and register elements.
- FrameElement(Type type,
- Register reg,
- SyncFlag is_synced,
- TypeInfo info) {
- value_ = TypeField::encode(type)
- | CopiedField::encode(false)
- | SyncedField::encode(is_synced != NOT_SYNCED)
- | TypeInfoField::encode(info.ToInt())
- | DataField::encode(reg.code_ > 0 ? reg.code_ : 0);
- }
-
- // Used to construct constant elements.
- FrameElement(Handle<Object> value, SyncFlag is_synced, TypeInfo info) {
- ZoneObjectList* constant_list =
- Isolate::Current()->frame_element_constant_list();
- value_ = TypeField::encode(CONSTANT)
- | CopiedField::encode(false)
- | SyncedField::encode(is_synced != NOT_SYNCED)
- | TypeInfoField::encode(info.ToInt())
- | DataField::encode(constant_list->length());
- constant_list->Add(value);
- }
-
- Type type() const { return TypeField::decode(value_); }
- void set_type(Type type) {
- value_ = value_ & ~TypeField::mask();
- value_ = value_ | TypeField::encode(type);
- }
-
- void set_index(int new_index) {
- ASSERT(is_copy());
- value_ = value_ & ~DataField::mask();
- value_ = value_ | DataField::encode(new_index);
- }
-
- void set_reg(Register new_reg) {
- ASSERT(is_register());
- value_ = value_ & ~DataField::mask();
- value_ = value_ | DataField::encode(new_reg.code_);
- }
-
- // Encode type, copied, synced and data in one 32 bit integer.
- uint32_t value_;
-
- // Declare BitFields with template parameters <type, start, size>.
- class TypeField: public BitField<Type, 0, 3> {};
- class CopiedField: public BitField<bool, 3, 1> {};
- class SyncedField: public BitField<bool, 4, 1> {};
- class UntaggedInt32Field: public BitField<bool, 5, 1> {};
- class TypeInfoField: public BitField<int, 6, 7> {};
- class DataField: public BitField<uint32_t, 13, 32 - 13> {};
-
- friend class VirtualFrame;
-};
-
-} } // namespace v8::internal
-
-#endif // V8_FRAME_ELEMENT_H_
diff --git a/src/frames.cc b/src/frames.cc
index e0517c88..d81d5afa 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -742,24 +742,30 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
// at the first position. Since we are always at a call when we need
// to construct a stack trace, the receiver is always in a stack slot.
opcode = static_cast<Translation::Opcode>(it.Next());
- ASSERT(opcode == Translation::STACK_SLOT);
- int input_slot_index = it.Next();
+ ASSERT(opcode == Translation::STACK_SLOT ||
+ opcode == Translation::LITERAL);
+ int index = it.Next();
// Get the correct receiver in the optimized frame.
Object* receiver = NULL;
- // Positive index means the value is spilled to the locals area. Negative
- // means it is stored in the incoming parameter area.
- if (input_slot_index >= 0) {
- receiver = GetExpression(input_slot_index);
+ if (opcode == Translation::LITERAL) {
+ receiver = data->LiteralArray()->get(index);
} else {
- // Index -1 overlaps with last parameter, -n with the first parameter,
- // (-n - 1) with the receiver with n being the number of parameters
- // of the outermost, optimized frame.
- int parameter_count = ComputeParametersCount();
- int parameter_index = input_slot_index + parameter_count;
- receiver = (parameter_index == -1)
- ? this->receiver()
- : this->GetParameter(parameter_index);
+ // Positive index means the value is spilled to the locals
+ // area. Negative means it is stored in the incoming parameter
+ // area.
+ if (index >= 0) {
+ receiver = GetExpression(index);
+ } else {
+ // Index -1 overlaps with last parameter, -n with the first parameter,
+ // (-n - 1) with the receiver with n being the number of parameters
+ // of the outermost, optimized frame.
+ int parameter_count = ComputeParametersCount();
+ int parameter_index = index + parameter_count;
+ receiver = (parameter_index == -1)
+ ? this->receiver()
+ : this->GetParameter(parameter_index);
+ }
}
Code* code = function->shared()->code();
@@ -938,6 +944,10 @@ void JavaScriptFrame::Print(StringStream* accumulator,
accumulator->Add("\n");
return;
}
+ if (is_optimized()) {
+ accumulator->Add(" {\n// optimized frame\n}\n");
+ return;
+ }
accumulator->Add(" {\n");
// Compute the number of locals and expression stack elements.
diff --git a/src/frames.h b/src/frames.h
index 6fe6a637..aa91026f 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -28,6 +28,7 @@
#ifndef V8_FRAMES_H_
#define V8_FRAMES_H_
+#include "allocation.h"
#include "handles.h"
#include "safepoint-table.h"
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index 5f97421c..2b43e897 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -747,7 +747,7 @@ void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
if (ShouldInlineSmiCase(op)) {
EmitInlineSmiBinaryOp(expr, op, mode, left, right);
} else {
- EmitBinaryOp(op, mode);
+ EmitBinaryOp(expr, op, mode);
}
break;
}
diff --git a/src/full-codegen.h b/src/full-codegen.h
index d6ed1b9f..0d26e389 100644
--- a/src/full-codegen.h
+++ b/src/full-codegen.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,6 +30,7 @@
#include "v8.h"
+#include "allocation.h"
#include "ast.h"
#include "code-stubs.h"
#include "codegen.h"
@@ -299,10 +300,19 @@ class FullCodeGenerator: public AstVisitor {
// Helper function to split control flow and avoid a branch to the
// fall-through label if it is set up.
+#ifdef V8_TARGET_ARCH_MIPS
void Split(Condition cc,
+ Register lhs,
+ const Operand& rhs,
Label* if_true,
Label* if_false,
Label* fall_through);
+#else // All non-mips arch.
+ void Split(Condition cc,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through);
+#endif // V8_TARGET_ARCH_MIPS
void Move(Slot* dst, Register source, Register scratch1, Register scratch2);
void Move(Register dst, Slot* source);
@@ -395,9 +405,9 @@ class FullCodeGenerator: public AstVisitor {
void EmitReturnSequence();
// Platform-specific code sequences for calls
- void EmitCallWithStub(Call* expr);
+ void EmitCallWithStub(Call* expr, CallFunctionFlags flags);
void EmitCallWithIC(Call* expr, Handle<Object> name, RelocInfo::Mode mode);
- void EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode);
+ void EmitKeyedCallWithIC(Call* expr, Expression* key);
// Platform-specific code for inline runtime calls.
InlineFunctionGenerator FindInlineFunctionGenerator(Runtime::FunctionId id);
@@ -445,12 +455,13 @@ class FullCodeGenerator: public AstVisitor {
// Apply the compound assignment operator. Expects the left operand on top
// of the stack and the right one in the accumulator.
- void EmitBinaryOp(Token::Value op,
+ void EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
OverwriteMode mode);
// Helper functions for generating inlined smi code for certain
// binary operations.
- void EmitInlineSmiBinaryOp(Expression* expr,
+ void EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
Expression* left,
@@ -512,12 +523,16 @@ class FullCodeGenerator: public AstVisitor {
static Register context_register();
// Helper for calling an IC stub.
- void EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode);
+ void EmitCallIC(Handle<Code> ic,
+ RelocInfo::Mode mode,
+ unsigned ast_id);
// Calling an IC stub with a patch site. Passing NULL for patch_site
// or non NULL patch_site which is not activated indicates no inlined smi code
// and emits a nop after the IC call.
- void EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site);
+ void EmitCallIC(Handle<Code> ic,
+ JumpPatchSite* patch_site,
+ unsigned ast_id);
// Set fields in the stack frame. Offsets are the frame pointer relative
// offsets defined in, e.g., StandardFrameConstants.
@@ -531,6 +546,9 @@ class FullCodeGenerator: public AstVisitor {
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
+
+ void EmitUnaryOperation(UnaryOperation* expr, const char* comment);
+
// Handles the shortcutted logical binary operations in VisitBinaryOperation.
void EmitLogicalOperation(BinaryOperation* expr);
diff --git a/src/func-name-inferrer.cc b/src/func-name-inferrer.cc
index c094251f..ebac4b9b 100644
--- a/src/func-name-inferrer.cc
+++ b/src/func-name-inferrer.cc
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -29,6 +29,7 @@
#include "ast.h"
#include "func-name-inferrer.h"
+#include "list-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/gdb-jit.h b/src/gdb-jit.h
index de6928f8..0c80fb65 100644
--- a/src/gdb-jit.h
+++ b/src/gdb-jit.h
@@ -28,6 +28,8 @@
#ifndef V8_GDB_JIT_H_
#define V8_GDB_JIT_H_
+#include "allocation.h"
+
//
// Basic implementation of GDB JIT Interface client.
// GBD JIT Interface is supported in GDB 7.0 and above.
diff --git a/src/global-handles.cc b/src/global-handles.cc
index c4e8f131..e4bbc958 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -48,6 +48,7 @@ class GlobalHandles::Node : public Malloced {
// Set the initial value of the handle.
object_ = object;
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
+ independent_ = false;
state_ = NORMAL;
parameter_or_next_free_.parameter = NULL;
callback_ = NULL;
@@ -138,6 +139,13 @@ class GlobalHandles::Node : public Malloced {
set_parameter(NULL);
}
+ void MarkIndependent(GlobalHandles* global_handles) {
+ LOG(global_handles->isolate(),
+ HandleEvent("GlobalHandle::MarkIndependent", handle().location()));
+ ASSERT(state_ != DESTROYED);
+ independent_ = true;
+ }
+
bool IsNearDeath() {
// Check for PENDING to ensure correct answer when processing callbacks.
return state_ == PENDING || state_ == NEAR_DEATH;
@@ -222,6 +230,8 @@ class GlobalHandles::Node : public Malloced {
};
State state_ : 4; // Need one more bit for MSVC as it treats enums as signed.
+ bool independent_ : 1;
+
private:
// Handle specific callback.
WeakReferenceCallback callback_;
@@ -364,6 +374,11 @@ void GlobalHandles::ClearWeakness(Object** location) {
}
+void GlobalHandles::MarkIndependent(Object** location) {
+ Node::FromLocation(location)->MarkIndependent(this);
+}
+
+
bool GlobalHandles::IsNearDeath(Object** location) {
return Node::FromLocation(location)->IsNearDeath();
}
@@ -381,8 +396,22 @@ void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
// Traversal of GC roots in the global handle list that are marked as
- // WEAK or PENDING.
+ // WEAK, PENDING or NEAR_DEATH.
+ for (Node* current = head_; current != NULL; current = current->next()) {
+ if (current->state_ == Node::WEAK
+ || current->state_ == Node::PENDING
+ || current->state_ == Node::NEAR_DEATH) {
+ v->VisitPointer(&current->object_);
+ }
+ }
+}
+
+
+void GlobalHandles::IterateWeakIndependentRoots(ObjectVisitor* v) {
+ // Traversal of GC roots in the global handle list that are independent
+ // and marked as WEAK, PENDING or NEAR_DEATH.
for (Node* current = head_; current != NULL; current = current->next()) {
+ if (!current->independent_) continue;
if (current->state_ == Node::WEAK
|| current->state_ == Node::PENDING
|| current->state_ == Node::NEAR_DEATH) {
@@ -415,7 +444,21 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
}
-bool GlobalHandles::PostGarbageCollectionProcessing() {
+void GlobalHandles::IdentifyWeakIndependentHandles(WeakSlotCallbackWithHeap f) {
+ for (Node* current = head_; current != NULL; current = current->next()) {
+ if (current->state_ == Node::WEAK && current->independent_) {
+ if (f(isolate_->heap(), &current->object_)) {
+ current->state_ = Node::PENDING;
+ LOG(isolate_,
+ HandleEvent("GlobalHandle::Pending", current->handle().location()));
+ }
+ }
+ }
+}
+
+
+bool GlobalHandles::PostGarbageCollectionProcessing(
+ GarbageCollector collector) {
// Process weak global handle callbacks. This must be done after the
// GC is completely done, because the callbacks may invoke arbitrary
// API functions.
@@ -425,6 +468,14 @@ bool GlobalHandles::PostGarbageCollectionProcessing() {
bool next_gc_likely_to_collect_more = false;
Node** p = &head_;
while (*p != NULL) {
+ // Skip dependent handles. Their weak callbacks might expect to be
+ // called between two global garbage collection callbacks which
+ // are not called for minor collections.
+ if (collector == SCAVENGER && !(*p)->independent_) {
+ p = (*p)->next_addr();
+ continue;
+ }
+
if ((*p)->PostGarbageCollectionProcessing(isolate_, this)) {
if (initial_post_gc_processing_count != post_gc_processing_count_) {
// Weak callback triggered another GC and another round of
@@ -476,6 +527,16 @@ void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
}
+void GlobalHandles::IterateStrongAndDependentRoots(ObjectVisitor* v) {
+ for (Node* current = head_; current != NULL; current = current->next()) {
+ if ((current->independent_ && current->state_ == Node::NORMAL) ||
+ (!current->independent_ && current->state_ != Node::DESTROYED)) {
+ v->VisitPointer(&current->object_);
+ }
+ }
+}
+
+
void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
for (Node* current = head_; current != NULL; current = current->next()) {
if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
@@ -558,6 +619,11 @@ void GlobalHandles::Print() {
void GlobalHandles::AddObjectGroup(Object*** handles,
size_t length,
v8::RetainedObjectInfo* info) {
+#ifdef DEBUG
+ for (size_t i = 0; i < length; ++i) {
+ ASSERT(!Node::FromLocation(handles[i])->independent_);
+ }
+#endif
if (length == 0) {
if (info != NULL) info->Dispose();
return;
@@ -569,6 +635,12 @@ void GlobalHandles::AddObjectGroup(Object*** handles,
void GlobalHandles::AddImplicitReferences(HeapObject** parent,
Object*** children,
size_t length) {
+#ifdef DEBUG
+ ASSERT(!Node::FromLocation(BitCast<Object**>(parent))->independent_);
+ for (size_t i = 0; i < length; ++i) {
+ ASSERT(!Node::FromLocation(children[i])->independent_);
+ }
+#endif
if (length == 0) return;
implicit_ref_groups_.Add(ImplicitRefGroup::New(parent, children, length));
}
diff --git a/src/global-handles.h b/src/global-handles.h
index 2171b2cf..3477bcaa 100644
--- a/src/global-handles.h
+++ b/src/global-handles.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,7 +30,7 @@
#include "../include/v8-profiler.h"
-#include "list-inl.h"
+#include "list.h"
namespace v8 {
namespace internal {
@@ -146,6 +146,9 @@ class GlobalHandles {
// Clear the weakness of a global handle.
void ClearWeakness(Object** location);
+ // Clear the weakness of a global handle.
+ void MarkIndependent(Object** location);
+
// Tells whether global handle is near death.
static bool IsNearDeath(Object** location);
@@ -154,11 +157,14 @@ class GlobalHandles {
// Process pending weak handles.
// Returns true if next major GC is likely to collect more garbage.
- bool PostGarbageCollectionProcessing();
+ bool PostGarbageCollectionProcessing(GarbageCollector collector);
// Iterates over all strong handles.
void IterateStrongRoots(ObjectVisitor* v);
+ // Iterates over all strong and dependent handles.
+ void IterateStrongAndDependentRoots(ObjectVisitor* v);
+
// Iterates over all handles.
void IterateAllRoots(ObjectVisitor* v);
@@ -168,6 +174,9 @@ class GlobalHandles {
// Iterates over all weak roots in heap.
void IterateWeakRoots(ObjectVisitor* v);
+ // Iterates over all weak independent roots in heap.
+ void IterateWeakIndependentRoots(ObjectVisitor* v);
+
// Iterates over weak roots that are bound to a given callback.
void IterateWeakRoots(WeakReferenceGuest f,
WeakReferenceCallback callback);
@@ -176,6 +185,10 @@ class GlobalHandles {
// them as pending.
void IdentifyWeakHandles(WeakSlotCallback f);
+ // Find all weak independent handles satisfying the callback predicate, mark
+ // them as pending.
+ void IdentifyWeakIndependentHandles(WeakSlotCallbackWithHeap f);
+
// Add an object group.
// Should be only used in GC callback function before a collection.
// All groups are destroyed after a mark-compact collection.
diff --git a/src/handles.cc b/src/handles.cc
index 326de863..b03b642c 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -362,6 +362,17 @@ Handle<Object> GetProperty(Handle<JSObject> obj,
Handle<Object> GetProperty(Handle<Object> obj,
+ const char* name,
+ LookupResult* result) {
+ Isolate* isolate = Isolate::Current();
+ Handle<String> str = isolate->factory()->LookupAsciiSymbol(name);
+ PropertyAttributes attributes;
+ CALL_HEAP_FUNCTION(
+ isolate, obj->GetProperty(*obj, result, *str, &attributes), Object);
+}
+
+
+Handle<Object> GetProperty(Handle<Object> obj,
Handle<Object> key) {
Isolate* isolate = Isolate::Current();
CALL_HEAP_FUNCTION(isolate,
@@ -533,7 +544,7 @@ Handle<Object> SetAccessor(Handle<JSObject> obj, Handle<AccessorInfo> info) {
// Wrappers for scripts are kept alive and cached in weak global
-// handles referred from proxy objects held by the scripts as long as
+// handles referred from foreign objects held by the scripts as long as
// they are used. When they are not used anymore, the garbage
// collector will call the weak callback on the global handle
// associated with the wrapper and get rid of both the wrapper and the
@@ -546,9 +557,9 @@ static void ClearWrapperCache(Persistent<v8::Value> handle, void*) {
#endif
Handle<Object> cache = Utils::OpenHandle(*handle);
JSValue* wrapper = JSValue::cast(*cache);
- Proxy* proxy = Script::cast(wrapper->value())->wrapper();
- ASSERT(proxy->proxy() == reinterpret_cast<Address>(cache.location()));
- proxy->set_proxy(0);
+ Foreign* foreign = Script::cast(wrapper->value())->wrapper();
+ ASSERT(foreign->address() == reinterpret_cast<Address>(cache.location()));
+ foreign->set_address(0);
Isolate* isolate = Isolate::Current();
isolate->global_handles()->Destroy(cache.location());
isolate->counters()->script_wrappers()->Decrement();
@@ -556,10 +567,10 @@ static void ClearWrapperCache(Persistent<v8::Value> handle, void*) {
Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
- if (script->wrapper()->proxy() != NULL) {
+ if (script->wrapper()->address() != NULL) {
// Return the script wrapper directly from the cache.
return Handle<JSValue>(
- reinterpret_cast<JSValue**>(script->wrapper()->proxy()));
+ reinterpret_cast<JSValue**>(script->wrapper()->address()));
}
Isolate* isolate = Isolate::Current();
// Construct a new script wrapper.
@@ -575,7 +586,7 @@ Handle<JSValue> GetScriptWrapper(Handle<Script> script) {
Handle<Object> handle = isolate->global_handles()->Create(*result);
isolate->global_handles()->MakeWeak(handle.location(), NULL,
&ClearWrapperCache);
- script->wrapper()->set_proxy(reinterpret_cast<Address>(handle.location()));
+ script->wrapper()->set_address(reinterpret_cast<Address>(handle.location()));
return result;
}
diff --git a/src/handles.h b/src/handles.h
index 3839f37a..3d930fd7 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -28,6 +28,7 @@
#ifndef V8_HANDLES_H_
#define V8_HANDLES_H_
+#include "allocation.h"
#include "apiutils.h"
namespace v8 {
@@ -242,6 +243,10 @@ Handle<Object> GetProperty(Handle<JSObject> obj,
const char* name);
Handle<Object> GetProperty(Handle<Object> obj,
+ const char* name,
+ LookupResult* result);
+
+Handle<Object> GetProperty(Handle<Object> obj,
Handle<Object> key);
Handle<Object> GetProperty(Handle<JSObject> obj,
diff --git a/src/hashmap.h b/src/hashmap.h
index bb3e3ceb..5c13212e 100644
--- a/src/hashmap.h
+++ b/src/hashmap.h
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,6 +28,8 @@
#ifndef V8_HASHMAP_H_
#define V8_HASHMAP_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/heap-inl.h b/src/heap-inl.h
index 296cb05c..3860fada 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -29,8 +29,9 @@
#define V8_HEAP_INL_H_
#include "heap.h"
-#include "objects.h"
#include "isolate.h"
+#include "list-inl.h"
+#include "objects.h"
#include "v8-counters.h"
namespace v8 {
diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
index 4815f826..ec078ed1 100644
--- a/src/heap-profiler.cc
+++ b/src/heap-profiler.cc
@@ -474,7 +474,7 @@ const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
ConstructorHeapProfile::ConstructorHeapProfile()
- : zscope_(DELETE_ON_EXIT) {
+ : zscope_(Isolate::Current(), DELETE_ON_EXIT) {
}
@@ -584,7 +584,7 @@ inline int ClustersCoarser::ClusterBackRefs::Compare(
ClustersCoarser::ClustersCoarser()
- : zscope_(DELETE_ON_EXIT),
+ : zscope_(Isolate::Current(), DELETE_ON_EXIT),
sim_list_(ClustersCoarser::kInitialSimilarityListCapacity),
current_pair_(NULL),
current_set_(NULL),
@@ -699,7 +699,7 @@ const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue =
RetainerHeapProfile::RetainerHeapProfile()
- : zscope_(DELETE_ON_EXIT),
+ : zscope_(Isolate::Current(), DELETE_ON_EXIT),
aggregator_(NULL) {
JSObjectsCluster roots(JSObjectsCluster::ROOTS);
ReferencesExtractor extractor(roots, this);
@@ -1027,7 +1027,7 @@ class AggregatingRetainerTreeIterator {
if (coarser_ != NULL &&
!coarser_->GetCoarseEquivalent(cluster).is_null()) return;
JSObjectsClusterTree* tree_to_iterate = tree;
- ZoneScope zs(DELETE_ON_EXIT);
+ ZoneScope zs(Isolate::Current(), DELETE_ON_EXIT);
JSObjectsClusterTree dest_tree_;
if (coarser_ != NULL) {
RetainersAggregator retainers_aggregator(coarser_, &dest_tree_);
diff --git a/src/heap-profiler.h b/src/heap-profiler.h
index 89a2e8a5..c1e93c09 100644
--- a/src/heap-profiler.h
+++ b/src/heap-profiler.h
@@ -28,6 +28,7 @@
#ifndef V8_HEAP_PROFILER_H_
#define V8_HEAP_PROFILER_H_
+#include "allocation.h"
#include "isolate.h"
#include "zone-inl.h"
diff --git a/src/heap.cc b/src/heap.cc
index 2b6c11f7..f82c83cb 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -33,8 +33,8 @@
#include "codegen.h"
#include "compilation-cache.h"
#include "debug.h"
-#include "heap-profiler.h"
#include "global-handles.h"
+#include "heap-profiler.h"
#include "liveobjectlist-inl.h"
#include "mark-compact.h"
#include "natives.h"
@@ -69,11 +69,11 @@ Heap::Heap()
: isolate_(NULL),
// semispace_size_ should be a power of 2 and old_generation_size_ should be
// a multiple of Page::kPageSize.
-#if defined(ANDROID)
+#if 0//defined(ANDROID)
reserved_semispace_size_(2*MB),
max_semispace_size_(2*MB),
initial_semispace_size_(128*KB),
- max_old_generation_size_(192*MB),
+ max_old_generation_size_(512*MB),
max_executable_size_(max_old_generation_size_),
code_range_size_(0),
#elif defined(V8_TARGET_ARCH_X64)
@@ -96,6 +96,7 @@ Heap::Heap()
// Will be 4 * reserved_semispace_size_ to ensure that young
// generation can be aligned to its size.
survived_since_last_expansion_(0),
+ sweep_generation_(0),
always_allocate_scope_depth_(0),
linear_allocation_scope_depth_(0),
contexts_disposed_(0),
@@ -736,7 +737,7 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector,
if (collector == MARK_COMPACTOR) {
// Perform mark-sweep with optional compaction.
MarkCompact(tracer);
-
+ sweep_generation_++;
bool high_survival_rate_during_scavenges = IsHighSurvivalRate() &&
IsStableOrIncreasingSurvivalTrend();
@@ -771,11 +772,10 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector,
isolate_->counters()->objs_since_last_young()->Set(0);
- if (collector == MARK_COMPACTOR) {
- DisableAssertNoAllocation allow_allocation;
+ { DisableAssertNoAllocation allow_allocation;
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
next_gc_likely_to_collect_more =
- isolate_->global_handles()->PostGarbageCollectionProcessing();
+ isolate_->global_handles()->PostGarbageCollectionProcessing(collector);
}
// Update relocatables.
@@ -935,6 +935,12 @@ void Heap::CheckNewSpaceExpansionCriteria() {
}
+static bool IsUnscavengedHeapObject(Heap* heap, Object** p) {
+ return heap->InNewSpace(*p) &&
+ !HeapObject::cast(*p)->map_word().IsForwardingAddress();
+}
+
+
void Heap::Scavenge() {
#ifdef DEBUG
if (FLAG_enable_slow_asserts) VerifyNonPointerSpacePointers();
@@ -1029,6 +1035,11 @@ void Heap::Scavenge() {
scavenge_visitor.VisitPointer(BitCast<Object**>(&global_contexts_list_));
new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
+ isolate_->global_handles()->IdentifyWeakIndependentHandles(
+ &IsUnscavengedHeapObject);
+ isolate_->global_handles()->IterateWeakIndependentRoots(&scavenge_visitor);
+ new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
+
UpdateNewSpaceReferencesInExternalStringTable(
&UpdateNewSpaceReferenceInExternalStringTableEntry);
@@ -1282,6 +1293,10 @@ class ScavengingVisitor : public StaticVisitorBase {
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<SharedFunctionInfo::kSize>);
+ table_.Register(kVisitJSRegExp,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
table_.Register(kVisitJSFunction,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<JSFunction::kSize>);
@@ -1348,7 +1363,7 @@ class ScavengingVisitor : public StaticVisitorBase {
#if defined(ENABLE_LOGGING_AND_PROFILING)
Isolate* isolate = heap->isolate();
if (isolate->logger()->is_logging() ||
- isolate->cpu_profiler()->is_profiling()) {
+ CpuProfiler::is_profiling(isolate)) {
if (target->IsSharedFunctionInfo()) {
PROFILE(isolate, SharedFunctionInfoMoveEvent(
source->address(), target->address()));
@@ -1523,8 +1538,8 @@ void Heap::SwitchScavengingVisitorsTableIfProfilingWasEnabled() {
return;
}
- if (isolate()->logger()->is_logging() ||
- isolate()->cpu_profiler()->is_profiling() ||
+ if (isolate()->logger()->is_logging() |
+ CpuProfiler::is_profiling(isolate()) ||
(isolate()->heap_profiler() != NULL &&
isolate()->heap_profiler()->is_profiling())) {
// If one of the isolates is doing scavenge at this moment of time
@@ -1593,7 +1608,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
map->set_instance_size(instance_size);
map->set_inobject_properties(0);
map->set_pre_allocated_property_fields(0);
- map->set_instance_descriptors(empty_descriptor_array());
+ map->init_instance_descriptors();
map->set_code_cache(empty_fixed_array());
map->set_prototype_transitions(empty_fixed_array());
map->set_unused_property_fields(0);
@@ -1686,15 +1701,15 @@ bool Heap::CreateInitialMaps() {
set_empty_descriptor_array(DescriptorArray::cast(obj));
// Fix the instance_descriptors for the existing maps.
- meta_map()->set_instance_descriptors(empty_descriptor_array());
+ meta_map()->init_instance_descriptors();
meta_map()->set_code_cache(empty_fixed_array());
meta_map()->set_prototype_transitions(empty_fixed_array());
- fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
+ fixed_array_map()->init_instance_descriptors();
fixed_array_map()->set_code_cache(empty_fixed_array());
fixed_array_map()->set_prototype_transitions(empty_fixed_array());
- oddball_map()->set_instance_descriptors(empty_descriptor_array());
+ oddball_map()->init_instance_descriptors();
oddball_map()->set_code_cache(empty_fixed_array());
oddball_map()->set_prototype_transitions(empty_fixed_array());
@@ -1720,10 +1735,10 @@ bool Heap::CreateInitialMaps() {
}
set_heap_number_map(Map::cast(obj));
- { MaybeObject* maybe_obj = AllocateMap(PROXY_TYPE, Proxy::kSize);
+ { MaybeObject* maybe_obj = AllocateMap(FOREIGN_TYPE, Foreign::kSize);
if (!maybe_obj->ToObject(&obj)) return false;
}
- set_proxy_map(Map::cast(obj));
+ set_foreign_map(Map::cast(obj));
for (unsigned i = 0; i < ARRAY_SIZE(string_type_table); i++) {
const StringTypeTable& entry = string_type_table[i];
@@ -1805,6 +1820,12 @@ bool Heap::CreateInitialMaps() {
}
set_external_float_array_map(Map::cast(obj));
+ { MaybeObject* maybe_obj = AllocateMap(EXTERNAL_DOUBLE_ARRAY_TYPE,
+ ExternalArray::kAlignedSize);
+ if (!maybe_obj->ToObject(&obj)) return false;
+ }
+ set_external_double_array_map(Map::cast(obj));
+
{ MaybeObject* maybe_obj = AllocateMap(CODE_TYPE, kVariableSizeSentinel);
if (!maybe_obj->ToObject(&obj)) return false;
}
@@ -2102,12 +2123,12 @@ bool Heap::CreateInitialObjects() {
}
hidden_symbol_ = String::cast(obj);
- // Allocate the proxy for __proto__.
+ // Allocate the foreign for __proto__.
{ MaybeObject* maybe_obj =
- AllocateProxy((Address) &Accessors::ObjectPrototype);
+ AllocateForeign((Address) &Accessors::ObjectPrototype);
if (!maybe_obj->ToObject(&obj)) return false;
}
- set_prototype_accessors(Proxy::cast(obj));
+ set_prototype_accessors(Foreign::cast(obj));
// Allocate the code_stubs dictionary. The initial size is set to avoid
// expanding the dictionary during bootstrapping.
@@ -2293,6 +2314,8 @@ Heap::RootListIndex Heap::RootIndexForExternalArrayType(
return kExternalUnsignedIntArrayMapRootIndex;
case kExternalFloatArray:
return kExternalFloatArrayMapRootIndex;
+ case kExternalDoubleArray:
+ return kExternalDoubleArrayMapRootIndex;
case kExternalPixelArray:
return kExternalPixelArrayMapRootIndex;
default:
@@ -2323,16 +2346,16 @@ MaybeObject* Heap::NumberFromDouble(double value, PretenureFlag pretenure) {
}
-MaybeObject* Heap::AllocateProxy(Address proxy, PretenureFlag pretenure) {
- // Statically ensure that it is safe to allocate proxies in paged spaces.
- STATIC_ASSERT(Proxy::kSize <= Page::kMaxHeapObjectSize);
+MaybeObject* Heap::AllocateForeign(Address address, PretenureFlag pretenure) {
+ // Statically ensure that it is safe to allocate foreigns in paged spaces.
+ STATIC_ASSERT(Foreign::kSize <= Page::kMaxHeapObjectSize);
AllocationSpace space = (pretenure == TENURED) ? OLD_DATA_SPACE : NEW_SPACE;
Object* result;
- { MaybeObject* maybe_result = Allocate(proxy_map(), space);
+ { MaybeObject* maybe_result = Allocate(foreign_map(), space);
if (!maybe_result->ToObject(&result)) return maybe_result;
}
- Proxy::cast(result)->set_proxy(proxy);
+ Foreign::cast(result)->set_address(address);
return result;
}
@@ -2370,6 +2393,7 @@ MaybeObject* Heap::AllocateSharedFunctionInfo(Object* name) {
share->set_num_literals(0);
share->set_end_position(0);
share->set_function_token_position(0);
+ share->set_es5_native(false);
return result;
}
@@ -2792,6 +2816,7 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc,
code->set_check_type(RECEIVER_MAP_CHECK);
}
code->set_deoptimization_data(empty_fixed_array());
+ code->set_next_code_flushing_candidate(undefined_value());
// Allow self references to created code object by patching the handle to
// point to the newly allocated Code object.
if (!self_reference.is_null()) {
@@ -3204,6 +3229,26 @@ MaybeObject* Heap::AllocateJSObject(JSFunction* constructor,
}
+MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) {
+ // Allocate map.
+ // TODO(rossberg): Once we optimize proxies, think about a scheme to share
+ // maps. Will probably depend on the identity of the handler object, too.
+ Map* map;
+ MaybeObject* maybe_map_obj = AllocateMap(JS_PROXY_TYPE, JSProxy::kSize);
+ if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
+ map->set_prototype(prototype);
+ map->set_pre_allocated_property_fields(1);
+ map->set_inobject_properties(1);
+
+ // Allocate the proxy object.
+ Object* result;
+ MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ JSProxy::cast(result)->set_handler(handler);
+ return result;
+}
+
+
MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) {
ASSERT(constructor->has_initial_map());
Map* map = constructor->initial_map();
@@ -3267,7 +3312,7 @@ MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) {
// Setup the global object as a normalized object.
global->set_map(new_map);
- global->map()->set_instance_descriptors(empty_descriptor_array());
+ global->map()->clear_instance_descriptors();
global->set_properties(dictionary);
// Make sure result is a global object with properties in dictionary.
@@ -4139,6 +4184,26 @@ MaybeObject* Heap::LookupAsciiSymbol(Vector<const char> string) {
}
+MaybeObject* Heap::LookupAsciiSymbol(Handle<SeqAsciiString> string,
+ int from,
+ int length) {
+ Object* symbol = NULL;
+ Object* new_table;
+ { MaybeObject* maybe_new_table =
+ symbol_table()->LookupSubStringAsciiSymbol(string,
+ from,
+ length,
+ &symbol);
+ if (!maybe_new_table->ToObject(&new_table)) return maybe_new_table;
+ }
+ // Can't use set_symbol_table because SymbolTable::cast knows that
+ // SymbolTable is a singleton and checks for identity.
+ roots_[kSymbolTableRootIndex] = new_table;
+ ASSERT(symbol != NULL);
+ return symbol;
+}
+
+
MaybeObject* Heap::LookupTwoByteSymbol(Vector<const uc16> string) {
Object* symbol = NULL;
Object* new_table;
@@ -4461,7 +4526,8 @@ void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) {
void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) {
v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex]));
v->Synchronize("symbol_table");
- if (mode != VISIT_ALL_IN_SCAVENGE) {
+ if (mode != VISIT_ALL_IN_SCAVENGE &&
+ mode != VISIT_ALL_IN_SWEEP_NEWSPACE) {
// Scavenge collections have special processing for this.
external_string_table_.Iterate(v);
}
@@ -4497,16 +4563,24 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
// Iterate over the builtin code objects and code stubs in the
// heap. Note that it is not necessary to iterate over code objects
// on scavenge collections.
- if (mode != VISIT_ALL_IN_SCAVENGE) {
+ if (mode != VISIT_ALL_IN_SCAVENGE &&
+ mode != VISIT_ALL_IN_SWEEP_NEWSPACE) {
isolate_->builtins()->IterateBuiltins(v);
}
v->Synchronize("builtins");
// Iterate over global handles.
- if (mode == VISIT_ONLY_STRONG) {
- isolate_->global_handles()->IterateStrongRoots(v);
- } else {
- isolate_->global_handles()->IterateAllRoots(v);
+ switch (mode) {
+ case VISIT_ONLY_STRONG:
+ isolate_->global_handles()->IterateStrongRoots(v);
+ break;
+ case VISIT_ALL_IN_SCAVENGE:
+ isolate_->global_handles()->IterateStrongAndDependentRoots(v);
+ break;
+ case VISIT_ALL_IN_SWEEP_NEWSPACE:
+ case VISIT_ALL:
+ isolate_->global_handles()->IterateAllRoots(v);
+ break;
}
v->Synchronize("globalhandles");
diff --git a/src/heap.h b/src/heap.h
index ae4e9e75..79ae9967 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,6 +30,7 @@
#include <math.h>
+#include "allocation.h"
#include "globals.h"
#include "list.h"
#include "mark-compact.h"
@@ -103,6 +104,7 @@ inline Heap* _inline_get_heap_();
V(Map, external_int_array_map, ExternalIntArrayMap) \
V(Map, external_unsigned_int_array_map, ExternalUnsignedIntArrayMap) \
V(Map, external_float_array_map, ExternalFloatArrayMap) \
+ V(Map, external_double_array_map, ExternalDoubleArrayMap) \
V(Map, context_map, ContextMap) \
V(Map, catch_context_map, CatchContextMap) \
V(Map, code_map, CodeMap) \
@@ -110,12 +112,12 @@ inline Heap* _inline_get_heap_();
V(Map, global_property_cell_map, GlobalPropertyCellMap) \
V(Map, shared_function_info_map, SharedFunctionInfoMap) \
V(Map, message_object_map, JSMessageObjectMap) \
- V(Map, proxy_map, ProxyMap) \
+ V(Map, foreign_map, ForeignMap) \
V(Object, nan_value, NanValue) \
V(Object, minus_zero_value, MinusZeroValue) \
V(Map, neander_map, NeanderMap) \
V(JSObject, message_listeners, MessageListeners) \
- V(Proxy, prototype_accessors, PrototypeAccessors) \
+ V(Foreign, prototype_accessors, PrototypeAccessors) \
V(NumberDictionary, code_stubs, CodeStubs) \
V(NumberDictionary, non_monomorphic_cache, NonMonomorphicCache) \
V(Code, js_entry_code, JsEntryCode) \
@@ -176,8 +178,14 @@ inline Heap* _inline_get_heap_();
V(value_of_symbol, "valueOf") \
V(InitializeVarGlobal_symbol, "InitializeVarGlobal") \
V(InitializeConstGlobal_symbol, "InitializeConstGlobal") \
- V(KeyedLoadSpecialized_symbol, "KeyedLoadSpecialized") \
- V(KeyedStoreSpecialized_symbol, "KeyedStoreSpecialized") \
+ V(KeyedLoadSpecializedMonomorphic_symbol, \
+ "KeyedLoadSpecializedMonomorphic") \
+ V(KeyedLoadSpecializedPolymorphic_symbol, \
+ "KeyedLoadSpecializedPolymorphic") \
+ V(KeyedStoreSpecializedMonomorphic_symbol, \
+ "KeyedStoreSpecializedMonomorphic") \
+ V(KeyedStoreSpecializedPolymorphic_symbol, \
+ "KeyedStoreSpecializedPolymorphic") \
V(stack_overflow_symbol, "kStackOverflowBoilerplate") \
V(illegal_access_symbol, "illegal access") \
V(out_of_memory_symbol, "out-of-memory") \
@@ -205,30 +213,7 @@ inline Heap* _inline_get_heap_();
V(global_eval_symbol, "GlobalEval") \
V(identity_hash_symbol, "v8::IdentityHash") \
V(closure_symbol, "(closure)") \
- V(use_strict, "use strict") \
- V(KeyedLoadExternalByteArray_symbol, "KeyedLoadExternalByteArray") \
- V(KeyedLoadExternalUnsignedByteArray_symbol, \
- "KeyedLoadExternalUnsignedByteArray") \
- V(KeyedLoadExternalShortArray_symbol, \
- "KeyedLoadExternalShortArray") \
- V(KeyedLoadExternalUnsignedShortArray_symbol, \
- "KeyedLoadExternalUnsignedShortArray") \
- V(KeyedLoadExternalIntArray_symbol, "KeyedLoadExternalIntArray") \
- V(KeyedLoadExternalUnsignedIntArray_symbol, \
- "KeyedLoadExternalUnsignedIntArray") \
- V(KeyedLoadExternalFloatArray_symbol, "KeyedLoadExternalFloatArray") \
- V(KeyedLoadExternalPixelArray_symbol, "KeyedLoadExternalPixelArray") \
- V(KeyedStoreExternalByteArray_symbol, "KeyedStoreExternalByteArray") \
- V(KeyedStoreExternalUnsignedByteArray_symbol, \
- "KeyedStoreExternalUnsignedByteArray") \
- V(KeyedStoreExternalShortArray_symbol, "KeyedStoreExternalShortArray") \
- V(KeyedStoreExternalUnsignedShortArray_symbol, \
- "KeyedStoreExternalUnsignedShortArray") \
- V(KeyedStoreExternalIntArray_symbol, "KeyedStoreExternalIntArray") \
- V(KeyedStoreExternalUnsignedIntArray_symbol, \
- "KeyedStoreExternalUnsignedIntArray") \
- V(KeyedStoreExternalFloatArray_symbol, "KeyedStoreExternalFloatArray") \
- V(KeyedStoreExternalPixelArray_symbol, "KeyedStoreExternalPixelArray")
+ V(use_strict, "use strict")
// Forward declarations.
class GCTracer;
@@ -448,6 +433,13 @@ class Heap {
// Please note this does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function);
+ // Allocates a Harmony Proxy.
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
+ Object* prototype);
+
// Reinitialize an JSGlobalProxy based on a constructor. The object
// must have the same size as objects allocated using the
// constructor. The object is reinitialized and behaves as an
@@ -696,12 +688,12 @@ class Heap {
// Please note this does not perform a garbage collection.
MUST_USE_RESULT inline MaybeObject* NumberFromUint32(uint32_t value);
- // Allocates a new proxy object.
+ // Allocates a new foreign object.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
- MUST_USE_RESULT MaybeObject* AllocateProxy(
- Address proxy, PretenureFlag pretenure = NOT_TENURED);
+ MUST_USE_RESULT MaybeObject* AllocateForeign(
+ Address address, PretenureFlag pretenure = NOT_TENURED);
// Allocates a new SharedFunctionInfo object.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
@@ -798,6 +790,10 @@ class Heap {
return LookupSymbol(CStrVector(str));
}
MUST_USE_RESULT MaybeObject* LookupSymbol(String* str);
+ MUST_USE_RESULT MaybeObject* LookupAsciiSymbol(Handle<SeqAsciiString> string,
+ int from,
+ int length);
+
bool LookupSymbolIfExists(String* str, String** symbol);
bool LookupTwoCharsSymbolIfExists(String* str, String** symbol);
@@ -1232,6 +1228,11 @@ class Heap {
return &external_string_table_;
}
+ // Returns the current sweep generation.
+ int sweep_generation() {
+ return sweep_generation_;
+ }
+
inline Isolate* isolate();
bool is_safe_to_read_maps() { return is_safe_to_read_maps_; }
@@ -1261,6 +1262,9 @@ class Heap {
// scavenge since last new space expansion.
int survived_since_last_expansion_;
+ // For keeping track on when to flush RegExp code.
+ int sweep_generation_;
+
int always_allocate_scope_depth_;
int linear_allocation_scope_depth_;
diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc
index 2cf60d7b..682bdb24 100644
--- a/src/hydrogen-instructions.cc
+++ b/src/hydrogen-instructions.cc
@@ -60,12 +60,20 @@ const char* Representation::Mnemonic() const {
case kDouble: return "d";
case kInteger32: return "i";
case kExternal: return "x";
- case kNumRepresentations:
+ default:
UNREACHABLE();
return NULL;
}
- UNREACHABLE();
- return NULL;
+}
+
+
+void HValue::AssumeRepresentation(Representation r) {
+ if (CheckFlag(kFlexibleRepresentation)) {
+ ChangeRepresentation(r);
+ // The representation of the value is dictated by type feedback and
+ // will not be changed later.
+ ClearFlag(kFlexibleRepresentation);
+ }
}
@@ -264,31 +272,60 @@ HType HType::TypeFromValue(Handle<Object> value) {
}
-int HValue::LookupOperandIndex(int occurrence_index, HValue* op) {
- for (int i = 0; i < OperandCount(); ++i) {
- if (OperandAt(i) == op) {
- if (occurrence_index == 0) return i;
- --occurrence_index;
- }
+bool HValue::IsDefinedAfter(HBasicBlock* other) const {
+ return block()->block_id() > other->block_id();
+}
+
+
+HUseIterator::HUseIterator(HUseListNode* head) : next_(head) {
+ Advance();
+}
+
+
+void HUseIterator::Advance() {
+ current_ = next_;
+ if (current_ != NULL) {
+ next_ = current_->tail();
+ value_ = current_->value();
+ index_ = current_->index();
}
- return -1;
}
-bool HValue::IsDefinedAfter(HBasicBlock* other) const {
- return block()->block_id() > other->block_id();
+int HValue::UseCount() const {
+ int count = 0;
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) ++count;
+ return count;
}
-bool HValue::UsesMultipleTimes(HValue* op) {
- bool seen = false;
- for (int i = 0; i < OperandCount(); ++i) {
- if (OperandAt(i) == op) {
- if (seen) return true;
- seen = true;
+HUseListNode* HValue::RemoveUse(HValue* value, int index) {
+ HUseListNode* previous = NULL;
+ HUseListNode* current = use_list_;
+ while (current != NULL) {
+ if (current->value() == value && current->index() == index) {
+ if (previous == NULL) {
+ use_list_ = current->tail();
+ } else {
+ previous->set_tail(current->tail());
+ }
+ break;
}
+
+ previous = current;
+ current = current->tail();
}
- return false;
+
+#ifdef DEBUG
+ // Do not reuse use list nodes in debug mode, zap them.
+ if (current != NULL) {
+ HUseListNode* temp =
+ new HUseListNode(current->value(), current->index(), NULL);
+ current->Zap();
+ current = temp;
+ }
+#endif
+ return current;
}
@@ -317,27 +354,42 @@ intptr_t HValue::Hashcode() {
}
+const char* HValue::Mnemonic() const {
+ switch (opcode()) {
+#define MAKE_CASE(type) case k##type: return #type;
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(MAKE_CASE)
+#undef MAKE_CASE
+ case kPhi: return "Phi";
+ default: return "";
+ }
+}
+
+
void HValue::SetOperandAt(int index, HValue* value) {
- ASSERT(value == NULL || !value->representation().IsNone());
RegisterUse(index, value);
InternalSetOperandAt(index, value);
}
-void HValue::ReplaceAndDelete(HValue* other) {
- if (other != NULL) ReplaceValue(other);
- Delete();
+void HValue::DeleteAndReplaceWith(HValue* other) {
+ // We replace all uses first, so Delete can assert that there are none.
+ if (other != NULL) ReplaceAllUsesWith(other);
+ ASSERT(HasNoUses());
+ ClearOperands();
+ DeleteFromGraph();
}
-void HValue::ReplaceValue(HValue* other) {
- for (int i = 0; i < uses_.length(); ++i) {
- HValue* use = uses_[i];
- ASSERT(!use->block()->IsStartBlock());
- InternalReplaceAtUse(use, other);
- other->uses_.Add(use);
+void HValue::ReplaceAllUsesWith(HValue* other) {
+ while (use_list_ != NULL) {
+ HUseListNode* list_node = use_list_;
+ HValue* value = list_node->value();
+ ASSERT(!value->block()->IsStartBlock());
+ value->InternalSetOperandAt(list_node->index(), other);
+ use_list_ = list_node->tail();
+ list_node->set_tail(other->use_list_);
+ other->use_list_ = list_node;
}
- uses_.Rewind(0);
}
@@ -348,55 +400,48 @@ void HValue::ClearOperands() {
}
-void HValue::Delete() {
- ASSERT(HasNoUses());
- ClearOperands();
- DeleteFromGraph();
-}
-
-
-void HValue::ReplaceAtUse(HValue* use, HValue* other) {
- for (int i = 0; i < use->OperandCount(); ++i) {
- if (use->OperandAt(i) == this) {
- use->SetOperandAt(i, other);
- }
+void HValue::SetBlock(HBasicBlock* block) {
+ ASSERT(block_ == NULL || block == NULL);
+ block_ = block;
+ if (id_ == kNoNumber && block != NULL) {
+ id_ = block->graph()->GetNextValueID(this);
}
}
-void HValue::ReplaceFirstAtUse(HValue* use, HValue* other, Representation r) {
- for (int i = 0; i < use->OperandCount(); ++i) {
- if (use->RequiredInputRepresentation(i).Equals(r) &&
- use->OperandAt(i) == this) {
- use->SetOperandAt(i, other);
- return;
- }
- }
+void HValue::PrintTypeTo(StringStream* stream) {
+ if (!representation().IsTagged() || type().Equals(HType::Tagged())) return;
+ stream->Add(" type[%s]", type().ToString());
}
-void HValue::InternalReplaceAtUse(HValue* use, HValue* other) {
- for (int i = 0; i < use->OperandCount(); ++i) {
- if (use->OperandAt(i) == this) {
- // Call internal method that does not update use lists. The caller is
- // responsible for doing so.
- use->InternalSetOperandAt(i, other);
- }
- }
+void HValue::PrintRangeTo(StringStream* stream) {
+ if (range() == NULL || range()->IsMostGeneric()) return;
+ stream->Add(" range[%d,%d,m0=%d]",
+ range()->lower(),
+ range()->upper(),
+ static_cast<int>(range()->CanBeMinusZero()));
}
-void HValue::SetBlock(HBasicBlock* block) {
- ASSERT(block_ == NULL || block == NULL);
- block_ = block;
- if (id_ == kNoNumber && block != NULL) {
- id_ = block->graph()->GetNextValueID(this);
+void HValue::PrintChangesTo(StringStream* stream) {
+ int changes_flags = (flags() & HValue::ChangesFlagsMask());
+ if (changes_flags == 0) return;
+ stream->Add(" changes[");
+ if (changes_flags == AllSideEffects()) {
+ stream->Add("*");
+ } else {
+ bool add_comma = false;
+#define PRINT_DO(type) \
+ if (changes_flags & (1 << kChanges##type)) { \
+ if (add_comma) stream->Add(","); \
+ add_comma = true; \
+ stream->Add(#type); \
+ }
+ GVN_FLAG_LIST(PRINT_DO);
+#undef PRINT_DO
}
-}
-
-
-void HValue::PrintTypeTo(HType type, StringStream* stream) {
- stream->Add(type.ToShortString());
+ stream->Add("]");
}
@@ -416,9 +461,20 @@ bool HValue::UpdateInferredType() {
void HValue::RegisterUse(int index, HValue* new_value) {
HValue* old_value = OperandAt(index);
if (old_value == new_value) return;
- if (old_value != NULL) old_value->uses_.RemoveElement(this);
+
+ HUseListNode* removed = NULL;
+ if (old_value != NULL) {
+ removed = old_value->RemoveUse(this, index);
+ }
+
if (new_value != NULL) {
- new_value->uses_.Add(this);
+ if (removed == NULL) {
+ new_value->use_list_ =
+ new HUseListNode(this, index, new_value->use_list_);
+ } else {
+ removed->set_tail(new_value->use_list_);
+ new_value->use_list_ = removed;
+ }
}
}
@@ -447,28 +503,18 @@ void HValue::ComputeInitialRange() {
void HInstruction::PrintTo(StringStream* stream) {
- stream->Add("%s", Mnemonic());
- if (HasSideEffects()) stream->Add("*");
- stream->Add(" ");
+ PrintMnemonicTo(stream);
PrintDataTo(stream);
+ PrintRangeTo(stream);
+ PrintChangesTo(stream);
+ PrintTypeTo(stream);
+}
- if (range() != NULL &&
- !range()->IsMostGeneric() &&
- !range()->CanBeMinusZero()) {
- stream->Add(" range[%d,%d,m0=%d]",
- range()->lower(),
- range()->upper(),
- static_cast<int>(range()->CanBeMinusZero()));
- }
-
- int changes_flags = (flags() & HValue::ChangesFlagsMask());
- if (changes_flags != 0) {
- stream->Add(" changes[0x%x]", changes_flags);
- }
- if (representation().IsTagged() && !type().Equals(HType::Tagged())) {
- stream->Add(" type[%s]", type().ToString());
- }
+void HInstruction::PrintMnemonicTo(StringStream* stream) {
+ stream->Add("%s", Mnemonic());
+ if (HasSideEffects()) stream->Add("*");
+ stream->Add(" ");
}
@@ -553,6 +599,8 @@ void HInstruction::Verify() {
ASSERT(cur == other_operand);
}
} else {
+ // If the following assert fires, you may have forgotten an
+ // AddInstruction.
ASSERT(other_block->Dominates(cur_block));
}
}
@@ -733,10 +781,38 @@ void HChange::PrintDataTo(StringStream* stream) {
}
-HCheckInstanceType* HCheckInstanceType::NewIsJSObjectOrJSFunction(
- HValue* value) {
- STATIC_ASSERT((LAST_JS_OBJECT_TYPE + 1) == JS_FUNCTION_TYPE);
- return new HCheckInstanceType(value, FIRST_JS_OBJECT_TYPE, JS_FUNCTION_TYPE);
+void HCheckInstanceType::GetCheckInterval(InstanceType* first,
+ InstanceType* last) {
+ ASSERT(is_interval_check());
+ switch (check_) {
+ case IS_JS_OBJECT_OR_JS_FUNCTION:
+ STATIC_ASSERT((LAST_JS_OBJECT_TYPE + 1) == JS_FUNCTION_TYPE);
+ *first = FIRST_JS_OBJECT_TYPE;
+ *last = JS_FUNCTION_TYPE;
+ return;
+ case IS_JS_ARRAY:
+ *first = *last = JS_ARRAY_TYPE;
+ return;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void HCheckInstanceType::GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag) {
+ ASSERT(!is_interval_check());
+ switch (check_) {
+ case IS_STRING:
+ *mask = kIsNotStringMask;
+ *tag = kStringTag;
+ return;
+ case IS_SYMBOL:
+ *mask = kIsSymbolMask;
+ *tag = kSymbolTag;
+ return;
+ default:
+ UNREACHABLE();
+ }
}
@@ -915,7 +991,7 @@ void HPhi::PrintTo(StringStream* stream) {
stream->Add(" ");
}
stream->Add(" uses%d_%di_%dd_%dt]",
- uses()->length(),
+ UseCount(),
int32_non_phi_uses() + int32_indirect_uses(),
double_non_phi_uses() + double_indirect_uses(),
tagged_non_phi_uses() + tagged_indirect_uses());
@@ -933,8 +1009,8 @@ void HPhi::AddInput(HValue* value) {
bool HPhi::HasRealUses() {
- for (int i = 0; i < uses()->length(); i++) {
- if (!uses()->at(i)->IsPhi()) return true;
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->IsPhi()) return true;
}
return false;
}
@@ -967,12 +1043,11 @@ void HPhi::DeleteFromGraph() {
void HPhi::InitRealUses(int phi_id) {
// Initialize real uses.
phi_id_ = phi_id;
- for (int j = 0; j < uses()->length(); j++) {
- HValue* use = uses()->at(j);
- if (!use->IsPhi()) {
- int index = use->LookupOperandIndex(0, this);
- Representation req_rep = use->RequiredInputRepresentation(index);
- non_phi_uses_[req_rep.kind()]++;
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ if (!value->IsPhi()) {
+ Representation rep = value->RequiredInputRepresentation(it.index());
+ ++non_phi_uses_[rep.kind()];
}
}
}
@@ -1017,10 +1092,9 @@ void HEnterInlined::PrintDataTo(StringStream* stream) {
HConstant::HConstant(Handle<Object> handle, Representation r)
: handle_(handle),
- constant_type_(HType::TypeFromValue(handle)),
has_int32_value_(false),
- int32_value_(0),
has_double_value_(false),
+ int32_value_(0),
double_value_(0) {
set_representation(r);
SetFlag(kUseGVN);
@@ -1194,13 +1268,23 @@ HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* object,
Handle<Map> map = types->at(i);
LookupResult lookup;
map->LookupInDescriptors(NULL, *name, &lookup);
- if (lookup.IsProperty() && lookup.type() == FIELD) {
- types_.Add(types->at(i));
- int index = lookup.GetLocalFieldIndexFromMap(*map);
- if (index < 0) {
- SetFlag(kDependsOnInobjectFields);
- } else {
- SetFlag(kDependsOnBackingStoreFields);
+ if (lookup.IsProperty()) {
+ switch (lookup.type()) {
+ case FIELD: {
+ int index = lookup.GetLocalFieldIndexFromMap(*map);
+ if (index < 0) {
+ SetFlag(kDependsOnInobjectFields);
+ } else {
+ SetFlag(kDependsOnBackingStoreFields);
+ }
+ types_.Add(types->at(i));
+ break;
+ }
+ case CONSTANT_FUNCTION:
+ types_.Add(types->at(i));
+ break;
+ default:
+ break;
}
}
}
@@ -1241,6 +1325,15 @@ void HLoadKeyedFastElement::PrintDataTo(StringStream* stream) {
}
+bool HLoadKeyedFastElement::RequiresHoleCheck() const {
+ for (HUseIterator it(uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ if (!use->IsChange()) return true;
+ }
+ return false;
+}
+
+
void HLoadKeyedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
stream->Add("[");
@@ -1275,6 +1368,9 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo(
case kExternalFloatArray:
stream->Add("float");
break;
+ case kExternalDoubleArray:
+ stream->Add("double");
+ break;
case kExternalPixelArray:
stream->Add("pixel");
break;
@@ -1352,6 +1448,9 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo(
case kExternalFloatArray:
stream->Add("float");
break;
+ case kExternalDoubleArray:
+ stream->Add("double");
+ break;
case kExternalPixelArray:
stream->Add("pixel");
break;
@@ -1439,7 +1538,7 @@ HType HPhi::CalculateInferredType() {
HType HConstant::CalculateInferredType() {
- return constant_type_;
+ return HType::TypeFromValue(handle_);
}
@@ -1543,6 +1642,13 @@ HValue* HChange::EnsureAndPropagateNotMinusZero(BitVector* visited) {
}
+HValue* HForceRepresentation::EnsureAndPropagateNotMinusZero(
+ BitVector* visited) {
+ visited->Add(id());
+ return value();
+}
+
+
HValue* HMod::EnsureAndPropagateNotMinusZero(BitVector* visited) {
visited->Add(id());
if (range() == NULL || range()->CanBeMinusZero()) {
@@ -1593,6 +1699,13 @@ HValue* HAdd::EnsureAndPropagateNotMinusZero(BitVector* visited) {
}
+void HIn::PrintDataTo(StringStream* stream) {
+ key()->PrintNameTo(stream);
+ stream->Add(" ");
+ object()->PrintNameTo(stream);
+}
+
+
// Node-specific verification code is only included in debug mode.
#ifdef DEBUG
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index d5f4ea13..4d32edd7 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -30,7 +30,9 @@
#include "v8.h"
+#include "allocation.h"
#include "code-stubs.h"
+#include "data-flow.h"
#include "small-pointer-list.h"
#include "string-stream.h"
#include "zone.h"
@@ -48,18 +50,10 @@ class LInstruction;
class LChunkBuilder;
-#define HYDROGEN_ALL_INSTRUCTION_LIST(V) \
- V(ArithmeticBinaryOperation) \
- V(BinaryCall) \
- V(BinaryOperation) \
+#define HYDROGEN_ABSTRACT_INSTRUCTION_LIST(V) \
V(BitwiseBinaryOperation) \
V(ControlInstruction) \
V(Instruction) \
- V(Phi) \
- V(UnaryCall) \
- V(UnaryControlInstruction) \
- V(UnaryOperation) \
- HYDROGEN_CONCRETE_INSTRUCTION_LIST(V)
#define HYDROGEN_CONCRETE_INSTRUCTION_LIST(V) \
@@ -93,10 +87,12 @@ class LChunkBuilder;
V(CheckNonSmi) \
V(CheckPrototypeMaps) \
V(CheckSmi) \
+ V(ClampToUint8) \
V(ClassOfTest) \
V(Compare) \
V(CompareJSObjectEq) \
V(CompareMap) \
+ V(CompareSymbolEq) \
V(Constant) \
V(Context) \
V(DeleteProperty) \
@@ -105,6 +101,7 @@ class LChunkBuilder;
V(EnterInlined) \
V(ExternalArrayLength) \
V(FixedArrayLength) \
+ V(ForceRepresentation) \
V(FunctionLiteral) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
@@ -112,12 +109,15 @@ class LChunkBuilder;
V(Goto) \
V(HasInstanceType) \
V(HasCachedArrayIndex) \
+ V(In) \
V(InstanceOf) \
V(InstanceOfKnownGlobal) \
+ V(InvokeFunction) \
+ V(IsConstructCall) \
V(IsNull) \
V(IsObject) \
V(IsSmi) \
- V(IsConstructCall) \
+ V(IsUndetectable) \
V(JSArrayLength) \
V(LeaveInlined) \
V(LoadContextSlot) \
@@ -155,6 +155,7 @@ class LChunkBuilder;
V(StoreKeyedGeneric) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -180,19 +181,21 @@ class LChunkBuilder;
V(ContextSlots) \
V(OsrEntries)
-#define DECLARE_INSTRUCTION(type) \
+#define DECLARE_ABSTRACT_INSTRUCTION(type) \
virtual bool Is##type() const { return true; } \
static H##type* cast(HValue* value) { \
ASSERT(value->Is##type()); \
return reinterpret_cast<H##type*>(value); \
- } \
- Opcode opcode() const { return HValue::k##type; }
+ }
-#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
+#define DECLARE_CONCRETE_INSTRUCTION(type) \
virtual LInstruction* CompileToLithium(LChunkBuilder* builder); \
- virtual const char* Mnemonic() const { return mnemonic; } \
- DECLARE_INSTRUCTION(type)
+ static H##type* cast(HValue* value) { \
+ ASSERT(value->Is##type()); \
+ return reinterpret_cast<H##type*>(value); \
+ } \
+ virtual Opcode opcode() const { return HValue::k##type; }
class Range: public ZoneObject {
@@ -407,6 +410,62 @@ class HType {
};
+class HUseListNode: public ZoneObject {
+ public:
+ HUseListNode(HValue* value, int index, HUseListNode* tail)
+ : tail_(tail), value_(value), index_(index) {
+ }
+
+ HUseListNode* tail() const { return tail_; }
+ HValue* value() const { return value_; }
+ int index() const { return index_; }
+
+ void set_tail(HUseListNode* list) { tail_ = list; }
+
+#ifdef DEBUG
+ void Zap() {
+ tail_ = reinterpret_cast<HUseListNode*>(1);
+ value_ = NULL;
+ index_ = -1;
+ }
+#endif
+
+ private:
+ HUseListNode* tail_;
+ HValue* value_;
+ int index_;
+};
+
+
+// We reuse use list nodes behind the scenes as uses are added and deleted.
+// This class is the safe way to iterate uses while deleting them.
+class HUseIterator BASE_EMBEDDED {
+ public:
+ bool Done() { return current_ == NULL; }
+ void Advance();
+
+ HValue* value() {
+ ASSERT(!Done());
+ return value_;
+ }
+
+ int index() {
+ ASSERT(!Done());
+ return index_;
+ }
+
+ private:
+ explicit HUseIterator(HUseListNode* head);
+
+ HUseListNode* current_;
+ HUseListNode* next_;
+ HValue* value_;
+ int index_;
+
+ friend class HValue;
+};
+
+
class HValue: public ZoneObject {
public:
static const int kNoNumber = -1;
@@ -455,15 +514,30 @@ class HValue: public ZoneObject {
enum Opcode {
// Declare a unique enum value for each hydrogen instruction.
- #define DECLARE_DO(type) k##type,
- HYDROGEN_ALL_INSTRUCTION_LIST(DECLARE_DO)
- #undef DECLARE_DO
- kMaxInstructionClass
+ #define DECLARE_OPCODE(type) k##type,
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE)
+ kPhi
+ #undef DECLARE_OPCODE
};
+ virtual Opcode opcode() const = 0;
+
+ // Declare a non-virtual predicates for each concrete HInstruction or HValue.
+ #define DECLARE_PREDICATE(type) \
+ bool Is##type() const { return opcode() == k##type; }
+ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE)
+ #undef DECLARE_PREDICATE
+ bool IsPhi() const { return opcode() == kPhi; }
+
+ // Declare virtual predicates for abstract HInstruction or HValue
+ #define DECLARE_PREDICATE(type) \
+ virtual bool Is##type() const { return false; }
+ HYDROGEN_ABSTRACT_INSTRUCTION_LIST(DECLARE_PREDICATE)
+ #undef DECLARE_PREDICATE
HValue() : block_(NULL),
id_(kNoNumber),
type_(HType::Tagged()),
+ use_list_(NULL),
range_(NULL),
flags_(0) {}
virtual ~HValue() {}
@@ -474,22 +548,24 @@ class HValue: public ZoneObject {
int id() const { return id_; }
void set_id(int id) { id_ = id; }
- SmallPointerList<HValue>* uses() { return &uses_; }
+ HUseIterator uses() const { return HUseIterator(use_list_); }
virtual bool EmitAtUses() { return false; }
Representation representation() const { return representation_; }
void ChangeRepresentation(Representation r) {
// Representation was already set and is allowed to be changed.
- ASSERT(!representation_.IsNone());
ASSERT(!r.IsNone());
ASSERT(CheckFlag(kFlexibleRepresentation));
RepresentationChanged(r);
representation_ = r;
}
+ void AssumeRepresentation(Representation r);
+
+ virtual bool IsConvertibleToInteger() const { return true; }
HType type() const { return type_; }
void set_type(HType type) {
- ASSERT(uses_.length() == 0);
+ ASSERT(HasNoUses());
type_ = type;
}
@@ -515,16 +591,14 @@ class HValue: public ZoneObject {
virtual HValue* OperandAt(int index) = 0;
void SetOperandAt(int index, HValue* value);
- int LookupOperandIndex(int occurrence_index, HValue* op);
- bool UsesMultipleTimes(HValue* op);
-
- void ReplaceAndDelete(HValue* other);
- void ReplaceValue(HValue* other);
- void ReplaceAtUse(HValue* use, HValue* other);
- void ReplaceFirstAtUse(HValue* use, HValue* other, Representation r);
- bool HasNoUses() const { return uses_.is_empty(); }
+ void DeleteAndReplaceWith(HValue* other);
+ void ReplaceAllUsesWith(HValue* other);
+ bool HasNoUses() const { return use_list_ == NULL; }
+ bool HasMultipleUses() const {
+ return use_list_ != NULL && use_list_->tail() != NULL;
+ }
+ int UseCount() const;
void ClearOperands();
- void Delete();
int flags() const { return flags_; }
void SetFlag(Flag f) { flags_ |= (1 << f); }
@@ -554,21 +628,17 @@ class HValue: public ZoneObject {
// then return it. Return NULL to have the instruction deleted.
virtual HValue* Canonicalize() { return this; }
- // Declare virtual type testers.
-#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
- HYDROGEN_ALL_INSTRUCTION_LIST(DECLARE_DO)
-#undef DECLARE_DO
-
bool Equals(HValue* other);
virtual intptr_t Hashcode();
// Printing support.
virtual void PrintTo(StringStream* stream) = 0;
void PrintNameTo(StringStream* stream);
- static void PrintTypeTo(HType type, StringStream* stream);
+ void PrintTypeTo(StringStream* stream);
+ void PrintRangeTo(StringStream* stream);
+ void PrintChangesTo(StringStream* stream);
- virtual const char* Mnemonic() const = 0;
- virtual Opcode opcode() const = 0;
+ const char* Mnemonic() const;
// Updated the inferred type of this instruction and returns true if
// it has changed.
@@ -608,7 +678,10 @@ class HValue: public ZoneObject {
return ChangesFlagsMask() & ~(1 << kChangesOsrEntries);
}
- void InternalReplaceAtUse(HValue* use, HValue* other);
+ // Remove the matching use from the use list if present. Returns the
+ // removed list node or NULL.
+ HUseListNode* RemoveUse(HValue* value, int index);
+
void RegisterUse(int index, HValue* new_value);
HBasicBlock* block_;
@@ -619,7 +692,7 @@ class HValue: public ZoneObject {
Representation representation_;
HType type_;
- SmallPointerList<HValue> uses_;
+ HUseListNode* use_list_;
Range* range_;
int flags_;
@@ -656,7 +729,7 @@ class HInstruction: public HValue {
virtual bool IsCall() { return false; }
- DECLARE_INSTRUCTION(Instruction)
+ DECLARE_ABSTRACT_INSTRUCTION(Instruction)
protected:
HInstruction()
@@ -674,6 +747,8 @@ class HInstruction: public HValue {
SetBlock(block);
}
+ void PrintMnemonicTo(StringStream* stream);
+
HInstruction* next_;
HInstruction* previous_;
int position_;
@@ -693,7 +768,7 @@ class HControlInstruction: public HInstruction {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_INSTRUCTION(ControlInstruction)
+ DECLARE_ABSTRACT_INSTRUCTION(ControlInstruction)
private:
HBasicBlock* first_successor_;
@@ -765,7 +840,7 @@ class HBlockEntry: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(BlockEntry, "block_entry")
+ DECLARE_CONCRETE_INSTRUCTION(BlockEntry)
};
@@ -787,7 +862,12 @@ class HDeoptimize: public HControlInstruction {
SetOperandAt(values_.length() - 1, value);
}
- DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
+ DECLARE_CONCRETE_INSTRUCTION(Deoptimize)
+
+ enum UseEnvironment {
+ kNoUses,
+ kUseAll
+ };
protected:
virtual void InternalSetOperandAt(int index, HValue* value) {
@@ -814,7 +894,7 @@ class HGoto: public HTemplateControlInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(Goto, "goto")
+ DECLARE_CONCRETE_INSTRUCTION(Goto)
private:
bool include_stack_check_;
@@ -833,8 +913,6 @@ class HUnaryControlInstruction: public HTemplateControlInstruction<1> {
virtual void PrintDataTo(StringStream* stream);
HValue* value() { return OperandAt(0); }
-
- DECLARE_INSTRUCTION(UnaryControlInstruction)
};
@@ -849,7 +927,7 @@ class HTest: public HUnaryControlInstruction {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(Test, "test")
+ DECLARE_CONCRETE_INSTRUCTION(Test)
};
@@ -874,7 +952,7 @@ class HCompareMap: public HUnaryControlInstruction {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(CompareMap, "compare_map")
+ DECLARE_CONCRETE_INSTRUCTION(CompareMap)
private:
Handle<Map> map_;
@@ -891,7 +969,7 @@ class HReturn: public HUnaryControlInstruction {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(Return, "return")
+ DECLARE_CONCRETE_INSTRUCTION(Return)
};
@@ -903,7 +981,7 @@ class HAbnormalExit: public HTemplateControlInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(AbnormalExit, "abnormal_exit")
+ DECLARE_CONCRETE_INSTRUCTION(AbnormalExit)
};
@@ -915,8 +993,6 @@ class HUnaryOperation: public HTemplateInstruction<1> {
HValue* value() { return OperandAt(0); }
virtual void PrintDataTo(StringStream* stream);
-
- DECLARE_INSTRUCTION(UnaryOperation)
};
@@ -930,7 +1006,26 @@ class HThrow: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
+ DECLARE_CONCRETE_INSTRUCTION(Throw)
+};
+
+
+class HForceRepresentation: public HTemplateInstruction<1> {
+ public:
+ HForceRepresentation(HValue* value, Representation required_representation) {
+ SetOperandAt(0, value);
+ set_representation(required_representation);
+ }
+
+ HValue* value() { return OperandAt(0); }
+
+ virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return representation(); // Same as the output representation.
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ForceRepresentation)
};
@@ -968,8 +1063,7 @@ class HChange: public HUnaryOperation {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(Change,
- CanTruncateToInt32() ? "truncate" : "change")
+ DECLARE_CONCRETE_INSTRUCTION(Change)
protected:
virtual bool DataEquals(HValue* other) {
@@ -986,6 +1080,46 @@ class HChange: public HUnaryOperation {
};
+class HClampToUint8: public HUnaryOperation {
+ public:
+ explicit HClampToUint8(HValue* value)
+ : HUnaryOperation(value),
+ input_rep_(Representation::None()) {
+ SetFlag(kFlexibleRepresentation);
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return input_rep_;
+ }
+
+ virtual Representation InferredRepresentation() {
+ // TODO(danno): Inference on input types should happen separately from
+ // return representation.
+ Representation new_rep = value()->representation();
+ if (input_rep_.IsNone()) {
+ if (!new_rep.IsNone()) {
+ input_rep_ = new_rep;
+ return Representation::Integer32();
+ } else {
+ return Representation::None();
+ }
+ } else {
+ return Representation::Integer32();
+ }
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampToUint8)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+
+ private:
+ Representation input_rep_;
+};
+
+
class HSimulate: public HInstruction {
public:
HSimulate(int ast_id, int pop_count)
@@ -1026,7 +1160,7 @@ class HSimulate: public HInstruction {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(Simulate, "simulate")
+ DECLARE_CONCRETE_INSTRUCTION(Simulate)
#ifdef DEBUG
virtual void Verify();
@@ -1062,30 +1196,36 @@ class HStackCheck: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack_check")
+ DECLARE_CONCRETE_INSTRUCTION(StackCheck)
};
class HEnterInlined: public HTemplateInstruction<0> {
public:
- HEnterInlined(Handle<JSFunction> closure, FunctionLiteral* function)
- : closure_(closure), function_(function) {
+ HEnterInlined(Handle<JSFunction> closure,
+ FunctionLiteral* function,
+ CallKind call_kind)
+ : closure_(closure),
+ function_(function),
+ call_kind_(call_kind) {
}
virtual void PrintDataTo(StringStream* stream);
Handle<JSFunction> closure() const { return closure_; }
FunctionLiteral* function() const { return function_; }
+ CallKind call_kind() const { return call_kind_; }
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(EnterInlined, "enter_inlined")
+ DECLARE_CONCRETE_INSTRUCTION(EnterInlined)
private:
Handle<JSFunction> closure_;
FunctionLiteral* function_;
+ CallKind call_kind_;
};
@@ -1097,7 +1237,7 @@ class HLeaveInlined: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(LeaveInlined, "leave_inlined")
+ DECLARE_CONCRETE_INSTRUCTION(LeaveInlined)
};
@@ -1113,7 +1253,7 @@ class HPushArgument: public HUnaryOperation {
HValue* argument() { return OperandAt(0); }
- DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push_argument")
+ DECLARE_CONCRETE_INSTRUCTION(PushArgument)
};
@@ -1128,7 +1268,7 @@ class HContext: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(Context, "context");
+ DECLARE_CONCRETE_INSTRUCTION(Context);
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1142,7 +1282,7 @@ class HOuterContext: public HUnaryOperation {
SetFlag(kUseGVN);
}
- DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer_context");
+ DECLARE_CONCRETE_INSTRUCTION(OuterContext);
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
@@ -1160,7 +1300,7 @@ class HGlobalObject: public HUnaryOperation {
SetFlag(kUseGVN);
}
- DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global_object")
+ DECLARE_CONCRETE_INSTRUCTION(GlobalObject)
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
@@ -1179,7 +1319,7 @@ class HGlobalReceiver: public HUnaryOperation {
SetFlag(kUseGVN);
}
- DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global_receiver")
+ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver)
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
@@ -1224,8 +1364,6 @@ class HUnaryCall: public HCall<1> {
virtual void PrintDataTo(StringStream* stream);
HValue* value() { return OperandAt(0); }
-
- DECLARE_INSTRUCTION(UnaryCall)
};
@@ -1245,8 +1383,23 @@ class HBinaryCall: public HCall<2> {
HValue* first() { return OperandAt(0); }
HValue* second() { return OperandAt(1); }
+};
+
- DECLARE_INSTRUCTION(BinaryCall)
+class HInvokeFunction: public HBinaryCall {
+ public:
+ HInvokeFunction(HValue* context, HValue* function, int argument_count)
+ : HBinaryCall(context, function, argument_count) {
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ HValue* context() { return first(); }
+ HValue* function() { return second(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
};
@@ -1268,7 +1421,7 @@ class HCallConstantFunction: public HCall<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction, "call_constant_function")
+ DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction)
private:
Handle<JSFunction> function_;
@@ -1288,7 +1441,7 @@ class HCallKeyed: public HBinaryCall {
HValue* context() { return first(); }
HValue* key() { return second(); }
- DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call_keyed")
+ DECLARE_CONCRETE_INSTRUCTION(CallKeyed)
};
@@ -1303,7 +1456,7 @@ class HCallNamed: public HUnaryCall {
HValue* context() { return value(); }
Handle<String> name() const { return name_; }
- DECLARE_CONCRETE_INSTRUCTION(CallNamed, "call_named")
+ DECLARE_CONCRETE_INSTRUCTION(CallNamed)
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
@@ -1326,7 +1479,7 @@ class HCallFunction: public HUnaryCall {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call_function")
+ DECLARE_CONCRETE_INSTRUCTION(CallFunction)
};
@@ -1345,7 +1498,7 @@ class HCallGlobal: public HUnaryCall {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(CallGlobal, "call_global")
+ DECLARE_CONCRETE_INSTRUCTION(CallGlobal)
private:
Handle<String> name_;
@@ -1365,7 +1518,7 @@ class HCallKnownGlobal: public HCall<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal, "call_known_global")
+ DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal)
private:
Handle<JSFunction> target_;
@@ -1385,7 +1538,7 @@ class HCallNew: public HBinaryCall {
HValue* context() { return first(); }
HValue* constructor() { return second(); }
- DECLARE_CONCRETE_INSTRUCTION(CallNew, "call_new")
+ DECLARE_CONCRETE_INSTRUCTION(CallNew)
};
@@ -1404,7 +1557,7 @@ class HCallRuntime: public HCall<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call_runtime")
+ DECLARE_CONCRETE_INSTRUCTION(CallRuntime)
private:
const Runtime::Function* c_function_;
@@ -1428,7 +1581,7 @@ class HJSArrayLength: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js_array_length")
+ DECLARE_CONCRETE_INSTRUCTION(JSArrayLength)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1447,7 +1600,7 @@ class HFixedArrayLength: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed_array_length")
+ DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1468,7 +1621,7 @@ class HExternalArrayLength: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(ExternalArrayLength, "external_array_length")
+ DECLARE_CONCRETE_INSTRUCTION(ExternalArrayLength)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1488,7 +1641,7 @@ class HBitNot: public HUnaryOperation {
}
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(BitNot, "bit_not")
+ DECLARE_CONCRETE_INSTRUCTION(BitNot)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1560,7 +1713,7 @@ class HUnaryMathOperation: public HUnaryOperation {
BuiltinFunctionId op() const { return op_; }
const char* OpName() const;
- DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary_math_operation")
+ DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation)
protected:
virtual bool DataEquals(HValue* other) {
@@ -1585,7 +1738,7 @@ class HLoadElements: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements")
+ DECLARE_CONCRETE_INSTRUCTION(LoadElements)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1608,8 +1761,7 @@ class HLoadExternalArrayPointer: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer,
- "load-external-array-pointer")
+ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1639,7 +1791,7 @@ class HCheckMap: public HUnaryOperation {
Handle<Map> map() const { return map_; }
- DECLARE_CONCRETE_INSTRUCTION(CheckMap, "check_map")
+ DECLARE_CONCRETE_INSTRUCTION(CheckMap)
protected:
virtual bool DataEquals(HValue* other) {
@@ -1674,7 +1826,7 @@ class HCheckFunction: public HUnaryOperation {
Handle<JSFunction> target() const { return target_; }
- DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check_function")
+ DECLARE_CONCRETE_INSTRUCTION(CheckFunction)
protected:
virtual bool DataEquals(HValue* other) {
@@ -1689,19 +1841,17 @@ class HCheckFunction: public HUnaryOperation {
class HCheckInstanceType: public HUnaryOperation {
public:
- // Check that the instance type is in the range [first, last] where
- // both first and last are included.
- HCheckInstanceType(HValue* value, InstanceType first, InstanceType last)
- : HUnaryOperation(value), first_(first), last_(last) {
- ASSERT(first <= last);
- set_representation(Representation::Tagged());
- SetFlag(kUseGVN);
- if ((FIRST_STRING_TYPE < first && last <= LAST_STRING_TYPE) ||
- (FIRST_STRING_TYPE <= first && last < LAST_STRING_TYPE)) {
- // A particular string instance type can change because of GC or
- // externalization, but the value still remains a string.
- SetFlag(kDependsOnMaps);
- }
+ static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value) {
+ return new HCheckInstanceType(value, IS_JS_OBJECT_OR_JS_FUNCTION);
+ }
+ static HCheckInstanceType* NewIsJSArray(HValue* value) {
+ return new HCheckInstanceType(value, IS_JS_ARRAY);
+ }
+ static HCheckInstanceType* NewIsString(HValue* value) {
+ return new HCheckInstanceType(value, IS_STRING);
+ }
+ static HCheckInstanceType* NewIsSymbol(HValue* value) {
+ return new HCheckInstanceType(value, IS_SYMBOL);
}
virtual bool IsCheckInstruction() const { return true; }
@@ -1714,12 +1864,20 @@ class HCheckInstanceType: public HUnaryOperation {
virtual void Verify();
#endif
- static HCheckInstanceType* NewIsJSObjectOrJSFunction(HValue* value);
+ virtual HValue* Canonicalize() {
+ if (!value()->type().IsUninitialized() &&
+ value()->type().IsString() &&
+ check_ == IS_STRING) {
+ return NULL;
+ }
+ return this;
+ }
- InstanceType first() const { return first_; }
- InstanceType last() const { return last_; }
+ bool is_interval_check() const { return check_ <= LAST_INTERVAL_CHECK; }
+ void GetCheckInterval(InstanceType* first, InstanceType* last);
+ void GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag);
- DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check_instance_type")
+ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType)
protected:
// TODO(ager): It could be nice to allow the ommision of instance
@@ -1727,12 +1885,25 @@ class HCheckInstanceType: public HUnaryOperation {
// with a larger range.
virtual bool DataEquals(HValue* other) {
HCheckInstanceType* b = HCheckInstanceType::cast(other);
- return (first_ == b->first()) && (last_ == b->last());
+ return check_ == b->check_;
}
private:
- InstanceType first_;
- InstanceType last_;
+ enum Check {
+ IS_JS_OBJECT_OR_JS_FUNCTION,
+ IS_JS_ARRAY,
+ IS_STRING,
+ IS_SYMBOL,
+ LAST_INTERVAL_CHECK = IS_JS_ARRAY
+ };
+
+ HCheckInstanceType(HValue* value, Check check)
+ : HUnaryOperation(value), check_(check) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ }
+
+ const Check check_;
};
@@ -1755,7 +1926,19 @@ class HCheckNonSmi: public HUnaryOperation {
virtual void Verify();
#endif
- DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check_non_smi")
+ virtual HValue* Canonicalize() {
+ HType value_type = value()->type();
+ if (!value_type.IsUninitialized() &&
+ (value_type.IsHeapNumber() ||
+ value_type.IsString() ||
+ value_type.IsBoolean() ||
+ value_type.IsNonPrimitive())) {
+ return NULL;
+ }
+ return this;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1779,7 +1962,7 @@ class HCheckPrototypeMaps: public HTemplateInstruction<0> {
Handle<JSObject> prototype() const { return prototype_; }
Handle<JSObject> holder() const { return holder_; }
- DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check_prototype_maps")
+ DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps)
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::None();
@@ -1823,7 +2006,7 @@ class HCheckSmi: public HUnaryOperation {
virtual void Verify();
#endif
- DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check_smi")
+ DECLARE_CONCRETE_INSTRUCTION(CheckSmi)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -1836,7 +2019,8 @@ class HPhi: public HValue {
: inputs_(2),
merged_index_(merged_index),
phi_id_(-1),
- is_live_(false) {
+ is_live_(false),
+ is_convertible_to_integer_(true) {
for (int i = 0; i < Representation::kNumRepresentations; i++) {
non_phi_uses_[i] = 0;
indirect_uses_[i] = 0;
@@ -1876,16 +2060,12 @@ class HPhi: public HValue {
int merged_index() const { return merged_index_; }
- virtual const char* Mnemonic() const { return "phi"; }
-
virtual void PrintTo(StringStream* stream);
#ifdef DEBUG
virtual void Verify();
#endif
- DECLARE_INSTRUCTION(Phi)
-
void InitRealUses(int id);
void AddNonPhiUsesFrom(HPhi* other);
void AddIndirectUsesTo(int* use_count);
@@ -1912,6 +2092,20 @@ class HPhi: public HValue {
bool is_live() { return is_live_; }
void set_is_live(bool b) { is_live_ = b; }
+ static HPhi* cast(HValue* value) {
+ ASSERT(value->IsPhi());
+ return reinterpret_cast<HPhi*>(value);
+ }
+ virtual Opcode opcode() const { return HValue::kPhi; }
+
+ virtual bool IsConvertibleToInteger() const {
+ return is_convertible_to_integer_;
+ }
+
+ void set_is_convertible_to_integer(bool b) {
+ is_convertible_to_integer_ = b;
+ }
+
protected:
virtual void DeleteFromGraph();
virtual void InternalSetOperandAt(int index, HValue* value) {
@@ -1926,6 +2120,7 @@ class HPhi: public HValue {
int indirect_uses_[Representation::kNumRepresentations];
int phi_id_;
bool is_live_;
+ bool is_convertible_to_integer_;
};
@@ -1940,7 +2135,7 @@ class HArgumentsObject: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject, "arguments-object")
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject)
};
@@ -1956,6 +2151,14 @@ class HConstant: public HTemplateInstruction<0> {
return Representation::None();
}
+ virtual bool IsConvertibleToInteger() const {
+ if (handle_->IsSmi()) return true;
+ if (handle_->IsHeapNumber() &&
+ (HeapNumber::cast(*handle_)->value() ==
+ static_cast<double>(NumberToInt32(*handle_)))) return true;
+ return false;
+ }
+
virtual bool EmitAtUses() { return !representation().IsDouble(); }
virtual void PrintDataTo(StringStream* stream);
virtual HType CalculateInferredType();
@@ -1985,7 +2188,7 @@ class HConstant: public HTemplateInstruction<0> {
virtual void Verify() { }
#endif
- DECLARE_CONCRETE_INSTRUCTION(Constant, "constant")
+ DECLARE_CONCRETE_INSTRUCTION(Constant)
protected:
virtual Range* InferRange();
@@ -1997,14 +2200,13 @@ class HConstant: public HTemplateInstruction<0> {
private:
Handle<Object> handle_;
- HType constant_type_;
// The following two values represent the int32 and the double value of the
// given constant if there is a lossless conversion between the constant
// and the specific representation.
- bool has_int32_value_;
+ bool has_int32_value_ : 1;
+ bool has_double_value_ : 1;
int32_t int32_value_;
- bool has_double_value_;
double double_value_;
};
@@ -2034,8 +2236,6 @@ class HBinaryOperation: public HTemplateInstruction<2> {
virtual bool IsCommutative() const { return false; }
virtual void PrintDataTo(StringStream* stream);
-
- DECLARE_INSTRUCTION(BinaryOperation)
};
@@ -2065,7 +2265,7 @@ class HApplyArguments: public HTemplateInstruction<4> {
HValue* length() { return OperandAt(2); }
HValue* elements() { return OperandAt(3); }
- DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply_arguments")
+ DECLARE_CONCRETE_INSTRUCTION(ApplyArguments)
};
@@ -2078,7 +2278,7 @@ class HArgumentsElements: public HTemplateInstruction<0> {
SetFlag(kUseGVN);
}
- DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments_elements")
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements)
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::None();
@@ -2100,7 +2300,7 @@ class HArgumentsLength: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments_length")
+ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2130,7 +2330,7 @@ class HAccessArgumentsAt: public HTemplateInstruction<3> {
HValue* length() { return OperandAt(1); }
HValue* index() { return OperandAt(2); }
- DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access_arguments_at")
+ DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt)
virtual bool DataEquals(HValue* other) { return true; }
};
@@ -2157,7 +2357,7 @@ class HBoundsCheck: public HBinaryOperation {
HValue* index() { return left(); }
HValue* length() { return right(); }
- DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds_check")
+ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2188,7 +2388,7 @@ class HBitwiseBinaryOperation: public HBinaryOperation {
virtual HType CalculateInferredType();
- DECLARE_INSTRUCTION(BitwiseBinaryOperation)
+ DECLARE_ABSTRACT_INSTRUCTION(BitwiseBinaryOperation)
};
@@ -2218,8 +2418,6 @@ class HArithmeticBinaryOperation: public HBinaryOperation {
}
return HValue::InferredRepresentation();
}
-
- DECLARE_INSTRUCTION(ArithmeticBinaryOperation)
};
@@ -2235,7 +2433,7 @@ class HCompare: public HBinaryOperation {
void SetInputRepresentation(Representation r);
virtual bool EmitAtUses() {
- return !HasSideEffects() && (uses()->length() <= 1);
+ return !HasSideEffects() && !HasMultipleUses();
}
virtual Representation RequiredInputRepresentation(int index) const {
@@ -2253,7 +2451,7 @@ class HCompare: public HBinaryOperation {
return HValue::Hashcode() * 7 + token_;
}
- DECLARE_CONCRETE_INSTRUCTION(Compare, "compare")
+ DECLARE_CONCRETE_INSTRUCTION(Compare)
protected:
virtual bool DataEquals(HValue* other) {
@@ -2277,7 +2475,7 @@ class HCompareJSObjectEq: public HBinaryOperation {
}
virtual bool EmitAtUses() {
- return !HasSideEffects() && (uses()->length() <= 1);
+ return !HasSideEffects() && !HasMultipleUses();
}
virtual Representation RequiredInputRepresentation(int index) const {
@@ -2285,13 +2483,47 @@ class HCompareJSObjectEq: public HBinaryOperation {
}
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(CompareJSObjectEq, "compare-js-object-eq")
+ DECLARE_CONCRETE_INSTRUCTION(CompareJSObjectEq)
protected:
virtual bool DataEquals(HValue* other) { return true; }
};
+class HCompareSymbolEq: public HBinaryOperation {
+ public:
+ HCompareSymbolEq(HValue* left, HValue* right, Token::Value op)
+ : HBinaryOperation(left, right), op_(op) {
+ ASSERT(op == Token::EQ || op == Token::EQ_STRICT);
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetFlag(kDependsOnMaps);
+ }
+
+ Token::Value op() const { return op_; }
+
+ virtual bool EmitAtUses() {
+ return !HasSideEffects() && !HasMultipleUses();
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ virtual HType CalculateInferredType() { return HType::Boolean(); }
+
+ DECLARE_CONCRETE_INSTRUCTION(CompareSymbolEq);
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ return op_ == HCompareSymbolEq::cast(other)->op_;
+ }
+
+ private:
+ const Token::Value op_;
+};
+
+
class HUnaryPredicate: public HUnaryOperation {
public:
explicit HUnaryPredicate(HValue* value) : HUnaryOperation(value) {
@@ -2300,7 +2532,7 @@ class HUnaryPredicate: public HUnaryOperation {
}
virtual bool EmitAtUses() {
- return !HasSideEffects() && (uses()->length() <= 1);
+ return !HasSideEffects() && !HasMultipleUses();
}
virtual Representation RequiredInputRepresentation(int index) const {
@@ -2317,7 +2549,7 @@ class HIsNull: public HUnaryPredicate {
bool is_strict() const { return is_strict_; }
- DECLARE_CONCRETE_INSTRUCTION(IsNull, "is_null")
+ DECLARE_CONCRETE_INSTRUCTION(IsNull)
protected:
virtual bool DataEquals(HValue* other) {
@@ -2334,7 +2566,7 @@ class HIsObject: public HUnaryPredicate {
public:
explicit HIsObject(HValue* value) : HUnaryPredicate(value) { }
- DECLARE_CONCRETE_INSTRUCTION(IsObject, "is_object")
+ DECLARE_CONCRETE_INSTRUCTION(IsObject)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2345,7 +2577,18 @@ class HIsSmi: public HUnaryPredicate {
public:
explicit HIsSmi(HValue* value) : HUnaryPredicate(value) { }
- DECLARE_CONCRETE_INSTRUCTION(IsSmi, "is_smi")
+ DECLARE_CONCRETE_INSTRUCTION(IsSmi)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+
+class HIsUndetectable: public HUnaryPredicate {
+ public:
+ explicit HIsUndetectable(HValue* value) : HUnaryPredicate(value) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectable)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2360,14 +2603,14 @@ class HIsConstructCall: public HTemplateInstruction<0> {
}
virtual bool EmitAtUses() {
- return !HasSideEffects() && (uses()->length() <= 1);
+ return !HasSideEffects() && !HasMultipleUses();
}
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(IsConstructCall, "is_construct_call")
+ DECLARE_CONCRETE_INSTRUCTION(IsConstructCall)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2388,7 +2631,7 @@ class HHasInstanceType: public HUnaryPredicate {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(HasInstanceType, "has_instance_type")
+ DECLARE_CONCRETE_INSTRUCTION(HasInstanceType)
protected:
virtual bool DataEquals(HValue* other) {
@@ -2406,7 +2649,7 @@ class HHasCachedArrayIndex: public HUnaryPredicate {
public:
explicit HHasCachedArrayIndex(HValue* value) : HUnaryPredicate(value) { }
- DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndex, "has_cached_array_index")
+ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndex)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2417,7 +2660,7 @@ class HGetCachedArrayIndex: public HUnaryPredicate {
public:
explicit HGetCachedArrayIndex(HValue* value) : HUnaryPredicate(value) { }
- DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get_cached_array_index")
+ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2429,7 +2672,7 @@ class HClassOfTest: public HUnaryPredicate {
HClassOfTest(HValue* value, Handle<String> class_name)
: HUnaryPredicate(value), class_name_(class_name) { }
- DECLARE_CONCRETE_INSTRUCTION(ClassOfTest, "class_of_test")
+ DECLARE_CONCRETE_INSTRUCTION(ClassOfTest)
virtual void PrintDataTo(StringStream* stream);
@@ -2454,7 +2697,7 @@ class HTypeofIs: public HUnaryPredicate {
Handle<String> type_literal() { return type_literal_; }
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(TypeofIs, "typeof_is")
+ DECLARE_CONCRETE_INSTRUCTION(TypeofIs)
protected:
virtual bool DataEquals(HValue* other) {
@@ -2482,7 +2725,7 @@ class HInstanceOf: public HTemplateInstruction<3> {
HValue* right() { return OperandAt(2); }
virtual bool EmitAtUses() {
- return !HasSideEffects() && (uses()->length() <= 1);
+ return !HasSideEffects() && !HasMultipleUses();
}
virtual Representation RequiredInputRepresentation(int index) const {
@@ -2491,7 +2734,7 @@ class HInstanceOf: public HTemplateInstruction<3> {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance_of")
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOf)
};
@@ -2509,8 +2752,7 @@ class HInstanceOfKnownGlobal: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
- "instance_of_known_global")
+ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal)
private:
Handle<JSFunction> function_;
@@ -2529,7 +2771,7 @@ class HPower: public HBinaryOperation {
return (index == 1) ? Representation::None() : Representation::Double();
}
- DECLARE_CONCRETE_INSTRUCTION(Power, "power")
+ DECLARE_CONCRETE_INSTRUCTION(Power)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2552,7 +2794,7 @@ class HAdd: public HArithmeticBinaryOperation {
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(Add, "add")
+ DECLARE_CONCRETE_INSTRUCTION(Add)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2569,7 +2811,7 @@ class HSub: public HArithmeticBinaryOperation {
virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
- DECLARE_CONCRETE_INSTRUCTION(Sub, "sub")
+ DECLARE_CONCRETE_INSTRUCTION(Sub)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2591,7 +2833,7 @@ class HMul: public HArithmeticBinaryOperation {
return !representation().IsTagged();
}
- DECLARE_CONCRETE_INSTRUCTION(Mul, "mul")
+ DECLARE_CONCRETE_INSTRUCTION(Mul)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2618,7 +2860,7 @@ class HMod: public HArithmeticBinaryOperation {
virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
- DECLARE_CONCRETE_INSTRUCTION(Mod, "mod")
+ DECLARE_CONCRETE_INSTRUCTION(Mod)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2636,7 +2878,7 @@ class HDiv: public HArithmeticBinaryOperation {
virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
- DECLARE_CONCRETE_INSTRUCTION(Div, "div")
+ DECLARE_CONCRETE_INSTRUCTION(Div)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2653,7 +2895,7 @@ class HBitAnd: public HBitwiseBinaryOperation {
virtual bool IsCommutative() const { return true; }
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(BitAnd, "bit_and")
+ DECLARE_CONCRETE_INSTRUCTION(BitAnd)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2670,7 +2912,7 @@ class HBitXor: public HBitwiseBinaryOperation {
virtual bool IsCommutative() const { return true; }
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(BitXor, "bit_xor")
+ DECLARE_CONCRETE_INSTRUCTION(BitXor)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2685,7 +2927,7 @@ class HBitOr: public HBitwiseBinaryOperation {
virtual bool IsCommutative() const { return true; }
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(BitOr, "bit_or")
+ DECLARE_CONCRETE_INSTRUCTION(BitOr)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2702,7 +2944,7 @@ class HShl: public HBitwiseBinaryOperation {
virtual Range* InferRange();
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(Shl, "shl")
+ DECLARE_CONCRETE_INSTRUCTION(Shl)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2716,7 +2958,7 @@ class HShr: public HBitwiseBinaryOperation {
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(Shr, "shr")
+ DECLARE_CONCRETE_INSTRUCTION(Shr)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2731,7 +2973,7 @@ class HSar: public HBitwiseBinaryOperation {
virtual Range* InferRange();
virtual HType CalculateInferredType();
- DECLARE_CONCRETE_INSTRUCTION(Sar, "sar")
+ DECLARE_CONCRETE_INSTRUCTION(Sar)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -2750,7 +2992,7 @@ class HOsrEntry: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr_entry")
+ DECLARE_CONCRETE_INSTRUCTION(OsrEntry)
private:
int ast_id_;
@@ -2771,7 +3013,7 @@ class HParameter: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter")
+ DECLARE_CONCRETE_INSTRUCTION(Parameter)
private:
unsigned index_;
@@ -2803,7 +3045,7 @@ class HCallStub: public HUnaryCall {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(CallStub, "call_stub")
+ DECLARE_CONCRETE_INSTRUCTION(CallStub)
private:
CodeStub::Major major_key_;
@@ -2819,7 +3061,7 @@ class HUnknownOSRValue: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown_osr_value")
+ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue)
};
@@ -2846,7 +3088,7 @@ class HLoadGlobalCell: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load_global_cell")
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell)
protected:
virtual bool DataEquals(HValue* other) {
@@ -2884,7 +3126,7 @@ class HLoadGlobalGeneric: public HBinaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load_global_generic")
+ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric)
private:
Handle<Object> name_;
@@ -2911,7 +3153,7 @@ class HStoreGlobalCell: public HUnaryOperation {
}
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store_global_cell")
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell)
private:
Handle<JSGlobalPropertyCell> cell_;
@@ -2947,7 +3189,7 @@ class HStoreGlobalGeneric: public HTemplateInstruction<3> {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store_global_generic")
+ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric)
private:
Handle<Object> name_;
@@ -2972,7 +3214,7 @@ class HLoadContextSlot: public HUnaryOperation {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load_context_slot")
+ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot)
protected:
virtual bool DataEquals(HValue* other) {
@@ -3012,7 +3254,7 @@ class HStoreContextSlot: public HBinaryOperation {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store_context_slot")
+ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot)
private:
int slot_index_;
@@ -3044,7 +3286,7 @@ class HLoadNamedField: public HUnaryOperation {
}
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load_named_field")
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField)
protected:
virtual bool DataEquals(HValue* other) {
@@ -3073,8 +3315,7 @@ class HLoadNamedFieldPolymorphic: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadNamedFieldPolymorphic,
- "load_named_field_polymorphic")
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedFieldPolymorphic)
static const int kMaxLoadPolymorphism = 4;
@@ -3105,7 +3346,7 @@ class HLoadNamedGeneric: public HBinaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load_named_generic")
+ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric)
private:
Handle<Object> name_;
@@ -3127,7 +3368,7 @@ class HLoadFunctionPrototype: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load_function_prototype")
+ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -3153,8 +3394,9 @@ class HLoadKeyedFastElement: public HBinaryOperation {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement,
- "load_keyed_fast_element")
+ bool RequiresHoleCheck() const;
+
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -3168,7 +3410,8 @@ class HLoadKeyedSpecializedArrayElement: public HBinaryOperation {
ExternalArrayType array_type)
: HBinaryOperation(external_elements, key),
array_type_(array_type) {
- if (array_type == kExternalFloatArray) {
+ if (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray) {
set_representation(Representation::Double());
} else {
set_representation(Representation::Integer32());
@@ -3192,8 +3435,7 @@ class HLoadKeyedSpecializedArrayElement: public HBinaryOperation {
HValue* key() { return OperandAt(1); }
ExternalArrayType array_type() const { return array_type_; }
- DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement,
- "load_keyed_specialized_array_element")
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement)
protected:
virtual bool DataEquals(HValue* other) {
@@ -3210,7 +3452,7 @@ class HLoadKeyedSpecializedArrayElement: public HBinaryOperation {
class HLoadKeyedGeneric: public HTemplateInstruction<3> {
public:
- HLoadKeyedGeneric(HContext* context, HValue* obj, HValue* key) {
+ HLoadKeyedGeneric(HValue* context, HValue* obj, HValue* key) {
set_representation(Representation::Tagged());
SetOperandAt(0, obj);
SetOperandAt(1, key);
@@ -3228,7 +3470,7 @@ class HLoadKeyedGeneric: public HTemplateInstruction<3> {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load_keyed_generic")
+ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric)
};
@@ -3250,7 +3492,7 @@ class HStoreNamedField: public HBinaryOperation {
}
}
- DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store_named_field")
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField)
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
@@ -3305,7 +3547,7 @@ class HStoreNamedGeneric: public HTemplateInstruction<3> {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store_named_generic")
+ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric)
private:
Handle<String> name_;
@@ -3338,8 +3580,7 @@ class HStoreKeyedFastElement: public HTemplateInstruction<3> {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
- "store_keyed_fast_element")
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement)
};
@@ -3362,7 +3603,8 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> {
if (index == 0) {
return Representation::External();
} else {
- if (index == 2 && array_type() == kExternalFloatArray) {
+ if (index == 2 && (array_type() == kExternalFloatArray ||
+ array_type() == kExternalDoubleArray)) {
return Representation::Double();
} else {
return Representation::Integer32();
@@ -3375,8 +3617,8 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> {
HValue* value() { return OperandAt(2); }
ExternalArrayType array_type() const { return array_type_; }
- DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement,
- "store_keyed_specialized_array_element")
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement)
+
private:
ExternalArrayType array_type_;
};
@@ -3409,13 +3651,36 @@ class HStoreKeyedGeneric: public HTemplateInstruction<4> {
virtual void PrintDataTo(StringStream* stream);
- DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store_keyed_generic")
+ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric)
private:
bool strict_mode_;
};
+class HStringAdd: public HBinaryOperation {
+ public:
+ HStringAdd(HValue* left, HValue* right) : HBinaryOperation(left, right) {
+ set_representation(Representation::Tagged());
+ SetFlag(kUseGVN);
+ SetFlag(kDependsOnMaps);
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ virtual HType CalculateInferredType() {
+ return HType::String();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd)
+
+ protected:
+ virtual bool DataEquals(HValue* other) { return true; }
+};
+
+
class HStringCharCodeAt: public HBinaryOperation {
public:
HStringCharCodeAt(HValue* string, HValue* index)
@@ -3434,7 +3699,7 @@ class HStringCharCodeAt: public HBinaryOperation {
HValue* string() { return OperandAt(0); }
HValue* index() { return OperandAt(1); }
- DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string_char_code_at")
+ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -3458,7 +3723,7 @@ class HStringCharFromCode: public HUnaryOperation {
virtual bool DataEquals(HValue* other) { return true; }
- DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string_char_from_code")
+ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode)
};
@@ -3479,7 +3744,7 @@ class HStringLength: public HUnaryOperation {
return HType::Smi();
}
- DECLARE_CONCRETE_INSTRUCTION(StringLength, "string_length")
+ DECLARE_CONCRETE_INSTRUCTION(StringLength)
protected:
virtual bool DataEquals(HValue* other) { return true; }
@@ -3526,7 +3791,7 @@ class HArrayLiteral: public HMaterializedLiteral<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array_literal")
+ DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral)
private:
int length_;
@@ -3560,7 +3825,7 @@ class HObjectLiteral: public HMaterializedLiteral<1> {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object_literal")
+ DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral)
private:
Handle<FixedArray> constant_properties_;
@@ -3585,7 +3850,7 @@ class HRegExpLiteral: public HMaterializedLiteral<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp_literal")
+ DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral)
private:
Handle<String> pattern_;
@@ -3604,7 +3869,7 @@ class HFunctionLiteral: public HTemplateInstruction<0> {
return Representation::None();
}
- DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function_literal")
+ DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral)
Handle<SharedFunctionInfo> shared_info() const { return shared_info_; }
bool pretenure() const { return pretenure_; }
@@ -3625,7 +3890,7 @@ class HTypeof: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
+ DECLARE_CONCRETE_INSTRUCTION(Typeof)
};
@@ -3643,7 +3908,7 @@ class HToFastProperties: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to_fast_properties")
+ DECLARE_CONCRETE_INSTRUCTION(ToFastProperties)
};
@@ -3657,7 +3922,7 @@ class HValueOf: public HUnaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value_of")
+ DECLARE_CONCRETE_INSTRUCTION(ValueOf)
};
@@ -3673,12 +3938,38 @@ class HDeleteProperty: public HBinaryOperation {
return Representation::Tagged();
}
- DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete_property")
+ DECLARE_CONCRETE_INSTRUCTION(DeleteProperty)
HValue* object() { return left(); }
HValue* key() { return right(); }
};
+
+class HIn: public HTemplateInstruction<2> {
+ public:
+ HIn(HValue* key, HValue* object) {
+ SetOperandAt(0, key);
+ SetOperandAt(1, object);
+ set_representation(Representation::Tagged());
+ SetAllSideEffects();
+ }
+
+ HValue* key() { return OperandAt(0); }
+ HValue* object() { return OperandAt(1); }
+
+ virtual Representation RequiredInputRepresentation(int index) const {
+ return Representation::Tagged();
+ }
+
+ virtual HType CalculateInferredType() {
+ return HType::Boolean();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(In)
+};
+
#undef DECLARE_INSTRUCTION
#undef DECLARE_CONCRETE_INSTRUCTION
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index d07e6c7a..1b37d939 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -29,7 +29,6 @@
#include "hydrogen.h"
#include "codegen.h"
-#include "data-flow.h"
#include "full-codegen.h"
#include "hashmap.h"
#include "lithium-allocator.h"
@@ -116,12 +115,13 @@ void HBasicBlock::AddInstruction(HInstruction* instr) {
}
-HDeoptimize* HBasicBlock::CreateDeoptimize() {
+HDeoptimize* HBasicBlock::CreateDeoptimize(
+ HDeoptimize::UseEnvironment has_uses) {
ASSERT(HasEnvironment());
- HEnvironment* environment = last_environment();
+ if (has_uses == HDeoptimize::kNoUses) return new(zone()) HDeoptimize(0);
+ HEnvironment* environment = last_environment();
HDeoptimize* instr = new(zone()) HDeoptimize(environment->length());
-
for (int i = 0; i < environment->length(); i++) {
HValue* val = environment->values()->at(i);
instr->AddEnvironmentValue(val);
@@ -242,7 +242,7 @@ void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
- if (!predecessors_.is_empty()) {
+ if (HasPredecessor()) {
// Only loop header blocks can have a predecessor added after
// instructions have been added to the block (they have phis for all
// values in the environment, these phis may be eliminated later).
@@ -521,6 +521,22 @@ HConstant* HGraph::GetConstantFalse() {
return GetConstant(&constant_false_, isolate()->heap()->false_value());
}
+HGraphBuilder::HGraphBuilder(CompilationInfo* info,
+ TypeFeedbackOracle* oracle)
+ : function_state_(NULL),
+ initial_function_state_(this, info, oracle),
+ ast_context_(NULL),
+ break_scope_(NULL),
+ graph_(NULL),
+ current_block_(NULL),
+ inlined_count_(0),
+ zone_(info->isolate()->zone()),
+ inline_bailout_(false) {
+ // This is not initialized in the initializer list because the
+ // constructor for the initial state relies on function_state_ == NULL
+ // to know it's the initial state.
+ function_state_= &initial_function_state_;
+}
HBasicBlock* HGraphBuilder::CreateJoin(HBasicBlock* first,
HBasicBlock* second,
@@ -644,7 +660,7 @@ void HGraph::Canonicalize() {
HInstruction* instr = blocks()->at(i)->first();
while (instr != NULL) {
HValue* value = instr->Canonicalize();
- if (value != instr) instr->ReplaceAndDelete(value);
+ if (value != instr) instr->DeleteAndReplaceWith(value);
instr = instr->next();
}
}
@@ -726,9 +742,9 @@ void HGraph::AssignDominators() {
void HGraph::EliminateRedundantPhis() {
HPhase phase("Redundant phi elimination", this);
- // Worklist of phis that can potentially be eliminated. Initialized
- // with all phi nodes. When elimination of a phi node modifies
- // another phi node the modified phi node is added to the worklist.
+ // Worklist of phis that can potentially be eliminated. Initialized with
+ // all phi nodes. When elimination of a phi node modifies another phi node
+ // the modified phi node is added to the worklist.
ZoneList<HPhi*> worklist(blocks_.length());
for (int i = 0; i < blocks_.length(); ++i) {
worklist.AddAll(*blocks_[i]->phis());
@@ -742,18 +758,14 @@ void HGraph::EliminateRedundantPhis() {
if (block == NULL) continue;
// Get replacement value if phi is redundant.
- HValue* value = phi->GetRedundantReplacement();
-
- if (value != NULL) {
- // Iterate through uses finding the ones that should be
- // replaced.
- SmallPointerList<HValue>* uses = phi->uses();
- while (!uses->is_empty()) {
- HValue* use = uses->RemoveLast();
- if (use != NULL) {
- phi->ReplaceAtUse(use, value);
- if (use->IsPhi()) worklist.Add(HPhi::cast(use));
- }
+ HValue* replacement = phi->GetRedundantReplacement();
+
+ if (replacement != NULL) {
+ // Iterate through the uses and replace them all.
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ value->SetOperandAt(it.index(), replacement);
+ if (value->IsPhi()) worklist.Add(HPhi::cast(value));
}
block->RemovePhi(phi);
}
@@ -805,6 +817,19 @@ void HGraph::EliminateUnreachablePhis() {
}
+bool HGraph::CheckPhis() {
+ int block_count = blocks_.length();
+ for (int i = 0; i < block_count; ++i) {
+ for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
+ HPhi* phi = blocks_[i]->phis()->at(j);
+ // We don't support phi uses of arguments for now.
+ if (phi->CheckFlag(HValue::kIsArguments)) return false;
+ }
+ }
+ return true;
+}
+
+
bool HGraph::CollectPhis() {
int block_count = blocks_.length();
phi_list_ = new ZoneList<HPhi*>(block_count);
@@ -812,8 +837,6 @@ bool HGraph::CollectPhis() {
for (int j = 0; j < blocks_[i]->phis()->length(); ++j) {
HPhi* phi = blocks_[i]->phis()->at(j);
phi_list_->Add(phi);
- // We don't support phi uses of arguments for now.
- if (phi->CheckFlag(HValue::kIsArguments)) return false;
}
}
return true;
@@ -831,8 +854,8 @@ void HGraph::InferTypes(ZoneList<HValue*>* worklist) {
HValue* current = worklist->RemoveLast();
in_worklist.Remove(current->id());
if (current->UpdateInferredType()) {
- for (int j = 0; j < current->uses()->length(); j++) {
- HValue* use = current->uses()->at(j);
+ for (HUseIterator it(current->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
if (!in_worklist.Contains(use->id())) {
in_worklist.Add(use->id());
worklist->Add(use);
@@ -1025,13 +1048,13 @@ void TraceGVN(const char* msg, ...) {
}
-HValueMap::HValueMap(const HValueMap* other)
+HValueMap::HValueMap(Zone* zone, const HValueMap* other)
: array_size_(other->array_size_),
lists_size_(other->lists_size_),
count_(other->count_),
present_flags_(other->present_flags_),
- array_(ZONE->NewArray<HValueMapListElement>(other->array_size_)),
- lists_(ZONE->NewArray<HValueMapListElement>(other->lists_size_)),
+ array_(zone->NewArray<HValueMapListElement>(other->array_size_)),
+ lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)),
free_list_head_(other->free_list_head_) {
memcpy(array_, other->array_, array_size_ * sizeof(HValueMapListElement));
memcpy(lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement));
@@ -1244,13 +1267,49 @@ void HStackCheckEliminator::RemoveStackCheck(HBasicBlock* block) {
}
+// Simple sparse set with O(1) add, contains, and clear.
+class SparseSet {
+ public:
+ SparseSet(Zone* zone, int capacity)
+ : capacity_(capacity),
+ length_(0),
+ dense_(zone->NewArray<int>(capacity)),
+ sparse_(zone->NewArray<int>(capacity)) {}
+
+ bool Contains(int n) const {
+ ASSERT(0 <= n && n < capacity_);
+ int d = sparse_[n];
+ return 0 <= d && d < length_ && dense_[d] == n;
+ }
+
+ bool Add(int n) {
+ if (Contains(n)) return false;
+ dense_[length_] = n;
+ sparse_[n] = length_;
+ ++length_;
+ return true;
+ }
+
+ void Clear() { length_ = 0; }
+
+ private:
+ int capacity_;
+ int length_;
+ int* dense_;
+ int* sparse_;
+
+ DISALLOW_COPY_AND_ASSIGN(SparseSet);
+};
+
+
class HGlobalValueNumberer BASE_EMBEDDED {
public:
explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info)
: graph_(graph),
info_(info),
- block_side_effects_(graph_->blocks()->length()),
- loop_side_effects_(graph_->blocks()->length()) {
+ block_side_effects_(graph->blocks()->length()),
+ loop_side_effects_(graph->blocks()->length()),
+ visited_on_paths_(graph->zone(), graph->blocks()->length()) {
ASSERT(info->isolate()->heap()->allow_allocation(false));
block_side_effects_.AddBlock(0, graph_->blocks()->length());
loop_side_effects_.AddBlock(0, graph_->blocks()->length());
@@ -1262,6 +1321,8 @@ class HGlobalValueNumberer BASE_EMBEDDED {
void Analyze();
private:
+ int CollectSideEffectsOnPathsToDominatedBlock(HBasicBlock* dominator,
+ HBasicBlock* dominated);
void AnalyzeBlock(HBasicBlock* block, HValueMap* map);
void ComputeBlockSideEffects();
void LoopInvariantCodeMotion();
@@ -1283,6 +1344,10 @@ class HGlobalValueNumberer BASE_EMBEDDED {
// A map of loop header block IDs to their loop's side effects.
ZoneList<int> loop_side_effects_;
+
+ // Used when collecting side effects on paths from dominator to
+ // dominated.
+ SparseSet visited_on_paths_;
};
@@ -1418,8 +1483,30 @@ bool HGlobalValueNumberer::ShouldMove(HInstruction* instr,
}
+int HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock(
+ HBasicBlock* dominator, HBasicBlock* dominated) {
+ int side_effects = 0;
+ for (int i = 0; i < dominated->predecessors()->length(); ++i) {
+ HBasicBlock* block = dominated->predecessors()->at(i);
+ if (dominator->block_id() < block->block_id() &&
+ block->block_id() < dominated->block_id() &&
+ visited_on_paths_.Add(block->block_id())) {
+ side_effects |= block_side_effects_[block->block_id()];
+ if (block->IsLoopHeader()) {
+ side_effects |= loop_side_effects_[block->block_id()];
+ }
+ side_effects |= CollectSideEffectsOnPathsToDominatedBlock(
+ dominator, block);
+ }
+ }
+ return side_effects;
+}
+
+
void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
- TraceGVN("Analyzing block B%d\n", block->block_id());
+ TraceGVN("Analyzing block B%d%s\n",
+ block->block_id(),
+ block->IsLoopHeader() ? " (loop header)" : "");
// If this is a loop header kill everything killed by the loop.
if (block->IsLoopHeader()) {
@@ -1445,7 +1532,7 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
instr->Mnemonic(),
other->id(),
other->Mnemonic());
- instr->ReplaceAndDelete(other);
+ instr->DeleteAndReplaceWith(other);
} else {
map->Add(instr);
}
@@ -1460,23 +1547,18 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) {
// No need to copy the map for the last child in the dominator tree.
HValueMap* successor_map = (i == length - 1) ? map : map->Copy(zone());
- // If the dominated block is not a successor to this block we have to
- // kill everything killed on any path between this block and the
- // dominated block. Note we rely on the block ordering.
- bool is_successor = false;
- int predecessor_count = dominated->predecessors()->length();
- for (int j = 0; !is_successor && j < predecessor_count; ++j) {
- is_successor = (dominated->predecessors()->at(j) == block);
- }
-
- if (!is_successor) {
- int side_effects = 0;
- for (int j = block->block_id() + 1; j < dominated->block_id(); ++j) {
- side_effects |= block_side_effects_[j];
- }
- successor_map->Kill(side_effects);
+ // Kill everything killed on any path between this block and the
+ // dominated block.
+ // We don't have to traverse these paths if the value map is
+ // already empty.
+ // If the range of block ids (block_id, dominated_id) is empty
+ // there are no such paths.
+ if (!successor_map->IsEmpty() &&
+ block->block_id() + 1 < dominated->block_id()) {
+ visited_on_paths_.Clear();
+ successor_map->Kill(CollectSideEffectsOnPathsToDominatedBlock(block,
+ dominated));
}
-
AnalyzeBlock(dominated, successor_map);
}
}
@@ -1529,12 +1611,12 @@ void HInferRepresentation::InferBasedOnInputs(HValue* current) {
}
-void HInferRepresentation::AddDependantsToWorklist(HValue* current) {
- for (int i = 0; i < current->uses()->length(); ++i) {
- AddToWorklist(current->uses()->at(i));
+void HInferRepresentation::AddDependantsToWorklist(HValue* value) {
+ for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
+ AddToWorklist(it.value());
}
- for (int i = 0; i < current->OperandCount(); ++i) {
- AddToWorklist(current->OperandAt(i));
+ for (int i = 0; i < value->OperandCount(); ++i) {
+ AddToWorklist(value->OperandAt(i));
}
}
@@ -1543,37 +1625,30 @@ void HInferRepresentation::AddDependantsToWorklist(HValue* current) {
// given as the parameter has a benefit in terms of less necessary type
// conversions. If there is a benefit, then the representation of the value is
// specialized.
-void HInferRepresentation::InferBasedOnUses(HValue* current) {
- Representation r = current->representation();
- if (r.IsSpecialization() || current->HasNoUses()) return;
- ASSERT(current->CheckFlag(HValue::kFlexibleRepresentation));
- Representation new_rep = TryChange(current);
+void HInferRepresentation::InferBasedOnUses(HValue* value) {
+ Representation r = value->representation();
+ if (r.IsSpecialization() || value->HasNoUses()) return;
+ ASSERT(value->CheckFlag(HValue::kFlexibleRepresentation));
+ Representation new_rep = TryChange(value);
if (!new_rep.IsNone()) {
- if (!current->representation().Equals(new_rep)) {
- current->ChangeRepresentation(new_rep);
- AddDependantsToWorklist(current);
+ if (!value->representation().Equals(new_rep)) {
+ value->ChangeRepresentation(new_rep);
+ AddDependantsToWorklist(value);
}
}
}
-Representation HInferRepresentation::TryChange(HValue* current) {
+Representation HInferRepresentation::TryChange(HValue* value) {
// Array of use counts for each representation.
- int use_count[Representation::kNumRepresentations];
- for (int i = 0; i < Representation::kNumRepresentations; i++) {
- use_count[i] = 0;
- }
+ int use_count[Representation::kNumRepresentations] = { 0 };
- for (int i = 0; i < current->uses()->length(); ++i) {
- HValue* use = current->uses()->at(i);
- int index = use->LookupOperandIndex(0, current);
- Representation req_rep = use->RequiredInputRepresentation(index);
- if (req_rep.IsNone()) continue;
- if (use->IsPhi()) {
- HPhi* phi = HPhi::cast(use);
- phi->AddIndirectUsesTo(&use_count[0]);
- }
- use_count[req_rep.kind()]++;
+ for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
+ Representation rep = use->RequiredInputRepresentation(it.index());
+ if (rep.IsNone()) continue;
+ if (use->IsPhi()) HPhi::cast(use)->AddIndirectUsesTo(&use_count[0]);
+ ++use_count[rep.kind()];
}
int tagged_count = use_count[Representation::kTagged];
int double_count = use_count[Representation::kDouble];
@@ -1581,19 +1656,17 @@ Representation HInferRepresentation::TryChange(HValue* current) {
int non_tagged_count = double_count + int32_count;
// If a non-loop phi has tagged uses, don't convert it to untagged.
- if (current->IsPhi() && !current->block()->IsLoopHeader()) {
+ if (value->IsPhi() && !value->block()->IsLoopHeader()) {
if (tagged_count > 0) return Representation::None();
}
if (non_tagged_count >= tagged_count) {
- // More untagged than tagged.
- if (double_count > 0) {
- // There is at least one usage that is a double => guess that the
- // correct representation is double.
- return Representation::Double();
- } else if (int32_count > 0) {
- return Representation::Integer32();
+ if (int32_count > 0) {
+ if (!value->IsPhi() || value->IsConvertibleToInteger()) {
+ return Representation::Integer32();
+ }
}
+ if (double_count > 0) return Representation::Double();
}
return Representation::None();
}
@@ -1602,41 +1675,40 @@ Representation HInferRepresentation::TryChange(HValue* current) {
void HInferRepresentation::Analyze() {
HPhase phase("Infer representations", graph_);
- // (1) Initialize bit vectors and count real uses. Each phi
- // gets a bit-vector of length <number of phis>.
+ // (1) Initialize bit vectors and count real uses. Each phi gets a
+ // bit-vector of length <number of phis>.
const ZoneList<HPhi*>* phi_list = graph_->phi_list();
- int num_phis = phi_list->length();
- ScopedVector<BitVector*> connected_phis(num_phis);
- for (int i = 0; i < num_phis; i++) {
+ int phi_count = phi_list->length();
+ ZoneList<BitVector*> connected_phis(phi_count);
+ for (int i = 0; i < phi_count; ++i) {
phi_list->at(i)->InitRealUses(i);
- connected_phis[i] = new(zone()) BitVector(num_phis);
- connected_phis[i]->Add(i);
+ BitVector* connected_set = new(zone()) BitVector(phi_count);
+ connected_set->Add(i);
+ connected_phis.Add(connected_set);
}
- // (2) Do a fixed point iteration to find the set of connected phis.
- // A phi is connected to another phi if its value is used either
- // directly or indirectly through a transitive closure of the def-use
- // relation.
+ // (2) Do a fixed point iteration to find the set of connected phis. A
+ // phi is connected to another phi if its value is used either directly or
+ // indirectly through a transitive closure of the def-use relation.
bool change = true;
while (change) {
change = false;
- for (int i = 0; i < num_phis; i++) {
+ for (int i = 0; i < phi_count; ++i) {
HPhi* phi = phi_list->at(i);
- for (int j = 0; j < phi->uses()->length(); j++) {
- HValue* use = phi->uses()->at(j);
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
if (use->IsPhi()) {
- int phi_use = HPhi::cast(use)->phi_id();
- if (connected_phis[i]->UnionIsChanged(*connected_phis[phi_use])) {
- change = true;
- }
+ int id = HPhi::cast(use)->phi_id();
+ change = change ||
+ connected_phis[i]->UnionIsChanged(*connected_phis[id]);
}
}
}
}
- // (3) Sum up the non-phi use counts of all connected phis.
- // Don't include the non-phi uses of the phi itself.
- for (int i = 0; i < num_phis; i++) {
+ // (3) Sum up the non-phi use counts of all connected phis. Don't include
+ // the non-phi uses of the phi itself.
+ for (int i = 0; i < phi_count; ++i) {
HPhi* phi = phi_list->at(i);
for (BitVector::Iterator it(connected_phis.at(i));
!it.Done();
@@ -1649,6 +1721,25 @@ void HInferRepresentation::Analyze() {
}
}
+ // (4) Compute phis that definitely can't be converted to integer
+ // without deoptimization and mark them to avoid unnecessary deoptimization.
+ change = true;
+ while (change) {
+ change = false;
+ for (int i = 0; i < phi_count; ++i) {
+ HPhi* phi = phi_list->at(i);
+ for (int j = 0; j < phi->OperandCount(); ++j) {
+ if (phi->IsConvertibleToInteger() &&
+ !phi->OperandAt(j)->IsConvertibleToInteger()) {
+ phi->set_is_convertible_to_integer(false);
+ change = true;
+ break;
+ }
+ }
+ }
+ }
+
+
for (int i = 0; i < graph_->blocks()->length(); ++i) {
HBasicBlock* block = graph_->blocks()->at(i);
const ZoneList<HPhi*>* phis = block->phis();
@@ -1746,17 +1837,16 @@ void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
void HGraph::InsertRepresentationChangeForUse(HValue* value,
- HValue* use,
+ HValue* use_value,
+ int use_index,
Representation to) {
// Insert the representation change right before its use. For phi-uses we
// insert at the end of the corresponding predecessor.
HInstruction* next = NULL;
- if (use->IsPhi()) {
- int index = 0;
- while (use->OperandAt(index) != value) ++index;
- next = use->block()->predecessors()->at(index)->end();
+ if (use_value->IsPhi()) {
+ next = use_value->block()->predecessors()->at(use_index)->end();
} else {
- next = HInstruction::cast(use);
+ next = HInstruction::cast(use_value);
}
// For constants we try to make the representation change at compile
@@ -1764,8 +1854,9 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value,
// information we treat constants like normal instructions and insert the
// change instructions for them.
HInstruction* new_value = NULL;
- bool is_truncating = use->CheckFlag(HValue::kTruncatingToInt32);
- bool deoptimize_on_undefined = use->CheckFlag(HValue::kDeoptimizeOnUndefined);
+ bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32);
+ bool deoptimize_on_undefined =
+ use_value->CheckFlag(HValue::kDeoptimizeOnUndefined);
if (value->IsConstant()) {
HConstant* constant = HConstant::cast(value);
// Try to create a new copy of the constant with the new representation.
@@ -1780,89 +1871,32 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value,
}
new_value->InsertBefore(next);
- value->ReplaceFirstAtUse(use, new_value, to);
+ use_value->SetOperandAt(use_index, new_value);
}
-int CompareConversionUses(HValue* a,
- HValue* b,
- Representation a_rep,
- Representation b_rep) {
- if (a_rep.kind() > b_rep.kind()) {
- // Make sure specializations are separated in the result array.
- return 1;
- }
- // Put truncating conversions before non-truncating conversions.
- bool a_truncate = a->CheckFlag(HValue::kTruncatingToInt32);
- bool b_truncate = b->CheckFlag(HValue::kTruncatingToInt32);
- if (a_truncate != b_truncate) {
- return a_truncate ? -1 : 1;
- }
- // Sort by increasing block ID.
- return a->block()->block_id() - b->block()->block_id();
-}
-
-
-void HGraph::InsertRepresentationChangesForValue(
- HValue* current,
- ZoneList<HValue*>* to_convert,
- ZoneList<Representation>* to_convert_reps) {
- Representation r = current->representation();
+void HGraph::InsertRepresentationChangesForValue(HValue* value) {
+ Representation r = value->representation();
if (r.IsNone()) return;
- if (current->uses()->length() == 0) return;
-
- // Collect the representation changes in a sorted list. This allows
- // us to avoid duplicate changes without searching the list.
- ASSERT(to_convert->is_empty());
- ASSERT(to_convert_reps->is_empty());
- for (int i = 0; i < current->uses()->length(); ++i) {
- HValue* use = current->uses()->at(i);
- // The occurrences index means the index within the operand array of "use"
- // at which "current" is used. While iterating through the use array we
- // also have to iterate over the different occurrence indices.
- int occurrence_index = 0;
- if (use->UsesMultipleTimes(current)) {
- occurrence_index = current->uses()->CountOccurrences(use, 0, i - 1);
- if (FLAG_trace_representation) {
- PrintF("Instruction %d is used multiple times at %d; occurrence=%d\n",
- current->id(),
- use->id(),
- occurrence_index);
- }
- }
- int operand_index = use->LookupOperandIndex(occurrence_index, current);
- Representation req = use->RequiredInputRepresentation(operand_index);
+ if (value->HasNoUses()) return;
+
+ for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
+ HValue* use_value = it.value();
+ int use_index = it.index();
+ Representation req = use_value->RequiredInputRepresentation(use_index);
if (req.IsNone() || req.Equals(r)) continue;
- int index = 0;
- while (index < to_convert->length() &&
- CompareConversionUses(to_convert->at(index),
- use,
- to_convert_reps->at(index),
- req) < 0) {
- ++index;
- }
- if (FLAG_trace_representation) {
- PrintF("Inserting a representation change to %s of %d for use at %d\n",
- req.Mnemonic(),
- current->id(),
- use->id());
- }
- to_convert->InsertAt(index, use);
- to_convert_reps->InsertAt(index, req);
+ InsertRepresentationChangeForUse(value, use_value, use_index, req);
}
-
- for (int i = 0; i < to_convert->length(); ++i) {
- HValue* use = to_convert->at(i);
- Representation r_to = to_convert_reps->at(i);
- InsertRepresentationChangeForUse(current, use, r_to);
+ if (value->HasNoUses()) {
+ ASSERT(value->IsConstant());
+ value->DeleteAndReplaceWith(NULL);
}
- if (current->uses()->is_empty()) {
- ASSERT(current->IsConstant());
- current->Delete();
+ // The only purpose of a HForceRepresentation is to represent the value
+ // after the (possible) HChange instruction. We make it disappear.
+ if (value->IsForceRepresentation()) {
+ value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
}
- to_convert->Rewind(0);
- to_convert_reps->Rewind(0);
}
@@ -1887,8 +1921,8 @@ void HGraph::InsertRepresentationChanges() {
for (int i = 0; i < phi_list()->length(); i++) {
HPhi* phi = phi_list()->at(i);
if (!phi->CheckFlag(HValue::kTruncatingToInt32)) continue;
- for (int j = 0; j < phi->uses()->length(); j++) {
- HValue* use = phi->uses()->at(j);
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ HValue* use = it.value();
if (!use->CheckFlag(HValue::kTruncatingToInt32)) {
phi->ClearFlag(HValue::kTruncatingToInt32);
change = true;
@@ -1898,19 +1932,17 @@ void HGraph::InsertRepresentationChanges() {
}
}
- ZoneList<HValue*> value_list(4);
- ZoneList<Representation> rep_list(4);
for (int i = 0; i < blocks_.length(); ++i) {
// Process phi instructions first.
- for (int j = 0; j < blocks_[i]->phis()->length(); j++) {
- HPhi* phi = blocks_[i]->phis()->at(j);
- InsertRepresentationChangesForValue(phi, &value_list, &rep_list);
+ const ZoneList<HPhi*>* phis = blocks_[i]->phis();
+ for (int j = 0; j < phis->length(); j++) {
+ InsertRepresentationChangesForValue(phis->at(j));
}
// Process normal instructions.
HInstruction* current = blocks_[i]->first();
while (current != NULL) {
- InsertRepresentationChangesForValue(current, &value_list, &rep_list);
+ InsertRepresentationChangesForValue(current);
current = current->next();
}
}
@@ -1940,9 +1972,8 @@ void HGraph::MarkDeoptimizeOnUndefined() {
for (int i = 0; i < phi_list()->length(); i++) {
HPhi* phi = phi_list()->at(i);
if (phi->representation().IsDouble()) {
- for (int j = 0; j < phi->uses()->length(); j++) {
- HValue* use = phi->uses()->at(j);
- if (use->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
+ for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) {
+ if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) {
RecursivelyMarkPhiDeoptimizeOnUndefined(phi);
break;
}
@@ -2058,6 +2089,9 @@ void EffectContext::ReturnValue(HValue* value) {
void ValueContext::ReturnValue(HValue* value) {
// The value is tracked in the bailout environment, and communicated
// through the environment as the result of the expression.
+ if (!arguments_allowed() && value->CheckFlag(HValue::kIsArguments)) {
+ owner()->Bailout("bad value context for arguments value");
+ }
owner()->Push(value);
}
@@ -2074,6 +2108,9 @@ void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) {
void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) {
+ if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) {
+ owner()->Bailout("bad value context for arguments object value");
+ }
owner()->AddInstruction(instr);
owner()->Push(instr);
if (instr->HasSideEffects()) owner()->AddSimulate(ast_id);
@@ -2100,6 +2137,9 @@ void TestContext::BuildBranch(HValue* value) {
// property by always adding an empty block on the outgoing edges of this
// branch.
HGraphBuilder* builder = owner();
+ if (value->CheckFlag(HValue::kIsArguments)) {
+ builder->Bailout("arguments object value in a test context");
+ }
HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
HTest* test = new(zone()) HTest(value, empty_true, empty_false);
@@ -2112,37 +2152,17 @@ void TestContext::BuildBranch(HValue* value) {
// HGraphBuilder infrastructure for bailing out and checking bailouts.
-#define BAILOUT(reason) \
- do { \
- Bailout(reason); \
- return; \
- } while (false)
-
-
-#define CHECK_BAILOUT \
+#define CHECK_BAILOUT(call) \
do { \
+ call; \
if (HasStackOverflow()) return; \
} while (false)
-#define VISIT_FOR_EFFECT(expr) \
- do { \
- VisitForEffect(expr); \
- if (HasStackOverflow()) return; \
- } while (false)
-
-
-#define VISIT_FOR_VALUE(expr) \
- do { \
- VisitForValue(expr); \
- if (HasStackOverflow()) return; \
- } while (false)
-
-
-#define VISIT_FOR_CONTROL(expr, true_block, false_block) \
+#define CHECK_ALIVE(call) \
do { \
- VisitForControl(expr, true_block, false_block); \
- if (HasStackOverflow()) return; \
+ call; \
+ if (HasStackOverflow() || current_block() == NULL) return; \
} while (false)
@@ -2161,14 +2181,14 @@ void HGraphBuilder::VisitForEffect(Expression* expr) {
}
-void HGraphBuilder::VisitForValue(Expression* expr) {
- ValueContext for_value(this);
+void HGraphBuilder::VisitForValue(Expression* expr, ArgumentsAllowedFlag flag) {
+ ValueContext for_value(this, flag);
Visit(expr);
}
void HGraphBuilder::VisitForTypeOf(Expression* expr) {
- ValueContext for_value(this);
+ ValueContext for_value(this, ARGUMENTS_NOT_ALLOWED);
for_value.set_for_typeof(true);
Visit(expr);
}
@@ -2184,22 +2204,21 @@ void HGraphBuilder::VisitForControl(Expression* expr,
void HGraphBuilder::VisitArgument(Expression* expr) {
- VISIT_FOR_VALUE(expr);
+ CHECK_ALIVE(VisitForValue(expr));
Push(AddInstruction(new(zone()) HPushArgument(Pop())));
}
void HGraphBuilder::VisitArgumentList(ZoneList<Expression*>* arguments) {
for (int i = 0; i < arguments->length(); i++) {
- VisitArgument(arguments->at(i));
- if (HasStackOverflow() || current_block() == NULL) return;
+ CHECK_ALIVE(VisitArgument(arguments->at(i)));
}
}
void HGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs) {
for (int i = 0; i < exprs->length(); ++i) {
- VISIT_FOR_VALUE(exprs->at(i));
+ CHECK_ALIVE(VisitForValue(exprs->at(i)));
}
}
@@ -2254,9 +2273,13 @@ HGraph* HGraphBuilder::CreateGraph() {
graph()->OrderBlocks();
graph()->AssignDominators();
graph()->EliminateRedundantPhis();
+ if (!graph()->CheckPhis()) {
+ Bailout("Unsupported phi use of arguments object");
+ return NULL;
+ }
if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
if (!graph()->CollectPhis()) {
- Bailout("Phi-use of arguments object");
+ Bailout("Unsupported phi use of uninitialized constant");
return NULL;
}
@@ -2302,8 +2325,8 @@ void HGraph::ReplaceCheckedValues() {
while (instr != NULL) {
if (instr->IsBoundsCheck()) {
// Replace all uses of the checked value with the original input.
- ASSERT(instr->uses()->length() > 0);
- instr->ReplaceValue(HBoundsCheck::cast(instr)->index());
+ ASSERT(instr->UseCount() > 0);
+ instr->ReplaceAllUsesWith(HBoundsCheck::cast(instr)->index());
}
instr = instr->next();
}
@@ -2353,7 +2376,7 @@ HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) {
void HGraphBuilder::SetupScope(Scope* scope) {
// We don't yet handle the function name for named function expressions.
- if (scope->function() != NULL) BAILOUT("named function expression");
+ if (scope->function() != NULL) return Bailout("named function expression");
HConstant* undefined_constant = new(zone()) HConstant(
isolate()->factory()->undefined_value(), Representation::Tagged());
@@ -2362,14 +2385,21 @@ void HGraphBuilder::SetupScope(Scope* scope) {
// Set the initial values of parameters including "this". "This" has
// parameter index 0.
- int count = scope->num_parameters() + 1;
- for (int i = 0; i < count; ++i) {
+ ASSERT_EQ(scope->num_parameters() + 1, environment()->parameter_count());
+
+ for (int i = 0; i < environment()->parameter_count(); ++i) {
HInstruction* parameter = AddInstruction(new(zone()) HParameter(i));
environment()->Bind(i, parameter);
}
- // Set the initial values of stack-allocated locals.
- for (int i = count; i < environment()->length(); ++i) {
+ // First special is HContext.
+ HInstruction* context = AddInstruction(new(zone()) HContext);
+ environment()->BindContext(context);
+
+ // Initialize specials and locals to undefined.
+ for (int i = environment()->parameter_count() + 1;
+ i < environment()->length();
+ ++i) {
environment()->Bind(i, undefined_constant);
}
@@ -2379,7 +2409,7 @@ void HGraphBuilder::SetupScope(Scope* scope) {
if (!scope->arguments()->IsStackAllocated() ||
(scope->arguments_shadow() != NULL &&
!scope->arguments_shadow()->IsStackAllocated())) {
- BAILOUT("context-allocated arguments");
+ return Bailout("context-allocated arguments");
}
HArgumentsObject* object = new(zone()) HArgumentsObject;
AddInstruction(object);
@@ -2394,8 +2424,7 @@ void HGraphBuilder::SetupScope(Scope* scope) {
void HGraphBuilder::VisitStatements(ZoneList<Statement*>* statements) {
for (int i = 0; i < statements->length(); i++) {
- Visit(statements->at(i));
- if (HasStackOverflow() || current_block() == NULL) break;
+ CHECK_ALIVE(Visit(statements->at(i)));
}
}
@@ -2417,10 +2446,12 @@ HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() {
void HGraphBuilder::VisitBlock(Block* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
BreakAndContinueInfo break_info(stmt);
{ BreakAndContinueScope push(&break_info, this);
- VisitStatements(stmt->statements());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(VisitStatements(stmt->statements()));
}
HBasicBlock* break_block = break_info.break_block();
if (break_block != NULL) {
@@ -2432,15 +2463,24 @@ void HGraphBuilder::VisitBlock(Block* stmt) {
void HGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
VisitForEffect(stmt->expression());
}
void HGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
}
void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (stmt->condition()->ToBooleanIsTrue()) {
AddSimulate(stmt->ThenId());
Visit(stmt->then_statement());
@@ -2450,20 +2490,27 @@ void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
} else {
HBasicBlock* cond_true = graph()->CreateBasicBlock();
HBasicBlock* cond_false = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->condition(), cond_true, cond_false);
- cond_true->SetJoinId(stmt->ThenId());
- cond_false->SetJoinId(stmt->ElseId());
+ CHECK_BAILOUT(VisitForControl(stmt->condition(), cond_true, cond_false));
- set_current_block(cond_true);
- Visit(stmt->then_statement());
- CHECK_BAILOUT;
- HBasicBlock* other = current_block();
+ if (cond_true->HasPredecessor()) {
+ cond_true->SetJoinId(stmt->ThenId());
+ set_current_block(cond_true);
+ CHECK_BAILOUT(Visit(stmt->then_statement()));
+ cond_true = current_block();
+ } else {
+ cond_true = NULL;
+ }
- set_current_block(cond_false);
- Visit(stmt->else_statement());
- CHECK_BAILOUT;
+ if (cond_false->HasPredecessor()) {
+ cond_false->SetJoinId(stmt->ElseId());
+ set_current_block(cond_false);
+ CHECK_BAILOUT(Visit(stmt->else_statement()));
+ cond_false = current_block();
+ } else {
+ cond_false = NULL;
+ }
- HBasicBlock* join = CreateJoin(other, current_block(), stmt->id());
+ HBasicBlock* join = CreateJoin(cond_true, cond_false, stmt->id());
set_current_block(join);
}
}
@@ -2501,6 +2548,9 @@ HBasicBlock* HGraphBuilder::BreakAndContinueScope::Get(
void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HBasicBlock* continue_block = break_scope()->Get(stmt->target(), CONTINUE);
current_block()->Goto(continue_block);
set_current_block(NULL);
@@ -2508,6 +2558,9 @@ void HGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HBasicBlock* break_block = break_scope()->Get(stmt->target(), BREAK);
current_block()->Goto(break_block);
set_current_block(NULL);
@@ -2515,10 +2568,13 @@ void HGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
AstContext* context = call_context();
if (context == NULL) {
// Not an inlined return, so an actual one.
- VISIT_FOR_VALUE(stmt->expression());
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
HValue* result = environment()->Pop();
current_block()->FinishExit(new(zone()) HReturn(result));
set_current_block(NULL);
@@ -2531,11 +2587,11 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
test->if_true(),
test->if_false());
} else if (context->IsEffect()) {
- VISIT_FOR_EFFECT(stmt->expression());
+ CHECK_ALIVE(VisitForEffect(stmt->expression()));
current_block()->Goto(function_return(), false);
} else {
ASSERT(context->IsValue());
- VISIT_FOR_VALUE(stmt->expression());
+ CHECK_ALIVE(VisitForValue(stmt->expression()));
HValue* return_value = environment()->Pop();
current_block()->AddLeaveInlined(return_value, function_return());
}
@@ -2545,26 +2601,35 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
- BAILOUT("WithEnterStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("WithEnterStatement");
}
void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
- BAILOUT("WithExitStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("WithExitStatement");
}
void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
// We only optimize switch statements with smi-literal smi comparisons,
// with a bounded number of clauses.
const int kCaseClauseLimit = 128;
ZoneList<CaseClause*>* clauses = stmt->cases();
int clause_count = clauses->length();
if (clause_count > kCaseClauseLimit) {
- BAILOUT("SwitchStatement: too many clauses");
+ return Bailout("SwitchStatement: too many clauses");
}
- VISIT_FOR_VALUE(stmt->tag());
+ CHECK_ALIVE(VisitForValue(stmt->tag()));
AddSimulate(stmt->EntryId());
HValue* tag_value = Pop();
HBasicBlock* first_test_block = current_block();
@@ -2575,19 +2640,21 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
CaseClause* clause = clauses->at(i);
if (clause->is_default()) continue;
if (!clause->label()->IsSmiLiteral()) {
- BAILOUT("SwitchStatement: non-literal switch label");
+ return Bailout("SwitchStatement: non-literal switch label");
}
// Unconditionally deoptimize on the first non-smi compare.
clause->RecordTypeFeedback(oracle());
if (!clause->IsSmiCompare()) {
- current_block()->FinishExitWithDeoptimization();
+ // Finish with deoptimize and add uses of enviroment values to
+ // account for invisible uses.
+ current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
set_current_block(NULL);
break;
}
// Otherwise generate a compare and branch.
- VISIT_FOR_VALUE(clause->label());
+ CHECK_ALIVE(VisitForValue(clause->label()));
HValue* label_value = Pop();
HCompare* compare =
new(zone()) HCompare(tag_value, label_value, Token::EQ_STRICT);
@@ -2651,8 +2718,7 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
set_current_block(join);
}
- VisitStatements(clause->statements());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(VisitStatements(clause->statements()));
fall_through_block = current_block();
}
}
@@ -2694,17 +2760,18 @@ void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
int osr_entry_id = statement->OsrEntryId();
// We want the correct environment at the OsrEntry instruction. Build
// it explicitly. The expression stack should be empty.
- int count = environment()->length();
- ASSERT(count ==
- (environment()->parameter_count() + environment()->local_count()));
- for (int i = 0; i < count; ++i) {
- HUnknownOSRValue* unknown = new(zone()) HUnknownOSRValue;
- AddInstruction(unknown);
- environment()->Bind(i, unknown);
+ ASSERT(environment()->ExpressionStackIsEmpty());
+ for (int i = 0; i < environment()->length(); ++i) {
+ HUnknownOSRValue* osr_value = new(zone()) HUnknownOSRValue;
+ AddInstruction(osr_value);
+ environment()->Bind(i, osr_value);
}
AddSimulate(osr_entry_id);
AddInstruction(new(zone()) HOsrEntry(osr_entry_id));
+ HContext* context = new(zone()) HContext;
+ AddInstruction(context);
+ environment()->BindContext(context);
current_block()->Goto(loop_predecessor);
loop_predecessor->SetJoinId(statement->EntryId());
set_current_block(loop_predecessor);
@@ -2712,6 +2779,9 @@ void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
ASSERT(current_block() != NULL);
PreProcessOsrEntry(stmt);
HBasicBlock* loop_entry = CreateLoopHeaderBlock();
@@ -2720,8 +2790,7 @@ void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
BreakAndContinueInfo break_info(stmt);
{ BreakAndContinueScope push(&break_info, this);
- Visit(stmt->body());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(Visit(stmt->body()));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -2732,9 +2801,17 @@ void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
// back edge.
body_exit = graph()->CreateBasicBlock();
loop_successor = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->cond(), body_exit, loop_successor);
- body_exit->SetJoinId(stmt->BackEdgeId());
- loop_successor->SetJoinId(stmt->ExitId());
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_exit, loop_successor));
+ if (body_exit->HasPredecessor()) {
+ body_exit->SetJoinId(stmt->BackEdgeId());
+ } else {
+ body_exit = NULL;
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
}
HBasicBlock* loop_exit = CreateLoop(stmt,
loop_entry,
@@ -2746,6 +2823,9 @@ void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
ASSERT(current_block() != NULL);
PreProcessOsrEntry(stmt);
HBasicBlock* loop_entry = CreateLoopHeaderBlock();
@@ -2757,16 +2837,22 @@ void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
if (!stmt->cond()->ToBooleanIsTrue()) {
HBasicBlock* body_entry = graph()->CreateBasicBlock();
loop_successor = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->cond(), body_entry, loop_successor);
- body_entry->SetJoinId(stmt->BodyId());
- loop_successor->SetJoinId(stmt->ExitId());
- set_current_block(body_entry);
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
+ if (body_entry->HasPredecessor()) {
+ body_entry->SetJoinId(stmt->BodyId());
+ set_current_block(body_entry);
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
}
BreakAndContinueInfo break_info(stmt);
- { BreakAndContinueScope push(&break_info, this);
- Visit(stmt->body());
- CHECK_BAILOUT;
+ if (current_block() != NULL) {
+ BreakAndContinueScope push(&break_info, this);
+ CHECK_BAILOUT(Visit(stmt->body()));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
@@ -2780,9 +2866,11 @@ void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (stmt->init() != NULL) {
- Visit(stmt->init());
- CHECK_BAILOUT;
+ CHECK_ALIVE(Visit(stmt->init()));
}
ASSERT(current_block() != NULL);
PreProcessOsrEntry(stmt);
@@ -2794,24 +2882,29 @@ void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
if (stmt->cond() != NULL) {
HBasicBlock* body_entry = graph()->CreateBasicBlock();
loop_successor = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(stmt->cond(), body_entry, loop_successor);
- body_entry->SetJoinId(stmt->BodyId());
- loop_successor->SetJoinId(stmt->ExitId());
- set_current_block(body_entry);
+ CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
+ if (body_entry->HasPredecessor()) {
+ body_entry->SetJoinId(stmt->BodyId());
+ set_current_block(body_entry);
+ }
+ if (loop_successor->HasPredecessor()) {
+ loop_successor->SetJoinId(stmt->ExitId());
+ } else {
+ loop_successor = NULL;
+ }
}
BreakAndContinueInfo break_info(stmt);
- { BreakAndContinueScope push(&break_info, this);
- Visit(stmt->body());
- CHECK_BAILOUT;
+ if (current_block() != NULL) {
+ BreakAndContinueScope push(&break_info, this);
+ CHECK_BAILOUT(Visit(stmt->body()));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
if (stmt->next() != NULL && body_exit != NULL) {
set_current_block(body_exit);
- Visit(stmt->next());
- CHECK_BAILOUT;
+ CHECK_BAILOUT(Visit(stmt->next()));
body_exit = current_block();
}
@@ -2825,22 +2918,34 @@ void HGraphBuilder::VisitForStatement(ForStatement* stmt) {
void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
- BAILOUT("ForInStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("ForInStatement");
}
void HGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
- BAILOUT("TryCatchStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("TryCatchStatement");
}
void HGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- BAILOUT("TryFinallyStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("TryFinallyStatement");
}
void HGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
- BAILOUT("DebuggerStatement");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("DebuggerStatement");
}
@@ -2865,13 +2970,17 @@ static Handle<SharedFunctionInfo> SearchSharedFunctionInfo(
void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Handle<SharedFunctionInfo> shared_info =
SearchSharedFunctionInfo(info()->shared_info()->code(),
expr);
if (shared_info.is_null()) {
shared_info = Compiler::BuildFunctionInfo(expr, info()->script());
}
- CHECK_BAILOUT;
+ // We also have a stack overflow if the recursive compilation did.
+ if (HasStackOverflow()) return;
HFunctionLiteral* instr =
new(zone()) HFunctionLiteral(shared_info, expr->pretenure());
ast_context()->ReturnInstruction(instr, expr->id());
@@ -2880,32 +2989,47 @@ void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
void HGraphBuilder::VisitSharedFunctionInfoLiteral(
SharedFunctionInfoLiteral* expr) {
- BAILOUT("SharedFunctionInfoLiteral");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("SharedFunctionInfoLiteral");
}
void HGraphBuilder::VisitConditional(Conditional* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HBasicBlock* cond_true = graph()->CreateBasicBlock();
HBasicBlock* cond_false = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(expr->condition(), cond_true, cond_false);
- cond_true->SetJoinId(expr->ThenId());
- cond_false->SetJoinId(expr->ElseId());
+ CHECK_BAILOUT(VisitForControl(expr->condition(), cond_true, cond_false));
// Visit the true and false subexpressions in the same AST context as the
// whole expression.
- set_current_block(cond_true);
- Visit(expr->then_expression());
- CHECK_BAILOUT;
- HBasicBlock* other = current_block();
+ if (cond_true->HasPredecessor()) {
+ cond_true->SetJoinId(expr->ThenId());
+ set_current_block(cond_true);
+ CHECK_BAILOUT(Visit(expr->then_expression()));
+ cond_true = current_block();
+ } else {
+ cond_true = NULL;
+ }
- set_current_block(cond_false);
- Visit(expr->else_expression());
- CHECK_BAILOUT;
+ if (cond_false->HasPredecessor()) {
+ cond_false->SetJoinId(expr->ElseId());
+ set_current_block(cond_false);
+ CHECK_BAILOUT(Visit(expr->else_expression()));
+ cond_false = current_block();
+ } else {
+ cond_false = NULL;
+ }
if (!ast_context()->IsTest()) {
- HBasicBlock* join = CreateJoin(other, current_block(), expr->id());
+ HBasicBlock* join = CreateJoin(cond_true, cond_false, expr->id());
set_current_block(join);
- if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+ if (join != NULL && !ast_context()->IsEffect()) {
+ ast_context()->ReturnValue(Pop());
+ }
}
}
@@ -2930,29 +3054,29 @@ HGraphBuilder::GlobalPropertyAccess HGraphBuilder::LookupGlobalProperty(
HValue* HGraphBuilder::BuildContextChainWalk(Variable* var) {
ASSERT(var->IsContextSlot());
- HInstruction* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
int length = info()->scope()->ContextChainLength(var->scope());
while (length-- > 0) {
- context = new(zone()) HOuterContext(context);
- AddInstruction(context);
+ HInstruction* context_instruction = new(zone()) HOuterContext(context);
+ AddInstruction(context_instruction);
+ context = context_instruction;
}
return context;
}
void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Variable* variable = expr->AsVariable();
if (variable == NULL) {
- BAILOUT("reference to rewritten variable");
+ return Bailout("reference to rewritten variable");
} else if (variable->IsStackAllocated()) {
- if (environment()->Lookup(variable)->CheckFlag(HValue::kIsArguments)) {
- BAILOUT("unsupported context for arguments object");
- }
ast_context()->ReturnValue(environment()->Lookup(variable));
} else if (variable->IsContextSlot()) {
if (variable->mode() == Variable::CONST) {
- BAILOUT("reference to const context slot");
+ return Bailout("reference to const context slot");
}
HValue* context = BuildContextChainWalk(variable);
int index = variable->AsSlot()->index();
@@ -2974,8 +3098,7 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole);
ast_context()->ReturnInstruction(instr, expr->id());
} else {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
HGlobalObject* global_object = new(zone()) HGlobalObject(context);
AddInstruction(global_object);
HLoadGlobalGeneric* instr =
@@ -2988,12 +3111,15 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
ast_context()->ReturnInstruction(instr, expr->id());
}
} else {
- BAILOUT("reference to a variable which requires dynamic lookup");
+ return Bailout("reference to a variable which requires dynamic lookup");
}
}
void HGraphBuilder::VisitLiteral(Literal* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HConstant* instr =
new(zone()) HConstant(expr->handle(), Representation::Tagged());
ast_context()->ReturnInstruction(instr, expr->id());
@@ -3001,6 +3127,9 @@ void HGraphBuilder::VisitLiteral(Literal* expr) {
void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
HRegExpLiteral* instr = new(zone()) HRegExpLiteral(expr->pattern(),
expr->flags(),
expr->literal_index());
@@ -3009,8 +3138,10 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ HValue* context = environment()->LookupContext();
HObjectLiteral* literal =
new(zone()) HObjectLiteral(context,
expr->constant_properties(),
@@ -3038,7 +3169,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
case ObjectLiteral::Property::COMPUTED:
if (key->handle()->IsSymbol()) {
if (property->emit_store()) {
- VISIT_FOR_VALUE(value);
+ CHECK_ALIVE(VisitForValue(value));
HValue* value = Pop();
Handle<String> name = Handle<String>::cast(key->handle());
HStoreNamedGeneric* store =
@@ -3051,7 +3182,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
AddInstruction(store);
AddSimulate(key->id());
} else {
- VISIT_FOR_EFFECT(value);
+ CHECK_ALIVE(VisitForEffect(value));
}
break;
}
@@ -3059,7 +3190,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
case ObjectLiteral::Property::PROTOTYPE:
case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
- BAILOUT("Object literal with complex property");
+ return Bailout("Object literal with complex property");
default: UNREACHABLE();
}
}
@@ -3080,6 +3211,9 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
ZoneList<Expression*>* subexprs = expr->values();
int length = subexprs->length();
@@ -3099,9 +3233,9 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
// is already set in the cloned array.
if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue;
- VISIT_FOR_VALUE(subexpr);
+ CHECK_ALIVE(VisitForValue(subexpr));
HValue* value = Pop();
- if (!Smi::IsValid(i)) BAILOUT("Non-smi key in array literal");
+ if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal");
// Load the elements array before the first store.
if (elements == NULL) {
@@ -3120,7 +3254,10 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
- BAILOUT("CatchExtensionObject");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("CatchExtensionObject");
}
@@ -3186,8 +3323,7 @@ HInstruction* HGraphBuilder::BuildStoreNamedField(HValue* object,
HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object,
Handle<String> name,
HValue* value) {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
return new(zone()) HStoreNamedGeneric(
context,
object,
@@ -3261,7 +3397,7 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr,
// know about and do not want to handle ones we've never seen. Otherwise
// use a generic IC.
if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
- current_block()->FinishExitWithDeoptimization();
+ current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
} else {
HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
instr->set_position(expr->position());
@@ -3299,14 +3435,14 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
Property* prop = expr->target()->AsProperty();
ASSERT(prop != NULL);
expr->RecordTypeFeedback(oracle());
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* value = NULL;
HInstruction* instr = NULL;
if (prop->key()->IsPropertyName()) {
// Named store.
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
value = Pop();
HValue* object = Pop();
@@ -3330,8 +3466,8 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
} else {
// Keyed store.
- VISIT_FOR_VALUE(prop->key());
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ CHECK_ALIVE(VisitForValue(expr->value()));
value = Pop();
HValue* key = Pop();
HValue* object = Pop();
@@ -3363,8 +3499,7 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var,
AddInstruction(instr);
if (instr->HasSideEffects()) AddSimulate(ast_id);
} else {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
HGlobalObject* global_object = new(zone()) HGlobalObject(context);
AddInstruction(global_object);
HStoreGlobalGeneric* instr =
@@ -3393,7 +3528,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
BinaryOperation* operation = expr->binary_operation();
if (var != NULL) {
- VISIT_FOR_VALUE(operation);
+ CHECK_ALIVE(VisitForValue(operation));
if (var->is_global()) {
HandleGlobalVariableAssignment(var,
@@ -3410,7 +3545,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
AddInstruction(instr);
if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
} else {
- BAILOUT("compound assignment to lookup slot");
+ return Bailout("compound assignment to lookup slot");
}
ast_context()->ReturnValue(Pop());
@@ -3419,7 +3554,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
if (prop->key()->IsPropertyName()) {
// Named property.
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* obj = Top();
HInstruction* load = NULL;
@@ -3433,7 +3568,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HValue* right = Pop();
HValue* left = Pop();
@@ -3451,8 +3586,8 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
} else {
// Keyed property.
- VISIT_FOR_VALUE(prop->obj());
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
HValue* obj = environment()->ExpressionStackAt(1);
HValue* key = environment()->ExpressionStackAt(0);
@@ -3460,7 +3595,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId());
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HValue* right = Pop();
HValue* left = Pop();
@@ -3479,12 +3614,15 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
}
} else {
- BAILOUT("invalid lhs in compound assignment");
+ return Bailout("invalid lhs in compound assignment");
}
}
void HGraphBuilder::VisitAssignment(Assignment* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
VariableProxy* proxy = expr->target()->AsVariableProxy();
Variable* var = proxy->AsVariable();
Property* prop = expr->target()->AsProperty();
@@ -3496,28 +3634,20 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
}
if (var != NULL) {
- if (proxy->IsArguments()) BAILOUT("assignment to arguments");
+ if (proxy->IsArguments()) return Bailout("assignment to arguments");
// Handle the assignment.
if (var->IsStackAllocated()) {
- HValue* value = NULL;
- // Handle stack-allocated variables on the right-hand side directly.
// We do not allow the arguments object to occur in a context where it
// may escape, but assignments to stack-allocated locals are
- // permitted. Handling such assignments here bypasses the check for
- // the arguments object in VisitVariableProxy.
- Variable* rhs_var = expr->value()->AsVariableProxy()->AsVariable();
- if (rhs_var != NULL && rhs_var->IsStackAllocated()) {
- value = environment()->Lookup(rhs_var);
- } else {
- VISIT_FOR_VALUE(expr->value());
- value = Pop();
- }
+ // permitted.
+ CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED));
+ HValue* value = Pop();
Bind(var, value);
ast_context()->ReturnValue(value);
} else if (var->IsContextSlot() && var->mode() != Variable::CONST) {
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HValue* context = BuildContextChainWalk(var);
int index = var->AsSlot()->index();
HStoreContextSlot* instr =
@@ -3527,7 +3657,7 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
ast_context()->ReturnValue(Pop());
} else if (var->is_global()) {
- VISIT_FOR_VALUE(expr->value());
+ CHECK_ALIVE(VisitForValue(expr->value()));
HandleGlobalVariableAssignment(var,
Top(),
expr->position(),
@@ -3535,23 +3665,26 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
ast_context()->ReturnValue(Pop());
} else {
- BAILOUT("assignment to LOOKUP or const CONTEXT variable");
+ return Bailout("assignment to LOOKUP or const CONTEXT variable");
}
} else if (prop != NULL) {
HandlePropertyAssignment(expr);
} else {
- BAILOUT("invalid left-hand side in assignment");
+ return Bailout("invalid left-hand side in assignment");
}
}
void HGraphBuilder::VisitThrow(Throw* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
// We don't optimize functions with invalid left-hand sides in
// assignments, count operations, or for-in. Consequently throw can
// currently only occur in an effect context.
ASSERT(ast_context()->IsEffect());
- VISIT_FOR_VALUE(expr->exception());
+ CHECK_ALIVE(VisitForValue(expr->exception()));
HValue* value = environment()->Pop();
HThrow* instr = new(zone()) HThrow(value);
@@ -3591,8 +3724,7 @@ HInstruction* HGraphBuilder::BuildLoadNamedGeneric(HValue* obj,
Property* expr) {
ASSERT(expr->key()->IsPropertyName());
Handle<Object> name = expr->key()->AsLiteral()->handle();
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
return new(zone()) HLoadNamedGeneric(context, obj, name);
}
@@ -3622,8 +3754,7 @@ HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj,
HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
HValue* key) {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
return new(zone()) HLoadKeyedGeneric(context, object, key);
}
@@ -3700,8 +3831,7 @@ HInstruction* HGraphBuilder::BuildLoadKeyed(HValue* obj,
HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object,
HValue* key,
HValue* value) {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
return new(zone()) HStoreKeyedGeneric(
context,
object,
@@ -3756,6 +3886,11 @@ HInstruction* HGraphBuilder::BuildStoreKeyedSpecializedArrayElement(
HLoadExternalArrayPointer* external_elements =
new(zone()) HLoadExternalArrayPointer(elements);
AddInstruction(external_elements);
+ if (expr->external_array_type() == kExternalPixelArray) {
+ HClampToUint8* clamp = new(zone()) HClampToUint8(val);
+ AddInstruction(clamp);
+ val = clamp;
+ }
return new(zone()) HStoreKeyedSpecializedArrayElement(
external_elements,
checked_key,
@@ -3810,7 +3945,7 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
} else {
Push(graph()->GetArgumentsObject());
VisitForValue(expr->key());
- if (HasStackOverflow()) return false;
+ if (HasStackOverflow() || current_block() == NULL) return true;
HValue* key = Pop();
Drop(1); // Arguments object.
HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
@@ -3826,31 +3961,29 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) {
void HGraphBuilder::VisitProperty(Property* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
expr->RecordTypeFeedback(oracle());
if (TryArgumentsAccess(expr)) return;
- CHECK_BAILOUT;
- VISIT_FOR_VALUE(expr->obj());
+ CHECK_ALIVE(VisitForValue(expr->obj()));
HInstruction* instr = NULL;
if (expr->IsArrayLength()) {
HValue* array = Pop();
AddInstruction(new(zone()) HCheckNonSmi(array));
- AddInstruction(new(zone()) HCheckInstanceType(array,
- JS_ARRAY_TYPE,
- JS_ARRAY_TYPE));
+ AddInstruction(HCheckInstanceType::NewIsJSArray(array));
instr = new(zone()) HJSArrayLength(array);
} else if (expr->IsStringLength()) {
HValue* string = Pop();
AddInstruction(new(zone()) HCheckNonSmi(string));
- AddInstruction(new(zone()) HCheckInstanceType(string,
- FIRST_STRING_TYPE,
- LAST_STRING_TYPE));
+ AddInstruction(HCheckInstanceType::NewIsString(string));
instr = new(zone()) HStringLength(string);
} else if (expr->IsStringAccess()) {
- VISIT_FOR_VALUE(expr->key());
+ CHECK_ALIVE(VisitForValue(expr->key()));
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
@@ -3877,7 +4010,7 @@ void HGraphBuilder::VisitProperty(Property* expr) {
}
} else {
- VISIT_FOR_VALUE(expr->key());
+ CHECK_ALIVE(VisitForValue(expr->key()));
HValue* key = Pop();
HValue* obj = Pop();
@@ -3938,10 +4071,11 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
PrintF("Trying to inline the polymorphic call to %s\n",
*name->ToCString());
}
- if (!FLAG_polymorphic_inlining || !TryInline(expr)) {
- // Check for bailout, as trying to inline might fail due to bailout
- // during hydrogen processing.
- CHECK_BAILOUT;
+ if (FLAG_polymorphic_inlining && TryInline(expr)) {
+ // Trying to inline will signal that we should bailout from the
+ // entire compilation by setting stack overflow on the visitor.
+ if (HasStackOverflow()) return;
+ } else {
HCallConstantFunction* call =
new(zone()) HCallConstantFunction(expr->target(), argument_count);
call->set_position(expr->position());
@@ -3959,10 +4093,9 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
// know about and do not want to handle ones we've never seen. Otherwise
// use a generic IC.
if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
- current_block()->FinishExitWithDeoptimization();
+ current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses);
} else {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count);
call->set_position(expr->position());
PreProcessCall(call);
@@ -3981,30 +4114,27 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
// even without predecessors to the join block, we set it as the exit
// block and continue by adding instructions there.
ASSERT(join != NULL);
- set_current_block(join);
if (join->HasPredecessor()) {
+ set_current_block(join);
join->SetJoinId(expr->id());
if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
+ } else {
+ set_current_block(NULL);
}
}
-void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) {
+void HGraphBuilder::TraceInline(Handle<JSFunction> target,
+ Handle<JSFunction> caller,
+ const char* reason) {
if (FLAG_trace_inlining) {
+ SmartPointer<char> target_name = target->shared()->DebugName()->ToCString();
+ SmartPointer<char> caller_name = caller->shared()->DebugName()->ToCString();
if (reason == NULL) {
- // We are currently in the context of inlined function thus we have
- // to go to an outer FunctionState to get caller.
- SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
- SmartPointer<char> caller =
- function_state()->outer()->compilation_info()->function()->
- debug_name()->ToCString();
- PrintF("Inlined %s called from %s.\n", *callee, *caller);
+ PrintF("Inlined %s called from %s.\n", *target_name, *caller_name);
} else {
- SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
- SmartPointer<char> caller =
- info()->function()->debug_name()->ToCString();
PrintF("Did not inline %s called from %s (%s).\n",
- *callee, *caller, reason);
+ *target_name, *caller_name, reason);
}
}
}
@@ -4013,20 +4143,28 @@ void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) {
bool HGraphBuilder::TryInline(Call* expr) {
if (!FLAG_use_inlining) return false;
+ // The function call we are inlining is a method call if the call
+ // is a property call.
+ CallKind call_kind = (expr->expression()->AsProperty() == NULL)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+
// Precondition: call is monomorphic and we have found a target with the
// appropriate arity.
+ Handle<JSFunction> caller = info()->closure();
Handle<JSFunction> target = expr->target();
+ Handle<SharedFunctionInfo> target_shared(target->shared());
// Do a quick check on source code length to avoid parsing large
// inlining candidates.
if (FLAG_limit_inlining && target->shared()->SourceSize() > kMaxSourceSize) {
- TraceInline(target, "target text too big");
+ TraceInline(target, caller, "target text too big");
return false;
}
// Target must be inlineable.
if (!target->IsInlineable()) {
- TraceInline(target, "target not inlineable");
+ TraceInline(target, caller, "target not inlineable");
return false;
}
@@ -4035,7 +4173,7 @@ bool HGraphBuilder::TryInline(Call* expr) {
if (target->context() != outer_info->closure()->context() ||
outer_info->scope()->contains_with() ||
outer_info->scope()->num_heap_slots() > 0) {
- TraceInline(target, "target requires context change");
+ TraceInline(target, caller, "target requires context change");
return false;
}
@@ -4044,7 +4182,7 @@ bool HGraphBuilder::TryInline(Call* expr) {
int current_level = 1;
while (env->outer() != NULL) {
if (current_level == Compiler::kMaxInliningLevels) {
- TraceInline(target, "inline depth limit reached");
+ TraceInline(target, caller, "inline depth limit reached");
return false;
}
current_level++;
@@ -4052,14 +4190,14 @@ bool HGraphBuilder::TryInline(Call* expr) {
}
// Don't inline recursive functions.
- if (target->shared() == outer_info->closure()->shared()) {
- TraceInline(target, "target is recursive");
+ if (*target_shared == outer_info->closure()->shared()) {
+ TraceInline(target, caller, "target is recursive");
return false;
}
// We don't want to add more than a certain number of nodes from inlining.
if (FLAG_limit_inlining && inlined_count_ > kMaxInlinedNodes) {
- TraceInline(target, "cumulative AST node limit reached");
+ TraceInline(target, caller, "cumulative AST node limit reached");
return false;
}
@@ -4072,14 +4210,14 @@ bool HGraphBuilder::TryInline(Call* expr) {
if (target_info.isolate()->has_pending_exception()) {
// Parse or scope error, never optimize this function.
SetStackOverflow();
- target->shared()->set_optimization_disabled(true);
+ target_shared->DisableOptimization(*target);
}
- TraceInline(target, "parse failure");
+ TraceInline(target, caller, "parse failure");
return false;
}
if (target_info.scope()->num_heap_slots() > 0) {
- TraceInline(target, "target has context-allocated variables");
+ TraceInline(target, caller, "target has context-allocated variables");
return false;
}
FunctionLiteral* function = target_info.function();
@@ -4087,32 +4225,31 @@ bool HGraphBuilder::TryInline(Call* expr) {
// Count the number of AST nodes added by inlining this call.
int nodes_added = AstNode::Count() - count_before;
if (FLAG_limit_inlining && nodes_added > kMaxInlinedSize) {
- TraceInline(target, "target AST is too large");
+ TraceInline(target, caller, "target AST is too large");
return false;
}
// Check if we can handle all declarations in the inlined functions.
VisitDeclarations(target_info.scope()->declarations());
if (HasStackOverflow()) {
- TraceInline(target, "target has non-trivial declaration");
+ TraceInline(target, caller, "target has non-trivial declaration");
ClearStackOverflow();
return false;
}
// Don't inline functions that uses the arguments object or that
// have a mismatching number of parameters.
- Handle<SharedFunctionInfo> target_shared(target->shared());
int arity = expr->arguments()->length();
if (function->scope()->arguments() != NULL ||
arity != target_shared->formal_parameter_count()) {
- TraceInline(target, "target requires special argument handling");
+ TraceInline(target, caller, "target requires special argument handling");
return false;
}
// All statements in the body must be inlineable.
for (int i = 0, count = function->body()->length(); i < count; ++i) {
if (!function->body()->at(i)->IsInlineable()) {
- TraceInline(target, "target contains unsupported syntax");
+ TraceInline(target, caller, "target contains unsupported syntax");
return false;
}
}
@@ -4124,7 +4261,7 @@ bool HGraphBuilder::TryInline(Call* expr) {
// generating the optimized inline code.
target_info.EnableDeoptimizationSupport();
if (!FullCodeGenerator::MakeCode(&target_info)) {
- TraceInline(target, "could not generate deoptimization info");
+ TraceInline(target, caller, "could not generate deoptimization info");
return false;
}
target_shared->EnableDeoptimizationSupport(*target_info.code());
@@ -4144,25 +4281,33 @@ bool HGraphBuilder::TryInline(Call* expr) {
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner_env =
- environment()->CopyForInlining(target, function, true, undefined);
+ environment()->CopyForInlining(target,
+ function,
+ HEnvironment::HYDROGEN,
+ undefined,
+ call_kind);
HBasicBlock* body_entry = CreateBasicBlock(inner_env);
current_block()->Goto(body_entry);
body_entry->SetJoinId(expr->ReturnId());
set_current_block(body_entry);
- AddInstruction(new(zone()) HEnterInlined(target, function));
+ AddInstruction(new(zone()) HEnterInlined(target,
+ function,
+ call_kind));
VisitStatements(function->body());
if (HasStackOverflow()) {
// Bail out if the inline function did, as we cannot residualize a call
// instead.
- TraceInline(target, "inline graph construction failed");
- return false;
+ TraceInline(target, caller, "inline graph construction failed");
+ target_shared->DisableOptimization(*target);
+ inline_bailout_ = true;
+ return true;
}
// Update inlined nodes count.
inlined_count_ += nodes_added;
- TraceInline(target, NULL);
+ TraceInline(target, caller, NULL);
if (current_block() != NULL) {
// Add a return of undefined if control can fall off the body. In a
@@ -4195,26 +4340,32 @@ bool HGraphBuilder::TryInline(Call* expr) {
if (inlined_test_context() != NULL) {
HBasicBlock* if_true = inlined_test_context()->if_true();
HBasicBlock* if_false = inlined_test_context()->if_false();
- if_true->SetJoinId(expr->id());
- if_false->SetJoinId(expr->id());
ASSERT(ast_context() == inlined_test_context());
// Pop the return test context from the expression context stack.
ClearInlinedTestContext();
// Forward to the real test context.
- HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
- HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
- if_true->Goto(true_target, false);
- if_false->Goto(false_target, false);
+ if (if_true->HasPredecessor()) {
+ if_true->SetJoinId(expr->id());
+ HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
+ if_true->Goto(true_target, false);
+ }
+ if (if_false->HasPredecessor()) {
+ if_false->SetJoinId(expr->id());
+ HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
+ if_false->Goto(false_target, false);
+ }
// TODO(kmillikin): Come up with a better way to handle this. It is too
// subtle. NULL here indicates that the enclosing context has no control
// flow to handle.
set_current_block(NULL);
- } else {
+ } else if (function_return()->HasPredecessor()) {
function_return()->SetJoinId(expr->id());
set_current_block(function_return());
+ } else {
+ set_current_block(NULL);
}
return true;
@@ -4351,14 +4502,16 @@ bool HGraphBuilder::TryCallApply(Call* expr) {
// Found pattern f.apply(receiver, arguments).
VisitForValue(prop->obj());
- if (HasStackOverflow()) return false;
- HValue* function = Pop();
+ if (HasStackOverflow() || current_block() == NULL) return true;
+ HValue* function = Top();
+ AddCheckConstantFunction(expr, function, function_map, true);
+ Drop(1);
+
VisitForValue(args->at(0));
- if (HasStackOverflow()) return false;
+ if (HasStackOverflow() || current_block() == NULL) return true;
HValue* receiver = Pop();
HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
- AddCheckConstantFunction(expr, function, function_map, true);
HInstruction* result =
new(zone()) HApplyArguments(function, receiver, length, elements);
result->set_position(expr->position());
@@ -4368,6 +4521,9 @@ bool HGraphBuilder::TryCallApply(Call* expr) {
void HGraphBuilder::VisitCall(Call* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Expression* callee = expr->expression();
int argument_count = expr->arguments()->length() + 1; // Plus receiver.
HInstruction* call = NULL;
@@ -4376,20 +4532,18 @@ void HGraphBuilder::VisitCall(Call* expr) {
if (prop != NULL) {
if (!prop->key()->IsPropertyName()) {
// Keyed function call.
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->key()));
// Push receiver and key like the non-optimized code generator expects it.
HValue* key = Pop();
HValue* receiver = Pop();
Push(key);
Push(receiver);
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
call = PreProcessCall(
new(zone()) HCallKeyed(context, key, argument_count));
call->set_position(expr->position());
@@ -4399,18 +4553,15 @@ void HGraphBuilder::VisitCall(Call* expr) {
}
// Named function call.
- expr->RecordTypeFeedback(oracle());
+ expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD);
if (TryCallApply(expr)) return;
- CHECK_BAILOUT;
- VISIT_FOR_VALUE(prop->obj());
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
- expr->RecordTypeFeedback(oracle());
ZoneMapList* types = expr->GetReceiverTypes();
HValue* receiver =
@@ -4430,23 +4581,16 @@ void HGraphBuilder::VisitCall(Call* expr) {
// When the target has a custom call IC generator, use the IC,
// because it is likely to generate better code. Also use the IC
// when a primitive receiver check is required.
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
call = PreProcessCall(
new(zone()) HCallNamed(context, name, argument_count));
} else {
AddCheckConstantFunction(expr, receiver, receiver_map, true);
- if (TryInline(expr)) {
- return;
- } else {
- // Check for bailout, as the TryInline call in the if condition above
- // might return false due to bailout during hydrogen processing.
- CHECK_BAILOUT;
- call = PreProcessCall(
- new(zone()) HCallConstantFunction(expr->target(),
- argument_count));
- }
+ if (TryInline(expr)) return;
+ call = PreProcessCall(
+ new(zone()) HCallConstantFunction(expr->target(),
+ argument_count));
}
} else if (types != NULL && types->length() > 1) {
ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
@@ -4454,8 +4598,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
return;
} else {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
call = PreProcessCall(
new(zone()) HCallNamed(context, name, argument_count));
}
@@ -4466,7 +4609,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
if (!global_call) {
++argument_count;
- VISIT_FOR_VALUE(expr->expression());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
}
if (global_call) {
@@ -4484,14 +4627,12 @@ void HGraphBuilder::VisitCall(Call* expr) {
if (known_global_function) {
// Push the global object instead of the global receiver because
// code generated by the full code generator expects it.
- HContext* context = new(zone()) HContext;
+ HValue* context = environment()->LookupContext();
HGlobalObject* global_object = new(zone()) HGlobalObject(context);
- AddInstruction(context);
PushAndAdd(global_object);
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- VISIT_FOR_VALUE(expr->expression());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* function = Pop();
AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
@@ -4505,35 +4646,25 @@ void HGraphBuilder::VisitCall(Call* expr) {
IsGlobalObject());
environment()->SetExpressionStackAt(receiver_index, global_receiver);
- if (TryInline(expr)) {
- return;
- }
- // Check for bailout, as trying to inline might fail due to bailout
- // during hydrogen processing.
- CHECK_BAILOUT;
-
+ if (TryInline(expr)) return;
call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
- argument_count));
+ argument_count));
} else {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
PushAndAdd(new(zone()) HGlobalObject(context));
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
call = PreProcessCall(new(zone()) HCallGlobal(context,
- var->name(),
- argument_count));
+ var->name(),
+ argument_count));
}
} else {
- HContext* context = new(zone()) HContext;
+ HValue* context = environment()->LookupContext();
HGlobalObject* global_object = new(zone()) HGlobalObject(context);
- AddInstruction(context);
AddInstruction(global_object);
PushAndAdd(new(zone()) HGlobalReceiver(global_object));
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
call = PreProcessCall(new(zone()) HCallFunction(context, argument_count));
}
@@ -4545,14 +4676,15 @@ void HGraphBuilder::VisitCall(Call* expr) {
void HGraphBuilder::VisitCallNew(CallNew* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
// The constructor function is also used as the receiver argument to the
// JS construct call builtin.
- VISIT_FOR_VALUE(expr->expression());
- VisitExpressions(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
// The constructor is both an operand to the instruction and an argument
// to the construct call.
@@ -4581,8 +4713,11 @@ const HGraphBuilder::InlineFunctionGenerator
void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (expr->is_jsruntime()) {
- BAILOUT("call to a JavaScript runtime function");
+ return Bailout("call to a JavaScript runtime function");
}
const Runtime::Function* function = expr->function();
@@ -4602,8 +4737,7 @@ void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
(this->*generator)(expr);
} else {
ASSERT(function->intrinsic_type == Runtime::RUNTIME);
- VisitArgumentList(expr->arguments());
- CHECK_BAILOUT;
+ CHECK_ALIVE(VisitArgumentList(expr->arguments()));
Handle<String> name = expr->name();
int argument_count = expr->arguments()->length();
@@ -4617,128 +4751,203 @@ void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
- Token::Value op = expr->op();
- if (op == Token::VOID) {
- VISIT_FOR_EFFECT(expr->expression());
- ast_context()->ReturnValue(graph()->GetConstantUndefined());
- } else if (op == Token::DELETE) {
- Property* prop = expr->expression()->AsProperty();
- Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
- if (prop == NULL && var == NULL) {
- // Result of deleting non-property, non-variable reference is true.
- // Evaluate the subexpression for side effects.
- VISIT_FOR_EFFECT(expr->expression());
- ast_context()->ReturnValue(graph()->GetConstantTrue());
- } else if (var != NULL &&
- !var->is_global() &&
- var->AsSlot() != NULL &&
- var->AsSlot()->type() != Slot::LOOKUP) {
- // Result of deleting non-global, non-dynamic variables is false.
- // The subexpression does not have side effects.
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ switch (expr->op()) {
+ case Token::DELETE: return VisitDelete(expr);
+ case Token::VOID: return VisitVoid(expr);
+ case Token::TYPEOF: return VisitTypeof(expr);
+ case Token::ADD: return VisitAdd(expr);
+ case Token::SUB: return VisitSub(expr);
+ case Token::BIT_NOT: return VisitBitNot(expr);
+ case Token::NOT: return VisitNot(expr);
+ default: UNREACHABLE();
+ }
+}
+
+void HGraphBuilder::VisitDelete(UnaryOperation* expr) {
+ Property* prop = expr->expression()->AsProperty();
+ Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
+ if (prop == NULL && var == NULL) {
+ // Result of deleting non-property, non-variable reference is true.
+ // Evaluate the subexpression for side effects.
+ CHECK_ALIVE(VisitForEffect(expr->expression()));
+ ast_context()->ReturnValue(graph()->GetConstantTrue());
+ } else if (var != NULL &&
+ !var->is_global() &&
+ var->AsSlot() != NULL &&
+ var->AsSlot()->type() != Slot::LOOKUP) {
+ // Result of deleting non-global, non-dynamic variables is false.
+ // The subexpression does not have side effects.
+ ast_context()->ReturnValue(graph()->GetConstantFalse());
+ } else if (prop != NULL) {
+ if (prop->is_synthetic()) {
+ // Result of deleting parameters is false, even when they rewrite
+ // to accesses on the arguments object.
ast_context()->ReturnValue(graph()->GetConstantFalse());
- } else if (prop != NULL) {
- if (prop->is_synthetic()) {
- // Result of deleting parameters is false, even when they rewrite
- // to accesses on the arguments object.
- ast_context()->ReturnValue(graph()->GetConstantFalse());
- } else {
- VISIT_FOR_VALUE(prop->obj());
- VISIT_FOR_VALUE(prop->key());
- HValue* key = Pop();
- HValue* obj = Pop();
- HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
- ast_context()->ReturnInstruction(instr, expr->id());
- }
- } else if (var->is_global()) {
- BAILOUT("delete with global variable");
} else {
- BAILOUT("delete with non-global variable");
- }
- } else if (op == Token::NOT) {
- if (ast_context()->IsTest()) {
- TestContext* context = TestContext::cast(ast_context());
- VisitForControl(expr->expression(),
- context->if_false(),
- context->if_true());
- } else if (ast_context()->IsValue()) {
- HBasicBlock* materialize_false = graph()->CreateBasicBlock();
- HBasicBlock* materialize_true = graph()->CreateBasicBlock();
- VISIT_FOR_CONTROL(expr->expression(),
- materialize_false,
- materialize_true);
- materialize_false->SetJoinId(expr->expression()->id());
- materialize_true->SetJoinId(expr->expression()->id());
-
- set_current_block(materialize_false);
- Push(graph()->GetConstantFalse());
- set_current_block(materialize_true);
- Push(graph()->GetConstantTrue());
-
- HBasicBlock* join =
- CreateJoin(materialize_false, materialize_true, expr->id());
- set_current_block(join);
- ast_context()->ReturnValue(Pop());
- } else {
- ASSERT(ast_context()->IsEffect());
- VisitForEffect(expr->expression());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
+ HValue* key = Pop();
+ HValue* obj = Pop();
+ HDeleteProperty* instr = new(zone()) HDeleteProperty(obj, key);
+ ast_context()->ReturnInstruction(instr, expr->id());
}
+ } else if (var->is_global()) {
+ Bailout("delete with global variable");
+ } else {
+ Bailout("delete with non-global variable");
+ }
+}
- } else if (op == Token::TYPEOF) {
- VisitForTypeOf(expr->expression());
- if (HasStackOverflow()) return;
- HValue* value = Pop();
- ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
+void HGraphBuilder::VisitVoid(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForEffect(expr->expression()));
+ ast_context()->ReturnValue(graph()->GetConstantUndefined());
+}
+
+
+void HGraphBuilder::VisitTypeof(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForTypeOf(expr->expression()));
+ HValue* value = Pop();
+ ast_context()->ReturnInstruction(new(zone()) HTypeof(value), expr->id());
+}
+
+
+void HGraphBuilder::VisitAdd(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* value = Pop();
+ HInstruction* instr = new(zone()) HMul(value, graph_->GetConstant1());
+ ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HGraphBuilder::VisitSub(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* value = Pop();
+ HInstruction* instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
+ TypeInfo info = oracle()->UnaryType(expr);
+ Representation rep = ToRepresentation(info);
+ TraceRepresentation(expr->op(), info, instr, rep);
+ instr->AssumeRepresentation(rep);
+ ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HGraphBuilder::VisitBitNot(UnaryOperation* expr) {
+ CHECK_ALIVE(VisitForValue(expr->expression()));
+ HValue* value = Pop();
+ HInstruction* instr = new(zone()) HBitNot(value);
+ ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HGraphBuilder::VisitNot(UnaryOperation* expr) {
+ // TODO(svenpanne) Perhaps a switch/virtual function is nicer here.
+ if (ast_context()->IsTest()) {
+ TestContext* context = TestContext::cast(ast_context());
+ VisitForControl(expr->expression(),
+ context->if_false(),
+ context->if_true());
+ return;
+ }
+
+ if (ast_context()->IsEffect()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ ASSERT(ast_context()->IsValue());
+ HBasicBlock* materialize_false = graph()->CreateBasicBlock();
+ HBasicBlock* materialize_true = graph()->CreateBasicBlock();
+ CHECK_BAILOUT(VisitForControl(expr->expression(),
+ materialize_false,
+ materialize_true));
+
+ if (materialize_false->HasPredecessor()) {
+ materialize_false->SetJoinId(expr->expression()->id());
+ set_current_block(materialize_false);
+ Push(graph()->GetConstantFalse());
} else {
- VISIT_FOR_VALUE(expr->expression());
- HValue* value = Pop();
- HInstruction* instr = NULL;
- switch (op) {
- case Token::BIT_NOT:
- instr = new(zone()) HBitNot(value);
- break;
- case Token::SUB:
- instr = new(zone()) HMul(value, graph_->GetConstantMinus1());
- break;
- case Token::ADD:
- instr = new(zone()) HMul(value, graph_->GetConstant1());
- break;
- default:
- BAILOUT("Value: unsupported unary operation");
- break;
- }
- ast_context()->ReturnInstruction(instr, expr->id());
+ materialize_false = NULL;
+ }
+
+ if (materialize_true->HasPredecessor()) {
+ materialize_true->SetJoinId(expr->expression()->id());
+ set_current_block(materialize_true);
+ Push(graph()->GetConstantTrue());
+ } else {
+ materialize_true = NULL;
}
+
+ HBasicBlock* join =
+ CreateJoin(materialize_false, materialize_true, expr->id());
+ set_current_block(join);
+ if (join != NULL) ast_context()->ReturnValue(Pop());
}
-HInstruction* HGraphBuilder::BuildIncrement(HValue* value, bool increment) {
- HConstant* delta = increment
+HInstruction* HGraphBuilder::BuildIncrement(bool returns_original_input,
+ CountOperation* expr) {
+ // The input to the count operation is on top of the expression stack.
+ TypeInfo info = oracle()->IncrementType(expr);
+ Representation rep = ToRepresentation(info);
+ if (rep.IsTagged()) {
+ rep = Representation::Integer32();
+ }
+
+ if (returns_original_input) {
+ // We need an explicit HValue representing ToNumber(input). The
+ // actual HChange instruction we need is (sometimes) added in a later
+ // phase, so it is not available now to be used as an input to HAdd and
+ // as the return value.
+ HInstruction* number_input = new(zone()) HForceRepresentation(Pop(), rep);
+ AddInstruction(number_input);
+ Push(number_input);
+ }
+
+ // The addition has no side effects, so we do not need
+ // to simulate the expression stack after this instruction.
+ // Any later failures deopt to the load of the input or earlier.
+ HConstant* delta = (expr->op() == Token::INC)
? graph_->GetConstant1()
: graph_->GetConstantMinus1();
- HInstruction* instr = new(zone()) HAdd(value, delta);
- AssumeRepresentation(instr, Representation::Integer32());
+ HInstruction* instr = new(zone()) HAdd(Top(), delta);
+ TraceRepresentation(expr->op(), info, instr, rep);
+ instr->AssumeRepresentation(rep);
+ AddInstruction(instr);
return instr;
}
void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
Expression* target = expr->expression();
VariableProxy* proxy = target->AsVariableProxy();
Variable* var = proxy->AsVariable();
Property* prop = target->AsProperty();
- ASSERT(var == NULL || prop == NULL);
- bool inc = expr->op() == Token::INC;
+ if (var == NULL && prop == NULL) {
+ return Bailout("invalid lhs in count operation");
+ }
+
+ // Match the full code generator stack by simulating an extra stack
+ // element for postfix operations in a non-effect context. The return
+ // value is ToNumber(input).
+ bool returns_original_input =
+ expr->is_postfix() && !ast_context()->IsEffect();
+ HValue* input = NULL; // ToNumber(original_input).
+ HValue* after = NULL; // The result after incrementing or decrementing.
if (var != NULL) {
- VISIT_FOR_VALUE(target);
-
- // Match the full code generator stack by simulating an extra stack
- // element for postfix operations in a non-effect context.
- bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
- HValue* before = has_extra ? Top() : Pop();
- HInstruction* after = BuildIncrement(before, inc);
- AddInstruction(after);
+ // Argument of the count operation is a variable, not a property.
+ ASSERT(prop == NULL);
+ CHECK_ALIVE(VisitForValue(target));
+
+ after = BuildIncrement(returns_original_input, expr);
+ input = returns_original_input ? Top() : Pop();
Push(after);
if (var->is_global()) {
@@ -4756,23 +4965,19 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
AddInstruction(instr);
if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId());
} else {
- BAILOUT("lookup variable in count operation");
+ return Bailout("lookup variable in count operation");
}
- Drop(has_extra ? 2 : 1);
- ast_context()->ReturnValue(expr->is_postfix() ? before : after);
- } else if (prop != NULL) {
+ } else {
+ // Argument of the count operation is a property.
+ ASSERT(prop != NULL);
prop->RecordTypeFeedback(oracle());
if (prop->key()->IsPropertyName()) {
// Named property.
+ if (returns_original_input) Push(graph_->GetConstantUndefined());
- // Match the full code generator stack by simulating an extra stack
- // element for postfix operations in a non-effect context.
- bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
- if (has_extra) Push(graph_->GetConstantUndefined());
-
- VISIT_FOR_VALUE(prop->obj());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
HValue* obj = Top();
HInstruction* load = NULL;
@@ -4786,11 +4991,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CountId());
- HValue* before = Pop();
- // There is no deoptimization to after the increment, so we don't need
- // to simulate the expression stack after this instruction.
- HInstruction* after = BuildIncrement(before, inc);
- AddInstruction(after);
+ after = BuildIncrement(returns_original_input, expr);
+ input = Pop();
HInstruction* store = BuildStoreNamed(obj, after, prop);
AddInstruction(store);
@@ -4799,22 +5001,15 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
// of the operation, and the placeholder with the original value if
// necessary.
environment()->SetExpressionStackAt(0, after);
- if (has_extra) environment()->SetExpressionStackAt(1, before);
+ if (returns_original_input) environment()->SetExpressionStackAt(1, input);
if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
- Drop(has_extra ? 2 : 1);
-
- ast_context()->ReturnValue(expr->is_postfix() ? before : after);
} else {
// Keyed property.
+ if (returns_original_input) Push(graph_->GetConstantUndefined());
- // Match the full code generator stack by simulate an extra stack element
- // for postfix operations in a non-effect context.
- bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
- if (has_extra) Push(graph_->GetConstantUndefined());
-
- VISIT_FOR_VALUE(prop->obj());
- VISIT_FOR_VALUE(prop->key());
+ CHECK_ALIVE(VisitForValue(prop->obj()));
+ CHECK_ALIVE(VisitForValue(prop->key()));
HValue* obj = environment()->ExpressionStackAt(1);
HValue* key = environment()->ExpressionStackAt(0);
@@ -4822,11 +5017,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
PushAndAdd(load);
if (load->HasSideEffects()) AddSimulate(expr->CountId());
- HValue* before = Pop();
- // There is no deoptimization to after the increment, so we don't need
- // to simulate the expression stack after this instruction.
- HInstruction* after = BuildIncrement(before, inc);
- AddInstruction(after);
+ after = BuildIncrement(returns_original_input, expr);
+ input = Pop();
expr->RecordTypeFeedback(oracle());
HInstruction* store = BuildStoreKeyed(obj, key, after, expr);
@@ -4837,24 +5029,32 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
// original value if necessary.
Drop(1);
environment()->SetExpressionStackAt(0, after);
- if (has_extra) environment()->SetExpressionStackAt(1, before);
+ if (returns_original_input) environment()->SetExpressionStackAt(1, input);
if (store->HasSideEffects()) AddSimulate(expr->AssignmentId());
- Drop(has_extra ? 2 : 1);
-
- ast_context()->ReturnValue(expr->is_postfix() ? before : after);
}
-
- } else {
- BAILOUT("invalid lhs in count operation");
}
+
+ Drop(returns_original_input ? 2 : 1);
+ ast_context()->ReturnValue(expr->is_postfix() ? input : after);
+}
+
+
+HCompareSymbolEq* HGraphBuilder::BuildSymbolCompare(HValue* left,
+ HValue* right,
+ Token::Value op) {
+ ASSERT(op == Token::EQ || op == Token::EQ_STRICT);
+ AddInstruction(new(zone()) HCheckNonSmi(left));
+ AddInstruction(HCheckInstanceType::NewIsSymbol(left));
+ AddInstruction(new(zone()) HCheckNonSmi(right));
+ AddInstruction(HCheckInstanceType::NewIsSymbol(right));
+ return new(zone()) HCompareSymbolEq(left, right, op);
}
HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HValue* index) {
AddInstruction(new(zone()) HCheckNonSmi(string));
- AddInstruction(new(zone()) HCheckInstanceType(
- string, FIRST_STRING_TYPE, LAST_STRING_TYPE));
+ AddInstruction(HCheckInstanceType::NewIsString(string));
HStringLength* length = new(zone()) HStringLength(string);
AddInstruction(length);
HInstruction* checked_index =
@@ -4866,45 +5066,8 @@ HStringCharCodeAt* HGraphBuilder::BuildStringCharCodeAt(HValue* string,
HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
HValue* left,
HValue* right) {
- HInstruction* instr = NULL;
- switch (expr->op()) {
- case Token::ADD:
- instr = new(zone()) HAdd(left, right);
- break;
- case Token::SUB:
- instr = new(zone()) HSub(left, right);
- break;
- case Token::MUL:
- instr = new(zone()) HMul(left, right);
- break;
- case Token::MOD:
- instr = new(zone()) HMod(left, right);
- break;
- case Token::DIV:
- instr = new(zone()) HDiv(left, right);
- break;
- case Token::BIT_XOR:
- instr = new(zone()) HBitXor(left, right);
- break;
- case Token::BIT_AND:
- instr = new(zone()) HBitAnd(left, right);
- break;
- case Token::BIT_OR:
- instr = new(zone()) HBitOr(left, right);
- break;
- case Token::SAR:
- instr = new(zone()) HSar(left, right);
- break;
- case Token::SHR:
- instr = new(zone()) HShr(left, right);
- break;
- case Token::SHL:
- instr = new(zone()) HShl(left, right);
- break;
- default:
- UNREACHABLE();
- }
TypeInfo info = oracle()->BinaryType(expr);
+ HInstruction* instr = BuildBinaryOperation(expr->op(), left, right, info);
// If we hit an uninitialized binary op stub we will get type info
// for a smi operation. If one of the operands is a constant string
// do not generate code assuming it is a smi operation.
@@ -4913,19 +5076,47 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr,
(right->IsConstant() && HConstant::cast(right)->HasStringValue()))) {
return instr;
}
- if (FLAG_trace_representation) {
- PrintF("Info: %s/%s\n", info.ToString(), ToRepresentation(info).Mnemonic());
- }
Representation rep = ToRepresentation(info);
// We only generate either int32 or generic tagged bitwise operations.
if (instr->IsBitwiseBinaryOperation() && rep.IsDouble()) {
rep = Representation::Integer32();
}
- AssumeRepresentation(instr, rep);
+ TraceRepresentation(expr->op(), info, instr, rep);
+ instr->AssumeRepresentation(rep);
return instr;
}
+HInstruction* HGraphBuilder::BuildBinaryOperation(
+ Token::Value op, HValue* left, HValue* right, TypeInfo info) {
+ switch (op) {
+ case Token::ADD:
+ if (info.IsString()) {
+ AddInstruction(new(zone()) HCheckNonSmi(left));
+ AddInstruction(HCheckInstanceType::NewIsString(left));
+ AddInstruction(new(zone()) HCheckNonSmi(right));
+ AddInstruction(HCheckInstanceType::NewIsString(right));
+ return new(zone()) HStringAdd(left, right);
+ } else {
+ return new(zone()) HAdd(left, right);
+ }
+ case Token::SUB: return new(zone()) HSub(left, right);
+ case Token::MUL: return new(zone()) HMul(left, right);
+ case Token::MOD: return new(zone()) HMod(left, right);
+ case Token::DIV: return new(zone()) HDiv(left, right);
+ case Token::BIT_XOR: return new(zone()) HBitXor(left, right);
+ case Token::BIT_AND: return new(zone()) HBitAnd(left, right);
+ case Token::BIT_OR: return new(zone()) HBitOr(left, right);
+ case Token::SAR: return new(zone()) HSar(left, right);
+ case Token::SHR: return new(zone()) HShr(left, right);
+ case Token::SHL: return new(zone()) HShl(left, right);
+ default:
+ UNREACHABLE();
+ return NULL;
+ }
+}
+
+
// Check for the form (%_ClassOf(foo) === 'BarClass').
static bool IsClassOfTest(CompareOperation* expr) {
if (expr->op() != Token::EQ_STRICT) return false;
@@ -4941,110 +5132,141 @@ static bool IsClassOfTest(CompareOperation* expr) {
void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
- if (expr->op() == Token::COMMA) {
- VISIT_FOR_EFFECT(expr->left());
- // Visit the right subexpression in the same AST context as the entire
- // expression.
- Visit(expr->right());
-
- } else if (expr->op() == Token::AND || expr->op() == Token::OR) {
- bool is_logical_and = (expr->op() == Token::AND);
- if (ast_context()->IsTest()) {
- TestContext* context = TestContext::cast(ast_context());
- // Translate left subexpression.
- HBasicBlock* eval_right = graph()->CreateBasicBlock();
- if (is_logical_and) {
- VISIT_FOR_CONTROL(expr->left(), eval_right, context->if_false());
- } else {
- VISIT_FOR_CONTROL(expr->left(), context->if_true(), eval_right);
- }
- eval_right->SetJoinId(expr->RightId());
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ switch (expr->op()) {
+ case Token::COMMA: return VisitComma(expr);
+ case Token::OR: return VisitAndOr(expr, false);
+ case Token::AND: return VisitAndOr(expr, true);
+ default: return VisitCommon(expr);
+ }
+}
+
- // Translate right subexpression by visiting it in the same AST
- // context as the entire expression.
+void HGraphBuilder::VisitComma(BinaryOperation* expr) {
+ CHECK_ALIVE(VisitForEffect(expr->left()));
+ // Visit the right subexpression in the same AST context as the entire
+ // expression.
+ Visit(expr->right());
+}
+
+
+void HGraphBuilder::VisitAndOr(BinaryOperation* expr, bool is_logical_and) {
+ if (ast_context()->IsTest()) {
+ TestContext* context = TestContext::cast(ast_context());
+ // Translate left subexpression.
+ HBasicBlock* eval_right = graph()->CreateBasicBlock();
+ if (is_logical_and) {
+ CHECK_BAILOUT(VisitForControl(expr->left(),
+ eval_right,
+ context->if_false()));
+ } else {
+ CHECK_BAILOUT(VisitForControl(expr->left(),
+ context->if_true(),
+ eval_right));
+ }
+
+ // Translate right subexpression by visiting it in the same AST
+ // context as the entire expression.
+ if (eval_right->HasPredecessor()) {
+ eval_right->SetJoinId(expr->RightId());
set_current_block(eval_right);
Visit(expr->right());
+ }
- } else if (ast_context()->IsValue()) {
- VISIT_FOR_VALUE(expr->left());
- ASSERT(current_block() != NULL);
+ } else if (ast_context()->IsValue()) {
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ ASSERT(current_block() != NULL);
- // We need an extra block to maintain edge-split form.
- HBasicBlock* empty_block = graph()->CreateBasicBlock();
- HBasicBlock* eval_right = graph()->CreateBasicBlock();
- HTest* test = is_logical_and
- ? new(zone()) HTest(Top(), eval_right, empty_block)
- : new(zone()) HTest(Top(), empty_block, eval_right);
- current_block()->Finish(test);
+ // We need an extra block to maintain edge-split form.
+ HBasicBlock* empty_block = graph()->CreateBasicBlock();
+ HBasicBlock* eval_right = graph()->CreateBasicBlock();
+ HTest* test = is_logical_and
+ ? new(zone()) HTest(Top(), eval_right, empty_block)
+ : new(zone()) HTest(Top(), empty_block, eval_right);
+ current_block()->Finish(test);
- set_current_block(eval_right);
- Drop(1); // Value of the left subexpression.
- VISIT_FOR_VALUE(expr->right());
+ set_current_block(eval_right);
+ Drop(1); // Value of the left subexpression.
+ CHECK_BAILOUT(VisitForValue(expr->right()));
- HBasicBlock* join_block =
- CreateJoin(empty_block, current_block(), expr->id());
- set_current_block(join_block);
- ast_context()->ReturnValue(Pop());
+ HBasicBlock* join_block =
+ CreateJoin(empty_block, current_block(), expr->id());
+ set_current_block(join_block);
+ ast_context()->ReturnValue(Pop());
+ } else {
+ ASSERT(ast_context()->IsEffect());
+ // In an effect context, we don't need the value of the left subexpression,
+ // only its control flow and side effects. We need an extra block to
+ // maintain edge-split form.
+ HBasicBlock* empty_block = graph()->CreateBasicBlock();
+ HBasicBlock* right_block = graph()->CreateBasicBlock();
+ if (is_logical_and) {
+ CHECK_BAILOUT(VisitForControl(expr->left(), right_block, empty_block));
} else {
- ASSERT(ast_context()->IsEffect());
- // In an effect context, we don't need the value of the left
- // subexpression, only its control flow and side effects. We need an
- // extra block to maintain edge-split form.
- HBasicBlock* empty_block = graph()->CreateBasicBlock();
- HBasicBlock* right_block = graph()->CreateBasicBlock();
- HBasicBlock* join_block = graph()->CreateBasicBlock();
- if (is_logical_and) {
- VISIT_FOR_CONTROL(expr->left(), right_block, empty_block);
- } else {
- VISIT_FOR_CONTROL(expr->left(), empty_block, right_block);
- }
- // TODO(kmillikin): Find a way to fix this. It's ugly that there are
- // actually two empty blocks (one here and one inserted by
- // TestContext::BuildBranch, and that they both have an HSimulate
- // though the second one is not a merge node, and that we really have
- // no good AST ID to put on that first HSimulate.
+ CHECK_BAILOUT(VisitForControl(expr->left(), empty_block, right_block));
+ }
+
+ // TODO(kmillikin): Find a way to fix this. It's ugly that there are
+ // actually two empty blocks (one here and one inserted by
+ // TestContext::BuildBranch, and that they both have an HSimulate though the
+ // second one is not a merge node, and that we really have no good AST ID to
+ // put on that first HSimulate.
+
+ if (empty_block->HasPredecessor()) {
empty_block->SetJoinId(expr->id());
+ } else {
+ empty_block = NULL;
+ }
+
+ if (right_block->HasPredecessor()) {
right_block->SetJoinId(expr->RightId());
set_current_block(right_block);
- VISIT_FOR_EFFECT(expr->right());
-
- empty_block->Goto(join_block);
- current_block()->Goto(join_block);
- join_block->SetJoinId(expr->id());
- set_current_block(join_block);
- // We did not materialize any value in the predecessor environments,
- // so there is no need to handle it here.
+ CHECK_BAILOUT(VisitForEffect(expr->right()));
+ right_block = current_block();
+ } else {
+ right_block = NULL;
}
- } else {
- VISIT_FOR_VALUE(expr->left());
- VISIT_FOR_VALUE(expr->right());
-
- HValue* right = Pop();
- HValue* left = Pop();
- HInstruction* instr = BuildBinaryOperation(expr, left, right);
- instr->set_position(expr->position());
- ast_context()->ReturnInstruction(instr, expr->id());
+ HBasicBlock* join_block =
+ CreateJoin(empty_block, right_block, expr->id());
+ set_current_block(join_block);
+ // We did not materialize any value in the predecessor environments,
+ // so there is no need to handle it here.
}
}
-void HGraphBuilder::AssumeRepresentation(HValue* value, Representation r) {
- if (value->CheckFlag(HValue::kFlexibleRepresentation)) {
- if (FLAG_trace_representation) {
- PrintF("Assume representation for %s to be %s (%d)\n",
- value->Mnemonic(),
- r.Mnemonic(),
- graph_->GetMaximumValueID());
- }
- value->ChangeRepresentation(r);
- // The representation of the value is dictated by type feedback and
- // will not be changed later.
- value->ClearFlag(HValue::kFlexibleRepresentation);
- } else if (FLAG_trace_representation) {
- PrintF("No representation assumed\n");
- }
+void HGraphBuilder::VisitCommon(BinaryOperation* expr) {
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ CHECK_ALIVE(VisitForValue(expr->right()));
+ HValue* right = Pop();
+ HValue* left = Pop();
+ HInstruction* instr = BuildBinaryOperation(expr, left, right);
+ instr->set_position(expr->position());
+ ast_context()->ReturnInstruction(instr, expr->id());
+}
+
+
+void HGraphBuilder::TraceRepresentation(Token::Value op,
+ TypeInfo info,
+ HValue* value,
+ Representation rep) {
+ if (!FLAG_trace_representation) return;
+ // TODO(svenpanne) Under which circumstances are we actually not flexible?
+ // At first glance, this looks a bit weird...
+ bool flexible = value->CheckFlag(HValue::kFlexibleRepresentation);
+ PrintF("Operation %s has type info %s, %schange representation assumption "
+ "for %s (ID %d) from %s to %s\n",
+ Token::Name(op),
+ info.ToString(),
+ flexible ? "" : " DO NOT ",
+ value->Mnemonic(),
+ graph_->GetMaximumValueID(),
+ value->representation().Mnemonic(),
+ rep.Mnemonic());
}
@@ -5058,9 +5280,12 @@ Representation HGraphBuilder::ToRepresentation(TypeInfo info) {
void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
if (IsClassOfTest(expr)) {
CallRuntime* call = expr->left()->AsCallRuntime();
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
Literal* literal = expr->right()->AsLiteral();
Handle<String> rhs = Handle<String>::cast(literal->handle());
@@ -5076,8 +5301,7 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
if ((expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT) &&
left_unary != NULL && left_unary->op() == Token::TYPEOF &&
right_literal != NULL && right_literal->handle()->IsString()) {
- VisitForTypeOf(left_unary->expression());
- if (HasStackOverflow()) return;
+ CHECK_ALIVE(VisitForTypeOf(left_unary->expression()));
HValue* left = Pop();
HInstruction* instr = new(zone()) HTypeofIs(left,
Handle<String>::cast(right_literal->handle()));
@@ -5086,8 +5310,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
return;
}
- VISIT_FOR_VALUE(expr->left());
- VISIT_FOR_VALUE(expr->right());
+ CHECK_ALIVE(VisitForValue(expr->left()));
+ CHECK_ALIVE(VisitForValue(expr->right()));
HValue* right = Pop();
HValue* left = Pop();
@@ -5124,15 +5348,14 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
// If the target is not null we have found a known global function that is
// assumed to stay the same for this instanceof.
if (target.is_null()) {
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ HValue* context = environment()->LookupContext();
instr = new(zone()) HInstanceOf(context, left, right);
} else {
AddInstruction(new(zone()) HCheckFunction(right, target));
instr = new(zone()) HInstanceOfKnownGlobal(left, target);
}
} else if (op == Token::IN) {
- BAILOUT("Unsupported comparison: in");
+ instr = new(zone()) HIn(left, right);
} else if (type_info.IsNonPrimitive()) {
switch (op) {
case Token::EQ:
@@ -5145,9 +5368,12 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
break;
}
default:
- BAILOUT("Unsupported non-primitive compare");
+ return Bailout("Unsupported non-primitive compare");
break;
}
+ } else if (type_info.IsString() && oracle()->IsSymbolCompare(expr) &&
+ (op == Token::EQ || op == Token::EQ_STRICT)) {
+ instr = BuildSymbolCompare(left, right, op);
} else {
HCompare* compare = new(zone()) HCompare(left, right, op);
Representation r = ToRepresentation(type_info);
@@ -5160,7 +5386,10 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
- VISIT_FOR_VALUE(expr->expression());
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* value = Pop();
HIsNull* compare = new(zone()) HIsNull(value, expr->is_strict());
@@ -5169,7 +5398,10 @@ void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
- BAILOUT("ThisFunction");
+ ASSERT(!HasStackOverflow());
+ ASSERT(current_block() != NULL);
+ ASSERT(current_block()->HasPredecessor());
+ return Bailout("ThisFunction");
}
@@ -5184,7 +5416,7 @@ void HGraphBuilder::VisitDeclaration(Declaration* decl) {
(slot != NULL && slot->type() == Slot::LOOKUP) ||
decl->mode() == Variable::CONST ||
decl->fun() != NULL) {
- BAILOUT("unsupported declaration");
+ return Bailout("unsupported declaration");
}
}
@@ -5193,7 +5425,7 @@ void HGraphBuilder::VisitDeclaration(Declaration* decl) {
// Support for types.
void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HIsSmi* result = new(zone()) HIsSmi(value);
ast_context()->ReturnInstruction(result, call->id());
@@ -5202,7 +5434,7 @@ void HGraphBuilder::GenerateIsSmi(CallRuntime* call) {
void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceType* result =
new(zone()) HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE);
@@ -5212,7 +5444,7 @@ void HGraphBuilder::GenerateIsSpecObject(CallRuntime* call) {
void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceType* result =
new(zone()) HHasInstanceType(value, JS_FUNCTION_TYPE);
@@ -5222,7 +5454,7 @@ void HGraphBuilder::GenerateIsFunction(CallRuntime* call) {
void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasCachedArrayIndex* result = new(zone()) HHasCachedArrayIndex(value);
ast_context()->ReturnInstruction(result, call->id());
@@ -5231,7 +5463,7 @@ void HGraphBuilder::GenerateHasCachedArrayIndex(CallRuntime* call) {
void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceType* result = new(zone()) HHasInstanceType(value, JS_ARRAY_TYPE);
ast_context()->ReturnInstruction(result, call->id());
@@ -5240,7 +5472,7 @@ void HGraphBuilder::GenerateIsArray(CallRuntime* call) {
void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceType* result =
new(zone()) HHasInstanceType(value, JS_REGEXP_TYPE);
@@ -5250,7 +5482,7 @@ void HGraphBuilder::GenerateIsRegExp(CallRuntime* call) {
void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HIsObject* test = new(zone()) HIsObject(value);
ast_context()->ReturnInstruction(test, call->id());
@@ -5258,18 +5490,23 @@ void HGraphBuilder::GenerateIsObject(CallRuntime* call) {
void HGraphBuilder::GenerateIsNonNegativeSmi(CallRuntime* call) {
- BAILOUT("inlined runtime function: IsNonNegativeSmi");
+ return Bailout("inlined runtime function: IsNonNegativeSmi");
}
void HGraphBuilder::GenerateIsUndetectableObject(CallRuntime* call) {
- BAILOUT("inlined runtime function: IsUndetectableObject");
+ ASSERT(call->arguments()->length() == 1);
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ HValue* value = Pop();
+ ast_context()->ReturnInstruction(new(zone()) HIsUndetectable(value),
+ call->id());
}
void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
CallRuntime* call) {
- BAILOUT("inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
+ return Bailout(
+ "inlined runtime function: IsStringWrapperSafeForDefaultValueOf");
}
@@ -5306,7 +5543,7 @@ void HGraphBuilder::GenerateArguments(CallRuntime* call) {
// function is blacklisted by AstNode::IsInlineable.
ASSERT(function_state()->outer() == NULL);
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* index = Pop();
HInstruction* elements = AddInstruction(new(zone()) HArgumentsElements);
HInstruction* length = AddInstruction(new(zone()) HArgumentsLength(elements));
@@ -5320,13 +5557,13 @@ void HGraphBuilder::GenerateArguments(CallRuntime* call) {
void HGraphBuilder::GenerateClassOf(CallRuntime* call) {
// The special form detected by IsClassOfTest is detected before we get here
// and does not cause a bailout.
- BAILOUT("inlined runtime function: ClassOf");
+ return Bailout("inlined runtime function: ClassOf");
}
void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HValueOf* result = new(zone()) HValueOf(value);
ast_context()->ReturnInstruction(result, call->id());
@@ -5334,15 +5571,15 @@ void HGraphBuilder::GenerateValueOf(CallRuntime* call) {
void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
- BAILOUT("inlined runtime function: SetValueOf");
+ return Bailout("inlined runtime function: SetValueOf");
}
// Fast support for charCodeAt(n).
void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
@@ -5353,7 +5590,7 @@ void HGraphBuilder::GenerateStringCharCodeAt(CallRuntime* call) {
// Fast support for string.charAt(n) and string[n].
void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* char_code = Pop();
HStringCharFromCode* result = new(zone()) HStringCharFromCode(char_code);
ast_context()->ReturnInstruction(result, call->id());
@@ -5363,8 +5600,8 @@ void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
// Fast support for string.charAt(n) and string[n].
void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* index = Pop();
HValue* string = Pop();
HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
@@ -5377,8 +5614,8 @@ void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
// Fast support for object equality testing.
void HGraphBuilder::GenerateObjectEquals(CallRuntime* call) {
ASSERT(call->arguments()->length() == 2);
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* right = Pop();
HValue* left = Pop();
HCompareJSObjectEq* result = new(zone()) HCompareJSObjectEq(left, right);
@@ -5394,17 +5631,15 @@ void HGraphBuilder::GenerateLog(CallRuntime* call) {
// Fast support for Math.random().
void HGraphBuilder::GenerateRandomHeapNumber(CallRuntime* call) {
- BAILOUT("inlined runtime function: RandomHeapNumber");
+ return Bailout("inlined runtime function: RandomHeapNumber");
}
// Fast support for StringAdd.
void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
ASSERT_EQ(2, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result = new(zone()) HCallStub(context, CodeStub::StringAdd, 2);
Drop(2);
ast_context()->ReturnInstruction(result, call->id());
@@ -5414,10 +5649,8 @@ void HGraphBuilder::GenerateStringAdd(CallRuntime* call) {
// Fast support for SubString.
void HGraphBuilder::GenerateSubString(CallRuntime* call) {
ASSERT_EQ(3, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result = new(zone()) HCallStub(context, CodeStub::SubString, 3);
Drop(3);
ast_context()->ReturnInstruction(result, call->id());
@@ -5427,10 +5660,8 @@ void HGraphBuilder::GenerateSubString(CallRuntime* call) {
// Fast support for StringCompare.
void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
ASSERT_EQ(2, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result =
new(zone()) HCallStub(context, CodeStub::StringCompare, 2);
Drop(2);
@@ -5441,10 +5672,8 @@ void HGraphBuilder::GenerateStringCompare(CallRuntime* call) {
// Support for direct calls from JavaScript to native RegExp code.
void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
ASSERT_EQ(4, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result = new(zone()) HCallStub(context, CodeStub::RegExpExec, 4);
Drop(4);
ast_context()->ReturnInstruction(result, call->id());
@@ -5454,10 +5683,8 @@ void HGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
// Construct a RegExp exec result with two in-object properties.
void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
ASSERT_EQ(3, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result =
new(zone()) HCallStub(context, CodeStub::RegExpConstructResult, 3);
Drop(3);
@@ -5467,17 +5694,15 @@ void HGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
// Support for fast native caches.
void HGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
- BAILOUT("inlined runtime function: GetFromCache");
+ return Bailout("inlined runtime function: GetFromCache");
}
// Fast support for number to string.
void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result =
new(zone()) HCallStub(context, CodeStub::NumberToString, 1);
Drop(1);
@@ -5489,21 +5714,34 @@ void HGraphBuilder::GenerateNumberToString(CallRuntime* call) {
// indices. This should only be used if the indices are known to be
// non-negative and within bounds of the elements array at the call site.
void HGraphBuilder::GenerateSwapElements(CallRuntime* call) {
- BAILOUT("inlined runtime function: SwapElements");
+ return Bailout("inlined runtime function: SwapElements");
}
// Fast call for custom callbacks.
void HGraphBuilder::GenerateCallFunction(CallRuntime* call) {
- BAILOUT("inlined runtime function: CallFunction");
+ // 1 ~ The function to call is not itself an argument to the call.
+ int arg_count = call->arguments()->length() - 1;
+ ASSERT(arg_count >= 1); // There's always at least a receiver.
+
+ for (int i = 0; i < arg_count; ++i) {
+ CHECK_ALIVE(VisitArgument(call->arguments()->at(i)));
+ }
+ CHECK_ALIVE(VisitForValue(call->arguments()->last()));
+ HValue* function = Pop();
+ HValue* context = environment()->LookupContext();
+ HInvokeFunction* result =
+ new(zone()) HInvokeFunction(context, function, arg_count);
+ Drop(arg_count);
+ ast_context()->ReturnInstruction(result, call->id());
}
// Fast call to math functions.
void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
ASSERT_EQ(2, call->arguments()->length());
- VISIT_FOR_VALUE(call->arguments()->at(0));
- VISIT_FOR_VALUE(call->arguments()->at(1));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* right = Pop();
HValue* left = Pop();
HPower* result = new(zone()) HPower(left, right);
@@ -5513,10 +5751,8 @@ void HGraphBuilder::GenerateMathPow(CallRuntime* call) {
void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result =
new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
result->set_transcendental_type(TranscendentalCache::SIN);
@@ -5527,10 +5763,8 @@ void HGraphBuilder::GenerateMathSin(CallRuntime* call) {
void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result =
new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
result->set_transcendental_type(TranscendentalCache::COS);
@@ -5541,10 +5775,8 @@ void HGraphBuilder::GenerateMathCos(CallRuntime* call) {
void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
ASSERT_EQ(1, call->arguments()->length());
- VisitArgumentList(call->arguments());
- CHECK_BAILOUT;
- HContext* context = new(zone()) HContext;
- AddInstruction(context);
+ CHECK_ALIVE(VisitArgumentList(call->arguments()));
+ HValue* context = environment()->LookupContext();
HCallStub* result =
new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1);
result->set_transcendental_type(TranscendentalCache::LOG);
@@ -5554,19 +5786,19 @@ void HGraphBuilder::GenerateMathLog(CallRuntime* call) {
void HGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
- BAILOUT("inlined runtime function: MathSqrt");
+ return Bailout("inlined runtime function: MathSqrt");
}
// Check whether two RegExps are equivalent
void HGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
- BAILOUT("inlined runtime function: IsRegExpEquivalent");
+ return Bailout("inlined runtime function: IsRegExpEquivalent");
}
void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
ASSERT(call->arguments()->length() == 1);
- VISIT_FOR_VALUE(call->arguments()->at(0));
+ CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HGetCachedArrayIndex* result = new(zone()) HGetCachedArrayIndex(value);
ast_context()->ReturnInstruction(result, call->id());
@@ -5574,15 +5806,12 @@ void HGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
void HGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) {
- BAILOUT("inlined runtime function: FastAsciiArrayJoin");
+ return Bailout("inlined runtime function: FastAsciiArrayJoin");
}
-#undef BAILOUT
#undef CHECK_BAILOUT
-#undef VISIT_FOR_EFFECT
-#undef VISIT_FOR_VALUE
-#undef ADD_TO_SUBGRAPH
+#undef CHECK_ALIVE
HEnvironment::HEnvironment(HEnvironment* outer,
@@ -5592,6 +5821,7 @@ HEnvironment::HEnvironment(HEnvironment* outer,
values_(0),
assigned_variables_(4),
parameter_count_(0),
+ specials_count_(1),
local_count_(0),
outer_(outer),
pop_count_(0),
@@ -5605,6 +5835,7 @@ HEnvironment::HEnvironment(const HEnvironment* other)
: values_(0),
assigned_variables_(0),
parameter_count_(0),
+ specials_count_(1),
local_count_(0),
outer_(NULL),
pop_count_(0),
@@ -5621,7 +5852,7 @@ void HEnvironment::Initialize(int parameter_count,
local_count_ = local_count;
// Avoid reallocating the temporaries' backing store on the first Push.
- int total = parameter_count + local_count + stack_height;
+ int total = parameter_count + specials_count_ + local_count + stack_height;
values_.Initialize(total + 4);
for (int i = 0; i < total; ++i) values_.Add(NULL);
}
@@ -5680,12 +5911,12 @@ void HEnvironment::Bind(int index, HValue* value) {
bool HEnvironment::HasExpressionAt(int index) const {
- return index >= parameter_count_ + local_count_;
+ return index >= parameter_count_ + specials_count_ + local_count_;
}
bool HEnvironment::ExpressionStackIsEmpty() const {
- int first_expression = parameter_count() + local_count();
+ int first_expression = parameter_count() + specials_count() + local_count();
ASSERT(length() >= first_expression);
return length() == first_expression;
}
@@ -5738,10 +5969,12 @@ HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
}
-HEnvironment* HEnvironment::CopyForInlining(Handle<JSFunction> target,
- FunctionLiteral* function,
- bool is_speculative,
- HConstant* undefined) const {
+HEnvironment* HEnvironment::CopyForInlining(
+ Handle<JSFunction> target,
+ FunctionLiteral* function,
+ CompilationPhase compilation_phase,
+ HConstant* undefined,
+ CallKind call_kind) const {
// Outer environment is a copy of this one without the arguments.
int arity = function->scope()->num_parameters();
HEnvironment* outer = Copy();
@@ -5751,22 +5984,27 @@ HEnvironment* HEnvironment::CopyForInlining(Handle<JSFunction> target,
HEnvironment* inner =
new(zone) HEnvironment(outer, function->scope(), target);
// Get the argument values from the original environment.
- if (is_speculative) {
+ if (compilation_phase == HYDROGEN) {
for (int i = 0; i <= arity; ++i) { // Include receiver.
HValue* push = ExpressionStackAt(arity - i);
inner->SetValueAt(i, push);
}
} else {
+ ASSERT(compilation_phase == LITHIUM);
for (int i = 0; i <= arity; ++i) { // Include receiver.
- inner->SetValueAt(i, ExpressionStackAt(arity - i));
+ HValue* push = ExpressionStackAt(arity - i);
+ inner->SetValueAt(i, push);
}
}
-
- // Initialize the stack-allocated locals to undefined.
- int local_base = arity + 1;
- int local_count = function->scope()->num_stack_slots();
- for (int i = 0; i < local_count; ++i) {
- inner->SetValueAt(local_base + i, undefined);
+ // If the function we are inlining is a strict mode function, pass
+ // undefined as the receiver for function calls (instead of the
+ // global receiver).
+ if (function->strict_mode() && call_kind == CALL_AS_FUNCTION) {
+ inner->SetValueAt(0, undefined);
+ }
+ inner->SetValueAt(arity + 1, outer->LookupContext());
+ for (int i = arity + 2; i < inner->length(); ++i) {
+ inner->SetValueAt(i, undefined);
}
inner->set_ast_id(AstNode::kFunctionEntryId);
@@ -5777,8 +6015,11 @@ HEnvironment* HEnvironment::CopyForInlining(Handle<JSFunction> target,
void HEnvironment::PrintTo(StringStream* stream) {
for (int i = 0; i < length(); i++) {
if (i == 0) stream->Add("parameters\n");
- if (i == parameter_count()) stream->Add("locals\n");
- if (i == parameter_count() + local_count()) stream->Add("expressions");
+ if (i == parameter_count()) stream->Add("specials\n");
+ if (i == parameter_count() + specials_count()) stream->Add("locals\n");
+ if (i == parameter_count() + specials_count() + local_count()) {
+ stream->Add("expressions");
+ }
HValue* val = values_.at(i);
stream->Add("%d: ", i);
if (val != NULL) {
@@ -5873,10 +6114,11 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
Tag states_tag(this, "states");
Tag locals_tag(this, "locals");
int total = current->phis()->length();
- trace_.Add("size %d\n", total);
- trace_.Add("method \"None\"");
+ PrintIntProperty("size", current->phis()->length());
+ PrintStringProperty("method", "None");
for (int j = 0; j < total; ++j) {
HPhi* phi = current->phis()->at(j);
+ PrintIndent();
trace_.Add("%d ", phi->merged_index());
phi->PrintNameTo(&trace_);
trace_.Add(" ");
@@ -5890,7 +6132,8 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
HInstruction* instruction = current->first();
while (instruction != NULL) {
int bci = 0;
- int uses = instruction->uses()->length();
+ int uses = instruction->UseCount();
+ PrintIndent();
trace_.Add("%d %d ", bci, uses);
instruction->PrintNameTo(&trace_);
trace_.Add(" ");
@@ -5910,6 +6153,7 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
for (int i = first_index; i <= last_index; ++i) {
LInstruction* linstr = instructions->at(i);
if (linstr != NULL) {
+ PrintIndent();
trace_.Add("%d ",
LifetimePosition::FromInstructionIndex(i).Value());
linstr->PrintTo(&trace_);
@@ -5945,6 +6189,7 @@ void HTracer::TraceLiveRanges(const char* name, LAllocator* allocator) {
void HTracer::TraceLiveRange(LiveRange* range, const char* type) {
if (range != NULL && !range->IsEmpty()) {
+ PrintIndent();
trace_.Add("%d %s", range->id(), type);
if (range->HasRegisterAssigned()) {
LOperand* op = range->CreateAssignedOperand();
diff --git a/src/hydrogen.h b/src/hydrogen.h
index 37671f4d..4d8a153f 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -30,16 +30,18 @@
#include "v8.h"
+#include "allocation.h"
#include "ast.h"
#include "compiler.h"
-#include "data-flow.h"
#include "hydrogen-instructions.h"
+#include "type-info.h"
#include "zone.h"
namespace v8 {
namespace internal {
// Forward declarations.
+class BitVector;
class HEnvironment;
class HGraph;
class HLoopInformation;
@@ -124,8 +126,8 @@ class HBasicBlock: public ZoneObject {
void AddSimulate(int id) { AddInstruction(CreateSimulate(id)); }
void AssignCommonDominator(HBasicBlock* other);
- void FinishExitWithDeoptimization() {
- FinishExit(CreateDeoptimize());
+ void FinishExitWithDeoptimization(HDeoptimize::UseEnvironment has_uses) {
+ FinishExit(CreateDeoptimize(has_uses));
}
// Add the inlined function exit sequence, adding an HLeaveInlined
@@ -152,7 +154,7 @@ class HBasicBlock: public ZoneObject {
void AddDominatedBlock(HBasicBlock* block);
HSimulate* CreateSimulate(int id);
- HDeoptimize* CreateDeoptimize();
+ HDeoptimize* CreateDeoptimize(HDeoptimize::UseEnvironment has_uses);
int block_id_;
HGraph* graph_;
@@ -226,6 +228,10 @@ class HGraph: public ZoneObject {
// Returns false if there are phi-uses of the arguments-object
// which are not supported by the optimizing compiler.
+ bool CheckPhis();
+
+ // Returns false if there are phi-uses of hole values comming
+ // from uninitialized consts.
bool CollectPhis();
Handle<Code> Compile(CompilationInfo* info);
@@ -280,11 +286,10 @@ class HGraph: public ZoneObject {
void PropagateMinusZeroChecks(HValue* value, BitVector* visited);
void RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi);
void InsertRepresentationChangeForUse(HValue* value,
- HValue* use,
+ HValue* use_value,
+ int use_index,
Representation to);
- void InsertRepresentationChangesForValue(HValue* current,
- ZoneList<HValue*>* value_list,
- ZoneList<Representation>* rep_list);
+ void InsertRepresentationChangesForValue(HValue* value);
void InferTypes(ZoneList<HValue*>* worklist);
void InitializeInferredTypes(int from_inclusive, int to_inclusive);
void CheckForBackEdge(HBasicBlock* block, HBasicBlock* successor);
@@ -312,6 +317,8 @@ Zone* HBasicBlock::zone() { return graph_->zone(); }
class HEnvironment: public ZoneObject {
public:
+ enum CompilationPhase { HYDROGEN, LITHIUM };
+
HEnvironment(HEnvironment* outer,
Scope* scope,
Handle<JSFunction> closure);
@@ -323,6 +330,7 @@ class HEnvironment: public ZoneObject {
return &assigned_variables_;
}
int parameter_count() const { return parameter_count_; }
+ int specials_count() const { return specials_count_; }
int local_count() const { return local_count_; }
HEnvironment* outer() const { return outer_; }
int pop_count() const { return pop_count_; }
@@ -332,6 +340,9 @@ class HEnvironment: public ZoneObject {
void set_ast_id(int id) { ast_id_ = id; }
int length() const { return values_.length(); }
+ bool is_special_index(int i) const {
+ return i >= parameter_count() && i < parameter_count() + specials_count();
+ }
void Bind(Variable* variable, HValue* value) {
Bind(IndexFor(variable), value);
@@ -339,6 +350,10 @@ class HEnvironment: public ZoneObject {
void Bind(int index, HValue* value);
+ void BindContext(HValue* value) {
+ Bind(parameter_count(), value);
+ }
+
HValue* Lookup(Variable* variable) const {
return Lookup(IndexFor(variable));
}
@@ -349,6 +364,11 @@ class HEnvironment: public ZoneObject {
return result;
}
+ HValue* LookupContext() const {
+ // Return first special.
+ return Lookup(parameter_count());
+ }
+
void Push(HValue* value) {
ASSERT(value != NULL);
++push_count_;
@@ -369,6 +389,8 @@ class HEnvironment: public ZoneObject {
HValue* Top() const { return ExpressionStackAt(0); }
+ bool ExpressionStackIsEmpty() const;
+
HValue* ExpressionStackAt(int index_from_top) const {
int index = length() - index_from_top - 1;
ASSERT(HasExpressionAt(index));
@@ -388,8 +410,9 @@ class HEnvironment: public ZoneObject {
// instructions, otherwise they are the actual values.
HEnvironment* CopyForInlining(Handle<JSFunction> target,
FunctionLiteral* function,
- bool is_speculative,
- HConstant* undefined) const;
+ CompilationPhase compilation_phase,
+ HConstant* undefined,
+ CallKind call_kind) const;
void AddIncomingEdge(HBasicBlock* block, HEnvironment* other);
@@ -413,8 +436,6 @@ class HEnvironment: public ZoneObject {
// True if index is included in the expression stack part of the environment.
bool HasExpressionAt(int index) const;
- bool ExpressionStackIsEmpty() const;
-
void Initialize(int parameter_count, int local_count, int stack_height);
void Initialize(const HEnvironment* other);
@@ -424,15 +445,18 @@ class HEnvironment: public ZoneObject {
int IndexFor(Variable* variable) const {
Slot* slot = variable->AsSlot();
ASSERT(slot != NULL && slot->IsStackAllocated());
- int shift = (slot->type() == Slot::PARAMETER) ? 1 : parameter_count_;
+ int shift = (slot->type() == Slot::PARAMETER)
+ ? 1
+ : parameter_count_ + specials_count_;
return slot->index() + shift;
}
Handle<JSFunction> closure_;
- // Value array [parameters] [locals] [temporaries].
+ // Value array [parameters] [specials] [locals] [temporaries].
ZoneList<HValue*> values_;
ZoneList<int> assigned_variables_;
int parameter_count_;
+ int specials_count_;
int local_count_;
HEnvironment* outer_;
int pop_count_;
@@ -443,6 +467,11 @@ class HEnvironment: public ZoneObject {
class HGraphBuilder;
+enum ArgumentsAllowedFlag {
+ ARGUMENTS_NOT_ALLOWED,
+ ARGUMENTS_ALLOWED
+};
+
// This class is not BASE_EMBEDDED because our inlining implementation uses
// new and delete.
class AstContext {
@@ -501,13 +530,18 @@ class EffectContext: public AstContext {
class ValueContext: public AstContext {
public:
- explicit ValueContext(HGraphBuilder* owner)
- : AstContext(owner, Expression::kValue) {
+ explicit ValueContext(HGraphBuilder* owner, ArgumentsAllowedFlag flag)
+ : AstContext(owner, Expression::kValue), flag_(flag) {
}
virtual ~ValueContext();
virtual void ReturnValue(HValue* value);
virtual void ReturnInstruction(HInstruction* instr, int ast_id);
+
+ bool arguments_allowed() { return flag_ == ARGUMENTS_ALLOWED; }
+
+ private:
+ ArgumentsAllowedFlag flag_;
};
@@ -634,20 +668,7 @@ class HGraphBuilder: public AstVisitor {
BreakAndContinueScope* next_;
};
- HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle)
- : function_state_(NULL),
- initial_function_state_(this, info, oracle),
- ast_context_(NULL),
- break_scope_(NULL),
- graph_(NULL),
- current_block_(NULL),
- inlined_count_(0),
- zone_(info->isolate()->zone()) {
- // This is not initialized in the initializer list because the
- // constructor for the initial state relies on function_state_ == NULL
- // to know it's the initial state.
- function_state_= &initial_function_state_;
- }
+ HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle);
HGraph* CreateGraph();
@@ -662,6 +683,8 @@ class HGraphBuilder: public AstVisitor {
return current_block()->last_environment();
}
+ bool inline_bailout() { return inline_bailout_; }
+
// Adding instructions.
HInstruction* AddInstruction(HInstruction* instr);
void AddSimulate(int id);
@@ -670,6 +693,8 @@ class HGraphBuilder: public AstVisitor {
void Push(HValue* value) { environment()->Push(value); }
HValue* Pop() { return environment()->Pop(); }
+ void Bailout(const char* reason);
+
private:
// Type of a member function that generates inline code for a native function.
typedef void (HGraphBuilder::*InlineFunctionGenerator)(CallRuntime* call);
@@ -724,7 +749,17 @@ class HGraphBuilder: public AstVisitor {
INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
#undef INLINE_FUNCTION_GENERATOR_DECLARATION
- void Bailout(const char* reason);
+ void VisitDelete(UnaryOperation* expr);
+ void VisitVoid(UnaryOperation* expr);
+ void VisitTypeof(UnaryOperation* expr);
+ void VisitAdd(UnaryOperation* expr);
+ void VisitSub(UnaryOperation* expr);
+ void VisitBitNot(UnaryOperation* expr);
+ void VisitNot(UnaryOperation* expr);
+
+ void VisitComma(BinaryOperation* expr);
+ void VisitAndOr(BinaryOperation* expr, bool is_logical_and);
+ void VisitCommon(BinaryOperation* expr);
void PreProcessOsrEntry(IterationStatement* statement);
// True iff. we are compiling for OSR and the statement is the entry.
@@ -755,7 +790,11 @@ class HGraphBuilder: public AstVisitor {
void Drop(int n) { environment()->Drop(n); }
void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); }
- void VisitForValue(Expression* expr);
+ // The value of the arguments object is allowed in some but not most value
+ // contexts. (It's allowed in all effect contexts and disallowed in all
+ // test contexts.)
+ void VisitForValue(Expression* expr,
+ ArgumentsAllowedFlag flag = ARGUMENTS_NOT_ALLOWED);
void VisitForTypeOf(Expression* expr);
void VisitForEffect(Expression* expr);
void VisitForControl(Expression* expr,
@@ -778,7 +817,11 @@ class HGraphBuilder: public AstVisitor {
// to push them as outgoing parameters.
template <int V> HInstruction* PreProcessCall(HCall<V>* call);
- void AssumeRepresentation(HValue* value, Representation r);
+ void TraceRepresentation(Token::Value op,
+ TypeInfo info,
+ HValue* value,
+ Representation rep);
+ void AssumeRepresentation(HValue* value, Representation rep);
static Representation ToRepresentation(TypeInfo info);
void SetupScope(Scope* scope);
@@ -814,7 +857,9 @@ class HGraphBuilder: public AstVisitor {
// If --trace-inlining, print a line of the inlining trace. Inlining
// succeeded if the reason string is NULL and failed if there is a
// non-NULL reason string.
- void TraceInline(Handle<JSFunction> target, const char* failure_reason);
+ void TraceInline(Handle<JSFunction> target,
+ Handle<JSFunction> caller,
+ const char* failure_reason);
void HandleGlobalVariableAssignment(Variable* var,
HValue* value,
@@ -833,12 +878,20 @@ class HGraphBuilder: public AstVisitor {
ZoneMapList* types,
Handle<String> name);
+ HCompareSymbolEq* BuildSymbolCompare(HValue* left,
+ HValue* right,
+ Token::Value op);
HStringCharCodeAt* BuildStringCharCodeAt(HValue* string,
HValue* index);
HInstruction* BuildBinaryOperation(BinaryOperation* expr,
HValue* left,
HValue* right);
- HInstruction* BuildIncrement(HValue* value, bool increment);
+ HInstruction* BuildBinaryOperation(Token::Value op,
+ HValue* left,
+ HValue* right,
+ TypeInfo info);
+ HInstruction* BuildIncrement(bool returns_original_input,
+ CountOperation* expr);
HLoadNamedField* BuildLoadNamedField(HValue* object,
Property* expr,
Handle<Map> type,
@@ -923,6 +976,8 @@ class HGraphBuilder: public AstVisitor {
Zone* zone_;
+ bool inline_bailout_;
+
friend class FunctionState; // Pushes and pops the state stack.
friend class AstContext; // Pushes and pops the AST context stack.
@@ -957,9 +1012,11 @@ class HValueMap: public ZoneObject {
HValue* Lookup(HValue* value) const;
HValueMap* Copy(Zone* zone) const {
- return new(zone) HValueMap(this);
+ return new(zone) HValueMap(zone, this);
}
+ bool IsEmpty() const { return count_ == 0; }
+
private:
// A linked list of HValue* values. Stored in arrays.
struct HValueMapListElement {
@@ -971,7 +1028,7 @@ class HValueMap: public ZoneObject {
// Must be a power of 2.
static const int kInitialSize = 16;
- explicit HValueMap(const HValueMap* other);
+ HValueMap(Zone* zone, const HValueMap* other);
void Resize(int new_size);
void ResizeLists(int new_size);
diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h
index a9247f46..0ca2d6b4 100644
--- a/src/ia32/assembler-ia32-inl.h
+++ b/src/ia32/assembler-ia32-inl.h
@@ -30,7 +30,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// A light-weight IA32 Assembler.
@@ -311,8 +311,12 @@ void Assembler::emit(Handle<Object> handle) {
}
-void Assembler::emit(uint32_t x, RelocInfo::Mode rmode) {
- if (rmode != RelocInfo::NONE) RecordRelocInfo(rmode);
+void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, unsigned id) {
+ if (rmode == RelocInfo::CODE_TARGET && id != kNoASTId) {
+ RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, static_cast<intptr_t>(id));
+ } else if (rmode != RelocInfo::NONE) {
+ RecordRelocInfo(rmode);
+ }
emit(x);
}
@@ -376,6 +380,18 @@ void Assembler::emit_disp(Label* L, Displacement::Type type) {
}
+void Assembler::emit_near_disp(Label* L) {
+ byte disp = 0x00;
+ if (L->is_near_linked()) {
+ int offset = L->near_link_pos() - pc_offset();
+ ASSERT(is_int8(offset));
+ disp = static_cast<byte>(offset & 0xFF);
+ }
+ L->link_to(pc_offset(), Label::kNear);
+ *pc_++ = disp;
+}
+
+
void Operand::set_modrm(int mod, Register rm) {
ASSERT((mod & -4) == 0);
buf_[0] = mod << 6 | rm.code();
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index 92730372..a7602e7d 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -32,7 +32,7 @@
// The original source code covered by the above license above has been modified
// significantly by Google Inc.
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#include "v8.h"
@@ -341,7 +341,6 @@ Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
pc_ = buffer_;
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
- last_pc_ = NULL;
#ifdef GENERATED_CODE_COVERAGE
InitCoverageLog();
#endif
@@ -389,7 +388,6 @@ void Assembler::CodeTargetAlign() {
void Assembler::cpuid() {
ASSERT(CpuFeatures::IsEnabled(CPUID));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xA2);
}
@@ -397,35 +395,30 @@ void Assembler::cpuid() {
void Assembler::pushad() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x60);
}
void Assembler::popad() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x61);
}
void Assembler::pushfd() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x9C);
}
void Assembler::popfd() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x9D);
}
void Assembler::push(const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (x.is_int8()) {
EMIT(0x6a);
EMIT(x.x_);
@@ -445,14 +438,12 @@ void Assembler::push_imm32(int32_t imm32) {
void Assembler::push(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x50 | src.code());
}
void Assembler::push(const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFF);
emit_operand(esi, src);
}
@@ -460,125 +451,13 @@ void Assembler::push(const Operand& src) {
void Assembler::pop(Register dst) {
ASSERT(reloc_info_writer.last_pc() != NULL);
- if (FLAG_peephole_optimization && (reloc_info_writer.last_pc() <= last_pc_)) {
- // (last_pc_ != NULL) is rolled into the above check.
- // If a last_pc_ is set, we need to make sure that there has not been any
- // relocation information generated between the last instruction and this
- // pop instruction.
- byte instr = last_pc_[0];
- if ((instr & ~0x7) == 0x50) {
- int push_reg_code = instr & 0x7;
- if (push_reg_code == dst.code()) {
- pc_ = last_pc_;
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (same reg) eliminated\n", pc_offset());
- }
- } else {
- // Convert 'push src; pop dst' to 'mov dst, src'.
- last_pc_[0] = 0x8b;
- Register src = { push_reg_code };
- EnsureSpace ensure_space(this);
- emit_operand(dst, Operand(src));
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (reg->reg) eliminated\n", pc_offset());
- }
- }
- last_pc_ = NULL;
- return;
- } else if (instr == 0xff) { // push of an operand, convert to a move
- byte op1 = last_pc_[1];
- // Check if the operation is really a push.
- if ((op1 & 0x38) == (6 << 3)) {
- op1 = (op1 & ~0x38) | static_cast<byte>(dst.code() << 3);
- last_pc_[0] = 0x8b;
- last_pc_[1] = op1;
- last_pc_ = NULL;
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (op->reg) eliminated\n", pc_offset());
- }
- return;
- }
- } else if ((instr == 0x89) &&
- (last_pc_[1] == 0x04) &&
- (last_pc_[2] == 0x24)) {
- // 0x71283c 396 890424 mov [esp],eax
- // 0x71283f 399 58 pop eax
- if (dst.is(eax)) {
- // change to
- // 0x710fac 216 83c404 add esp,0x4
- last_pc_[0] = 0x83;
- last_pc_[1] = 0xc4;
- last_pc_[2] = 0x04;
- last_pc_ = NULL;
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (mov-pop) eliminated\n", pc_offset());
- }
- return;
- }
- } else if (instr == 0x6a && dst.is(eax)) { // push of immediate 8 bit
- byte imm8 = last_pc_[1];
- if (imm8 == 0) {
- // 6a00 push 0x0
- // 58 pop eax
- last_pc_[0] = 0x31;
- last_pc_[1] = 0xc0;
- // change to
- // 31c0 xor eax,eax
- last_pc_ = NULL;
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (imm->reg) eliminated\n", pc_offset());
- }
- return;
- } else {
- // 6a00 push 0xXX
- // 58 pop eax
- last_pc_[0] = 0xb8;
- EnsureSpace ensure_space(this);
- if ((imm8 & 0x80) != 0) {
- EMIT(0xff);
- EMIT(0xff);
- EMIT(0xff);
- // change to
- // b8XXffffff mov eax,0xffffffXX
- } else {
- EMIT(0x00);
- EMIT(0x00);
- EMIT(0x00);
- // change to
- // b8XX000000 mov eax,0x000000XX
- }
- last_pc_ = NULL;
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (imm->reg) eliminated\n", pc_offset());
- }
- return;
- }
- } else if (instr == 0x68 && dst.is(eax)) { // push of immediate 32 bit
- // 68XXXXXXXX push 0xXXXXXXXX
- // 58 pop eax
- last_pc_[0] = 0xb8;
- last_pc_ = NULL;
- // change to
- // b8XXXXXXXX mov eax,0xXXXXXXXX
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop (imm->reg) eliminated\n", pc_offset());
- }
- return;
- }
-
- // Other potential patterns for peephole:
- // 0x712716 102 890424 mov [esp], eax
- // 0x712719 105 8b1424 mov edx, [esp]
- }
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x58 | dst.code());
}
void Assembler::pop(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x8F);
emit_operand(eax, dst);
}
@@ -586,7 +465,6 @@ void Assembler::pop(const Operand& dst) {
void Assembler::enter(const Immediate& size) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xC8);
emit_w(size);
EMIT(0);
@@ -595,7 +473,6 @@ void Assembler::enter(const Immediate& size) {
void Assembler::leave() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xC9);
}
@@ -603,7 +480,6 @@ void Assembler::leave() {
void Assembler::mov_b(Register dst, const Operand& src) {
ASSERT(dst.code() < 4);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x8A);
emit_operand(dst, src);
}
@@ -611,7 +487,6 @@ void Assembler::mov_b(Register dst, const Operand& src) {
void Assembler::mov_b(const Operand& dst, int8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xC6);
emit_operand(eax, dst);
EMIT(imm8);
@@ -621,7 +496,6 @@ void Assembler::mov_b(const Operand& dst, int8_t imm8) {
void Assembler::mov_b(const Operand& dst, Register src) {
ASSERT(src.code() < 4);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x88);
emit_operand(src, dst);
}
@@ -629,7 +503,6 @@ void Assembler::mov_b(const Operand& dst, Register src) {
void Assembler::mov_w(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x8B);
emit_operand(dst, src);
@@ -638,7 +511,6 @@ void Assembler::mov_w(Register dst, const Operand& src) {
void Assembler::mov_w(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x89);
emit_operand(src, dst);
@@ -647,7 +519,6 @@ void Assembler::mov_w(const Operand& dst, Register src) {
void Assembler::mov(Register dst, int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xB8 | dst.code());
emit(imm32);
}
@@ -655,7 +526,6 @@ void Assembler::mov(Register dst, int32_t imm32) {
void Assembler::mov(Register dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xB8 | dst.code());
emit(x);
}
@@ -663,7 +533,6 @@ void Assembler::mov(Register dst, const Immediate& x) {
void Assembler::mov(Register dst, Handle<Object> handle) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xB8 | dst.code());
emit(handle);
}
@@ -671,7 +540,6 @@ void Assembler::mov(Register dst, Handle<Object> handle) {
void Assembler::mov(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x8B);
emit_operand(dst, src);
}
@@ -679,7 +547,6 @@ void Assembler::mov(Register dst, const Operand& src) {
void Assembler::mov(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x89);
EMIT(0xC0 | src.code() << 3 | dst.code());
}
@@ -687,7 +554,6 @@ void Assembler::mov(Register dst, Register src) {
void Assembler::mov(const Operand& dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xC7);
emit_operand(eax, dst);
emit(x);
@@ -696,7 +562,6 @@ void Assembler::mov(const Operand& dst, const Immediate& x) {
void Assembler::mov(const Operand& dst, Handle<Object> handle) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xC7);
emit_operand(eax, dst);
emit(handle);
@@ -705,7 +570,6 @@ void Assembler::mov(const Operand& dst, Handle<Object> handle) {
void Assembler::mov(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x89);
emit_operand(src, dst);
}
@@ -713,7 +577,6 @@ void Assembler::mov(const Operand& dst, Register src) {
void Assembler::movsx_b(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xBE);
emit_operand(dst, src);
@@ -722,7 +585,6 @@ void Assembler::movsx_b(Register dst, const Operand& src) {
void Assembler::movsx_w(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xBF);
emit_operand(dst, src);
@@ -731,7 +593,6 @@ void Assembler::movsx_w(Register dst, const Operand& src) {
void Assembler::movzx_b(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xB6);
emit_operand(dst, src);
@@ -740,7 +601,6 @@ void Assembler::movzx_b(Register dst, const Operand& src) {
void Assembler::movzx_w(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xB7);
emit_operand(dst, src);
@@ -750,7 +610,6 @@ void Assembler::movzx_w(Register dst, const Operand& src) {
void Assembler::cmov(Condition cc, Register dst, int32_t imm32) {
ASSERT(CpuFeatures::IsEnabled(CMOV));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
UNIMPLEMENTED();
USE(cc);
USE(dst);
@@ -761,7 +620,6 @@ void Assembler::cmov(Condition cc, Register dst, int32_t imm32) {
void Assembler::cmov(Condition cc, Register dst, Handle<Object> handle) {
ASSERT(CpuFeatures::IsEnabled(CMOV));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
UNIMPLEMENTED();
USE(cc);
USE(dst);
@@ -772,7 +630,6 @@ void Assembler::cmov(Condition cc, Register dst, Handle<Object> handle) {
void Assembler::cmov(Condition cc, Register dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(CMOV));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Opcode: 0f 40 + cc /r.
EMIT(0x0F);
EMIT(0x40 + cc);
@@ -782,14 +639,12 @@ void Assembler::cmov(Condition cc, Register dst, const Operand& src) {
void Assembler::cld() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFC);
}
void Assembler::rep_movs() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0xA5);
}
@@ -797,7 +652,6 @@ void Assembler::rep_movs() {
void Assembler::rep_stos() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0xAB);
}
@@ -805,14 +659,12 @@ void Assembler::rep_stos() {
void Assembler::stos() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xAB);
}
void Assembler::xchg(Register dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (src.is(eax) || dst.is(eax)) { // Single-byte encoding.
EMIT(0x90 | (src.is(eax) ? dst.code() : src.code()));
} else {
@@ -824,14 +676,12 @@ void Assembler::xchg(Register dst, Register src) {
void Assembler::adc(Register dst, int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(2, Operand(dst), Immediate(imm32));
}
void Assembler::adc(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x13);
emit_operand(dst, src);
}
@@ -839,7 +689,6 @@ void Assembler::adc(Register dst, const Operand& src) {
void Assembler::add(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x03);
emit_operand(dst, src);
}
@@ -847,24 +696,7 @@ void Assembler::add(Register dst, const Operand& src) {
void Assembler::add(const Operand& dst, const Immediate& x) {
ASSERT(reloc_info_writer.last_pc() != NULL);
- if (FLAG_peephole_optimization && (reloc_info_writer.last_pc() <= last_pc_)) {
- byte instr = last_pc_[0];
- if ((instr & 0xf8) == 0x50) {
- // Last instruction was a push. Check whether this is a pop without a
- // result.
- if ((dst.is_reg(esp)) &&
- (x.x_ == kPointerSize) && (x.rmode_ == RelocInfo::NONE)) {
- pc_ = last_pc_;
- last_pc_ = NULL;
- if (FLAG_print_peephole_optimization) {
- PrintF("%d push/pop(noreg) eliminated\n", pc_offset());
- }
- return;
- }
- }
- }
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(0, dst, x);
}
@@ -876,14 +708,12 @@ void Assembler::and_(Register dst, int32_t imm32) {
void Assembler::and_(Register dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(4, Operand(dst), x);
}
void Assembler::and_(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x23);
emit_operand(dst, src);
}
@@ -891,14 +721,12 @@ void Assembler::and_(Register dst, const Operand& src) {
void Assembler::and_(const Operand& dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(4, dst, x);
}
void Assembler::and_(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x21);
emit_operand(src, dst);
}
@@ -906,7 +734,6 @@ void Assembler::and_(const Operand& dst, Register src) {
void Assembler::cmpb(const Operand& op, int8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x80);
emit_operand(edi, op); // edi == 7
EMIT(imm8);
@@ -916,7 +743,6 @@ void Assembler::cmpb(const Operand& op, int8_t imm8) {
void Assembler::cmpb(const Operand& dst, Register src) {
ASSERT(src.is_byte_register());
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x38);
emit_operand(src, dst);
}
@@ -925,7 +751,6 @@ void Assembler::cmpb(const Operand& dst, Register src) {
void Assembler::cmpb(Register dst, const Operand& src) {
ASSERT(dst.is_byte_register());
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x3A);
emit_operand(dst, src);
}
@@ -934,7 +759,6 @@ void Assembler::cmpb(Register dst, const Operand& src) {
void Assembler::cmpw(const Operand& op, Immediate imm16) {
ASSERT(imm16.is_int16());
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x81);
emit_operand(edi, op);
@@ -944,21 +768,18 @@ void Assembler::cmpw(const Operand& op, Immediate imm16) {
void Assembler::cmp(Register reg, int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(7, Operand(reg), Immediate(imm32));
}
void Assembler::cmp(Register reg, Handle<Object> handle) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(7, Operand(reg), Immediate(handle));
}
void Assembler::cmp(Register reg, const Operand& op) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x3B);
emit_operand(reg, op);
}
@@ -966,21 +787,18 @@ void Assembler::cmp(Register reg, const Operand& op) {
void Assembler::cmp(const Operand& op, const Immediate& imm) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(7, op, imm);
}
void Assembler::cmp(const Operand& op, Handle<Object> handle) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(7, op, Immediate(handle));
}
void Assembler::cmpb_al(const Operand& op) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x38); // CMP r/m8, r8
emit_operand(eax, op); // eax has same code as register al.
}
@@ -988,7 +806,6 @@ void Assembler::cmpb_al(const Operand& op) {
void Assembler::cmpw_ax(const Operand& op) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x39); // CMP r/m16, r16
emit_operand(eax, op); // eax has same code as register ax.
@@ -997,7 +814,6 @@ void Assembler::cmpw_ax(const Operand& op) {
void Assembler::dec_b(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFE);
EMIT(0xC8 | dst.code());
}
@@ -1005,7 +821,6 @@ void Assembler::dec_b(Register dst) {
void Assembler::dec_b(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFE);
emit_operand(ecx, dst);
}
@@ -1013,14 +828,12 @@ void Assembler::dec_b(const Operand& dst) {
void Assembler::dec(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x48 | dst.code());
}
void Assembler::dec(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFF);
emit_operand(ecx, dst);
}
@@ -1028,14 +841,12 @@ void Assembler::dec(const Operand& dst) {
void Assembler::cdq() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x99);
}
void Assembler::idiv(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF7);
EMIT(0xF8 | src.code());
}
@@ -1043,7 +854,6 @@ void Assembler::idiv(Register src) {
void Assembler::imul(Register reg) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF7);
EMIT(0xE8 | reg.code());
}
@@ -1051,7 +861,6 @@ void Assembler::imul(Register reg) {
void Assembler::imul(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xAF);
emit_operand(dst, src);
@@ -1060,7 +869,6 @@ void Assembler::imul(Register dst, const Operand& src) {
void Assembler::imul(Register dst, Register src, int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (is_int8(imm32)) {
EMIT(0x6B);
EMIT(0xC0 | dst.code() << 3 | src.code());
@@ -1075,14 +883,12 @@ void Assembler::imul(Register dst, Register src, int32_t imm32) {
void Assembler::inc(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x40 | dst.code());
}
void Assembler::inc(const Operand& dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFF);
emit_operand(eax, dst);
}
@@ -1090,7 +896,6 @@ void Assembler::inc(const Operand& dst) {
void Assembler::lea(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x8D);
emit_operand(dst, src);
}
@@ -1098,7 +903,6 @@ void Assembler::lea(Register dst, const Operand& src) {
void Assembler::mul(Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF7);
EMIT(0xE0 | src.code());
}
@@ -1106,7 +910,6 @@ void Assembler::mul(Register src) {
void Assembler::neg(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF7);
EMIT(0xD8 | dst.code());
}
@@ -1114,7 +917,6 @@ void Assembler::neg(Register dst) {
void Assembler::not_(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF7);
EMIT(0xD0 | dst.code());
}
@@ -1122,14 +924,12 @@ void Assembler::not_(Register dst) {
void Assembler::or_(Register dst, int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(1, Operand(dst), Immediate(imm32));
}
void Assembler::or_(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0B);
emit_operand(dst, src);
}
@@ -1137,14 +937,12 @@ void Assembler::or_(Register dst, const Operand& src) {
void Assembler::or_(const Operand& dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(1, dst, x);
}
void Assembler::or_(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x09);
emit_operand(src, dst);
}
@@ -1152,7 +950,6 @@ void Assembler::or_(const Operand& dst, Register src) {
void Assembler::rcl(Register dst, uint8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
EMIT(0xD1);
@@ -1167,7 +964,6 @@ void Assembler::rcl(Register dst, uint8_t imm8) {
void Assembler::rcr(Register dst, uint8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
EMIT(0xD1);
@@ -1182,7 +978,6 @@ void Assembler::rcr(Register dst, uint8_t imm8) {
void Assembler::sar(Register dst, uint8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
EMIT(0xD1);
@@ -1197,7 +992,6 @@ void Assembler::sar(Register dst, uint8_t imm8) {
void Assembler::sar_cl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD3);
EMIT(0xF8 | dst.code());
}
@@ -1205,7 +999,6 @@ void Assembler::sar_cl(Register dst) {
void Assembler::sbb(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x1B);
emit_operand(dst, src);
}
@@ -1213,7 +1006,6 @@ void Assembler::sbb(Register dst, const Operand& src) {
void Assembler::shld(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xA5);
emit_operand(dst, src);
@@ -1222,7 +1014,6 @@ void Assembler::shld(Register dst, const Operand& src) {
void Assembler::shl(Register dst, uint8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
EMIT(0xD1);
@@ -1237,7 +1028,6 @@ void Assembler::shl(Register dst, uint8_t imm8) {
void Assembler::shl_cl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD3);
EMIT(0xE0 | dst.code());
}
@@ -1245,7 +1035,6 @@ void Assembler::shl_cl(Register dst) {
void Assembler::shrd(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xAD);
emit_operand(dst, src);
@@ -1254,7 +1043,6 @@ void Assembler::shrd(Register dst, const Operand& src) {
void Assembler::shr(Register dst, uint8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint5(imm8)); // illegal shift count
if (imm8 == 1) {
EMIT(0xD1);
@@ -1269,7 +1057,6 @@ void Assembler::shr(Register dst, uint8_t imm8) {
void Assembler::shr_cl(Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD3);
EMIT(0xE8 | dst.code());
}
@@ -1277,7 +1064,6 @@ void Assembler::shr_cl(Register dst) {
void Assembler::subb(const Operand& op, int8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (op.is_reg(eax)) {
EMIT(0x2c);
} else {
@@ -1290,14 +1076,12 @@ void Assembler::subb(const Operand& op, int8_t imm8) {
void Assembler::sub(const Operand& dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(5, dst, x);
}
void Assembler::sub(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x2B);
emit_operand(dst, src);
}
@@ -1306,7 +1090,6 @@ void Assembler::sub(Register dst, const Operand& src) {
void Assembler::subb(Register dst, const Operand& src) {
ASSERT(dst.code() < 4);
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x2A);
emit_operand(dst, src);
}
@@ -1314,7 +1097,6 @@ void Assembler::subb(Register dst, const Operand& src) {
void Assembler::sub(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x29);
emit_operand(src, dst);
}
@@ -1322,7 +1104,6 @@ void Assembler::sub(const Operand& dst, Register src) {
void Assembler::test(Register reg, const Immediate& imm) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
// Only use test against byte for registers that have a byte
// variant: eax, ebx, ecx, and edx.
if (imm.rmode_ == RelocInfo::NONE && is_uint8(imm.x_) && reg.code() < 4) {
@@ -1349,7 +1130,6 @@ void Assembler::test(Register reg, const Immediate& imm) {
void Assembler::test(Register reg, const Operand& op) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x85);
emit_operand(reg, op);
}
@@ -1357,7 +1137,6 @@ void Assembler::test(Register reg, const Operand& op) {
void Assembler::test_b(Register reg, const Operand& op) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x84);
emit_operand(reg, op);
}
@@ -1365,7 +1144,6 @@ void Assembler::test_b(Register reg, const Operand& op) {
void Assembler::test(const Operand& op, const Immediate& imm) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF7);
emit_operand(eax, op);
emit(imm);
@@ -1374,7 +1152,6 @@ void Assembler::test(const Operand& op, const Immediate& imm) {
void Assembler::test_b(const Operand& op, uint8_t imm8) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF6);
emit_operand(eax, op);
EMIT(imm8);
@@ -1383,14 +1160,12 @@ void Assembler::test_b(const Operand& op, uint8_t imm8) {
void Assembler::xor_(Register dst, int32_t imm32) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(6, Operand(dst), Immediate(imm32));
}
void Assembler::xor_(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x33);
emit_operand(dst, src);
}
@@ -1398,7 +1173,6 @@ void Assembler::xor_(Register dst, const Operand& src) {
void Assembler::xor_(const Operand& src, Register dst) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x31);
emit_operand(dst, src);
}
@@ -1406,14 +1180,12 @@ void Assembler::xor_(const Operand& src, Register dst) {
void Assembler::xor_(const Operand& dst, const Immediate& x) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_arith(6, dst, x);
}
void Assembler::bt(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xA3);
emit_operand(src, dst);
@@ -1422,7 +1194,6 @@ void Assembler::bt(const Operand& dst, Register src) {
void Assembler::bts(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0xAB);
emit_operand(src, dst);
@@ -1431,21 +1202,18 @@ void Assembler::bts(const Operand& dst, Register src) {
void Assembler::hlt() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF4);
}
void Assembler::int3() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xCC);
}
void Assembler::nop() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x90);
}
@@ -1453,7 +1221,6 @@ void Assembler::nop() {
void Assembler::rdtsc() {
ASSERT(CpuFeatures::IsEnabled(RDTSC));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0x31);
}
@@ -1461,7 +1228,6 @@ void Assembler::rdtsc() {
void Assembler::ret(int imm16) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(is_uint16(imm16));
if (imm16 == 0) {
EMIT(0xC3);
@@ -1507,7 +1273,6 @@ void Assembler::print(Label* L) {
void Assembler::bind_to(Label* L, int pos) {
EnsureSpace ensure_space(this);
- last_pc_ = NULL;
ASSERT(0 <= pos && pos <= pc_offset()); // must have a valid binding position
while (L->is_linked()) {
Displacement disp = disp_at(L);
@@ -1525,36 +1290,35 @@ void Assembler::bind_to(Label* L, int pos) {
}
disp.next(L);
}
+ while (L->is_near_linked()) {
+ int fixup_pos = L->near_link_pos();
+ int offset_to_next =
+ static_cast<int>(*reinterpret_cast<int8_t*>(addr_at(fixup_pos)));
+ ASSERT(offset_to_next <= 0);
+ // Relative address, relative to point after address.
+ int disp = pos - fixup_pos - sizeof(int8_t);
+ ASSERT(0 <= disp && disp <= 127);
+ set_byte_at(fixup_pos, disp);
+ if (offset_to_next < 0) {
+ L->link_to(fixup_pos + offset_to_next, Label::kNear);
+ } else {
+ L->UnuseNear();
+ }
+ }
L->bind_to(pos);
}
void Assembler::bind(Label* L) {
EnsureSpace ensure_space(this);
- last_pc_ = NULL;
ASSERT(!L->is_bound()); // label can only be bound once
bind_to(L, pc_offset());
}
-void Assembler::bind(NearLabel* L) {
- ASSERT(!L->is_bound());
- last_pc_ = NULL;
- while (L->unresolved_branches_ > 0) {
- int branch_pos = L->unresolved_positions_[L->unresolved_branches_ - 1];
- int disp = pc_offset() - branch_pos;
- ASSERT(is_int8(disp));
- set_byte_at(branch_pos - sizeof(int8_t), disp);
- L->unresolved_branches_--;
- }
- L->bind_to(pc_offset());
-}
-
-
void Assembler::call(Label* L) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (L->is_bound()) {
const int long_size = 5;
int offs = L->pos() - pc_offset();
@@ -1573,35 +1337,44 @@ void Assembler::call(Label* L) {
void Assembler::call(byte* entry, RelocInfo::Mode rmode) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(!RelocInfo::IsCodeTarget(rmode));
EMIT(0xE8);
emit(entry - (pc_ + sizeof(int32_t)), rmode);
}
+int Assembler::CallSize(const Operand& adr) {
+ // Call size is 1 (opcode) + adr.len_ (operand).
+ return 1 + adr.len_;
+}
+
+
void Assembler::call(const Operand& adr) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFF);
emit_operand(edx, adr);
}
-void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) {
+int Assembler::CallSize(Handle<Code> code, RelocInfo::Mode rmode) {
+ return 1 /* EMIT */ + sizeof(uint32_t) /* emit */;
+}
+
+
+void Assembler::call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ unsigned ast_id) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(RelocInfo::IsCodeTarget(rmode));
EMIT(0xE8);
- emit(reinterpret_cast<intptr_t>(code.location()), rmode);
+ emit(reinterpret_cast<intptr_t>(code.location()), rmode, ast_id);
}
-void Assembler::jmp(Label* L) {
+void Assembler::jmp(Label* L, Label::Distance distance) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
if (L->is_bound()) {
const int short_size = 2;
const int long_size = 5;
@@ -1616,6 +1389,9 @@ void Assembler::jmp(Label* L) {
EMIT(0xE9);
emit(offs - long_size);
}
+ } else if (distance == Label::kNear) {
+ EMIT(0xEB);
+ emit_near_disp(L);
} else {
// 1110 1001 #32-bit disp.
EMIT(0xE9);
@@ -1626,7 +1402,6 @@ void Assembler::jmp(Label* L) {
void Assembler::jmp(byte* entry, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(!RelocInfo::IsCodeTarget(rmode));
EMIT(0xE9);
emit(entry - (pc_ + sizeof(int32_t)), rmode);
@@ -1635,7 +1410,6 @@ void Assembler::jmp(byte* entry, RelocInfo::Mode rmode) {
void Assembler::jmp(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xFF);
emit_operand(esp, adr);
}
@@ -1643,37 +1417,15 @@ void Assembler::jmp(const Operand& adr) {
void Assembler::jmp(Handle<Code> code, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(RelocInfo::IsCodeTarget(rmode));
EMIT(0xE9);
emit(reinterpret_cast<intptr_t>(code.location()), rmode);
}
-void Assembler::jmp(NearLabel* L) {
+void Assembler::j(Condition cc, Label* L, Label::Distance distance) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- if (L->is_bound()) {
- const int short_size = 2;
- int offs = L->pos() - pc_offset();
- ASSERT(offs <= 0);
- ASSERT(is_int8(offs - short_size));
- // 1110 1011 #8-bit disp.
- EMIT(0xEB);
- EMIT((offs - short_size) & 0xFF);
- } else {
- EMIT(0xEB);
- EMIT(0x00); // The displacement will be resolved later.
- L->link_to(pc_offset());
- }
-}
-
-
-void Assembler::j(Condition cc, Label* L, Hint hint) {
- EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT(0 <= cc && cc < 16);
- if (FLAG_emit_branch_hints && hint != no_hint) EMIT(hint);
if (L->is_bound()) {
const int short_size = 2;
const int long_size = 6;
@@ -1689,6 +1441,9 @@ void Assembler::j(Condition cc, Label* L, Hint hint) {
EMIT(0x80 | cc);
emit(offs - long_size);
}
+ } else if (distance == Label::kNear) {
+ EMIT(0x70 | cc);
+ emit_near_disp(L);
} else {
// 0000 1111 1000 tttn #32-bit disp
// Note: could eliminate cond. jumps to this jump if condition
@@ -1700,11 +1455,9 @@ void Assembler::j(Condition cc, Label* L, Hint hint) {
}
-void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint) {
+void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
ASSERT((0 <= cc) && (cc < 16));
- if (FLAG_emit_branch_hints && hint != no_hint) EMIT(hint);
// 0000 1111 1000 tttn #32-bit disp.
EMIT(0x0F);
EMIT(0x80 | cc);
@@ -1712,10 +1465,8 @@ void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint) {
}
-void Assembler::j(Condition cc, Handle<Code> code, Hint hint) {
+void Assembler::j(Condition cc, Handle<Code> code) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- if (FLAG_emit_branch_hints && hint != no_hint) EMIT(hint);
// 0000 1111 1000 tttn #32-bit disp
EMIT(0x0F);
EMIT(0x80 | cc);
@@ -1723,46 +1474,22 @@ void Assembler::j(Condition cc, Handle<Code> code, Hint hint) {
}
-void Assembler::j(Condition cc, NearLabel* L, Hint hint) {
- EnsureSpace ensure_space(this);
- last_pc_ = pc_;
- ASSERT(0 <= cc && cc < 16);
- if (FLAG_emit_branch_hints && hint != no_hint) EMIT(hint);
- if (L->is_bound()) {
- const int short_size = 2;
- int offs = L->pos() - pc_offset();
- ASSERT(offs <= 0);
- ASSERT(is_int8(offs - short_size));
- // 0111 tttn #8-bit disp
- EMIT(0x70 | cc);
- EMIT((offs - short_size) & 0xFF);
- } else {
- EMIT(0x70 | cc);
- EMIT(0x00); // The displacement will be resolved later.
- L->link_to(pc_offset());
- }
-}
-
-
// FPU instructions.
void Assembler::fld(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xD9, 0xC0, i);
}
void Assembler::fstp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDD, 0xD8, i);
}
void Assembler::fld1() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xE8);
}
@@ -1770,7 +1497,6 @@ void Assembler::fld1() {
void Assembler::fldpi() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xEB);
}
@@ -1778,7 +1504,6 @@ void Assembler::fldpi() {
void Assembler::fldz() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xEE);
}
@@ -1786,7 +1511,6 @@ void Assembler::fldz() {
void Assembler::fldln2() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xED);
}
@@ -1794,7 +1518,6 @@ void Assembler::fldln2() {
void Assembler::fld_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
emit_operand(eax, adr);
}
@@ -1802,7 +1525,6 @@ void Assembler::fld_s(const Operand& adr) {
void Assembler::fld_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDD);
emit_operand(eax, adr);
}
@@ -1810,7 +1532,6 @@ void Assembler::fld_d(const Operand& adr) {
void Assembler::fstp_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
emit_operand(ebx, adr);
}
@@ -1818,7 +1539,6 @@ void Assembler::fstp_s(const Operand& adr) {
void Assembler::fstp_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDD);
emit_operand(ebx, adr);
}
@@ -1826,7 +1546,6 @@ void Assembler::fstp_d(const Operand& adr) {
void Assembler::fst_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDD);
emit_operand(edx, adr);
}
@@ -1834,7 +1553,6 @@ void Assembler::fst_d(const Operand& adr) {
void Assembler::fild_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDB);
emit_operand(eax, adr);
}
@@ -1842,7 +1560,6 @@ void Assembler::fild_s(const Operand& adr) {
void Assembler::fild_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDF);
emit_operand(ebp, adr);
}
@@ -1850,7 +1567,6 @@ void Assembler::fild_d(const Operand& adr) {
void Assembler::fistp_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDB);
emit_operand(ebx, adr);
}
@@ -1859,7 +1575,6 @@ void Assembler::fistp_s(const Operand& adr) {
void Assembler::fisttp_s(const Operand& adr) {
ASSERT(CpuFeatures::IsEnabled(SSE3));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDB);
emit_operand(ecx, adr);
}
@@ -1868,7 +1583,6 @@ void Assembler::fisttp_s(const Operand& adr) {
void Assembler::fisttp_d(const Operand& adr) {
ASSERT(CpuFeatures::IsEnabled(SSE3));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDD);
emit_operand(ecx, adr);
}
@@ -1876,7 +1590,6 @@ void Assembler::fisttp_d(const Operand& adr) {
void Assembler::fist_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDB);
emit_operand(edx, adr);
}
@@ -1884,7 +1597,6 @@ void Assembler::fist_s(const Operand& adr) {
void Assembler::fistp_d(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDF);
emit_operand(edi, adr);
}
@@ -1892,7 +1604,6 @@ void Assembler::fistp_d(const Operand& adr) {
void Assembler::fabs() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xE1);
}
@@ -1900,7 +1611,6 @@ void Assembler::fabs() {
void Assembler::fchs() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xE0);
}
@@ -1908,7 +1618,6 @@ void Assembler::fchs() {
void Assembler::fcos() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xFF);
}
@@ -1916,7 +1625,6 @@ void Assembler::fcos() {
void Assembler::fsin() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xFE);
}
@@ -1924,7 +1632,6 @@ void Assembler::fsin() {
void Assembler::fyl2x() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xF1);
}
@@ -1932,21 +1639,18 @@ void Assembler::fyl2x() {
void Assembler::fadd(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xC0, i);
}
void Assembler::fsub(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xE8, i);
}
void Assembler::fisub_s(const Operand& adr) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDA);
emit_operand(esp, adr);
}
@@ -1954,56 +1658,48 @@ void Assembler::fisub_s(const Operand& adr) {
void Assembler::fmul(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xC8, i);
}
void Assembler::fdiv(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDC, 0xF8, i);
}
void Assembler::faddp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xC0, i);
}
void Assembler::fsubp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xE8, i);
}
void Assembler::fsubrp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xE0, i);
}
void Assembler::fmulp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xC8, i);
}
void Assembler::fdivp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDE, 0xF8, i);
}
void Assembler::fprem() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xF8);
}
@@ -2011,7 +1707,6 @@ void Assembler::fprem() {
void Assembler::fprem1() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xF5);
}
@@ -2019,14 +1714,12 @@ void Assembler::fprem1() {
void Assembler::fxch(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xD9, 0xC8, i);
}
void Assembler::fincstp() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xF7);
}
@@ -2034,14 +1727,12 @@ void Assembler::fincstp() {
void Assembler::ffree(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDD, 0xC0, i);
}
void Assembler::ftst() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xE4);
}
@@ -2049,14 +1740,12 @@ void Assembler::ftst() {
void Assembler::fucomp(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
emit_farith(0xDD, 0xE8, i);
}
void Assembler::fucompp() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDA);
EMIT(0xE9);
}
@@ -2064,7 +1753,6 @@ void Assembler::fucompp() {
void Assembler::fucomi(int i) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDB);
EMIT(0xE8 + i);
}
@@ -2072,7 +1760,6 @@ void Assembler::fucomi(int i) {
void Assembler::fucomip() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDF);
EMIT(0xE9);
}
@@ -2080,7 +1767,6 @@ void Assembler::fucomip() {
void Assembler::fcompp() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDE);
EMIT(0xD9);
}
@@ -2088,7 +1774,6 @@ void Assembler::fcompp() {
void Assembler::fnstsw_ax() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDF);
EMIT(0xE0);
}
@@ -2096,14 +1781,12 @@ void Assembler::fnstsw_ax() {
void Assembler::fwait() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x9B);
}
void Assembler::frndint() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xD9);
EMIT(0xFC);
}
@@ -2111,7 +1794,6 @@ void Assembler::frndint() {
void Assembler::fnclex() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xDB);
EMIT(0xE2);
}
@@ -2119,7 +1801,6 @@ void Assembler::fnclex() {
void Assembler::sahf() {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x9E);
}
@@ -2127,7 +1808,6 @@ void Assembler::sahf() {
void Assembler::setcc(Condition cc, Register reg) {
ASSERT(reg.is_byte_register());
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0x90 | cc);
EMIT(0xC0 | reg.code());
@@ -2137,7 +1817,6 @@ void Assembler::setcc(Condition cc, Register reg) {
void Assembler::cvttss2si(Register dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x2C);
@@ -2148,7 +1827,6 @@ void Assembler::cvttss2si(Register dst, const Operand& src) {
void Assembler::cvttsd2si(Register dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x2C);
@@ -2159,7 +1837,6 @@ void Assembler::cvttsd2si(Register dst, const Operand& src) {
void Assembler::cvtsi2sd(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x2A);
@@ -2170,7 +1847,6 @@ void Assembler::cvtsi2sd(XMMRegister dst, const Operand& src) {
void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x5A);
@@ -2181,7 +1857,6 @@ void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x5A);
@@ -2192,7 +1867,6 @@ void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
void Assembler::addsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x58);
@@ -2203,7 +1877,6 @@ void Assembler::addsd(XMMRegister dst, XMMRegister src) {
void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x59);
@@ -2214,7 +1887,6 @@ void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
void Assembler::subsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x5C);
@@ -2225,7 +1897,6 @@ void Assembler::subsd(XMMRegister dst, XMMRegister src) {
void Assembler::divsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x5E);
@@ -2236,7 +1907,6 @@ void Assembler::divsd(XMMRegister dst, XMMRegister src) {
void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x57);
@@ -2244,9 +1914,16 @@ void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
}
+void Assembler::xorps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ EMIT(0x0F);
+ EMIT(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x51);
@@ -2256,7 +1933,6 @@ void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
void Assembler::andpd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x54);
@@ -2267,7 +1943,6 @@ void Assembler::andpd(XMMRegister dst, XMMRegister src) {
void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x2E);
@@ -2278,7 +1953,6 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
void Assembler::movmskpd(Register dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x50);
@@ -2289,7 +1963,6 @@ void Assembler::movmskpd(Register dst, XMMRegister src) {
void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0xC2);
@@ -2301,7 +1974,6 @@ void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) {
void Assembler::movaps(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0x28);
emit_sse_operand(dst, src);
@@ -2311,7 +1983,6 @@ void Assembler::movaps(XMMRegister dst, XMMRegister src) {
void Assembler::movdqa(const Operand& dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x7F);
@@ -2322,7 +1993,6 @@ void Assembler::movdqa(const Operand& dst, XMMRegister src) {
void Assembler::movdqa(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x6F);
@@ -2333,7 +2003,6 @@ void Assembler::movdqa(XMMRegister dst, const Operand& src) {
void Assembler::movdqu(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x7F);
@@ -2344,7 +2013,6 @@ void Assembler::movdqu(const Operand& dst, XMMRegister src ) {
void Assembler::movdqu(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x6F);
@@ -2355,7 +2023,6 @@ void Assembler::movdqu(XMMRegister dst, const Operand& src) {
void Assembler::movntdqa(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x38);
@@ -2367,7 +2034,6 @@ void Assembler::movntdqa(XMMRegister dst, const Operand& src) {
void Assembler::movntdq(const Operand& dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0xE7);
@@ -2378,7 +2044,6 @@ void Assembler::movntdq(const Operand& dst, XMMRegister src) {
void Assembler::prefetch(const Operand& src, int level) {
ASSERT(is_uint2(level));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x0F);
EMIT(0x18);
XMMRegister code = { level }; // Emit hint number in Reg position of RegR/M.
@@ -2388,14 +2053,12 @@ void Assembler::prefetch(const Operand& src, int level) {
void Assembler::movdbl(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
movsd(dst, src);
}
void Assembler::movdbl(const Operand& dst, XMMRegister src) {
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
movsd(dst, src);
}
@@ -2403,7 +2066,6 @@ void Assembler::movdbl(const Operand& dst, XMMRegister src) {
void Assembler::movsd(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2); // double
EMIT(0x0F);
EMIT(0x11); // store
@@ -2414,7 +2076,6 @@ void Assembler::movsd(const Operand& dst, XMMRegister src ) {
void Assembler::movsd(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2); // double
EMIT(0x0F);
EMIT(0x10); // load
@@ -2425,7 +2086,6 @@ void Assembler::movsd(XMMRegister dst, const Operand& src) {
void Assembler::movsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF2);
EMIT(0x0F);
EMIT(0x10);
@@ -2436,7 +2096,6 @@ void Assembler::movsd(XMMRegister dst, XMMRegister src) {
void Assembler::movss(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3); // float
EMIT(0x0F);
EMIT(0x11); // store
@@ -2447,7 +2106,6 @@ void Assembler::movss(const Operand& dst, XMMRegister src ) {
void Assembler::movss(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3); // float
EMIT(0x0F);
EMIT(0x10); // load
@@ -2458,7 +2116,6 @@ void Assembler::movss(XMMRegister dst, const Operand& src) {
void Assembler::movss(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x10);
@@ -2469,7 +2126,6 @@ void Assembler::movss(XMMRegister dst, XMMRegister src) {
void Assembler::movd(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x6E);
@@ -2480,7 +2136,6 @@ void Assembler::movd(XMMRegister dst, const Operand& src) {
void Assembler::movd(const Operand& dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x7E);
@@ -2491,7 +2146,6 @@ void Assembler::movd(const Operand& dst, XMMRegister src) {
void Assembler::pand(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0xDB);
@@ -2502,7 +2156,6 @@ void Assembler::pand(XMMRegister dst, XMMRegister src) {
void Assembler::pxor(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0xEF);
@@ -2513,7 +2166,6 @@ void Assembler::pxor(XMMRegister dst, XMMRegister src) {
void Assembler::por(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0xEB);
@@ -2524,7 +2176,6 @@ void Assembler::por(XMMRegister dst, XMMRegister src) {
void Assembler::ptest(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x38);
@@ -2536,7 +2187,6 @@ void Assembler::ptest(XMMRegister dst, XMMRegister src) {
void Assembler::psllq(XMMRegister reg, int8_t shift) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x73);
@@ -2548,7 +2198,6 @@ void Assembler::psllq(XMMRegister reg, int8_t shift) {
void Assembler::psllq(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0xF3);
@@ -2559,7 +2208,6 @@ void Assembler::psllq(XMMRegister dst, XMMRegister src) {
void Assembler::psrlq(XMMRegister reg, int8_t shift) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x73);
@@ -2571,7 +2219,6 @@ void Assembler::psrlq(XMMRegister reg, int8_t shift) {
void Assembler::psrlq(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0xD3);
@@ -2582,7 +2229,6 @@ void Assembler::psrlq(XMMRegister dst, XMMRegister src) {
void Assembler::pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x70);
@@ -2594,7 +2240,6 @@ void Assembler::pshufd(XMMRegister dst, XMMRegister src, int8_t shuffle) {
void Assembler::pextrd(const Operand& dst, XMMRegister src, int8_t offset) {
ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x3A);
@@ -2607,7 +2252,6 @@ void Assembler::pextrd(const Operand& dst, XMMRegister src, int8_t offset) {
void Assembler::pinsrd(XMMRegister dst, const Operand& src, int8_t offset) {
ASSERT(CpuFeatures::IsEnabled(SSE4_1));
EnsureSpace ensure_space(this);
- last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x3A);
@@ -2706,9 +2350,6 @@ void Assembler::GrowBuffer() {
buffer_ = desc.buffer;
buffer_size_ = desc.buffer_size;
pc_ += pc_delta;
- if (last_pc_ != NULL) {
- last_pc_ += pc_delta;
- }
reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
reloc_info_writer.last_pc() + pc_delta);
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index 46fda3b4..e933102c 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -249,23 +249,6 @@ inline Condition ReverseCondition(Condition cc) {
}
-enum Hint {
- no_hint = 0,
- not_taken = 0x2e,
- taken = 0x3e
-};
-
-
-// The result of negating a hint is as if the corresponding condition
-// were negated by NegateCondition. That is, no_hint is mapped to
-// itself and not_taken and taken are mapped to each other.
-inline Hint NegateHint(Hint hint) {
- return (hint == no_hint)
- ? no_hint
- : ((hint == not_taken) ? taken : not_taken);
-}
-
-
// -----------------------------------------------------------------------------
// Machine instruction Immediates
@@ -843,30 +826,30 @@ class Assembler : public AssemblerBase {
// but it may be bound only once.
void bind(Label* L); // binds an unbound label L to the current code position
- void bind(NearLabel* L);
// Calls
void call(Label* L);
void call(byte* entry, RelocInfo::Mode rmode);
+ int CallSize(const Operand& adr);
void call(const Operand& adr);
- void call(Handle<Code> code, RelocInfo::Mode rmode);
+ int CallSize(Handle<Code> code, RelocInfo::Mode mode);
+ void call(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ unsigned ast_id = kNoASTId);
// Jumps
- void jmp(Label* L); // unconditional jump to L
+ // unconditional jump to L
+ void jmp(Label* L, Label::Distance distance = Label::kFar);
void jmp(byte* entry, RelocInfo::Mode rmode);
void jmp(const Operand& adr);
void jmp(Handle<Code> code, RelocInfo::Mode rmode);
- // Short jump
- void jmp(NearLabel* L);
-
// Conditional jumps
- void j(Condition cc, Label* L, Hint hint = no_hint);
- void j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint = no_hint);
- void j(Condition cc, Handle<Code> code, Hint hint = no_hint);
-
- // Conditional short jump
- void j(Condition cc, NearLabel* L, Hint hint = no_hint);
+ void j(Condition cc,
+ Label* L,
+ Label::Distance distance = Label::kFar);
+ void j(Condition cc, byte* entry, RelocInfo::Mode rmode);
+ void j(Condition cc, Handle<Code> code);
// Floating-point operations
void fld(int i);
@@ -951,6 +934,7 @@ class Assembler : public AssemblerBase {
void mulsd(XMMRegister dst, XMMRegister src);
void divsd(XMMRegister dst, XMMRegister src);
void xorpd(XMMRegister dst, XMMRegister src);
+ void xorps(XMMRegister dst, XMMRegister src);
void sqrtsd(XMMRegister dst, XMMRegister src);
void andpd(XMMRegister dst, XMMRegister src);
@@ -1071,7 +1055,9 @@ class Assembler : public AssemblerBase {
void GrowBuffer();
inline void emit(uint32_t x);
inline void emit(Handle<Object> handle);
- inline void emit(uint32_t x, RelocInfo::Mode rmode);
+ inline void emit(uint32_t x,
+ RelocInfo::Mode rmode,
+ unsigned ast_id = kNoASTId);
inline void emit(const Immediate& x);
inline void emit_w(const Immediate& x);
@@ -1099,6 +1085,7 @@ class Assembler : public AssemblerBase {
inline Displacement disp_at(Label* L);
inline void disp_at_put(Label* L, Displacement disp);
inline void emit_disp(Label* L, Displacement::Type type);
+ inline void emit_near_disp(Label* L);
// record reloc info for current pc_
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
@@ -1117,9 +1104,6 @@ class Assembler : public AssemblerBase {
byte* pc_; // the program counter; moves forward
RelocInfoWriter reloc_info_writer;
- // push-pop elimination
- byte* last_pc_;
-
PositionsRecorder positions_recorder_;
bool emit_debug_code_;
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 29c67b59..12125666 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -102,6 +102,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
Handle<Code> arguments_adaptor =
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ __ SetCallKind(ecx, CALL_AS_METHOD);
__ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
}
@@ -339,11 +340,12 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
Handle<Code> code =
masm->isolate()->builtins()->HandleApiCallConstruct();
ParameterCount expected(0);
- __ InvokeCode(code, expected, expected,
- RelocInfo::CODE_TARGET, CALL_FUNCTION);
+ __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
} else {
ParameterCount actual(eax);
- __ InvokeFunction(edi, actual, CALL_FUNCTION);
+ __ InvokeFunction(edi, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// Restore context from the frame.
@@ -356,12 +358,12 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// If the result is a smi, it is *not* an object in the ECMA sense.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &use_receiver, not_taken);
+ __ j(zero, &use_receiver);
// If the type of the result (stored in its map) is less than
// FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(above_equal, &exit, not_taken);
+ __ j(above_equal, &exit);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
@@ -442,7 +444,8 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(eax);
- __ InvokeFunction(edi, actual, CALL_FUNCTION);
+ __ InvokeFunction(edi, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// Exit the JS frame. Notice that this also removes the empty
@@ -467,19 +470,25 @@ void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
// Enter an internal frame.
__ EnterInternalFrame();
- // Push a copy of the function onto the stack.
+ // Push a copy of the function.
__ push(edi);
+ // Push call kind information.
+ __ push(ecx);
__ push(edi); // Function is also the parameter to the runtime call.
__ CallRuntime(Runtime::kLazyCompile, 1);
+
+ // Restore call kind information.
+ __ pop(ecx);
+ // Restore receiver.
__ pop(edi);
// Tear down temporary frame.
__ LeaveInternalFrame();
// Do a tail-call of the compiled function.
- __ lea(ecx, FieldOperand(eax, Code::kHeaderSize));
- __ jmp(Operand(ecx));
+ __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
+ __ jmp(Operand(eax));
}
@@ -489,17 +498,23 @@ void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
// Push a copy of the function onto the stack.
__ push(edi);
+ // Push call kind information.
+ __ push(ecx);
__ push(edi); // Function is also the parameter to the runtime call.
__ CallRuntime(Runtime::kLazyRecompile, 1);
- // Restore function and tear down temporary frame.
+ // Restore call kind information.
+ __ pop(ecx);
+ // Restore receiver.
__ pop(edi);
+
+ // Tear down temporary frame.
__ LeaveInternalFrame();
// Do a tail-call of the compiled function.
- __ lea(ecx, FieldOperand(eax, Code::kHeaderSize));
- __ jmp(Operand(ecx));
+ __ lea(eax, FieldOperand(eax, Code::kHeaderSize));
+ __ jmp(Operand(eax));
}
@@ -520,15 +535,15 @@ static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
__ SmiUntag(ecx);
// Switch on the state.
- NearLabel not_no_registers, not_tos_eax;
+ Label not_no_registers, not_tos_eax;
__ cmp(ecx, FullCodeGenerator::NO_REGISTERS);
- __ j(not_equal, &not_no_registers);
+ __ j(not_equal, &not_no_registers, Label::kNear);
__ ret(1 * kPointerSize); // Remove state.
__ bind(&not_no_registers);
__ mov(eax, Operand(esp, 2 * kPointerSize));
__ cmp(ecx, FullCodeGenerator::TOS_REG);
- __ j(not_equal, &not_tos_eax);
+ __ j(not_equal, &not_tos_eax, Label::kNear);
__ ret(2 * kPointerSize); // Remove state, eax.
__ bind(&not_tos_eax);
@@ -568,7 +583,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1. Make sure we have at least one argument.
{ Label done;
__ test(eax, Operand(eax));
- __ j(not_zero, &done, taken);
+ __ j(not_zero, &done);
__ pop(ebx);
__ push(Immediate(factory->undefined_value()));
__ push(ebx);
@@ -582,9 +597,9 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1 ~ return address.
__ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
__ test(edi, Immediate(kSmiTagMask));
- __ j(zero, &non_function, not_taken);
+ __ j(zero, &non_function);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &non_function, not_taken);
+ __ j(not_equal, &non_function);
// 3a. Patch the first argument if necessary when calling a function.
@@ -599,22 +614,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
1 << SharedFunctionInfo::kStrictModeBitWithinByte);
__ j(not_equal, &shift_arguments);
+ // Do not transform the receiver for natives (shared already in ebx).
+ __ test_b(FieldOperand(ebx, SharedFunctionInfo::kES5NativeByteOffset),
+ 1 << SharedFunctionInfo::kES5NativeBitWithinByte);
+ __ j(not_equal, &shift_arguments);
+
// Compute the receiver in non-strict mode.
__ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
+
+ // Call ToObject on the receiver if it is not an object, or use the
+ // global object if it is null or undefined.
__ test(ebx, Immediate(kSmiTagMask));
__ j(zero, &convert_to_object);
-
__ cmp(ebx, factory->null_value());
__ j(equal, &use_global_receiver);
__ cmp(ebx, factory->undefined_value());
__ j(equal, &use_global_receiver);
-
- // We don't use IsObjectJSObjectType here because we jump on success.
- __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- __ sub(Operand(ecx), Immediate(FIRST_JS_OBJECT_TYPE));
- __ cmp(ecx, LAST_JS_OBJECT_TYPE - FIRST_JS_OBJECT_TYPE);
- __ j(below_equal, &shift_arguments);
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ __ CmpObjectType(ebx, FIRST_JS_OBJECT_TYPE, ecx);
+ __ j(above_equal, &shift_arguments);
__ bind(&convert_to_object);
__ EnterInternalFrame(); // In order to preserve argument count.
@@ -675,9 +694,10 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
{ Label function;
__ test(edi, Operand(edi));
- __ j(not_zero, &function, taken);
+ __ j(not_zero, &function);
__ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
+ __ SetCallKind(ecx, CALL_AS_METHOD);
__ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
__ bind(&function);
@@ -691,12 +711,14 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
__ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset));
__ SmiUntag(ebx);
+ __ SetCallKind(ecx, CALL_AS_METHOD);
__ cmp(eax, Operand(ebx));
__ j(not_equal,
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline());
ParameterCount expected(0);
- __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
+ __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
@@ -724,7 +746,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
__ shl(edx, kPointerSizeLog2 - kSmiTagSize);
// Check if the arguments will overflow the stack.
__ cmp(ecx, Operand(edx));
- __ j(greater, &okay, taken); // Signed comparison.
+ __ j(greater, &okay); // Signed comparison.
// Out of stack space.
__ push(Operand(ebp, 4 * kPointerSize)); // push this
@@ -755,25 +777,27 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
1 << SharedFunctionInfo::kStrictModeBitWithinByte);
__ j(not_equal, &push_receiver);
+ Factory* factory = masm->isolate()->factory();
+
+ // Do not transform the receiver for natives (shared already in ecx).
+ __ test_b(FieldOperand(ecx, SharedFunctionInfo::kES5NativeByteOffset),
+ 1 << SharedFunctionInfo::kES5NativeBitWithinByte);
+ __ j(not_equal, &push_receiver);
+
// Compute the receiver in non-strict mode.
+ // Call ToObject on the receiver if it is not an object, or use the
+ // global object if it is null or undefined.
__ test(ebx, Immediate(kSmiTagMask));
__ j(zero, &call_to_object);
- Factory* factory = masm->isolate()->factory();
__ cmp(ebx, factory->null_value());
__ j(equal, &use_global_receiver);
__ cmp(ebx, factory->undefined_value());
__ j(equal, &use_global_receiver);
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ __ CmpObjectType(ebx, FIRST_JS_OBJECT_TYPE, ecx);
+ __ j(above_equal, &push_receiver);
- // If given receiver is already a JavaScript object then there's no
- // reason for converting it.
- // We don't use IsObjectJSObjectType here because we jump on success.
- __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- __ sub(Operand(ecx), Immediate(FIRST_JS_OBJECT_TYPE));
- __ cmp(ecx, LAST_JS_OBJECT_TYPE - FIRST_JS_OBJECT_TYPE);
- __ j(below_equal, &push_receiver);
-
- // Convert the receiver to an object.
__ bind(&call_to_object);
__ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
@@ -824,7 +848,8 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
ParameterCount actual(eax);
__ SmiUntag(eax);
__ mov(edi, Operand(ebp, 4 * kPointerSize));
- __ InvokeFunction(edi, actual, CALL_FUNCTION);
+ __ InvokeFunction(edi, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ LeaveInternalFrame();
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
@@ -1418,12 +1443,12 @@ static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
// Push the function on the stack.
__ push(edi);
- // Preserve the number of arguments on the stack. Must preserve both
- // eax and ebx because these registers are used when copying the
+ // Preserve the number of arguments on the stack. Must preserve eax,
+ // ebx and ecx because these registers are used when copying the
// arguments and the receiver.
ASSERT(kSmiTagSize == 1);
- __ lea(ecx, Operand(eax, eax, times_1, kSmiTag));
- __ push(ecx);
+ __ lea(edi, Operand(eax, eax, times_1, kSmiTag));
+ __ push(edi);
}
@@ -1446,6 +1471,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : actual number of arguments
// -- ebx : expected number of arguments
+ // -- ecx : call kind information
// -- edx : code entry to call
// -----------------------------------
@@ -1465,14 +1491,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// Copy receiver and all expected arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(eax, Operand(ebp, eax, times_4, offset));
- __ mov(ecx, -1); // account for receiver
+ __ mov(edi, -1); // account for receiver
Label copy;
__ bind(&copy);
- __ inc(ecx);
+ __ inc(edi);
__ push(Operand(eax, 0));
__ sub(Operand(eax), Immediate(kPointerSize));
- __ cmp(ecx, Operand(ebx));
+ __ cmp(edi, Operand(ebx));
__ j(less, &copy);
__ jmp(&invoke);
}
@@ -1484,30 +1510,33 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// Copy receiver and all actual arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(edi, Operand(ebp, eax, times_4, offset));
- __ mov(ecx, -1); // account for receiver
+ // ebx = expected - actual.
+ __ sub(ebx, Operand(eax));
+ // eax = -actual - 1
+ __ neg(eax);
+ __ sub(Operand(eax), Immediate(1));
Label copy;
__ bind(&copy);
- __ inc(ecx);
+ __ inc(eax);
__ push(Operand(edi, 0));
__ sub(Operand(edi), Immediate(kPointerSize));
- __ cmp(ecx, Operand(eax));
- __ j(less, &copy);
+ __ test(eax, Operand(eax));
+ __ j(not_zero, &copy);
// Fill remaining expected arguments with undefined values.
Label fill;
__ bind(&fill);
- __ inc(ecx);
+ __ inc(eax);
__ push(Immediate(masm->isolate()->factory()->undefined_value()));
- __ cmp(ecx, Operand(ebx));
+ __ cmp(eax, Operand(ebx));
__ j(less, &fill);
-
- // Restore function pointer.
- __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
}
// Call the entry point.
__ bind(&invoke);
+ // Restore function pointer.
+ __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ call(Operand(edx));
// Leave frame and return.
@@ -1558,19 +1587,19 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
// If the result was -1 it means that we couldn't optimize the
// function. Just return and continue in the unoptimized version.
- NearLabel skip;
+ Label skip;
__ cmp(Operand(eax), Immediate(Smi::FromInt(-1)));
- __ j(not_equal, &skip);
+ __ j(not_equal, &skip, Label::kNear);
__ ret(0);
// If we decide not to perform on-stack replacement we perform a
// stack guard check to enable interrupts.
__ bind(&stack_check);
- NearLabel ok;
+ Label ok;
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(masm->isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
- __ j(above_equal, &ok, taken);
+ __ j(above_equal, &ok, Label::kNear);
StackCheckStub stub;
__ TailCallStub(&stub);
__ Abort("Unreachable code: returned from tail call.");
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 5d32095f..8bf2dd40 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -29,10 +29,10 @@
#if defined(V8_TARGET_ARCH_IA32)
-#include "code-stubs.h"
#include "bootstrapper.h"
-#include "jsregexp.h"
+#include "code-stubs.h"
#include "isolate.h"
+#include "jsregexp.h"
#include "regexp-macro-assembler.h"
namespace v8 {
@@ -42,16 +42,16 @@ namespace internal {
void ToNumberStub::Generate(MacroAssembler* masm) {
// The ToNumber stub takes one argument in eax.
- NearLabel check_heap_number, call_builtin;
+ Label check_heap_number, call_builtin;
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &check_heap_number);
+ __ j(not_zero, &check_heap_number, Label::kNear);
__ ret(0);
__ bind(&check_heap_number);
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
Factory* factory = masm->isolate()->factory();
__ cmp(Operand(ebx), Immediate(factory->heap_number_map()));
- __ j(not_equal, &call_builtin);
+ __ j(not_equal, &call_builtin, Label::kNear);
__ ret(0);
__ bind(&call_builtin);
@@ -242,13 +242,29 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
// NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
void ToBooleanStub::Generate(MacroAssembler* masm) {
- NearLabel false_result, true_result, not_string;
+ Label false_result, true_result, not_string;
__ mov(eax, Operand(esp, 1 * kPointerSize));
+ Factory* factory = masm->isolate()->factory();
+
+ // undefined -> false
+ __ cmp(eax, factory->undefined_value());
+ __ j(equal, &false_result);
+
+ // Boolean -> its value
+ __ cmp(eax, factory->true_value());
+ __ j(equal, &true_result);
+ __ cmp(eax, factory->false_value());
+ __ j(equal, &false_result);
+
+ // Smis: 0 -> false, all other -> true
+ __ test(eax, Operand(eax));
+ __ j(zero, &false_result);
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &true_result);
// 'null' => false.
- Factory* factory = masm->isolate()->factory();
__ cmp(eax, factory->null_value());
- __ j(equal, &false_result);
+ __ j(equal, &false_result, Label::kNear);
// Get the map and type of the heap object.
__ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
@@ -257,28 +273,28 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
// Undetectable => false.
__ test_b(FieldOperand(edx, Map::kBitFieldOffset),
1 << Map::kIsUndetectable);
- __ j(not_zero, &false_result);
+ __ j(not_zero, &false_result, Label::kNear);
// JavaScript object => true.
__ CmpInstanceType(edx, FIRST_JS_OBJECT_TYPE);
- __ j(above_equal, &true_result);
+ __ j(above_equal, &true_result, Label::kNear);
// String value => false iff empty.
__ CmpInstanceType(edx, FIRST_NONSTRING_TYPE);
- __ j(above_equal, &not_string);
+ __ j(above_equal, &not_string, Label::kNear);
STATIC_ASSERT(kSmiTag == 0);
__ cmp(FieldOperand(eax, String::kLengthOffset), Immediate(0));
- __ j(zero, &false_result);
- __ jmp(&true_result);
+ __ j(zero, &false_result, Label::kNear);
+ __ jmp(&true_result, Label::kNear);
__ bind(&not_string);
// HeapNumber => false iff +0, -0, or NaN.
__ cmp(edx, factory->heap_number_map());
- __ j(not_equal, &true_result);
+ __ j(not_equal, &true_result, Label::kNear);
__ fldz();
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ FCmp();
- __ j(zero, &false_result);
+ __ j(zero, &false_result, Label::kNear);
// Fall through to |true_result|.
// Return 1/0 for true/false in eax.
@@ -331,14 +347,6 @@ class FloatingPointHelper : public AllStatic {
// Takes the operands in edx and eax and loads them as integers in eax
// and ecx.
- static void LoadAsIntegers(MacroAssembler* masm,
- TypeInfo type_info,
- bool use_sse3,
- Label* operand_conversion_failure);
- static void LoadNumbersAsIntegers(MacroAssembler* masm,
- TypeInfo type_info,
- bool use_sse3,
- Label* operand_conversion_failure);
static void LoadUnknownsAsIntegers(MacroAssembler* masm,
bool use_sse3,
Label* operand_conversion_failure);
@@ -374,15 +382,486 @@ class FloatingPointHelper : public AllStatic {
};
-Handle<Code> GetTypeRecordingBinaryOpStub(int key,
- TRBinaryOpIC::TypeInfo type_info,
- TRBinaryOpIC::TypeInfo result_type_info) {
- TypeRecordingBinaryOpStub stub(key, type_info, result_type_info);
+// Get the integer part of a heap number. Surprisingly, all this bit twiddling
+// is faster than using the built-in instructions on floating point registers.
+// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
+// trashed registers.
+static void IntegerConvert(MacroAssembler* masm,
+ Register source,
+ bool use_sse3,
+ Label* conversion_failure) {
+ ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
+ Label done, right_exponent, normal_exponent;
+ Register scratch = ebx;
+ Register scratch2 = edi;
+ // Get exponent word.
+ __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
+ // Get exponent alone in scratch2.
+ __ mov(scratch2, scratch);
+ __ and_(scratch2, HeapNumber::kExponentMask);
+ if (use_sse3) {
+ CpuFeatures::Scope scope(SSE3);
+ // Check whether the exponent is too big for a 64 bit signed integer.
+ static const uint32_t kTooBigExponent =
+ (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
+ __ cmp(Operand(scratch2), Immediate(kTooBigExponent));
+ __ j(greater_equal, conversion_failure);
+ // Load x87 register with heap number.
+ __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
+ // Reserve space for 64 bit answer.
+ __ sub(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint.
+ // Do conversion, which cannot fail because we checked the exponent.
+ __ fisttp_d(Operand(esp, 0));
+ __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
+ __ add(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint.
+ } else {
+ // Load ecx with zero. We use this either for the final shift or
+ // for the answer.
+ __ xor_(ecx, Operand(ecx));
+ // Check whether the exponent matches a 32 bit signed int that cannot be
+ // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
+ // exponent is 30 (biased). This is the exponent that we are fastest at and
+ // also the highest exponent we can handle here.
+ const uint32_t non_smi_exponent =
+ (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
+ __ cmp(Operand(scratch2), Immediate(non_smi_exponent));
+ // If we have a match of the int32-but-not-Smi exponent then skip some
+ // logic.
+ __ j(equal, &right_exponent);
+ // If the exponent is higher than that then go to slow case. This catches
+ // numbers that don't fit in a signed int32, infinities and NaNs.
+ __ j(less, &normal_exponent);
+
+ {
+ // Handle a big exponent. The only reason we have this code is that the
+ // >>> operator has a tendency to generate numbers with an exponent of 31.
+ const uint32_t big_non_smi_exponent =
+ (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
+ __ cmp(Operand(scratch2), Immediate(big_non_smi_exponent));
+ __ j(not_equal, conversion_failure);
+ // We have the big exponent, typically from >>>. This means the number is
+ // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
+ __ mov(scratch2, scratch);
+ __ and_(scratch2, HeapNumber::kMantissaMask);
+ // Put back the implicit 1.
+ __ or_(scratch2, 1 << HeapNumber::kExponentShift);
+ // Shift up the mantissa bits to take up the space the exponent used to
+ // take. We just orred in the implicit bit so that took care of one and
+ // we want to use the full unsigned range so we subtract 1 bit from the
+ // shift distance.
+ const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
+ __ shl(scratch2, big_shift_distance);
+ // Get the second half of the double.
+ __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
+ // Shift down 21 bits to get the most significant 11 bits or the low
+ // mantissa word.
+ __ shr(ecx, 32 - big_shift_distance);
+ __ or_(ecx, Operand(scratch2));
+ // We have the answer in ecx, but we may need to negate it.
+ __ test(scratch, Operand(scratch));
+ __ j(positive, &done);
+ __ neg(ecx);
+ __ jmp(&done);
+ }
+
+ __ bind(&normal_exponent);
+ // Exponent word in scratch, exponent part of exponent word in scratch2.
+ // Zero in ecx.
+ // We know the exponent is smaller than 30 (biased). If it is less than
+ // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
+ // it rounds to zero.
+ const uint32_t zero_exponent =
+ (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
+ __ sub(Operand(scratch2), Immediate(zero_exponent));
+ // ecx already has a Smi zero.
+ __ j(less, &done);
+
+ // We have a shifted exponent between 0 and 30 in scratch2.
+ __ shr(scratch2, HeapNumber::kExponentShift);
+ __ mov(ecx, Immediate(30));
+ __ sub(ecx, Operand(scratch2));
+
+ __ bind(&right_exponent);
+ // Here ecx is the shift, scratch is the exponent word.
+ // Get the top bits of the mantissa.
+ __ and_(scratch, HeapNumber::kMantissaMask);
+ // Put back the implicit 1.
+ __ or_(scratch, 1 << HeapNumber::kExponentShift);
+ // Shift up the mantissa bits to take up the space the exponent used to
+ // take. We have kExponentShift + 1 significant bits int he low end of the
+ // word. Shift them to the top bits.
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
+ __ shl(scratch, shift_distance);
+ // Get the second half of the double. For some exponents we don't
+ // actually need this because the bits get shifted out again, but
+ // it's probably slower to test than just to do it.
+ __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
+ // Shift down 22 bits to get the most significant 10 bits or the low
+ // mantissa word.
+ __ shr(scratch2, 32 - shift_distance);
+ __ or_(scratch2, Operand(scratch));
+ // Move down according to the exponent.
+ __ shr_cl(scratch2);
+ // Now the unsigned answer is in scratch2. We need to move it to ecx and
+ // we may need to fix the sign.
+ Label negative;
+ __ xor_(ecx, Operand(ecx));
+ __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
+ __ j(greater, &negative, Label::kNear);
+ __ mov(ecx, scratch2);
+ __ jmp(&done, Label::kNear);
+ __ bind(&negative);
+ __ sub(ecx, Operand(scratch2));
+ __ bind(&done);
+ }
+}
+
+
+Handle<Code> GetUnaryOpStub(int key, UnaryOpIC::TypeInfo type_info) {
+ UnaryOpStub stub(key, type_info);
return stub.GetCode();
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+const char* UnaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name = NULL; // Make g++ happy.
+ switch (mode_) {
+ case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "UnaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ UnaryOpIC::GetName(operand_type_));
+ return name_;
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operand_type_) {
+ case UnaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case UnaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case UnaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case UnaryOpIC::GENERIC:
+ GenerateGenericStub(masm);
+ break;
+ }
+}
+
+
+void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ __ pop(ecx); // Save return address.
+ __ push(eax);
+ // the argument is now on top.
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ push(Immediate(Smi::FromInt(MinorKey())));
+ __ push(Immediate(Smi::FromInt(op_)));
+ __ push(Immediate(Smi::FromInt(operand_type_)));
+
+ __ push(ecx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kUnaryOp_Patch),
+ masm->isolate()), 4, 1);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateSmiStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateSmiStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
+ Label non_smi, undo, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &undo, &slow,
+ Label::kNear, Label::kNear, Label::kNear);
+ __ bind(&undo);
+ GenerateSmiCodeUndo(masm);
+ __ bind(&non_smi);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
+ Label non_smi;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* undo,
+ Label* slow,
+ Label::Distance non_smi_near,
+ Label::Distance undo_near,
+ Label::Distance slow_near) {
+ // Check whether the value is a smi.
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, non_smi, non_smi_near);
+
+ // We can't handle -0 with smis, so use a type transition for that case.
+ __ test(eax, Operand(eax));
+ __ j(zero, slow, slow_near);
+
+ // Try optimistic subtraction '0 - value', saving operand in eax for undo.
+ __ mov(edx, Operand(eax));
+ __ Set(eax, Immediate(0));
+ __ sub(eax, Operand(edx));
+ __ j(overflow, undo, undo_near);
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeBitNot(
+ MacroAssembler* masm,
+ Label* non_smi,
+ Label::Distance non_smi_near) {
+ // Check whether the value is a smi.
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, non_smi, non_smi_near);
+
+ // Flip bits and revert inverted smi-tag.
+ __ not_(eax);
+ __ and_(eax, ~kSmiTagMask);
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeUndo(MacroAssembler* masm) {
+ __ mov(eax, Operand(edx));
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateHeapNumberStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateHeapNumberStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
+ Label non_smi, undo, slow, call_builtin;
+ GenerateSmiCodeSub(masm, &non_smi, &undo, &call_builtin, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&undo);
+ GenerateSmiCodeUndo(masm);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+ __ bind(&call_builtin);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubBitNot(
+ MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
+ Label* slow) {
+ __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ cmp(edx, masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, slow);
+
+ if (mode_ == UNARY_OVERWRITE) {
+ __ xor_(FieldOperand(eax, HeapNumber::kExponentOffset),
+ Immediate(HeapNumber::kSignMask)); // Flip sign.
+ } else {
+ __ mov(edx, Operand(eax));
+ // edx: operand
+
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(eax, ebx, ecx, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(edx);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ pop(edx);
+ __ LeaveInternalFrame();
+
+ __ bind(&heapnumber_allocated);
+ // eax: allocated 'empty' number
+ __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
+ __ xor_(ecx, HeapNumber::kSignMask); // Flip sign.
+ __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ecx);
+ __ mov(ecx, FieldOperand(edx, HeapNumber::kMantissaOffset));
+ __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
+ }
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm,
+ Label* slow) {
+ __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ cmp(edx, masm->isolate()->factory()->heap_number_map());
+ __ j(not_equal, slow);
+
+ // Convert the heap number in eax to an untagged integer in ecx.
+ IntegerConvert(masm, eax, CpuFeatures::IsSupported(SSE3), slow);
+
+ // Do the bitwise operation and check if the result fits in a smi.
+ Label try_float;
+ __ not_(ecx);
+ __ cmp(ecx, 0xc0000000);
+ __ j(sign, &try_float, Label::kNear);
+
+ // Tag the result as a smi and we're done.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ __ lea(eax, Operand(ecx, times_2, kSmiTag));
+ __ ret(0);
+
+ // Try to store the result in a heap number.
+ __ bind(&try_float);
+ if (mode_ == UNARY_NO_OVERWRITE) {
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ mov(ebx, eax);
+ __ AllocateHeapNumber(eax, edx, edi, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ // Push the original HeapNumber on the stack. The integer value can't
+ // be stored since it's untagged and not in the smi range (so we can't
+ // smi-tag it). We'll recalculate the value after the GC instead.
+ __ push(ebx);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ // New HeapNumber is in eax.
+ __ pop(edx);
+ __ LeaveInternalFrame();
+ // IntegerConvert uses ebx and edi as scratch registers.
+ // This conversion won't go slow-case.
+ IntegerConvert(masm, edx, CpuFeatures::IsSupported(SSE3), slow);
+ __ not_(ecx);
+
+ __ bind(&heapnumber_allocated);
+ }
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ cvtsi2sd(xmm0, Operand(ecx));
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ push(ecx);
+ __ fild_s(Operand(esp, 0));
+ __ pop(ecx);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ __ ret(0);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateGenericStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateGenericStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
+ Label non_smi, undo, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &undo, &slow, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&undo);
+ GenerateSmiCodeUndo(masm);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
+ // Handle the slow case by jumping to the corresponding JavaScript builtin.
+ __ pop(ecx); // pop return address.
+ __ push(eax);
+ __ push(ecx); // push return address
+ switch (op_) {
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
+ break;
+ case Token::BIT_NOT:
+ __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+Handle<Code> GetBinaryOpStub(int key,
+ BinaryOpIC::TypeInfo type_info,
+ BinaryOpIC::TypeInfo result_type_info) {
+ BinaryOpStub stub(key, type_info, result_type_info);
+ return stub.GetCode();
+}
+
+
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
__ pop(ecx); // Save return address.
__ push(edx);
__ push(eax);
@@ -398,7 +877,7 @@ void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
// Patch the caller to an appropriate specialized stub and return the
// operation result to the caller of the stub.
__ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch),
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
masm->isolate()),
5,
1);
@@ -407,8 +886,7 @@ void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
// Prepare for a type transition runtime call when the args are already on
// the stack, under the return address.
-void TypeRecordingBinaryOpStub::GenerateTypeTransitionWithSavedArgs(
- MacroAssembler* masm) {
+void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm) {
__ pop(ecx); // Save return address.
// Left and right arguments are already on top of the stack.
// Push this stub's key. Although the operation and the type info are
@@ -422,34 +900,37 @@ void TypeRecordingBinaryOpStub::GenerateTypeTransitionWithSavedArgs(
// Patch the caller to an appropriate specialized stub and return the
// operation result to the caller of the stub.
__ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch),
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
masm->isolate()),
5,
1);
}
-void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
+void BinaryOpStub::Generate(MacroAssembler* masm) {
switch (operands_type_) {
- case TRBinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::UNINITIALIZED:
GenerateTypeTransition(masm);
break;
- case TRBinaryOpIC::SMI:
+ case BinaryOpIC::SMI:
GenerateSmiStub(masm);
break;
- case TRBinaryOpIC::INT32:
+ case BinaryOpIC::INT32:
GenerateInt32Stub(masm);
break;
- case TRBinaryOpIC::HEAP_NUMBER:
+ case BinaryOpIC::HEAP_NUMBER:
GenerateHeapNumberStub(masm);
break;
- case TRBinaryOpIC::ODDBALL:
+ case BinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
- case TRBinaryOpIC::STRING:
+ case BinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
+ case BinaryOpIC::STRING:
GenerateStringStub(masm);
break;
- case TRBinaryOpIC::GENERIC:
+ case BinaryOpIC::GENERIC:
GenerateGeneric(masm);
break;
default:
@@ -458,7 +939,7 @@ void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
}
-const char* TypeRecordingBinaryOpStub::GetName() {
+const char* BinaryOpStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
@@ -474,15 +955,16 @@ const char* TypeRecordingBinaryOpStub::GetName() {
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "TypeRecordingBinaryOpStub_%s_%s_%s",
+ "BinaryOpStub_%s_%s_%s",
op_name,
overwrite_name,
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
return name_;
}
-void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+void BinaryOpStub::GenerateSmiCode(
+ MacroAssembler* masm,
Label* slow,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
// 1. Move arguments into edx, eax except for DIV and MOD, which need the
@@ -542,7 +1024,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
// 3. Perform the smi check of the operands.
STATIC_ASSERT(kSmiTag == 0); // Adjust zero check if not the case.
__ test(combined, Immediate(kSmiTagMask));
- __ j(not_zero, &not_smis, not_taken);
+ __ j(not_zero, &not_smis);
// 4. Operands are both smis, perform the operation leaving the result in
// eax and check the result if necessary.
@@ -571,7 +1053,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
__ shl_cl(left);
// Check that the *signed* result fits in a smi.
__ cmp(left, 0xc0000000);
- __ j(sign, &use_fp_on_smis, not_taken);
+ __ j(sign, &use_fp_on_smis);
// Tag the result and store it in register eax.
__ SmiTag(left);
__ mov(eax, left);
@@ -601,7 +1083,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
// Smi tagging these two cases can only happen with shifts
// by 0 or 1 when handed a valid smi.
__ test(left, Immediate(0xc0000000));
- __ j(not_zero, slow, not_taken);
+ __ j(not_zero, &use_fp_on_smis);
// Tag the result and store it in register eax.
__ SmiTag(left);
__ mov(eax, left);
@@ -610,12 +1092,12 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
case Token::ADD:
ASSERT(right.is(eax));
__ add(right, Operand(left)); // Addition is commutative.
- __ j(overflow, &use_fp_on_smis, not_taken);
+ __ j(overflow, &use_fp_on_smis);
break;
case Token::SUB:
__ sub(left, Operand(right));
- __ j(overflow, &use_fp_on_smis, not_taken);
+ __ j(overflow, &use_fp_on_smis);
__ mov(eax, left);
break;
@@ -629,7 +1111,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
__ SmiUntag(right);
// Do multiplication.
__ imul(right, Operand(left)); // Multiplication is commutative.
- __ j(overflow, &use_fp_on_smis, not_taken);
+ __ j(overflow, &use_fp_on_smis);
// Check for negative zero result. Use combined = left | right.
__ NegativeZeroTest(right, combined, &use_fp_on_smis);
break;
@@ -640,7 +1122,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
__ mov(edi, left);
// Check for 0 divisor.
__ test(right, Operand(right));
- __ j(zero, &use_fp_on_smis, not_taken);
+ __ j(zero, &use_fp_on_smis);
// Sign extend left into edx:eax.
ASSERT(left.is(eax));
__ cdq();
@@ -664,7 +1146,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
case Token::MOD:
// Check for 0 divisor.
__ test(right, Operand(right));
- __ j(zero, &not_smis, not_taken);
+ __ j(zero, &not_smis);
// Sign extend left into edx:eax.
ASSERT(left.is(eax));
@@ -737,26 +1219,35 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
} else {
ASSERT(allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS);
switch (op_) {
- case Token::SHL: {
+ case Token::SHL:
+ case Token::SHR: {
Comment perform_float(masm, "-- Perform float operation on smis");
__ bind(&use_fp_on_smis);
// Result we want is in left == edx, so we can put the allocated heap
// number in eax.
__ AllocateHeapNumber(eax, ecx, ebx, slow);
// Store the result in the HeapNumber and return.
- if (CpuFeatures::IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- __ cvtsi2sd(xmm0, Operand(left));
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- } else {
- // It's OK to overwrite the right argument on the stack because we
- // are about to return.
+ // It's OK to overwrite the arguments on the stack because we
+ // are about to return.
+ if (op_ == Token::SHR) {
__ mov(Operand(esp, 1 * kPointerSize), left);
- __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ mov(Operand(esp, 2 * kPointerSize), Immediate(0));
+ __ fild_d(Operand(esp, 1 * kPointerSize));
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ } else {
+ ASSERT_EQ(Token::SHL, op_);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ cvtsi2sd(xmm0, Operand(left));
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), left);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
}
- __ ret(2 * kPointerSize);
- break;
+ __ ret(2 * kPointerSize);
+ break;
}
case Token::ADD:
@@ -848,7 +1339,7 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
}
-void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
Label call_runtime;
switch (op_) {
@@ -870,8 +1361,8 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
UNREACHABLE();
}
- if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
- result_type_ == TRBinaryOpIC::SMI) {
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
GenerateSmiCode(masm, &call_runtime, NO_HEAPNUMBER_RESULTS);
} else {
GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
@@ -899,19 +1390,51 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
- ASSERT(operands_type_ == TRBinaryOpIC::STRING);
+void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::STRING);
ASSERT(op_ == Token::ADD);
// Try to add arguments as strings, otherwise, transition to the generic
- // TRBinaryOpIC type.
+ // BinaryOpIC type.
GenerateAddStrings(masm);
GenerateTypeTransition(masm);
}
-void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
Label call_runtime;
- ASSERT(operands_type_ == TRBinaryOpIC::INT32);
+ ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+
+ // Test if left operand is a string.
+ __ test(left, Immediate(kSmiTagMask));
+ __ j(zero, &call_runtime);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime);
+
+ // Test if right operand is a string.
+ __ test(right, Immediate(kSmiTagMask));
+ __ j(zero, &call_runtime);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
+ __ j(above_equal, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == BinaryOpIC::INT32);
// Floating point case.
switch (op_) {
@@ -933,7 +1456,7 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
default: UNREACHABLE();
}
// Check result type if it is currently Int32.
- if (result_type_ <= TRBinaryOpIC::INT32) {
+ if (result_type_ <= BinaryOpIC::INT32) {
__ cvttsd2si(ecx, Operand(xmm0));
__ cvtsi2sd(xmm2, Operand(ecx));
__ ucomisd(xmm0, xmm2);
@@ -1024,7 +1547,7 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
__ bind(&non_smi_result);
// Allocate a heap number if needed.
__ mov(ebx, Operand(eax)); // ebx: result
- NearLabel skip_allocation;
+ Label skip_allocation;
switch (mode_) {
case OVERWRITE_LEFT:
case OVERWRITE_RIGHT:
@@ -1033,7 +1556,7 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
__ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1 * kPointerSize : 2 * kPointerSize));
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
+ __ j(not_zero, &skip_allocation, Label::kNear);
// Fall through!
case NO_OVERWRITE:
__ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
@@ -1111,32 +1634,32 @@ void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
- Label call_runtime;
-
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
if (op_ == Token::ADD) {
// Handle string addition here, because it is the only operation
// that does not do a ToNumber conversion on the operands.
GenerateAddStrings(masm);
}
+ Factory* factory = masm->isolate()->factory();
+
// Convert odd ball arguments to numbers.
- NearLabel check, done;
- __ cmp(edx, FACTORY->undefined_value());
- __ j(not_equal, &check);
+ Label check, done;
+ __ cmp(edx, factory->undefined_value());
+ __ j(not_equal, &check, Label::kNear);
if (Token::IsBitOp(op_)) {
__ xor_(edx, Operand(edx));
} else {
- __ mov(edx, Immediate(FACTORY->nan_value()));
+ __ mov(edx, Immediate(factory->nan_value()));
}
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&check);
- __ cmp(eax, FACTORY->undefined_value());
- __ j(not_equal, &done);
+ __ cmp(eax, factory->undefined_value());
+ __ j(not_equal, &done, Label::kNear);
if (Token::IsBitOp(op_)) {
__ xor_(eax, Operand(eax));
} else {
- __ mov(eax, Immediate(FACTORY->nan_value()));
+ __ mov(eax, Immediate(factory->nan_value()));
}
__ bind(&done);
@@ -1144,7 +1667,7 @@ void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
Label call_runtime;
// Floating point case.
@@ -1239,7 +1762,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
__ bind(&non_smi_result);
// Allocate a heap number if needed.
__ mov(ebx, Operand(eax)); // ebx: result
- NearLabel skip_allocation;
+ Label skip_allocation;
switch (mode_) {
case OVERWRITE_LEFT:
case OVERWRITE_RIGHT:
@@ -1248,7 +1771,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
__ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1 * kPointerSize : 2 * kPointerSize));
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
+ __ j(not_zero, &skip_allocation, Label::kNear);
// Fall through!
case NO_OVERWRITE:
__ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
@@ -1325,7 +1848,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
Label call_runtime;
Counters* counters = masm->isolate()->counters();
@@ -1439,7 +1962,7 @@ void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
__ bind(&non_smi_result);
// Allocate a heap number if needed.
__ mov(ebx, Operand(eax)); // ebx: result
- NearLabel skip_allocation;
+ Label skip_allocation;
switch (mode_) {
case OVERWRITE_LEFT:
case OVERWRITE_RIGHT:
@@ -1448,7 +1971,7 @@ void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
__ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
1 * kPointerSize : 2 * kPointerSize));
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
+ __ j(not_zero, &skip_allocation, Label::kNear);
// Fall through!
case NO_OVERWRITE:
__ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
@@ -1522,9 +2045,9 @@ void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
ASSERT(op_ == Token::ADD);
- NearLabel left_not_string, call_runtime;
+ Label left_not_string, call_runtime;
// Registers containing left and right operands respectively.
Register left = edx;
@@ -1532,9 +2055,9 @@ void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
// Test if left operand is a string.
__ test(left, Immediate(kSmiTagMask));
- __ j(zero, &left_not_string);
+ __ j(zero, &left_not_string, Label::kNear);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);
- __ j(above_equal, &left_not_string);
+ __ j(above_equal, &left_not_string, Label::kNear);
StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
GenerateRegisterArgsPush(masm);
@@ -1543,9 +2066,9 @@ void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
// Left operand is not a string, test right.
__ bind(&left_not_string);
__ test(right, Immediate(kSmiTagMask));
- __ j(zero, &call_runtime);
+ __ j(zero, &call_runtime, Label::kNear);
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, ecx);
- __ j(above_equal, &call_runtime);
+ __ j(above_equal, &call_runtime, Label::kNear);
StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
GenerateRegisterArgsPush(masm);
@@ -1556,7 +2079,7 @@ void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
+void BinaryOpStub::GenerateHeapResultAllocation(
MacroAssembler* masm,
Label* alloc_failure) {
Label skip_allocation;
@@ -1566,7 +2089,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
// If the argument in edx is already an object, we skip the
// allocation of a heap number.
__ test(edx, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
+ __ j(not_zero, &skip_allocation);
// Allocate a heap number for the result. Keep eax and edx intact
// for the possible runtime call.
__ AllocateHeapNumber(ebx, ecx, no_reg, alloc_failure);
@@ -1582,7 +2105,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
// If the argument in eax is already an object, we skip the
// allocation of a heap number.
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
+ __ j(not_zero, &skip_allocation);
// Fall through!
case NO_OVERWRITE:
// Allocate a heap number for the result. Keep eax and edx intact
@@ -1598,7 +2121,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
}
-void TypeRecordingBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
__ pop(ecx);
__ push(edx);
__ push(eax);
@@ -1626,11 +2149,11 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
const bool tagged = (argument_type_ == TAGGED);
if (tagged) {
// Test that eax is a number.
- NearLabel input_not_smi;
- NearLabel loaded;
+ Label input_not_smi;
+ Label loaded;
__ mov(eax, Operand(esp, kPointerSize));
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &input_not_smi);
+ __ j(not_zero, &input_not_smi, Label::kNear);
// Input is a smi. Untag and load it onto the FPU stack.
// Then load the low and high words of the double into ebx, edx.
STATIC_ASSERT(kSmiTagSize == 1);
@@ -1641,7 +2164,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
__ fst_d(Operand(esp, 0));
__ pop(edx);
__ pop(ebx);
- __ jmp(&loaded);
+ __ jmp(&loaded, Label::kNear);
__ bind(&input_not_smi);
// Check if input is a HeapNumber.
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
@@ -1715,11 +2238,11 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
__ lea(ecx, Operand(ecx, ecx, times_2, 0));
__ lea(ecx, Operand(eax, ecx, times_4, 0));
// Check if cache matches: Double value is stored in uint32_t[2] array.
- NearLabel cache_miss;
+ Label cache_miss;
__ cmp(ebx, Operand(ecx, 0));
- __ j(not_equal, &cache_miss);
+ __ j(not_equal, &cache_miss, Label::kNear);
__ cmp(edx, Operand(ecx, kIntSize));
- __ j(not_equal, &cache_miss);
+ __ j(not_equal, &cache_miss, Label::kNear);
// Cache hit!
__ mov(eax, Operand(ecx, 2 * kIntSize));
if (tagged) {
@@ -1817,7 +2340,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
// Both fsin and fcos require arguments in the range +/-2^63 and
// return NaN for infinities and NaN. They can share all code except
// the actual fsin/fcos operation.
- NearLabel in_range, done;
+ Label in_range, done;
// If argument is outside the range -2^63..2^63, fsin/cos doesn't
// work. We must reduce it to the appropriate range.
__ mov(edi, edx);
@@ -1825,11 +2348,11 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
int supported_exponent_limit =
(63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
__ cmp(Operand(edi), Immediate(supported_exponent_limit));
- __ j(below, &in_range, taken);
+ __ j(below, &in_range, Label::kNear);
// Check for infinity and NaN. Both return NaN for sin.
__ cmp(Operand(edi), Immediate(0x7ff00000));
- NearLabel non_nan_result;
- __ j(not_equal, &non_nan_result, taken);
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, Label::kNear);
// Input is +/-Infinity or NaN. Result is NaN.
__ fstp(0);
// NaN is represented by 0x7ff8000000000000.
@@ -1837,7 +2360,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
__ push(Immediate(0));
__ fld_d(Operand(esp, 0));
__ add(Operand(esp), Immediate(2 * kPointerSize));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&non_nan_result);
@@ -1848,19 +2371,19 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
__ fld(1);
// FPU Stack: input, 2*pi, input.
{
- NearLabel no_exceptions;
+ Label no_exceptions;
__ fwait();
__ fnstsw_ax();
// Clear if Illegal Operand or Zero Division exceptions are set.
__ test(Operand(eax), Immediate(5));
- __ j(zero, &no_exceptions);
+ __ j(zero, &no_exceptions, Label::kNear);
__ fnclex();
__ bind(&no_exceptions);
}
// Compute st(0) % st(1)
{
- NearLabel partial_remainder_loop;
+ Label partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
@@ -1897,203 +2420,6 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
}
-// Get the integer part of a heap number. Surprisingly, all this bit twiddling
-// is faster than using the built-in instructions on floating point registers.
-// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
-// trashed registers.
-void IntegerConvert(MacroAssembler* masm,
- Register source,
- TypeInfo type_info,
- bool use_sse3,
- Label* conversion_failure) {
- ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
- Label done, right_exponent, normal_exponent;
- Register scratch = ebx;
- Register scratch2 = edi;
- if (type_info.IsInteger32() && CpuFeatures::IsSupported(SSE2)) {
- CpuFeatures::Scope scope(SSE2);
- __ cvttsd2si(ecx, FieldOperand(source, HeapNumber::kValueOffset));
- return;
- }
- if (!type_info.IsInteger32() || !use_sse3) {
- // Get exponent word.
- __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
- // Get exponent alone in scratch2.
- __ mov(scratch2, scratch);
- __ and_(scratch2, HeapNumber::kExponentMask);
- }
- if (use_sse3) {
- CpuFeatures::Scope scope(SSE3);
- if (!type_info.IsInteger32()) {
- // Check whether the exponent is too big for a 64 bit signed integer.
- static const uint32_t kTooBigExponent =
- (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
- __ cmp(Operand(scratch2), Immediate(kTooBigExponent));
- __ j(greater_equal, conversion_failure);
- }
- // Load x87 register with heap number.
- __ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
- // Reserve space for 64 bit answer.
- __ sub(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint.
- // Do conversion, which cannot fail because we checked the exponent.
- __ fisttp_d(Operand(esp, 0));
- __ mov(ecx, Operand(esp, 0)); // Load low word of answer into ecx.
- __ add(Operand(esp), Immediate(sizeof(uint64_t))); // Nolint.
- } else {
- // Load ecx with zero. We use this either for the final shift or
- // for the answer.
- __ xor_(ecx, Operand(ecx));
- // Check whether the exponent matches a 32 bit signed int that cannot be
- // represented by a Smi. A non-smi 32 bit integer is 1.xxx * 2^30 so the
- // exponent is 30 (biased). This is the exponent that we are fastest at and
- // also the highest exponent we can handle here.
- const uint32_t non_smi_exponent =
- (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
- __ cmp(Operand(scratch2), Immediate(non_smi_exponent));
- // If we have a match of the int32-but-not-Smi exponent then skip some
- // logic.
- __ j(equal, &right_exponent);
- // If the exponent is higher than that then go to slow case. This catches
- // numbers that don't fit in a signed int32, infinities and NaNs.
- __ j(less, &normal_exponent);
-
- {
- // Handle a big exponent. The only reason we have this code is that the
- // >>> operator has a tendency to generate numbers with an exponent of 31.
- const uint32_t big_non_smi_exponent =
- (HeapNumber::kExponentBias + 31) << HeapNumber::kExponentShift;
- __ cmp(Operand(scratch2), Immediate(big_non_smi_exponent));
- __ j(not_equal, conversion_failure);
- // We have the big exponent, typically from >>>. This means the number is
- // in the range 2^31 to 2^32 - 1. Get the top bits of the mantissa.
- __ mov(scratch2, scratch);
- __ and_(scratch2, HeapNumber::kMantissaMask);
- // Put back the implicit 1.
- __ or_(scratch2, 1 << HeapNumber::kExponentShift);
- // Shift up the mantissa bits to take up the space the exponent used to
- // take. We just orred in the implicit bit so that took care of one and
- // we want to use the full unsigned range so we subtract 1 bit from the
- // shift distance.
- const int big_shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 1;
- __ shl(scratch2, big_shift_distance);
- // Get the second half of the double.
- __ mov(ecx, FieldOperand(source, HeapNumber::kMantissaOffset));
- // Shift down 21 bits to get the most significant 11 bits or the low
- // mantissa word.
- __ shr(ecx, 32 - big_shift_distance);
- __ or_(ecx, Operand(scratch2));
- // We have the answer in ecx, but we may need to negate it.
- __ test(scratch, Operand(scratch));
- __ j(positive, &done);
- __ neg(ecx);
- __ jmp(&done);
- }
-
- __ bind(&normal_exponent);
- // Exponent word in scratch, exponent part of exponent word in scratch2.
- // Zero in ecx.
- // We know the exponent is smaller than 30 (biased). If it is less than
- // 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
- // it rounds to zero.
- const uint32_t zero_exponent =
- (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
- __ sub(Operand(scratch2), Immediate(zero_exponent));
- // ecx already has a Smi zero.
- __ j(less, &done);
-
- // We have a shifted exponent between 0 and 30 in scratch2.
- __ shr(scratch2, HeapNumber::kExponentShift);
- __ mov(ecx, Immediate(30));
- __ sub(ecx, Operand(scratch2));
-
- __ bind(&right_exponent);
- // Here ecx is the shift, scratch is the exponent word.
- // Get the top bits of the mantissa.
- __ and_(scratch, HeapNumber::kMantissaMask);
- // Put back the implicit 1.
- __ or_(scratch, 1 << HeapNumber::kExponentShift);
- // Shift up the mantissa bits to take up the space the exponent used to
- // take. We have kExponentShift + 1 significant bits int he low end of the
- // word. Shift them to the top bits.
- const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
- __ shl(scratch, shift_distance);
- // Get the second half of the double. For some exponents we don't
- // actually need this because the bits get shifted out again, but
- // it's probably slower to test than just to do it.
- __ mov(scratch2, FieldOperand(source, HeapNumber::kMantissaOffset));
- // Shift down 22 bits to get the most significant 10 bits or the low
- // mantissa word.
- __ shr(scratch2, 32 - shift_distance);
- __ or_(scratch2, Operand(scratch));
- // Move down according to the exponent.
- __ shr_cl(scratch2);
- // Now the unsigned answer is in scratch2. We need to move it to ecx and
- // we may need to fix the sign.
- NearLabel negative;
- __ xor_(ecx, Operand(ecx));
- __ cmp(ecx, FieldOperand(source, HeapNumber::kExponentOffset));
- __ j(greater, &negative);
- __ mov(ecx, scratch2);
- __ jmp(&done);
- __ bind(&negative);
- __ sub(ecx, Operand(scratch2));
- __ bind(&done);
- }
-}
-
-
-// Input: edx, eax are the left and right objects of a bit op.
-// Output: eax, ecx are left and right integers for a bit op.
-void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm,
- TypeInfo type_info,
- bool use_sse3,
- Label* conversion_failure) {
- // Check float operands.
- Label arg1_is_object, check_undefined_arg1;
- Label arg2_is_object, check_undefined_arg2;
- Label load_arg2, done;
-
- if (!type_info.IsDouble()) {
- if (!type_info.IsSmi()) {
- __ test(edx, Immediate(kSmiTagMask));
- __ j(not_zero, &arg1_is_object);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(edx);
- }
- __ SmiUntag(edx);
- __ jmp(&load_arg2);
- }
-
- __ bind(&arg1_is_object);
-
- // Get the untagged integer version of the edx heap number in ecx.
- IntegerConvert(masm, edx, type_info, use_sse3, conversion_failure);
- __ mov(edx, ecx);
-
- // Here edx has the untagged integer, eax has a Smi or a heap number.
- __ bind(&load_arg2);
- if (!type_info.IsDouble()) {
- // Test if arg2 is a Smi.
- if (!type_info.IsSmi()) {
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &arg2_is_object);
- } else {
- if (FLAG_debug_code) __ AbortIfNotSmi(eax);
- }
- __ SmiUntag(eax);
- __ mov(ecx, eax);
- __ jmp(&done);
- }
-
- __ bind(&arg2_is_object);
-
- // Get the untagged integer version of the eax heap number in ecx.
- IntegerConvert(masm, eax, type_info, use_sse3, conversion_failure);
- __ bind(&done);
- __ mov(eax, edx);
-}
-
-
// Input: edx, eax are the left and right objects of a bit op.
// Output: eax, ecx are left and right integers for a bit op.
void FloatingPointHelper::LoadUnknownsAsIntegers(MacroAssembler* masm,
@@ -2125,11 +2451,7 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(MacroAssembler* masm,
__ j(not_equal, &check_undefined_arg1);
// Get the untagged integer version of the edx heap number in ecx.
- IntegerConvert(masm,
- edx,
- TypeInfo::Unknown(),
- use_sse3,
- conversion_failure);
+ IntegerConvert(masm, edx, use_sse3, conversion_failure);
__ mov(edx, ecx);
// Here edx has the untagged integer, eax has a Smi or a heap number.
@@ -2156,28 +2478,12 @@ void FloatingPointHelper::LoadUnknownsAsIntegers(MacroAssembler* masm,
__ j(not_equal, &check_undefined_arg2);
// Get the untagged integer version of the eax heap number in ecx.
- IntegerConvert(masm,
- eax,
- TypeInfo::Unknown(),
- use_sse3,
- conversion_failure);
+ IntegerConvert(masm, eax, use_sse3, conversion_failure);
__ bind(&done);
__ mov(eax, edx);
}
-void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
- TypeInfo type_info,
- bool use_sse3,
- Label* conversion_failure) {
- if (type_info.IsNumber()) {
- LoadNumbersAsIntegers(masm, type_info, use_sse3, conversion_failure);
- } else {
- LoadUnknownsAsIntegers(masm, use_sse3, conversion_failure);
- }
-}
-
-
void FloatingPointHelper::CheckLoadedIntegersWereInt32(MacroAssembler* masm,
bool use_sse3,
Label* not_int32) {
@@ -2187,12 +2493,12 @@ void FloatingPointHelper::CheckLoadedIntegersWereInt32(MacroAssembler* masm,
void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
Register number) {
- NearLabel load_smi, done;
+ Label load_smi, done;
__ test(number, Immediate(kSmiTagMask));
- __ j(zero, &load_smi, not_taken);
+ __ j(zero, &load_smi, Label::kNear);
__ fld_d(FieldOperand(number, HeapNumber::kValueOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&load_smi);
__ SmiUntag(number);
@@ -2205,18 +2511,20 @@ void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
- NearLabel load_smi_edx, load_eax, load_smi_eax, done;
+ Label load_smi_edx, load_eax, load_smi_eax, done;
// Load operand in edx into xmm0.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
+ // Argument in edx is a smi.
+ __ j(zero, &load_smi_edx, Label::kNear);
__ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
__ bind(&load_eax);
// Load operand in eax into xmm1.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &load_smi_eax, not_taken); // Argument in eax is a smi.
+ // Argument in eax is a smi.
+ __ j(zero, &load_smi_eax, Label::kNear);
__ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&load_smi_edx);
__ SmiUntag(edx); // Untag smi before converting to float.
@@ -2235,10 +2543,11 @@ void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
Label* not_numbers) {
- NearLabel load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
+ Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
// Load operand in edx into xmm0, or branch to not_numbers.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
+ // Argument in edx is a smi.
+ __ j(zero, &load_smi_edx, Label::kNear);
Factory* factory = masm->isolate()->factory();
__ cmp(FieldOperand(edx, HeapObject::kMapOffset), factory->heap_number_map());
__ j(not_equal, not_numbers); // Argument in edx is not a number.
@@ -2246,9 +2555,10 @@ void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
__ bind(&load_eax);
// Load operand in eax into xmm1, or branch to not_numbers.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &load_smi_eax, not_taken); // Argument in eax is a smi.
+ // Argument in eax is a smi.
+ __ j(zero, &load_smi_eax, Label::kNear);
__ cmp(FieldOperand(eax, HeapObject::kMapOffset), factory->heap_number_map());
- __ j(equal, &load_float_eax);
+ __ j(equal, &load_float_eax, Label::kNear);
__ jmp(not_numbers); // Argument in eax is not a number.
__ bind(&load_smi_edx);
__ SmiUntag(edx); // Untag smi before converting to float.
@@ -2259,7 +2569,7 @@ void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
__ SmiUntag(eax); // Untag smi before converting to float.
__ cvtsi2sd(xmm1, Operand(eax));
__ SmiTag(eax); // Retag smi for heap number overwriting test.
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&load_float_eax);
__ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
__ bind(&done);
@@ -2300,14 +2610,14 @@ void FloatingPointHelper::CheckSSE2OperandsAreInt32(MacroAssembler* masm,
void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
Register scratch,
ArgLocation arg_location) {
- NearLabel load_smi_1, load_smi_2, done_load_1, done;
+ Label load_smi_1, load_smi_2, done_load_1, done;
if (arg_location == ARGS_IN_REGISTERS) {
__ mov(scratch, edx);
} else {
__ mov(scratch, Operand(esp, 2 * kPointerSize));
}
__ test(scratch, Immediate(kSmiTagMask));
- __ j(zero, &load_smi_1, not_taken);
+ __ j(zero, &load_smi_1, Label::kNear);
__ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
__ bind(&done_load_1);
@@ -2317,9 +2627,9 @@ void FloatingPointHelper::LoadFloatOperands(MacroAssembler* masm,
__ mov(scratch, Operand(esp, 1 * kPointerSize));
}
__ test(scratch, Immediate(kSmiTagMask));
- __ j(zero, &load_smi_2, not_taken);
+ __ j(zero, &load_smi_2, Label::kNear);
__ fld_d(FieldOperand(scratch, HeapNumber::kValueOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&load_smi_1);
__ SmiUntag(scratch);
@@ -2359,11 +2669,11 @@ void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm,
void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
Label* non_float,
Register scratch) {
- NearLabel test_other, done;
+ Label test_other, done;
// Test if both operands are floats or smi -> scratch=k_is_float;
// Otherwise scratch = k_not_float.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &test_other, not_taken); // argument in edx is OK
+ __ j(zero, &test_other, Label::kNear); // argument in edx is OK
__ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset));
Factory* factory = masm->isolate()->factory();
__ cmp(scratch, factory->heap_number_map());
@@ -2371,7 +2681,7 @@ void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
__ bind(&test_other);
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &done); // argument in eax is OK
+ __ j(zero, &done, Label::kNear); // argument in eax is OK
__ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset));
__ cmp(scratch, factory->heap_number_map());
__ j(not_equal, non_float); // argument in eax is not a number -> NaN
@@ -2387,140 +2697,6 @@ void FloatingPointHelper::CheckFloatOperandsAreInt32(MacroAssembler* masm,
}
-void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
- Label slow, done, undo;
-
- if (op_ == Token::SUB) {
- if (include_smi_code_) {
- // Check whether the value is a smi.
- NearLabel try_float;
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &try_float, not_taken);
-
- if (negative_zero_ == kStrictNegativeZero) {
- // Go slow case if the value of the expression is zero
- // to make sure that we switch between 0 and -0.
- __ test(eax, Operand(eax));
- __ j(zero, &slow, not_taken);
- }
-
- // The value of the expression is a smi that is not zero. Try
- // optimistic subtraction '0 - value'.
- __ mov(edx, Operand(eax));
- __ Set(eax, Immediate(0));
- __ sub(eax, Operand(edx));
- __ j(overflow, &undo, not_taken);
- __ StubReturn(1);
-
- // Try floating point case.
- __ bind(&try_float);
- } else if (FLAG_debug_code) {
- __ AbortIfSmi(eax);
- }
-
- __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
- __ cmp(edx, masm->isolate()->factory()->heap_number_map());
- __ j(not_equal, &slow);
- if (overwrite_ == UNARY_OVERWRITE) {
- __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
- __ xor_(edx, HeapNumber::kSignMask); // Flip sign.
- __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), edx);
- } else {
- __ mov(edx, Operand(eax));
- // edx: operand
- __ AllocateHeapNumber(eax, ebx, ecx, &undo);
- // eax: allocated 'empty' number
- __ mov(ecx, FieldOperand(edx, HeapNumber::kExponentOffset));
- __ xor_(ecx, HeapNumber::kSignMask); // Flip sign.
- __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ecx);
- __ mov(ecx, FieldOperand(edx, HeapNumber::kMantissaOffset));
- __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
- }
- } else if (op_ == Token::BIT_NOT) {
- if (include_smi_code_) {
- Label non_smi;
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &non_smi);
- __ not_(eax);
- __ and_(eax, ~kSmiTagMask); // Remove inverted smi-tag.
- __ ret(0);
- __ bind(&non_smi);
- } else if (FLAG_debug_code) {
- __ AbortIfSmi(eax);
- }
-
- // Check if the operand is a heap number.
- __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
- __ cmp(edx, masm->isolate()->factory()->heap_number_map());
- __ j(not_equal, &slow, not_taken);
-
- // Convert the heap number in eax to an untagged integer in ecx.
- IntegerConvert(masm,
- eax,
- TypeInfo::Unknown(),
- CpuFeatures::IsSupported(SSE3),
- &slow);
-
- // Do the bitwise operation and check if the result fits in a smi.
- NearLabel try_float;
- __ not_(ecx);
- __ cmp(ecx, 0xc0000000);
- __ j(sign, &try_float, not_taken);
-
- // Tag the result as a smi and we're done.
- STATIC_ASSERT(kSmiTagSize == 1);
- __ lea(eax, Operand(ecx, times_2, kSmiTag));
- __ jmp(&done);
-
- // Try to store the result in a heap number.
- __ bind(&try_float);
- if (overwrite_ == UNARY_NO_OVERWRITE) {
- // Allocate a fresh heap number, but don't overwrite eax until
- // we're sure we can do it without going through the slow case
- // that needs the value in eax.
- __ AllocateHeapNumber(ebx, edx, edi, &slow);
- __ mov(eax, Operand(ebx));
- }
- if (CpuFeatures::IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- __ cvtsi2sd(xmm0, Operand(ecx));
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- } else {
- __ push(ecx);
- __ fild_s(Operand(esp, 0));
- __ pop(ecx);
- __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- }
- } else {
- UNIMPLEMENTED();
- }
-
- // Return from the stub.
- __ bind(&done);
- __ StubReturn(1);
-
- // Restore eax and go slow case.
- __ bind(&undo);
- __ mov(eax, Operand(edx));
-
- // Handle the slow case by jumping to the JavaScript builtin.
- __ bind(&slow);
- __ pop(ecx); // pop return address.
- __ push(eax);
- __ push(ecx); // push return address
- switch (op_) {
- case Token::SUB:
- __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
- break;
- case Token::BIT_NOT:
- __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
void MathPowStub::Generate(MacroAssembler* masm) {
// Registers are used as follows:
// edx = base
@@ -2570,20 +2746,20 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ mov(edx, eax);
// Get absolute value of exponent.
- NearLabel no_neg;
+ Label no_neg;
__ cmp(eax, 0);
- __ j(greater_equal, &no_neg);
+ __ j(greater_equal, &no_neg, Label::kNear);
__ neg(eax);
__ bind(&no_neg);
// Load xmm1 with 1.
__ movsd(xmm1, xmm3);
- NearLabel while_true;
- NearLabel no_multiply;
+ Label while_true;
+ Label no_multiply;
__ bind(&while_true);
__ shr(eax, 1);
- __ j(not_carry, &no_multiply);
+ __ j(not_carry, &no_multiply, Label::kNear);
__ mulsd(xmm1, xmm0);
__ bind(&no_multiply);
__ mulsd(xmm0, xmm0);
@@ -2614,13 +2790,13 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ ucomisd(xmm1, xmm1);
__ j(parity_even, &call_runtime);
- NearLabel base_not_smi;
- NearLabel handle_special_cases;
+ Label base_not_smi;
+ Label handle_special_cases;
__ test(edx, Immediate(kSmiTagMask));
- __ j(not_zero, &base_not_smi);
+ __ j(not_zero, &base_not_smi, Label::kNear);
__ SmiUntag(edx);
__ cvtsi2sd(xmm0, Operand(edx));
- __ jmp(&handle_special_cases);
+ __ jmp(&handle_special_cases, Label::kNear);
__ bind(&base_not_smi);
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
@@ -2635,7 +2811,7 @@ void MathPowStub::Generate(MacroAssembler* masm) {
// base is in xmm0 and exponent is in xmm1.
__ bind(&handle_special_cases);
- NearLabel not_minus_half;
+ Label not_minus_half;
// Test for -0.5.
// Load xmm2 with -0.5.
__ mov(ecx, Immediate(0xBF000000));
@@ -2643,11 +2819,11 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ cvtss2sd(xmm2, xmm2);
// xmm2 now has -0.5.
__ ucomisd(xmm2, xmm1);
- __ j(not_equal, &not_minus_half);
+ __ j(not_equal, &not_minus_half, Label::kNear);
// Calculates reciprocal of square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
+ __ xorps(xmm1, xmm1);
__ addsd(xmm1, xmm0);
__ sqrtsd(xmm1, xmm1);
__ divsd(xmm3, xmm1);
@@ -2664,7 +2840,7 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ j(not_equal, &call_runtime);
// Calculates square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
+ __ xorps(xmm1, xmm1);
__ addsd(xmm1, xmm0);
__ sqrtsd(xmm1, xmm1);
@@ -2690,20 +2866,20 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// Check that the key is a smi.
Label slow;
__ test(edx, Immediate(kSmiTagMask));
- __ j(not_zero, &slow, not_taken);
+ __ j(not_zero, &slow);
// Check if the calling frame is an arguments adaptor frame.
- NearLabel adaptor;
+ Label adaptor;
__ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(ecx, Operand(ebx, StandardFrameConstants::kContextOffset));
__ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ j(equal, &adaptor);
+ __ j(equal, &adaptor, Label::kNear);
// Check index against formal parameters count limit passed in
// through register eax. Use unsigned comparison to get negative
// check for free.
__ cmp(edx, Operand(eax));
- __ j(above_equal, &slow, not_taken);
+ __ j(above_equal, &slow);
// Read the argument from the stack and return it.
STATIC_ASSERT(kSmiTagSize == 1);
@@ -2719,7 +2895,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
__ bind(&adaptor);
__ mov(ecx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ cmp(edx, Operand(ecx));
- __ j(above_equal, &slow, not_taken);
+ __ j(above_equal, &slow);
// Read the argument from the stack and return it.
STATIC_ASSERT(kSmiTagSize == 1);
@@ -2770,10 +2946,10 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
// Try the new space allocation. Start out with computing the size of
// the arguments object and the elements array.
- NearLabel add_arguments_object;
+ Label add_arguments_object;
__ bind(&try_allocate);
__ test(ecx, Operand(ecx));
- __ j(zero, &add_arguments_object);
+ __ j(zero, &add_arguments_object, Label::kNear);
__ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
__ bind(&add_arguments_object);
__ add(Operand(ecx), Immediate(GetArgumentsObjectSize()));
@@ -2829,7 +3005,7 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
__ SmiUntag(ecx);
// Copy the fixed array slots.
- NearLabel loop;
+ Label loop;
__ bind(&loop);
__ mov(ebx, Operand(edx, -1 * kPointerSize)); // Skip receiver.
__ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
@@ -2882,7 +3058,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
__ mov(ebx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
__ test(ebx, Operand(ebx));
- __ j(zero, &runtime, not_taken);
+ __ j(zero, &runtime);
// Check that the first argument is a JSRegExp object.
__ mov(eax, Operand(esp, kJSRegExpOffset));
@@ -3023,9 +3199,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ bind(&check_code);
// Check that the irregexp code has been generated for the actual string
// encoding. If it has, the field contains a code object otherwise it contains
- // the hole.
- __ CmpObjectType(edx, CODE_TYPE, ebx);
- __ j(not_equal, &runtime);
+ // a smi (code flushing support).
+ __ JumpIfSmi(edx, &runtime);
// eax: subject string
// edx: code
@@ -3066,16 +3241,16 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// Argument 4: End of string data
// Argument 3: Start of string data
- NearLabel setup_two_byte, setup_rest;
+ Label setup_two_byte, setup_rest;
__ test(edi, Operand(edi));
__ mov(edi, FieldOperand(eax, String::kLengthOffset));
- __ j(zero, &setup_two_byte);
+ __ j(zero, &setup_two_byte, Label::kNear);
__ SmiUntag(edi);
__ lea(ecx, FieldOperand(eax, edi, times_1, SeqAsciiString::kHeaderSize));
__ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
__ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize));
__ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
- __ jmp(&setup_rest);
+ __ jmp(&setup_rest, Label::kNear);
__ bind(&setup_two_byte);
STATIC_ASSERT(kSmiTag == 0);
@@ -3103,10 +3278,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// Check the result.
Label success;
__ cmp(eax, NativeRegExpMacroAssembler::SUCCESS);
- __ j(equal, &success, taken);
+ __ j(equal, &success);
Label failure;
__ cmp(eax, NativeRegExpMacroAssembler::FAILURE);
- __ j(equal, &failure, taken);
+ __ j(equal, &failure);
__ cmp(eax, NativeRegExpMacroAssembler::EXCEPTION);
// If not exception it can only be retry. Handle that in the runtime system.
__ j(not_equal, &runtime);
@@ -3183,12 +3358,12 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// ebx: last_match_info backing store (FixedArray)
// ecx: offsets vector
// edx: number of capture registers
- NearLabel next_capture, done;
+ Label next_capture, done;
// Capture register counter starts from number of capture registers and
// counts down until wraping after zero.
__ bind(&next_capture);
__ sub(Operand(edx), Immediate(1));
- __ j(negative, &done);
+ __ j(negative, &done, Label::kNear);
// Read the value from the static offsets vector buffer.
__ mov(edi, Operand(ecx, edx, times_int_size, 0));
__ SmiTag(edi);
@@ -3215,7 +3390,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
const int kMaxInlineLength = 100;
Label slowcase;
- NearLabel done;
+ Label done;
__ mov(ebx, Operand(esp, kPointerSize * 3));
__ test(ebx, Immediate(kSmiTagMask));
__ j(not_zero, &slowcase);
@@ -3281,7 +3456,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
Label loop;
__ test(ecx, Operand(ecx));
__ bind(&loop);
- __ j(less_equal, &done); // Jump if ecx is negative or zero.
+ __ j(less_equal, &done, Label::kNear); // Jump if ecx is negative or zero.
__ sub(Operand(ecx), Immediate(1));
__ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
__ jmp(&loop);
@@ -3322,19 +3497,19 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
// number string cache for smis is just the smi value, and the hash for
// doubles is the xor of the upper and lower words. See
// Heap::GetNumberStringCache.
- NearLabel smi_hash_calculated;
- NearLabel load_result_from_cache;
+ Label smi_hash_calculated;
+ Label load_result_from_cache;
if (object_is_smi) {
__ mov(scratch, object);
__ SmiUntag(scratch);
} else {
- NearLabel not_smi, hash_calculated;
+ Label not_smi;
STATIC_ASSERT(kSmiTag == 0);
__ test(object, Immediate(kSmiTagMask));
- __ j(not_zero, &not_smi);
+ __ j(not_zero, &not_smi, Label::kNear);
__ mov(scratch, object);
__ SmiUntag(scratch);
- __ jmp(&smi_hash_calculated);
+ __ jmp(&smi_hash_calculated, Label::kNear);
__ bind(&not_smi);
__ cmp(FieldOperand(object, HeapObject::kMapOffset),
masm->isolate()->factory()->heap_number_map());
@@ -3365,7 +3540,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
}
__ j(parity_even, not_found); // Bail out if NaN is involved.
__ j(not_equal, not_found); // The cache did not contain this value.
- __ jmp(&load_result_from_cache);
+ __ jmp(&load_result_from_cache, Label::kNear);
}
__ bind(&smi_hash_calculated);
@@ -3425,7 +3600,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ mov(ecx, Operand(edx));
__ or_(ecx, Operand(eax));
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &non_smi, not_taken);
+ __ j(not_zero, &non_smi);
__ sub(edx, Operand(eax)); // Return on the result of the subtraction.
__ j(no_overflow, &smi_done);
__ not_(edx); // Correct sign in case of overflow. edx is never 0 here.
@@ -3453,9 +3628,9 @@ void CompareStub::Generate(MacroAssembler* masm) {
if (cc_ != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
- NearLabel check_for_nan;
+ Label check_for_nan;
__ cmp(edx, masm->isolate()->factory()->undefined_value());
- __ j(not_equal, &check_for_nan);
+ __ j(not_equal, &check_for_nan, Label::kNear);
__ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
__ ret(0);
__ bind(&check_for_nan);
@@ -3468,10 +3643,10 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
} else {
- NearLabel heap_number;
+ Label heap_number;
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(masm->isolate()->factory()->heap_number_map()));
- __ j(equal, &heap_number);
+ __ j(equal, &heap_number, Label::kNear);
if (cc_ != equal) {
// Call runtime on identical JSObjects. Otherwise return equal.
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
@@ -3503,8 +3678,8 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ setcc(above_equal, eax);
__ ret(0);
} else {
- NearLabel nan;
- __ j(above_equal, &nan);
+ Label nan;
+ __ j(above_equal, &nan, Label::kNear);
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
__ bind(&nan);
@@ -3520,7 +3695,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Non-strict object equality is slower, so it is handled later in the stub.
if (cc_ == equal && strict_) {
Label slow; // Fallthrough label.
- NearLabel not_smis;
+ Label not_smis;
// If we're doing a strict equality comparison, we don't have to do
// type conversion, so we generate code to do fast comparison for objects
// and oddballs. Non-smi numbers and strings still go through the usual
@@ -3532,7 +3707,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ mov(ecx, Immediate(kSmiTagMask));
__ and_(ecx, Operand(eax));
__ test(ecx, Operand(edx));
- __ j(not_zero, &not_smis);
+ __ j(not_zero, &not_smis, Label::kNear);
// One operand is a smi.
// Check whether the non-smi is a heap number.
@@ -3561,13 +3736,13 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Get the type of the first operand.
// If the first object is a JS object, we have done pointer comparison.
- NearLabel first_non_object;
+ Label first_non_object;
STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(below, &first_non_object);
+ __ j(below, &first_non_object, Label::kNear);
// Return non-zero (eax is not zero)
- NearLabel return_not_equal;
+ Label return_not_equal;
STATIC_ASSERT(kHeapObjectTag != 0);
__ bind(&return_not_equal);
__ ret(0);
@@ -3600,7 +3775,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ ucomisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered, not_taken);
+ __ j(parity_even, &unordered);
// Return a result of -1, 0, or 1, based on EFLAGS.
__ mov(eax, 0); // equal
__ mov(ecx, Immediate(Smi::FromInt(1)));
@@ -3616,12 +3791,12 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ FCmp();
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered, not_taken);
+ __ j(parity_even, &unordered);
- NearLabel below_label, above_label;
+ Label below_label, above_label;
// Return a result of -1, 0, or 1, based on EFLAGS.
- __ j(below, &below_label, not_taken);
- __ j(above, &above_label, not_taken);
+ __ j(below, &below_label);
+ __ j(above, &above_label);
__ Set(eax, Immediate(0));
__ ret(0);
@@ -3668,12 +3843,20 @@ void CompareStub::Generate(MacroAssembler* masm) {
&check_unequal_objects);
// Inline comparison of ascii strings.
- StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ if (cc_ == equal) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
edx,
eax,
ecx,
- ebx,
- edi);
+ ebx);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ edx,
+ eax,
+ ecx,
+ ebx,
+ edi);
+ }
#ifdef DEBUG
__ Abort("Unexpected fall-through from string comparison");
#endif
@@ -3683,8 +3866,8 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Non-strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
- NearLabel not_both_objects;
- NearLabel return_unequal;
+ Label not_both_objects;
+ Label return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
// a heap object has the low bit clear.
@@ -3692,20 +3875,20 @@ void CompareStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(kSmiTagMask == 1);
__ lea(ecx, Operand(eax, edx, times_1, 0));
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &not_both_objects);
+ __ j(not_zero, &not_both_objects, Label::kNear);
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(below, &not_both_objects);
+ __ j(below, &not_both_objects, Label::kNear);
__ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, ebx);
- __ j(below, &not_both_objects);
+ __ j(below, &not_both_objects, Label::kNear);
// We do not bail out after this point. Both are JSObjects, and
// they are equal if and only if both are undetectable.
// The and of the undetectable flags is 1 if and only if they are equal.
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
1 << Map::kIsUndetectable);
- __ j(zero, &return_unequal);
+ __ j(zero, &return_unequal, Label::kNear);
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
1 << Map::kIsUndetectable);
- __ j(zero, &return_unequal);
+ __ j(zero, &return_unequal, Label::kNear);
// The objects are both undetectable, so they both compare as the value
// undefined, and are equal.
__ Set(eax, Immediate(EQUAL));
@@ -3761,31 +3944,22 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow;
- // If the receiver might be a value (string, number or boolean) check for this
- // and box it if it is.
- if (ReceiverMightBeValue()) {
+ // The receiver might implicitly be the global object. This is
+ // indicated by passing the hole as the receiver to the call
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
// Get the receiver from the stack.
// +1 ~ return address
- Label receiver_is_value, receiver_is_js_object;
__ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
-
- // Check if receiver is a smi (which is a number value).
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &receiver_is_value, not_taken);
-
- // Check if the receiver is a valid JS object.
- __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, edi);
- __ j(above_equal, &receiver_is_js_object);
-
- // Call the runtime to box the value.
- __ bind(&receiver_is_value);
- __ EnterInternalFrame();
- __ push(eax);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
- __ LeaveInternalFrame();
- __ mov(Operand(esp, (argc_ + 1) * kPointerSize), eax);
-
- __ bind(&receiver_is_js_object);
+ // Call as function is indicated with the hole.
+ __ cmp(eax, masm->isolate()->factory()->the_hole_value());
+ __ j(not_equal, &call, Label::kNear);
+ // Patch the receiver on the stack with the global receiver object.
+ __ mov(ebx, GlobalObjectOperand());
+ __ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ebx);
+ __ bind(&call);
}
// Get the function to call from the stack.
@@ -3794,14 +3968,30 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Check that the function really is a JavaScript function.
__ test(edi, Immediate(kSmiTagMask));
- __ j(zero, &slow, not_taken);
+ __ j(zero, &slow);
// Goto slow case if we do not have a function.
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &slow, not_taken);
+ __ j(not_equal, &slow);
// Fast-case: Just invoke the function.
ParameterCount actual(argc_);
- __ InvokeFunction(edi, actual, JUMP_FUNCTION);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ cmp(eax, masm->isolate()->factory()->the_hole_value());
+ __ j(equal, &call_as_function);
+ __ InvokeFunction(edi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(edi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
// Slow-case: Non-function called.
__ bind(&slow);
@@ -3878,9 +4068,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
// Make sure we're not trying to return 'the hole' from the runtime
// call as this may lead to crashes in the IC code later.
if (FLAG_debug_code) {
- NearLabel okay;
+ Label okay;
__ cmp(eax, masm->isolate()->factory()->the_hole_value());
- __ j(not_equal, &okay);
+ __ j(not_equal, &okay, Label::kNear);
__ int3();
__ bind(&okay);
}
@@ -3891,7 +4081,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ lea(ecx, Operand(eax, 1));
// Lower 2 bits of ecx are 0 iff eax has failure tag.
__ test(ecx, Immediate(kFailureTagMask));
- __ j(zero, &failure_returned, not_taken);
+ __ j(zero, &failure_returned);
ExternalReference pending_exception_address(
Isolate::k_pending_exception_address, masm->isolate());
@@ -3902,10 +4092,10 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ push(edx);
__ mov(edx, Operand::StaticVariable(
ExternalReference::the_hole_value_location(masm->isolate())));
- NearLabel okay;
+ Label okay;
__ cmp(edx, Operand::StaticVariable(pending_exception_address));
// Cannot use check here as it attempts to generate call into runtime.
- __ j(equal, &okay);
+ __ j(equal, &okay, Label::kNear);
__ int3();
__ bind(&okay);
__ pop(edx);
@@ -3922,7 +4112,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
// If the returned exception is RETRY_AFTER_GC continue at retry label
STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
__ test(eax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
- __ j(zero, &retry, taken);
+ __ j(zero, &retry);
// Special handling of out of memory exceptions.
__ cmp(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException()));
@@ -4178,22 +4368,22 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// Check that the left hand is a JS object.
__ test(object, Immediate(kSmiTagMask));
- __ j(zero, &not_js_object, not_taken);
+ __ j(zero, &not_js_object);
__ IsObjectJSObjectType(object, map, scratch, &not_js_object);
// If there is a call site cache don't look in the global cache, but do the
// real lookup and update the call site cache.
if (!HasCallSiteInlineCheck()) {
// Look up the function and the map in the instanceof cache.
- NearLabel miss;
+ Label miss;
__ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
__ cmp(function,
Operand::StaticArray(scratch, times_pointer_size, roots_address));
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
__ cmp(map, Operand::StaticArray(
scratch, times_pointer_size, roots_address));
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
__ mov(eax, Operand::StaticArray(
scratch, times_pointer_size, roots_address));
@@ -4206,7 +4396,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// Check that the function prototype is a JS object.
__ test(prototype, Immediate(kSmiTagMask));
- __ j(zero, &slow, not_taken);
+ __ j(zero, &slow);
__ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
// Update the global instanceof or call site inlined cache with the current
@@ -4236,13 +4426,13 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// Loop through the prototype chain of the object looking for the function
// prototype.
__ mov(scratch, FieldOperand(map, Map::kPrototypeOffset));
- NearLabel loop, is_instance, is_not_instance;
+ Label loop, is_instance, is_not_instance;
__ bind(&loop);
__ cmp(scratch, Operand(prototype));
- __ j(equal, &is_instance);
+ __ j(equal, &is_instance, Label::kNear);
Factory* factory = masm->isolate()->factory();
__ cmp(Operand(scratch), Immediate(factory->null_value()));
- __ j(equal, &is_not_instance);
+ __ j(equal, &is_not_instance, Label::kNear);
__ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
__ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
__ jmp(&loop);
@@ -4296,9 +4486,9 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// Before null, smi and string value checks, check that the rhs is a function
// as for a non-function rhs an exception needs to be thrown.
__ test(function, Immediate(kSmiTagMask));
- __ j(zero, &slow, not_taken);
+ __ j(zero, &slow);
__ CmpObjectType(function, JS_FUNCTION_TYPE, scratch);
- __ j(not_equal, &slow, not_taken);
+ __ j(not_equal, &slow);
// Null is not instance of anything.
__ cmp(object, factory->null_value());
@@ -4309,7 +4499,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ bind(&object_not_null);
// Smi values is not instance of anything.
__ test(object, Immediate(kSmiTagMask));
- __ j(not_zero, &object_not_null_or_smi, not_taken);
+ __ j(not_zero, &object_not_null_or_smi);
__ Set(eax, Immediate(Smi::FromInt(1)));
__ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
@@ -4339,11 +4529,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ push(function);
__ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
__ LeaveInternalFrame();
- NearLabel true_value, done;
+ Label true_value, done;
__ test(eax, Operand(eax));
- __ j(zero, &true_value);
+ __ j(zero, &true_value, Label::kNear);
__ mov(eax, factory->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ mov(eax, factory->true_value());
__ bind(&done);
@@ -4522,7 +4712,7 @@ void StringCharCodeAtGenerator::GenerateSlow(
__ CheckMap(index_,
masm->isolate()->factory()->heap_number_map(),
index_not_number_,
- true);
+ DONT_DO_SMI_CHECK);
call_helper.BeforeCall(masm);
__ push(object_);
__ push(index_);
@@ -4581,7 +4771,7 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
__ test(code_,
Immediate(kSmiTagMask |
((~String::kMaxAsciiCharCode) << kSmiTagSize)));
- __ j(not_zero, &slow_case_, not_taken);
+ __ j(not_zero, &slow_case_);
Factory* factory = masm->isolate()->factory();
__ Set(result_, Immediate(factory->single_character_string_cache()));
@@ -4593,7 +4783,7 @@ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
code_, times_half_pointer_size,
FixedArray::kHeaderSize));
__ cmp(result_, factory->undefined_value());
- __ j(equal, &slow_case_, not_taken);
+ __ j(equal, &slow_case_);
__ bind(&exit_);
}
@@ -4672,11 +4862,11 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// eax: first string
// edx: second string
// Check if either of the strings are empty. In that case return the other.
- NearLabel second_not_zero_length, both_not_zero_length;
+ Label second_not_zero_length, both_not_zero_length;
__ mov(ecx, FieldOperand(edx, String::kLengthOffset));
STATIC_ASSERT(kSmiTag == 0);
__ test(ecx, Operand(ecx));
- __ j(not_zero, &second_not_zero_length);
+ __ j(not_zero, &second_not_zero_length, Label::kNear);
// Second string is empty, result is first string which is already in eax.
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->string_add_native(), 1);
@@ -4685,7 +4875,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ mov(ebx, FieldOperand(eax, String::kLengthOffset));
STATIC_ASSERT(kSmiTag == 0);
__ test(ebx, Operand(ebx));
- __ j(not_zero, &both_not_zero_length);
+ __ j(not_zero, &both_not_zero_length, Label::kNear);
// First string is empty, result is second string which is in edx.
__ mov(eax, edx);
__ IncrementCounter(counters->string_add_native(), 1);
@@ -4959,7 +5149,7 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
Register count,
Register scratch,
bool ascii) {
- NearLabel loop;
+ Label loop;
__ bind(&loop);
// This loop just copies one character at a time, as it is only used for very
// short strings.
@@ -5006,9 +5196,9 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
}
// Don't enter the rep movs if there are less than 4 bytes to copy.
- NearLabel last_bytes;
+ Label last_bytes;
__ test(count, Immediate(~3));
- __ j(zero, &last_bytes);
+ __ j(zero, &last_bytes, Label::kNear);
// Copy from edi to esi using rep movs instruction.
__ mov(scratch, count);
@@ -5026,7 +5216,7 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
__ j(zero, &done);
// Copy remaining characters.
- NearLabel loop;
+ Label loop;
__ bind(&loop);
__ mov_b(scratch, Operand(src, 0));
__ mov_b(Operand(dest, 0), scratch);
@@ -5052,11 +5242,11 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
// Make sure that both characters are not digits as such strings has a
// different hash algorithm. Don't try to look for these in the symbol table.
- NearLabel not_array_index;
+ Label not_array_index;
__ mov(scratch, c1);
__ sub(Operand(scratch), Immediate(static_cast<int>('0')));
__ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
- __ j(above, &not_array_index);
+ __ j(above, &not_array_index, Label::kNear);
__ mov(scratch, c2);
__ sub(Operand(scratch), Immediate(static_cast<int>('0')));
__ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
@@ -5214,9 +5404,9 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
__ add(hash, Operand(scratch));
// if (hash == 0) hash = 27;
- NearLabel hash_not_zero;
+ Label hash_not_zero;
__ test(hash, Operand(hash));
- __ j(not_zero, &hash_not_zero);
+ __ j(not_zero, &hash_not_zero, Label::kNear);
__ mov(hash, Immediate(27));
__ bind(&hash_not_zero);
}
@@ -5371,28 +5561,60 @@ void SubStringStub::Generate(MacroAssembler* masm) {
}
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label strings_not_equal, check_zero_length;
+ __ mov(length, FieldOperand(left, String::kLengthOffset));
+ __ cmp(length, FieldOperand(right, String::kLengthOffset));
+ __ j(equal, &check_zero_length, Label::kNear);
+ __ bind(&strings_not_equal);
+ __ Set(eax, Immediate(Smi::FromInt(NOT_EQUAL)));
+ __ ret(0);
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ test(length, Operand(length));
+ __ j(not_zero, &compare_chars, Label::kNear);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
+ &strings_not_equal, Label::kNear);
+
+ // Characters are equal.
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+}
+
+
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
Register scratch1,
Register scratch2,
Register scratch3) {
- Label result_not_equal;
- Label result_greater;
- Label compare_lengths;
-
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->string_compare_native(), 1);
// Find minimum length.
- NearLabel left_shorter;
+ Label left_shorter;
__ mov(scratch1, FieldOperand(left, String::kLengthOffset));
__ mov(scratch3, scratch1);
__ sub(scratch3, FieldOperand(right, String::kLengthOffset));
Register length_delta = scratch3;
- __ j(less_equal, &left_shorter);
+ __ j(less_equal, &left_shorter, Label::kNear);
// Right string is shorter. Change scratch1 to be length of right string.
__ sub(scratch1, Operand(length_delta));
__ bind(&left_shorter);
@@ -5400,41 +5622,19 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register min_length = scratch1;
// If either length is zero, just compare lengths.
+ Label compare_lengths;
__ test(min_length, Operand(min_length));
- __ j(zero, &compare_lengths);
+ __ j(zero, &compare_lengths, Label::kNear);
- // Change index to run from -min_length to -1 by adding min_length
- // to string start. This means that loop ends when index reaches zero,
- // which doesn't need an additional compare.
- __ SmiUntag(min_length);
- __ lea(left,
- FieldOperand(left,
- min_length, times_1,
- SeqAsciiString::kHeaderSize));
- __ lea(right,
- FieldOperand(right,
- min_length, times_1,
- SeqAsciiString::kHeaderSize));
- __ neg(min_length);
-
- Register index = min_length; // index = -min_length;
-
- {
- // Compare loop.
- NearLabel loop;
- __ bind(&loop);
- // Compare characters.
- __ mov_b(scratch2, Operand(left, index, times_1, 0));
- __ cmpb(scratch2, Operand(right, index, times_1, 0));
- __ j(not_equal, &result_not_equal);
- __ add(Operand(index), Immediate(1));
- __ j(not_zero, &loop);
- }
+ // Compare characters.
+ Label result_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
+ &result_not_equal, Label::kNear);
// Compare lengths - strings up to min-length are equal.
__ bind(&compare_lengths);
__ test(length_delta, Operand(length_delta));
- __ j(not_zero, &result_not_equal);
+ __ j(not_zero, &result_not_equal, Label::kNear);
// Result is EQUAL.
STATIC_ASSERT(EQUAL == 0);
@@ -5442,8 +5642,9 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
__ ret(0);
+ Label result_greater;
__ bind(&result_not_equal);
- __ j(greater, &result_greater);
+ __ j(greater, &result_greater, Label::kNear);
// Result is LESS.
__ Set(eax, Immediate(Smi::FromInt(LESS)));
@@ -5456,6 +5657,36 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
}
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance chars_not_equal_near) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiUntag(length);
+ __ lea(left,
+ FieldOperand(left, length, times_1, SeqAsciiString::kHeaderSize));
+ __ lea(right,
+ FieldOperand(right, length, times_1, SeqAsciiString::kHeaderSize));
+ __ neg(length);
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ mov_b(scratch, Operand(left, index, times_1, 0));
+ __ cmpb(scratch, Operand(right, index, times_1, 0));
+ __ j(not_equal, chars_not_equal, chars_not_equal_near);
+ __ add(Operand(index), Immediate(1));
+ __ j(not_zero, &loop);
+}
+
+
void StringCompareStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -5467,9 +5698,9 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
__ mov(edx, Operand(esp, 2 * kPointerSize)); // left
__ mov(eax, Operand(esp, 1 * kPointerSize)); // right
- NearLabel not_same;
+ Label not_same;
__ cmp(edx, Operand(eax));
- __ j(not_equal, &not_same);
+ __ j(not_equal, &not_same, Label::kNear);
STATIC_ASSERT(EQUAL == 0);
STATIC_ASSERT(kSmiTag == 0);
__ Set(eax, Immediate(Smi::FromInt(EQUAL)));
@@ -5497,19 +5728,19 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::SMIS);
- NearLabel miss;
+ Label miss;
__ mov(ecx, Operand(edx));
__ or_(ecx, Operand(eax));
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &miss, not_taken);
+ __ j(not_zero, &miss, Label::kNear);
if (GetCondition() == equal) {
// For equality we do not care about the sign of the result.
__ sub(eax, Operand(edx));
} else {
- NearLabel done;
+ Label done;
__ sub(edx, Operand(eax));
- __ j(no_overflow, &done);
+ __ j(no_overflow, &done, Label::kNear);
// Correct sign of result in case of overflow.
__ not_(edx);
__ bind(&done);
@@ -5525,18 +5756,18 @@ void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::HEAP_NUMBERS);
- NearLabel generic_stub;
- NearLabel unordered;
- NearLabel miss;
+ Label generic_stub;
+ Label unordered;
+ Label miss;
__ mov(ecx, Operand(edx));
__ and_(ecx, Operand(eax));
__ test(ecx, Immediate(kSmiTagMask));
- __ j(zero, &generic_stub, not_taken);
+ __ j(zero, &generic_stub, Label::kNear);
__ CmpObjectType(eax, HEAP_NUMBER_TYPE, ecx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
__ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
// Inlining the double comparison and falling back to the general compare
// stub if NaN is involved or SS2 or CMOV is unsupported.
@@ -5552,7 +5783,7 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
__ ucomisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered, not_taken);
+ __ j(parity_even, &unordered, Label::kNear);
// Return a result of -1, 0, or 1, based on EFLAGS.
// Performing mov, because xor would destroy the flag register.
@@ -5575,18 +5806,141 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
}
+void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SYMBOLS);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+ Register tmp1 = ecx;
+ Register tmp2 = ebx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ __ mov(tmp1, Operand(left));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ and_(tmp1, Operand(right));
+ __ test(tmp1, Immediate(kSmiTagMask));
+ __ j(zero, &miss, Label::kNear);
+
+ // Check that both operands are symbols.
+ __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, Operand(tmp2));
+ __ test(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &miss, Label::kNear);
+
+ // Symbols are compared by identity.
+ Label done;
+ __ cmp(left, Operand(right));
+ // Make sure eax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(eax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRINGS);
+ ASSERT(GetCondition() == equal);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = edx;
+ Register right = eax;
+ Register tmp1 = ecx;
+ Register tmp2 = ebx;
+ Register tmp3 = edi;
+
+ // Check that both operands are heap objects.
+ __ mov(tmp1, Operand(left));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ and_(tmp1, Operand(right));
+ __ test(tmp1, Immediate(kSmiTagMask));
+ __ j(zero, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ __ mov(tmp3, tmp1);
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ or_(tmp3, Operand(tmp2));
+ __ test(tmp3, Immediate(kIsNotStringMask));
+ __ j(not_zero, &miss);
+
+ // Fast check for identical strings.
+ Label not_same;
+ __ cmp(left, Operand(right));
+ __ j(not_equal, &not_same, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
+ // Handle not identical strings.
+ __ bind(&not_same);
+
+ // Check that both strings are symbols. If they are, we're done
+ // because we already know they are not identical.
+ Label do_compare;
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, Operand(tmp2));
+ __ test(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &do_compare, Label::kNear);
+ // Make sure eax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(eax));
+ __ ret(0);
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ bind(&do_compare);
+ __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ pop(tmp1); // Return address.
+ __ push(left);
+ __ push(right);
+ __ push(tmp1);
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::OBJECTS);
- NearLabel miss;
+ Label miss;
__ mov(ecx, Operand(edx));
__ and_(ecx, Operand(eax));
__ test(ecx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss, Label::kNear);
__ CmpObjectType(eax, JS_OBJECT_TYPE, ecx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
__ CmpObjectType(edx, JS_OBJECT_TYPE, ecx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
ASSERT(GetCondition() == equal);
__ sub(eax, Operand(edx));
@@ -5628,6 +5982,218 @@ void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
}
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// must always call a backup property check that is complete.
+// This function is safe to call if the receiver has fast properties.
+// Name must be a symbol and receiver must be a heap object.
+MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ String* name,
+ Register r0) {
+ ASSERT(name->IsSymbol());
+
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = r0;
+ // Capacity is smi 2^n.
+ __ mov(index, FieldOperand(properties, kCapacityOffset));
+ __ dec(index);
+ __ and_(Operand(index),
+ Immediate(Smi::FromInt(name->Hash() +
+ StringDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
+ Register entity_name = r0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
+ __ j(equal, done);
+
+ // Stop if found the property.
+ __ cmp(entity_name, Handle<String>(name));
+ __ j(equal, miss);
+
+ // Check if the entry name is not a symbol.
+ __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(entity_name, Map::kInstanceTypeOffset),
+ kIsSymbolMask);
+ __ j(zero, miss);
+ }
+
+ StringDictionaryLookupStub stub(properties,
+ r0,
+ r0,
+ StringDictionaryLookupStub::NEGATIVE_LOOKUP);
+ __ push(Immediate(Handle<Object>(name)));
+ __ push(Immediate(name->Hash()));
+ MaybeObject* result = masm->TryCallStub(&stub);
+ if (result->IsFailure()) return result;
+ __ test(r0, Operand(r0));
+ __ j(not_zero, miss);
+ __ jmp(done);
+ return result;
+}
+
+
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r0|. Jump to the |miss| label
+// otherwise.
+void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
+ // Assert that name contains a string.
+ if (FLAG_debug_code) __ AbortIfNotString(name);
+
+ __ mov(r1, FieldOperand(elements, kCapacityOffset));
+ __ shr(r1, kSmiTagSize); // convert smi to int
+ __ dec(r1);
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up. Measurements done on Gmail indicate that 2 probes
+ // cover ~93% of loads from dictionaries.
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ mov(r0, FieldOperand(name, String::kHashFieldOffset));
+ __ shr(r0, String::kHashShift);
+ if (i > 0) {
+ __ add(Operand(r0), Immediate(StringDictionary::GetProbeOffset(i)));
+ }
+ __ and_(r0, Operand(r1));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
+
+ // Check if the key is identical to the name.
+ __ cmp(name, Operand(elements,
+ r0,
+ times_4,
+ kElementsStartOffset - kHeapObjectTag));
+ __ j(equal, done);
+ }
+
+ StringDictionaryLookupStub stub(elements,
+ r1,
+ r0,
+ POSITIVE_LOOKUP);
+ __ push(name);
+ __ mov(r0, FieldOperand(name, String::kHashFieldOffset));
+ __ shr(r0, String::kHashShift);
+ __ push(r0);
+ __ CallStub(&stub);
+
+ __ test(r1, Operand(r1));
+ __ j(zero, miss);
+ __ jmp(done);
+}
+
+
+void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
+ // Stack frame on entry:
+ // esp[0 * kPointerSize]: return address.
+ // esp[1 * kPointerSize]: key's hash.
+ // esp[2 * kPointerSize]: key.
+ // Registers:
+ // dictionary_: StringDictionary to probe.
+ // result_: used as scratch.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ Register scratch = result_;
+
+ __ mov(scratch, FieldOperand(dictionary_, kCapacityOffset));
+ __ dec(scratch);
+ __ SmiUntag(scratch);
+ __ push(scratch);
+
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ mov(scratch, Operand(esp, 2 * kPointerSize));
+ if (i > 0) {
+ __ add(Operand(scratch),
+ Immediate(StringDictionary::GetProbeOffset(i)));
+ }
+ __ and_(scratch, Operand(esp, 0));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
+
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ mov(scratch, Operand(dictionary_,
+ index_,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ cmp(scratch, masm->isolate()->factory()->undefined_value());
+ __ j(equal, &not_in_dictionary);
+
+ // Stop if found the property.
+ __ cmp(scratch, Operand(esp, 3 * kPointerSize));
+ __ j(equal, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // If we hit a non symbol key during negative lookup
+ // we have to bailout as this key might be equal to the
+ // key we are looking for.
+
+ // Check if the entry name is not a symbol.
+ __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(scratch, Map::kInstanceTypeOffset),
+ kIsSymbolMask);
+ __ j(zero, &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ mov(result_, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+ }
+
+ __ bind(&in_dictionary);
+ __ mov(result_, Immediate(1));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&not_in_dictionary);
+ __ mov(result_, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+}
+
+
#undef __
} } // namespace v8::internal
diff --git a/src/ia32/code-stubs-ia32.h b/src/ia32/code-stubs-ia32.h
index 80a75cd8..ead7761f 100644
--- a/src/ia32/code-stubs-ia32.h
+++ b/src/ia32/code-stubs-ia32.h
@@ -72,22 +72,115 @@ class ToBooleanStub: public CodeStub {
};
-class TypeRecordingBinaryOpStub: public CodeStub {
+class UnaryOpStub: public CodeStub {
public:
- TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
+ UnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
- operands_type_(TRBinaryOpIC::UNINITIALIZED),
- result_type_(TRBinaryOpIC::UNINITIALIZED),
+ operand_type_(UnaryOpIC::UNINITIALIZED),
+ name_(NULL) {
+ }
+
+ UnaryOpStub(int key, UnaryOpIC::TypeInfo operand_type)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ operand_type_(operand_type),
+ name_(NULL) {
+ }
+
+ private:
+ Token::Value op_;
+ UnaryOverwriteMode mode_;
+
+ // Operand type information determined at runtime.
+ UnaryOpIC::TypeInfo operand_type_;
+
+ char* name_;
+
+ const char* GetName();
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("TypeRecordingUnaryOpStub %d (op %s), "
+ "(mode %d, runtime_type_info %s)\n",
+ MinorKey(),
+ Token::String(op_),
+ static_cast<int>(mode_),
+ UnaryOpIC::GetName(operand_type_));
+ }
+#endif
+
+ class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
+ class OpBits: public BitField<Token::Value, 1, 7> {};
+ class OperandTypeInfoBits: public BitField<UnaryOpIC::TypeInfo, 8, 3> {};
+
+ Major MajorKey() { return UnaryOp; }
+ int MinorKey() {
+ return ModeBits::encode(mode_)
+ | OpBits::encode(op_)
+ | OperandTypeInfoBits::encode(operand_type_);
+ }
+
+ // Note: A lot of the helper functions below will vanish when we use virtual
+ // function instead of switch more often.
+ void Generate(MacroAssembler* masm);
+
+ void GenerateTypeTransition(MacroAssembler* masm);
+
+ void GenerateSmiStub(MacroAssembler* masm);
+ void GenerateSmiStubSub(MacroAssembler* masm);
+ void GenerateSmiStubBitNot(MacroAssembler* masm);
+ void GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* undo,
+ Label* slow,
+ Label::Distance non_smi_near = Label::kFar,
+ Label::Distance undo_near = Label::kFar,
+ Label::Distance slow_near = Label::kFar);
+ void GenerateSmiCodeBitNot(MacroAssembler* masm,
+ Label* non_smi,
+ Label::Distance non_smi_near = Label::kFar);
+ void GenerateSmiCodeUndo(MacroAssembler* masm);
+
+ void GenerateHeapNumberStub(MacroAssembler* masm);
+ void GenerateHeapNumberStubSub(MacroAssembler* masm);
+ void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
+ void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
+ void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
+
+ void GenerateGenericStub(MacroAssembler* masm);
+ void GenerateGenericStubSub(MacroAssembler* masm);
+ void GenerateGenericStubBitNot(MacroAssembler* masm);
+ void GenerateGenericCodeFallback(MacroAssembler* masm);
+
+ virtual int GetCodeKind() { return Code::UNARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return UnaryOpIC::ToState(operand_type_);
+ }
+
+ virtual void FinishCode(Code* code) {
+ code->set_unary_op_type(operand_type_);
+ }
+};
+
+
+class BinaryOpStub: public CodeStub {
+ public:
+ BinaryOpStub(Token::Value op, OverwriteMode mode)
+ : op_(op),
+ mode_(mode),
+ operands_type_(BinaryOpIC::UNINITIALIZED),
+ result_type_(BinaryOpIC::UNINITIALIZED),
name_(NULL) {
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
- TypeRecordingBinaryOpStub(
+ BinaryOpStub(
int key,
- TRBinaryOpIC::TypeInfo operands_type,
- TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED)
+ BinaryOpIC::TypeInfo operands_type,
+ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
use_sse3_(SSE3Bits::decode(key)),
@@ -106,8 +199,8 @@ class TypeRecordingBinaryOpStub: public CodeStub {
bool use_sse3_;
// Operand type information determined at runtime.
- TRBinaryOpIC::TypeInfo operands_type_;
- TRBinaryOpIC::TypeInfo result_type_;
+ BinaryOpIC::TypeInfo operands_type_;
+ BinaryOpIC::TypeInfo result_type_;
char* name_;
@@ -115,12 +208,12 @@ class TypeRecordingBinaryOpStub: public CodeStub {
#ifdef DEBUG
void Print() {
- PrintF("TypeRecordingBinaryOpStub %d (op %s), "
+ PrintF("BinaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
}
#endif
@@ -128,10 +221,10 @@ class TypeRecordingBinaryOpStub: public CodeStub {
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
class SSE3Bits: public BitField<bool, 9, 1> {};
- class OperandTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 10, 3> {};
- class ResultTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 13, 3> {};
+ class OperandTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 10, 3> {};
+ class ResultTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 13, 3> {};
- Major MajorKey() { return TypeRecordingBinaryOp; }
+ Major MajorKey() { return BinaryOp; }
int MinorKey() {
return OpBits::encode(op_)
| ModeBits::encode(mode_)
@@ -153,6 +246,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
@@ -161,15 +255,15 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
- virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; }
+ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
virtual InlineCacheState GetICState() {
- return TRBinaryOpIC::ToState(operands_type_);
+ return BinaryOpIC::ToState(operands_type_);
}
virtual void FinishCode(Code* code) {
- code->set_type_recording_binary_op_type(operands_type_);
- code->set_type_recording_binary_op_result_type(result_type_);
+ code->set_binary_op_type(operands_type_);
+ code->set_binary_op_result_type(result_type_);
}
friend class CodeGenerator;
@@ -283,11 +377,9 @@ class SubStringStub: public CodeStub {
class StringCompareStub: public CodeStub {
public:
- explicit StringCompareStub() {
- }
+ StringCompareStub() { }
- // Compare two flat ascii strings and returns result in eax after popping two
- // arguments from the stack.
+ // Compares two flat ASCII strings and returns result in eax.
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -295,11 +387,27 @@ class StringCompareStub: public CodeStub {
Register scratch2,
Register scratch3);
- private:
- Major MajorKey() { return StringCompare; }
- int MinorKey() { return 0; }
+ // Compares two flat ASCII strings for equality and returns result
+ // in eax.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2);
- void Generate(MacroAssembler* masm);
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance chars_not_equal_near = Label::kFar);
};
@@ -335,6 +443,75 @@ class NumberToStringStub: public CodeStub {
#endif
};
+
+class StringDictionaryLookupStub: public CodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ StringDictionaryLookupStub(Register dictionary,
+ Register result,
+ Register index,
+ LookupMode mode)
+ : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ String* name,
+ Register r0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("StringDictionaryLookupStub\n");
+ }
+#endif
+
+ Major MajorKey() { return StringDictionaryNegativeLookup; }
+
+ int MinorKey() {
+ return DictionaryBits::encode(dictionary_.code()) |
+ ResultBits::encode(result_.code()) |
+ IndexBits::encode(index_.code()) |
+ LookupModeBits::encode(mode_);
+ }
+
+ class DictionaryBits: public BitField<int, 0, 3> {};
+ class ResultBits: public BitField<int, 3, 3> {};
+ class IndexBits: public BitField<int, 6, 3> {};
+ class LookupModeBits: public BitField<LookupMode, 9, 1> {};
+
+ Register dictionary_;
+ Register result_;
+ Register index_;
+ LookupMode mode_;
+};
+
+
} } // namespace v8::internal
#endif // V8_IA32_CODE_STUBS_IA32_H_
diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc
index d1c869a2..7a59a4f6 100644
--- a/src/ia32/disasm-ia32.cc
+++ b/src/ia32/disasm-ia32.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -981,6 +981,14 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
NameOfXMMRegister(regop),
NameOfXMMRegister(rm));
data++;
+ } else if (f0byte == 0x57) {
+ data += 2;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, &regop, &rm);
+ AppendToBuffer("xorps %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else if ((f0byte & 0xF0) == 0x80) {
data += JumpConditional(data, branch_hint);
} else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
diff --git a/src/ia32/frames-ia32.h b/src/ia32/frames-ia32.h
index 0f95abd8..bc65ddfa 100644
--- a/src/ia32/frames-ia32.h
+++ b/src/ia32/frames-ia32.h
@@ -80,8 +80,8 @@ class EntryFrameConstants : public AllStatic {
class ExitFrameConstants : public AllStatic {
public:
- static const int kCodeOffset = -2 * kPointerSize;
- static const int kSPOffset = -1 * kPointerSize;
+ static const int kCodeOffset = -2 * kPointerSize;
+ static const int kSPOffset = -1 * kPointerSize;
static const int kCallerFPOffset = 0 * kPointerSize;
static const int kCallerPCOffset = +1 * kPointerSize;
@@ -94,7 +94,9 @@ class ExitFrameConstants : public AllStatic {
class StandardFrameConstants : public AllStatic {
public:
- static const int kFixedFrameSize = 4;
+ // StandardFrame::IterateExpressions assumes that kContextOffset is the last
+ // object pointer.
+ static const int kFixedFrameSize = 4; // Currently unused.
static const int kExpressionsOffset = -3 * kPointerSize;
static const int kMarkerOffset = -2 * kPointerSize;
static const int kContextOffset = -1 * kPointerSize;
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 5d153a81..5f0a0b6a 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -45,6 +45,12 @@ namespace internal {
#define __ ACCESS_MASM(masm_)
+static unsigned GetPropertyId(Property* property) {
+ if (property->is_synthetic()) return AstNode::kNoNumber;
+ return property->id();
+}
+
+
class JumpPatchSite BASE_EMBEDDED {
public:
explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) {
@@ -57,14 +63,18 @@ class JumpPatchSite BASE_EMBEDDED {
ASSERT(patch_site_.is_bound() == info_emitted_);
}
- void EmitJumpIfNotSmi(Register reg, NearLabel* target) {
+ void EmitJumpIfNotSmi(Register reg,
+ Label* target,
+ Label::Distance distance = Label::kFar) {
__ test(reg, Immediate(kSmiTagMask));
- EmitJump(not_carry, target); // Always taken before patched.
+ EmitJump(not_carry, target, distance); // Always taken before patched.
}
- void EmitJumpIfSmi(Register reg, NearLabel* target) {
+ void EmitJumpIfSmi(Register reg,
+ Label* target,
+ Label::Distance distance = Label::kFar) {
__ test(reg, Immediate(kSmiTagMask));
- EmitJump(carry, target); // Never taken before patched.
+ EmitJump(carry, target, distance); // Never taken before patched.
}
void EmitPatchInfo() {
@@ -80,11 +90,11 @@ class JumpPatchSite BASE_EMBEDDED {
private:
// jc will be patched with jz, jnc will become jnz.
- void EmitJump(Condition cc, NearLabel* target) {
+ void EmitJump(Condition cc, Label* target, Label::Distance distance) {
ASSERT(!patch_site_.is_bound() && !info_emitted_);
ASSERT(cc == carry || cc == not_carry);
__ bind(&patch_site_);
- __ j(cc, target);
+ __ j(cc, target, distance);
}
MacroAssembler* masm_;
@@ -121,6 +131,21 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
}
#endif
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). ecx is zero for method calls and non-zero for function
+ // calls.
+ if (info->is_strict_mode()) {
+ Label ok;
+ __ test(ecx, Operand(ecx));
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
+ __ mov(Operand(esp, receiver_offset),
+ Immediate(isolate()->factory()->undefined_value()));
+ __ bind(&ok);
+ }
+
__ push(ebp); // Caller's frame pointer.
__ mov(ebp, esp);
__ push(esi); // Callee's context.
@@ -232,11 +257,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
{ Comment cmnt(masm_, "[ Stack check");
PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS);
- NearLabel ok;
+ Label ok;
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
- __ j(above_equal, &ok, taken);
+ __ j(above_equal, &ok, Label::kNear);
StackCheckStub stub;
__ CallStub(&stub);
__ bind(&ok);
@@ -265,11 +290,11 @@ void FullCodeGenerator::ClearAccumulator() {
void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) {
Comment cmnt(masm_, "[ Stack check");
- NearLabel ok;
+ Label ok;
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
- __ j(above_equal, &ok, taken);
+ __ j(above_equal, &ok, Label::kNear);
StackCheckStub stub;
__ CallStub(&stub);
// Record a mapping of this PC offset to the OSR id. This is used to find
@@ -473,10 +498,10 @@ void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
void FullCodeGenerator::AccumulatorValueContext::Plug(
Label* materialize_true,
Label* materialize_false) const {
- NearLabel done;
+ Label done;
__ bind(materialize_true);
__ mov(result_register(), isolate()->factory()->true_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(materialize_false);
__ mov(result_register(), isolate()->factory()->false_value());
__ bind(&done);
@@ -486,10 +511,10 @@ void FullCodeGenerator::AccumulatorValueContext::Plug(
void FullCodeGenerator::StackValueContext::Plug(
Label* materialize_true,
Label* materialize_false) const {
- NearLabel done;
+ Label done;
__ bind(materialize_true);
__ push(Immediate(isolate()->factory()->true_value()));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(materialize_false);
__ push(Immediate(isolate()->factory()->false_value()));
__ bind(&done);
@@ -539,25 +564,10 @@ void FullCodeGenerator::TestContext::Plug(bool flag) const {
void FullCodeGenerator::DoTest(Label* if_true,
Label* if_false,
Label* fall_through) {
- // Emit the inlined tests assumed by the stub.
- __ cmp(result_register(), isolate()->factory()->undefined_value());
- __ j(equal, if_false);
- __ cmp(result_register(), isolate()->factory()->true_value());
- __ j(equal, if_true);
- __ cmp(result_register(), isolate()->factory()->false_value());
- __ j(equal, if_false);
- STATIC_ASSERT(kSmiTag == 0);
- __ test(result_register(), Operand(result_register()));
- __ j(zero, if_false);
- __ test(result_register(), Immediate(kSmiTagMask));
- __ j(zero, if_true);
-
- // Call the ToBoolean stub for all other cases.
ToBooleanStub stub;
__ push(result_register());
__ CallStub(&stub);
__ test(eax, Operand(eax));
-
// The stub returns nonzero for true.
Split(not_zero, if_true, if_false, fall_through);
}
@@ -629,8 +639,8 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state,
// preparation to avoid preparing with the same AST id twice.
if (!context()->IsTest() || !info_->IsOptimizable()) return;
- NearLabel skip;
- if (should_normalize) __ jmp(&skip);
+ Label skip;
+ if (should_normalize) __ jmp(&skip, Label::kNear);
ForwardBailoutStack* current = forward_bailout_stack_;
while (current != NULL) {
@@ -717,24 +727,22 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
}
} else if (prop != NULL) {
- if (function != NULL || mode == Variable::CONST) {
- // We are declaring a function or constant that rewrites to a
- // property. Use (keyed) IC to set the initial value. We cannot
- // visit the rewrite because it's shared and we risk recording
- // duplicate AST IDs for bailouts from optimized code.
+ // A const declaration aliasing a parameter is an illegal redeclaration.
+ ASSERT(mode != Variable::CONST);
+ if (function != NULL) {
+ // We are declaring a function that rewrites to a property.
+ // Use (keyed) IC to set the initial value. We cannot visit the
+ // rewrite because it's shared and we risk recording duplicate AST
+ // IDs for bailouts from optimized code.
ASSERT(prop->obj()->AsVariableProxy() != NULL);
{ AccumulatorValueContext for_object(this);
EmitVariableLoad(prop->obj()->AsVariableProxy()->var());
}
- if (function != NULL) {
- __ push(eax);
- VisitForAccumulatorValue(function);
- __ pop(edx);
- } else {
- __ mov(edx, eax);
- __ mov(eax, isolate()->factory()->the_hole_value());
- }
+ __ push(eax);
+ VisitForAccumulatorValue(function);
+ __ pop(edx);
+
ASSERT(prop->key()->AsLiteral() != NULL &&
prop->key()->AsLiteral()->handle()->IsSmi());
__ SafeSet(ecx, Immediate(prop->key()->AsLiteral()->handle()));
@@ -742,7 +750,7 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
}
}
}
@@ -800,10 +808,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
JumpPatchSite patch_site(masm_);
if (inline_smi_code) {
- NearLabel slow_case;
+ Label slow_case;
__ mov(ecx, edx);
__ or_(ecx, Operand(eax));
- patch_site.EmitJumpIfNotSmi(ecx, &slow_case);
+ patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear);
__ cmp(edx, Operand(eax));
__ j(not_equal, &next_test);
@@ -815,7 +823,7 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT);
- EmitCallIC(ic, &patch_site);
+ EmitCallIC(ic, &patch_site, clause->CompareId());
__ test(eax, Operand(eax));
__ j(not_equal, &next_test);
__ Drop(1); // Switch value is no longer needed.
@@ -864,11 +872,11 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ j(equal, &exit);
// Convert the object to a JS object.
- NearLabel convert, done_convert;
+ Label convert, done_convert;
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &convert);
+ __ j(zero, &convert, Label::kNear);
__ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(above_equal, &done_convert);
+ __ j(above_equal, &done_convert, Label::kNear);
__ bind(&convert);
__ push(eax);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
@@ -893,9 +901,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// check for an enum cache. Leave the map in ebx for the subsequent
// prototype load.
__ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
- __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
- __ cmp(edx, isolate()->factory()->empty_descriptor_array());
- __ j(equal, &call_runtime);
+ __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOrBitField3Offset));
+ __ JumpIfSmi(edx, &call_runtime);
// Check that there is an enum cache in the non-empty instance
// descriptors (edx). This is the case if the next enumeration
@@ -905,9 +912,9 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ j(zero, &call_runtime);
// For all objects but the receiver, check that the cache is empty.
- NearLabel check_prototype;
+ Label check_prototype;
__ cmp(ecx, Operand(eax));
- __ j(equal, &check_prototype);
+ __ j(equal, &check_prototype, Label::kNear);
__ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
__ cmp(edx, isolate()->factory()->empty_fixed_array());
__ j(not_equal, &call_runtime);
@@ -920,9 +927,9 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.
- NearLabel use_cache;
+ Label use_cache;
__ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
- __ jmp(&use_cache);
+ __ jmp(&use_cache, Label::kNear);
// Get the set of properties to enumerate.
__ bind(&call_runtime);
@@ -932,14 +939,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// If we got a map from the runtime call, we can do a fast
// modification check. Otherwise, we got a fixed array, and we have
// to do a slow check.
- NearLabel fixed_array;
+ Label fixed_array;
__ cmp(FieldOperand(eax, HeapObject::kMapOffset),
isolate()->factory()->meta_map());
- __ j(not_equal, &fixed_array);
+ __ j(not_equal, &fixed_array, Label::kNear);
// We got a map in register eax. Get the enumeration cache from it.
__ bind(&use_cache);
- __ mov(ecx, FieldOperand(eax, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(eax, ecx);
__ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
__ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
@@ -975,10 +982,10 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
- NearLabel update_each;
+ Label update_each;
__ mov(ecx, Operand(esp, 4 * kPointerSize));
__ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset));
- __ j(equal, &update_each);
+ __ j(equal, &update_each, Label::kNear);
// Convert the entry to a string or null if it isn't a property
// anymore. If the property has been removed while iterating, we
@@ -1086,7 +1093,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
if (s != NULL && s->is_eval_scope()) {
// Loop up the context chain. There is no frame effect so it is
// safe to use raw labels here.
- NearLabel next, fast;
+ Label next, fast;
if (!context.is(temp)) {
__ mov(temp, context);
}
@@ -1094,7 +1101,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
// Terminate at global context.
__ cmp(FieldOperand(temp, HeapObject::kMapOffset),
Immediate(isolate()->factory()->global_context_map()));
- __ j(equal, &fast);
+ __ j(equal, &fast, Label::kNear);
// Check that extension is NULL.
__ cmp(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0));
__ j(not_equal, slow);
@@ -1113,7 +1120,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
? RelocInfo::CODE_TARGET
: RelocInfo::CODE_TARGET_CONTEXT;
- EmitCallIC(ic, mode);
+ EmitCallIC(ic, mode, AstNode::kNoNumber);
}
@@ -1194,7 +1201,7 @@ void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
__ SafeSet(eax, Immediate(key_literal->handle()));
Handle<Code> ic =
isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
__ jmp(done);
}
}
@@ -1217,7 +1224,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
__ mov(eax, GlobalObjectOperand());
__ mov(ecx, var->name());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
context()->Plug(eax);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
@@ -1243,11 +1250,11 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
if (var->mode() == Variable::CONST) {
// Constants may be the hole value if they have not been initialized.
// Unhole them.
- NearLabel done;
+ Label done;
MemOperand slot_operand = EmitSlotSearch(slot, eax);
__ mov(eax, slot_operand);
__ cmp(eax, isolate()->factory()->the_hole_value());
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
__ mov(eax, isolate()->factory()->undefined_value());
__ bind(&done);
context()->Plug(eax);
@@ -1280,7 +1287,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
// Do a keyed property load.
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
// Drop key and object left on the stack by IC.
context()->Plug(eax);
@@ -1290,7 +1297,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
Comment cmnt(masm_, "[ RegExpLiteral");
- NearLabel materialized;
+ Label materialized;
// Registers will be used as follows:
// edi = JS function.
// ecx = literals array.
@@ -1302,7 +1309,7 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
__ mov(ebx, FieldOperand(ecx, literal_offset));
__ cmp(ebx, isolate()->factory()->undefined_value());
- __ j(not_equal, &materialized);
+ __ j(not_equal, &materialized, Label::kNear);
// Create regexp literal using runtime function
// Result will be in eax.
@@ -1393,7 +1400,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, key->id());
PrepareForBailoutForId(key->id(), NO_REGISTERS);
} else {
VisitForEffect(value);
@@ -1600,13 +1607,13 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
SetSourcePosition(expr->position() + 1);
AccumulatorValueContext context(this);
if (ShouldInlineSmiCase(op)) {
- EmitInlineSmiBinaryOp(expr,
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
op,
mode,
expr->target(),
expr->value());
} else {
- EmitBinaryOp(op, mode);
+ EmitBinaryOp(expr->binary_operation(), op, mode);
}
// Deoptimization point in case the binary operation may have side effects.
@@ -1642,36 +1649,36 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
ASSERT(!key->handle()->IsSmi());
__ mov(ecx, Immediate(key->handle()));
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
-void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
Expression* left,
Expression* right) {
// Do combined smi check of the operands. Left operand is on the
// stack. Right operand is in eax.
- NearLabel done, smi_case, stub_call;
+ Label smi_case, done, stub_call;
__ pop(edx);
__ mov(ecx, eax);
__ or_(eax, Operand(edx));
JumpPatchSite patch_site(masm_);
- patch_site.EmitJumpIfSmi(eax, &smi_case);
+ patch_site.EmitJumpIfSmi(eax, &smi_case, Label::kNear);
__ bind(&stub_call);
__ mov(eax, ecx);
- TypeRecordingBinaryOpStub stub(op, mode);
- EmitCallIC(stub.GetCode(), &patch_site);
- __ jmp(&done);
+ BinaryOpStub stub(op, mode);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->id());
+ __ jmp(&done, Label::kNear);
// Smi case.
__ bind(&smi_case);
@@ -1724,7 +1731,7 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
__ imul(eax, Operand(ecx));
__ j(overflow, &stub_call);
__ test(eax, Operand(eax));
- __ j(not_zero, &done, taken);
+ __ j(not_zero, &done, Label::kNear);
__ mov(ebx, edx);
__ or_(ebx, Operand(ecx));
__ j(negative, &stub_call);
@@ -1748,11 +1755,13 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
}
-void FullCodeGenerator::EmitBinaryOp(Token::Value op,
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
OverwriteMode mode) {
__ pop(edx);
- TypeRecordingBinaryOpStub stub(op, mode);
- EmitCallIC(stub.GetCode(), NULL); // NULL signals no inlined smi code.
+ BinaryOpStub stub(op, mode);
+ // NULL signals no inlined smi code.
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
context()->Plug(eax);
}
@@ -1792,7 +1801,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
break;
}
case KEYED_PROPERTY: {
@@ -1815,7 +1824,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
break;
}
}
@@ -1841,7 +1850,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
} else if (op == Token::INIT_CONST) {
// Like var declarations, const declarations are hoisted to function
@@ -1944,7 +1953,7 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -1984,7 +1993,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -2033,9 +2042,9 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Record source position of the IC call.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(
- arg_count, in_loop);
- EmitCallIC(ic, mode);
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ EmitCallIC(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
@@ -2044,8 +2053,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
- Expression* key,
- RelocInfo::Mode mode) {
+ Expression* key) {
// Load the key.
VisitForAccumulatorValue(key);
@@ -2069,7 +2077,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(
arg_count, in_loop);
__ mov(ecx, Operand(esp, (arg_count + 1) * kPointerSize)); // Key.
- EmitCallIC(ic, mode);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
@@ -2077,7 +2085,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
}
-void FullCodeGenerator::EmitCallWithStub(Call* expr) {
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@@ -2089,7 +2097,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Record source position for debugger.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arg_count, in_loop, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2180,7 +2188,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Record source position for debugger.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2228,7 +2236,10 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ bind(&call);
}
- EmitCallWithStub(expr);
+ // The receiver is either the global receiver or an object found
+ // by LoadContextSlot. That object could be the hole if the
+ // receiver is implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
} else if (fun->AsProperty() != NULL) {
// Call to an object property.
Property* prop = fun->AsProperty();
@@ -2260,18 +2271,18 @@ void FullCodeGenerator::VisitCall(Call* expr) {
SetSourcePosition(prop->position());
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
// Push result (function).
__ push(eax);
// Push Global receiver.
__ mov(ecx, GlobalObjectOperand());
__ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
- EmitCallWithStub(expr);
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
} else {
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
- EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
+ EmitKeyedCallWithIC(expr, prop->key());
}
}
} else {
@@ -2282,7 +2293,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ mov(ebx, GlobalObjectOperand());
__ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
// Emit function call.
- EmitCallWithStub(expr);
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
}
#ifdef DEBUG
@@ -2474,7 +2485,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
- __ mov(ebx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(ebx, ebx);
__ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
// ebx: descriptor array
// ecx: length of descriptor array
@@ -2793,7 +2804,7 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
__ movd(xmm1, Operand(ebx));
__ movd(xmm0, Operand(eax));
__ cvtss2sd(xmm1, xmm1);
- __ pxor(xmm0, xmm1);
+ __ xorps(xmm0, xmm1);
__ subsd(xmm0, xmm1);
__ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
} else {
@@ -2842,13 +2853,13 @@ void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) {
VisitForAccumulatorValue(args->at(0)); // Load the object.
- NearLabel done;
+ Label done;
// If the object is a smi return the object.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// If the object is not a value type, return the object.
__ CmpObjectType(eax, JS_VALUE_TYPE, ebx);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
__ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
__ bind(&done);
@@ -2879,14 +2890,14 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
VisitForAccumulatorValue(args->at(1)); // Load the value.
__ pop(ebx); // eax = value. ebx = object.
- NearLabel done;
+ Label done;
// If the object is a smi, return the value.
__ test(ebx, Immediate(kSmiTagMask));
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// If the object is not a value type, return the value.
__ CmpObjectType(ebx, JS_VALUE_TYPE, ecx);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
// Store the value.
__ mov(FieldOperand(ebx, JSValue::kValueOffset), eax);
@@ -3095,17 +3106,17 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);
- int arg_count = args->length() - 2; // For receiver and function.
- VisitForStackValue(args->at(0)); // Receiver.
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i + 1));
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; ++i) {
+ VisitForStackValue(args->at(i));
}
- VisitForAccumulatorValue(args->at(arg_count + 1)); // Function.
+ VisitForAccumulatorValue(args->last()); // Function.
- // InvokeFunction requires function in edi. Move it in there.
- if (!result_register().is(edi)) __ mov(edi, result_register());
+ // InvokeFunction requires the function in edi. Move it in there.
+ __ mov(edi, result_register());
ParameterCount count(arg_count);
- __ InvokeFunction(edi, count, CALL_FUNCTION);
+ __ InvokeFunction(edi, count, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
context()->Plug(eax);
}
@@ -3618,9 +3629,10 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
// Call the JS runtime function via a call IC.
__ Set(ecx, Immediate(expr->name()));
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(
- arg_count, in_loop);
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ arg_count, in_loop, mode);
+ EmitCallIC(ic, mode, expr->id());
// Restore context register.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
} else {
@@ -3734,48 +3746,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
break;
}
- case Token::SUB: {
- Comment cmt(masm_, "[ UnaryOperation (SUB)");
- bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode overwrite =
- can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
- // GenericUnaryOpStub expects the argument to be in the
- // accumulator register eax.
- VisitForAccumulatorValue(expr->expression());
- __ CallStub(&stub);
- context()->Plug(eax);
+ case Token::SUB:
+ EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
break;
- }
- case Token::BIT_NOT: {
- Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
- // The generic unary operation stub expects the argument to be
- // in the accumulator register eax.
- VisitForAccumulatorValue(expr->expression());
- Label done;
- bool inline_smi_case = ShouldInlineSmiCase(expr->op());
- if (inline_smi_case) {
- NearLabel call_stub;
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &call_stub);
- __ lea(eax, Operand(eax, kSmiTagMask));
- __ not_(eax);
- __ jmp(&done);
- __ bind(&call_stub);
- }
- bool overwrite = expr->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode mode =
- overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- UnaryOpFlags flags = inline_smi_case
- ? NO_UNARY_SMI_CODE_IN_STUB
- : NO_UNARY_FLAGS;
- GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
- __ CallStub(&stub);
- __ bind(&done);
- context()->Plug(eax);
+ case Token::BIT_NOT:
+ EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
break;
- }
default:
UNREACHABLE();
@@ -3783,6 +3760,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
+void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
+ const char* comment) {
+ // TODO(svenpanne): Allowing format strings in Comment would be nice here...
+ Comment cmt(masm_, comment);
+ bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+ UnaryOpStub stub(expr->op(), overwrite);
+ // UnaryOpStub expects the argument to be in the
+ // accumulator register eax.
+ VisitForAccumulatorValue(expr->expression());
+ SetSourcePosition(expr->position());
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
+ context()->Plug(eax);
+}
+
+
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
SetSourcePosition(expr->position());
@@ -3847,10 +3841,10 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
}
// Call ToNumber only if operand is not a smi.
- NearLabel no_conversion;
+ Label no_conversion;
if (ShouldInlineSmiCase(expr->op())) {
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &no_conversion);
+ __ j(zero, &no_conversion, Label::kNear);
}
ToNumberStub convert_stub;
__ CallStub(&convert_stub);
@@ -3877,7 +3871,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
}
// Inline smi case if we are in a loop.
- NearLabel stub_call, done;
+ Label done, stub_call;
JumpPatchSite patch_site(masm_);
if (ShouldInlineSmiCase(expr->op())) {
@@ -3886,10 +3880,10 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
} else {
__ sub(Operand(eax), Immediate(Smi::FromInt(1)));
}
- __ j(overflow, &stub_call);
+ __ j(overflow, &stub_call, Label::kNear);
// We could eliminate this smi check if we split the code at
// the first smi check before calling ToNumber.
- patch_site.EmitJumpIfSmi(eax, &done);
+ patch_site.EmitJumpIfSmi(eax, &done, Label::kNear);
__ bind(&stub_call);
// Call stub. Undo operation first.
@@ -3906,8 +3900,8 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
// Call stub for +1/-1.
__ mov(edx, eax);
__ mov(eax, Immediate(Smi::FromInt(1)));
- TypeRecordingBinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
- EmitCallIC(stub.GetCode(), &patch_site);
+ BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->CountId());
__ bind(&done);
// Store the value returned in eax.
@@ -3940,7 +3934,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
@@ -3957,7 +3951,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
// Result is on the stack
@@ -3985,7 +3979,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
// Use a regular load, not a contextual load, to avoid a reference
// error.
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
PrepareForBailout(expr, TOS_REG);
context()->Plug(eax);
} else if (proxy != NULL &&
@@ -4172,10 +4166,10 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
bool inline_smi_code = ShouldInlineSmiCase(op);
JumpPatchSite patch_site(masm_);
if (inline_smi_code) {
- NearLabel slow_case;
+ Label slow_case;
__ mov(ecx, Operand(edx));
__ or_(ecx, Operand(eax));
- patch_site.EmitJumpIfNotSmi(ecx, &slow_case);
+ patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear);
__ cmp(edx, Operand(eax));
Split(cc, if_true, if_false, NULL);
__ bind(&slow_case);
@@ -4184,7 +4178,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CompareIC::GetUninitialized(op);
- EmitCallIC(ic, &patch_site);
+ EmitCallIC(ic, &patch_site, expr->id());
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ test(eax, Operand(eax));
@@ -4244,7 +4238,9 @@ Register FullCodeGenerator::context_register() {
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ RelocInfo::Mode mode,
+ unsigned ast_id) {
ASSERT(mode == RelocInfo::CODE_TARGET ||
mode == RelocInfo::CODE_TARGET_CONTEXT);
switch (ic->kind()) {
@@ -4262,34 +4258,13 @@ void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
default:
break;
}
-
- __ call(ic, mode);
-
- // Crankshaft doesn't need patching of inlined loads and stores.
- // When compiling the snapshot we need to produce code that works
- // with and without Crankshaft.
- if (V8::UseCrankshaft() && !Serializer::enabled()) {
- return;
- }
-
- // If we're calling a (keyed) load or store stub, we have to mark
- // the call as containing no inlined code so we will not attempt to
- // patch it.
- switch (ic->kind()) {
- case Code::LOAD_IC:
- case Code::KEYED_LOAD_IC:
- case Code::STORE_IC:
- case Code::KEYED_STORE_IC:
- __ nop(); // Signals no inlined code.
- break;
- default:
- // Do nothing.
- break;
- }
+ __ call(ic, mode, ast_id);
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) {
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ JumpPatchSite* patch_site,
+ unsigned ast_id) {
Counters* counters = isolate()->counters();
switch (ic->kind()) {
case Code::LOAD_IC:
@@ -4306,8 +4281,7 @@ void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) {
default:
break;
}
-
- __ call(ic, RelocInfo::CODE_TARGET);
+ __ call(ic, RelocInfo::CODE_TARGET, ast_id);
if (patch_site != NULL && patch_site->is_bound()) {
patch_site->EmitPatchInfo();
} else {
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index b7af03c4..3941cfce 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -50,11 +50,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Register usage:
// type: holds the receiver instance type on entry.
__ cmp(type, JS_GLOBAL_OBJECT_TYPE);
- __ j(equal, global_object, not_taken);
+ __ j(equal, global_object);
__ cmp(type, JS_BUILTINS_OBJECT_TYPE);
- __ j(equal, global_object, not_taken);
+ __ j(equal, global_object);
__ cmp(type, JS_GLOBAL_PROXY_TYPE);
- __ j(equal, global_object, not_taken);
+ __ j(equal, global_object);
}
@@ -73,13 +73,13 @@ static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
// Check that the receiver is a valid JS object.
__ mov(r1, FieldOperand(receiver, HeapObject::kMapOffset));
__ movzx_b(r0, FieldOperand(r1, Map::kInstanceTypeOffset));
__ cmp(r0, FIRST_JS_OBJECT_TYPE);
- __ j(below, miss, not_taken);
+ __ j(below, miss);
// If this assert fails, we have to check upper bound too.
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
@@ -90,68 +90,13 @@ static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
__ test_b(FieldOperand(r1, Map::kBitFieldOffset),
(1 << Map::kIsAccessCheckNeeded) |
(1 << Map::kHasNamedInterceptor));
- __ j(not_zero, miss, not_taken);
+ __ j(not_zero, miss);
__ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
- __ CheckMap(r0, FACTORY->hash_table_map(), miss, true);
-}
-
-
-// Probe the string dictionary in the |elements| register. Jump to the
-// |done| label if a property with the given name is found leaving the
-// index into the dictionary in |r0|. Jump to the |miss| label
-// otherwise.
-static void GenerateStringDictionaryProbes(MacroAssembler* masm,
- Label* miss,
- Label* done,
- Register elements,
- Register name,
- Register r0,
- Register r1) {
- // Assert that name contains a string.
- if (FLAG_debug_code) __ AbortIfNotString(name);
-
- // Compute the capacity mask.
- const int kCapacityOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kCapacityIndex * kPointerSize;
- __ mov(r1, FieldOperand(elements, kCapacityOffset));
- __ shr(r1, kSmiTagSize); // convert smi to int
- __ dec(r1);
-
- // Generate an unrolled loop that performs a few probes before
- // giving up. Measurements done on Gmail indicate that 2 probes
- // cover ~93% of loads from dictionaries.
- static const int kProbes = 4;
- const int kElementsStartOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kElementsStartIndex * kPointerSize;
- for (int i = 0; i < kProbes; i++) {
- // Compute the masked index: (hash + i + i * i) & mask.
- __ mov(r0, FieldOperand(name, String::kHashFieldOffset));
- __ shr(r0, String::kHashShift);
- if (i > 0) {
- __ add(Operand(r0), Immediate(StringDictionary::GetProbeOffset(i)));
- }
- __ and_(r0, Operand(r1));
-
- // Scale the index by multiplying by the entry size.
- ASSERT(StringDictionary::kEntrySize == 3);
- __ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
-
- // Check if the key is identical to the name.
- __ cmp(name, Operand(elements, r0, times_4,
- kElementsStartOffset - kHeapObjectTag));
- if (i != kProbes - 1) {
- __ j(equal, done, taken);
- } else {
- __ j(not_equal, miss, not_taken);
- }
- }
+ __ CheckMap(r0, FACTORY->hash_table_map(), miss, DONT_DO_SMI_CHECK);
}
-
// Helper function used to load a property from a dictionary backing
// storage. This function may fail to load a property even though it is
// in the dictionary, so code at miss_label must always call a backup
@@ -183,13 +128,13 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
Label done;
// Probe the dictionary.
- GenerateStringDictionaryProbes(masm,
- miss_label,
- &done,
- elements,
- name,
- r0,
- r1);
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
// If probing finds an entry in the dictionary, r0 contains the
// index into the dictionary. Check that the value is a normal
@@ -201,7 +146,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize));
- __ j(not_zero, miss_label, not_taken);
+ __ j(not_zero, miss_label);
// Get the value at the masked, scaled index.
const int kValueOffset = kElementsStartOffset + kPointerSize;
@@ -238,13 +183,13 @@ static void GenerateDictionaryStore(MacroAssembler* masm,
// Probe the dictionary.
- GenerateStringDictionaryProbes(masm,
- miss_label,
- &done,
- elements,
- name,
- r0,
- r1);
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
// If probing finds an entry in the dictionary, r0 contains the
// index into the dictionary. Check that the value is a normal
@@ -259,7 +204,7 @@ static void GenerateDictionaryStore(MacroAssembler* masm,
PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
Immediate(kTypeAndReadOnlyMask));
- __ j(not_zero, miss_label, not_taken);
+ __ j(not_zero, miss_label);
// Store the value at the masked, scaled index.
const int kValueOffset = kElementsStartOffset + kPointerSize;
@@ -349,9 +294,9 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
times_pointer_size,
NumberDictionary::kElementsStartOffset));
if (i != (kProbes - 1)) {
- __ j(equal, &done, taken);
+ __ j(equal, &done);
} else {
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
}
}
@@ -371,12 +316,6 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
}
-// The offset from the inlined patch site to the start of the
-// inlined load instruction. It is 7 bytes (test eax, imm) plus
-// 6 bytes (jne slow_label).
-const int LoadIC::kOffsetToLoadInstruction = 13;
-
-
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : receiver
@@ -435,7 +374,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
// Check that the object isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, slow, not_taken);
+ __ j(zero, slow);
// Get the map of the receiver.
__ mov(map, FieldOperand(receiver, HeapObject::kMapOffset));
@@ -443,7 +382,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
// Check bit field.
__ test_b(FieldOperand(map, Map::kBitFieldOffset),
(1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit));
- __ j(not_zero, slow, not_taken);
+ __ j(not_zero, slow);
// Check that the object is some kind of JS object EXCEPT JS Value type.
// In the case that the object is a value-wrapper object,
// we enter the runtime system to make sure that indexing
@@ -451,7 +390,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
__ CmpInstanceType(map, JS_OBJECT_TYPE);
- __ j(below, slow, not_taken);
+ __ j(below, slow);
}
@@ -475,7 +414,10 @@ static void GenerateFastArrayLoad(MacroAssembler* masm,
__ mov(scratch, FieldOperand(receiver, JSObject::kElementsOffset));
if (not_fast_array != NULL) {
// Check that the object is in fast mode and writable.
- __ CheckMap(scratch, FACTORY->fixed_array_map(), not_fast_array, true);
+ __ CheckMap(scratch,
+ FACTORY->fixed_array_map(),
+ not_fast_array,
+ DONT_DO_SMI_CHECK);
} else {
__ AssertFastElements(scratch);
}
@@ -514,12 +456,12 @@ static void GenerateKeyStringCheck(MacroAssembler* masm,
// Is the string an array index, with cached numeric value?
__ mov(hash, FieldOperand(key, String::kHashFieldOffset));
__ test(hash, Immediate(String::kContainsCachedArrayIndexMask));
- __ j(zero, index_string, not_taken);
+ __ j(zero, index_string);
// Is the string a symbol?
ASSERT(kSymbolTag != 0);
__ test_b(FieldOperand(map, Map::kInstanceTypeOffset), kIsSymbolMask);
- __ j(zero, not_symbol, not_taken);
+ __ j(zero, not_symbol);
}
@@ -534,7 +476,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// Check that the key is a smi.
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &check_string, not_taken);
+ __ j(not_zero, &check_string);
__ bind(&index_smi);
// Now the key is known to be a smi. This place is also jumped to from
// where a numeric string is converted to a smi.
@@ -546,7 +488,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// now in ecx.
__ test_b(FieldOperand(ecx, Map::kBitField2Offset),
1 << Map::kHasFastElements);
- __ j(zero, &check_number_dictionary, not_taken);
+ __ j(zero, &check_number_dictionary);
GenerateFastArrayLoad(masm,
edx,
@@ -570,7 +512,10 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ebx: untagged index
// eax: key
// ecx: elements
- __ CheckMap(ecx, isolate->factory()->hash_table_map(), &slow, true);
+ __ CheckMap(ecx,
+ isolate->factory()->hash_table_map(),
+ &slow,
+ DONT_DO_SMI_CHECK);
Label slow_pop_receiver;
// Push receiver on the stack to free up a register for the dictionary
// probing.
@@ -710,7 +655,7 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
}
@@ -724,11 +669,11 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
// Check that the receiver isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &slow, not_taken);
+ __ j(zero, &slow);
// Check that the key is an array index, that is Uint32.
__ test(eax, Immediate(kSmiTagMask | kSmiSignMask));
- __ j(not_zero, &slow, not_taken);
+ __ j(not_zero, &slow);
// Get the map of the receiver.
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
@@ -738,7 +683,7 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
__ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset));
__ and_(Operand(ecx), Immediate(kSlowCaseBitFieldMask));
__ cmp(Operand(ecx), Immediate(1 << Map::kHasIndexedInterceptor));
- __ j(not_zero, &slow, not_taken);
+ __ j(not_zero, &slow);
// Everything is fine, call runtime.
__ pop(ecx);
@@ -753,7 +698,7 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
__ TailCallExternalReference(ref, 2, 1);
__ bind(&slow);
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
}
@@ -769,22 +714,22 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &slow, not_taken);
+ __ j(zero, &slow);
// Get the map from the receiver.
__ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
__ test_b(FieldOperand(edi, Map::kBitFieldOffset),
1 << Map::kIsAccessCheckNeeded);
- __ j(not_zero, &slow, not_taken);
+ __ j(not_zero, &slow);
// Check that the key is a smi.
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &slow, not_taken);
+ __ j(not_zero, &slow);
__ CmpInstanceType(edi, JS_ARRAY_TYPE);
__ j(equal, &array);
// Check that the object is some kind of JS object.
__ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE);
- __ j(below, &slow, not_taken);
+ __ j(below, &slow);
// Object case: Check key against length in the elements array.
// eax: value
@@ -792,9 +737,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// ecx: key (a smi)
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode and writable.
- __ CheckMap(edi, FACTORY->fixed_array_map(), &slow, true);
+ __ CheckMap(edi, FACTORY->fixed_array_map(), &slow, DONT_DO_SMI_CHECK);
__ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
- __ j(below, &fast, taken);
+ __ j(below, &fast);
// Slow case: call runtime.
__ bind(&slow);
@@ -809,9 +754,10 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// ecx: key, a smi.
// edi: receiver->elements, a FixedArray
// flags: compare (ecx, edx.length())
- __ j(not_equal, &slow, not_taken); // do not leave holes in the array
+ // do not leave holes in the array:
+ __ j(not_equal, &slow);
__ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset));
- __ j(above_equal, &slow, not_taken);
+ __ j(above_equal, &slow);
// Add 1 to receiver->length, and go to fast array write.
__ add(FieldOperand(edx, JSArray::kLengthOffset),
Immediate(Smi::FromInt(1)));
@@ -825,12 +771,12 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// edx: receiver, a JSArray
// ecx: key, a smi.
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
- __ CheckMap(edi, FACTORY->fixed_array_map(), &slow, true);
+ __ CheckMap(edi, FACTORY->fixed_array_map(), &slow, DONT_DO_SMI_CHECK);
// Check the key against the length in the array, compute the
// address to store into and fall through to fast case.
__ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
- __ j(above_equal, &extra, not_taken);
+ __ j(above_equal, &extra);
// Fast case: Do the store.
__ bind(&fast);
@@ -850,7 +796,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
int argc,
- Code::Kind kind) {
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- ecx : name
// -- edx : receiver
@@ -861,7 +808,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
MONOMORPHIC,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc);
Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx,
@@ -874,9 +821,9 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
//
// Check for number.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &number, not_taken);
+ __ j(zero, &number);
__ CmpObjectType(edx, HEAP_NUMBER_TYPE, ebx);
- __ j(not_equal, &non_number, taken);
+ __ j(not_equal, &non_number);
__ bind(&number);
StubCompiler::GenerateLoadGlobalFunctionPrototype(
masm, Context::NUMBER_FUNCTION_INDEX, edx);
@@ -885,7 +832,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
// Check for string.
__ bind(&non_number);
__ CmpInstanceType(ebx, FIRST_NONSTRING_TYPE);
- __ j(above_equal, &non_string, taken);
+ __ j(above_equal, &non_string);
StubCompiler::GenerateLoadGlobalFunctionPrototype(
masm, Context::STRING_FUNCTION_INDEX, edx);
__ jmp(&probe);
@@ -893,9 +840,9 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
// Check for boolean.
__ bind(&non_string);
__ cmp(edx, FACTORY->true_value());
- __ j(equal, &boolean, not_taken);
+ __ j(equal, &boolean);
__ cmp(edx, FACTORY->false_value());
- __ j(not_equal, &miss, taken);
+ __ j(not_equal, &miss);
__ bind(&boolean);
StubCompiler::GenerateLoadGlobalFunctionPrototype(
masm, Context::BOOLEAN_FUNCTION_INDEX, edx);
@@ -922,15 +869,16 @@ static void GenerateFunctionTailCall(MacroAssembler* masm,
// Check that the result is not a smi.
__ test(edi, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
// Check that the value is a JavaScript function, fetching its map into eax.
__ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
// Invoke the function.
ParameterCount actual(argc);
- __ InvokeFunction(edi, actual, JUMP_FUNCTION);
+ __ InvokeFunction(edi, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// The generated code falls through if the call should be handled by runtime.
@@ -960,7 +908,8 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
static void GenerateCallMiss(MacroAssembler* masm,
int argc,
- IC::UtilityId id) {
+ IC::UtilityId id,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@@ -1002,13 +951,13 @@ static void GenerateCallMiss(MacroAssembler* masm,
Label invoke, global;
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &invoke, not_taken);
+ __ j(zero, &invoke, Label::kNear);
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
__ cmp(ebx, JS_GLOBAL_OBJECT_TYPE);
- __ j(equal, &global);
+ __ j(equal, &global, Label::kNear);
__ cmp(ebx, JS_BUILTINS_OBJECT_TYPE);
- __ j(not_equal, &invoke);
+ __ j(not_equal, &invoke, Label::kNear);
// Patch the receiver on the stack.
__ bind(&global);
@@ -1018,12 +967,21 @@ static void GenerateCallMiss(MacroAssembler* masm,
}
// Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
ParameterCount actual(argc);
- __ InvokeFunction(edi, actual, JUMP_FUNCTION);
+ __ InvokeFunction(edi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
}
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@@ -1034,8 +992,9 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack; 1 ~ return address.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
- GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
- GenerateMiss(masm, argc);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+
+ GenerateMiss(masm, argc, extra_ic_state);
}
@@ -1049,11 +1008,13 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
// -----------------------------------
GenerateCallNormal(masm, argc);
- GenerateMiss(masm, argc);
+ GenerateMiss(masm, argc, Code::kNoExtraICState);
}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMiss(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@@ -1062,7 +1023,7 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state);
}
@@ -1084,7 +1045,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Check that the key is a smi.
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &check_string, not_taken);
+ __ j(not_zero, &check_string);
__ bind(&index_smi);
// Now the key is known to be a smi. This place is also jumped to from
@@ -1109,7 +1070,10 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// eax: elements
// ecx: smi key
// Check whether the elements is a number dictionary.
- __ CheckMap(eax, isolate->factory()->hash_table_map(), &slow_load, true);
+ __ CheckMap(eax,
+ isolate->factory()->hash_table_map(),
+ &slow_load,
+ DONT_DO_SMI_CHECK);
__ mov(ebx, ecx);
__ SmiUntag(ebx);
// ebx: untagged index
@@ -1150,7 +1114,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
__ CheckMap(ebx,
isolate->factory()->hash_table_map(),
&lookup_monomorphic_cache,
- true);
+ DONT_DO_SMI_CHECK);
GenerateDictionaryLoad(masm, &slow_load, ebx, ecx, eax, edi, edi);
__ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1);
@@ -1158,7 +1122,10 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
__ bind(&lookup_monomorphic_cache);
__ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1);
- GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
// Fall through on miss.
__ bind(&slow_call);
@@ -1208,7 +1175,7 @@ void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState);
}
@@ -1273,173 +1240,7 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
}
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // If the instruction following the call is not a test eax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- Address delta_address = test_instruction_address + 1;
- // The delta to the start of the map check instruction.
- int delta = *reinterpret_cast<int*>(delta_address);
-
- // The map address is the last 4 bytes of the 7-byte
- // operand-immediate compare instruction, so we add 3 to get the
- // offset to the last 4 bytes.
- Address map_address = test_instruction_address + delta + 3;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // The offset is in the last 4 bytes of a six byte
- // memory-to-register move instruction, so we add 2 to get the
- // offset to the last 4 bytes.
- Address offset_address =
- test_instruction_address + delta + kOffsetToLoadInstruction + 2;
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
- return true;
-}
-
-
-// One byte opcode for mov ecx,0xXXXXXXXX.
-// Marks inlined contextual loads using all kinds of cells. Generated
-// code has the hole check:
-// mov reg, <cell>
-// mov reg, (<cell>, value offset)
-// cmp reg, <the hole>
-// je slow
-// ;; use reg
-static const byte kMovEcxByte = 0xB9;
-
-// One byte opcode for mov edx,0xXXXXXXXX.
-// Marks inlined contextual loads using only "don't delete"
-// cells. Generated code doesn't have the hole check:
-// mov reg, <cell>
-// mov reg, (<cell>, value offset)
-// ;; use reg
-static const byte kMovEdxByte = 0xBA;
-
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address mov_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // If the instruction following the call is not a mov ecx/edx,
- // nothing was inlined.
- byte b = *mov_instruction_address;
- if (b != kMovEcxByte && b != kMovEdxByte) return false;
- // If we don't have the hole check generated, we can only support
- // "don't delete" cells.
- if (b == kMovEdxByte && !is_dont_delete) return false;
-
- Address delta_address = mov_instruction_address + 1;
- // The delta to the start of the map check instruction.
- int delta = *reinterpret_cast<int*>(delta_address);
-
- // The map address is the last 4 bytes of the 7-byte
- // operand-immediate compare instruction, so we add 3 to get the
- // offset to the last 4 bytes.
- Address map_address = mov_instruction_address + delta + 3;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // The cell is in the last 4 bytes of a five byte mov reg, imm32
- // instruction, so we add 1 to get the offset to the last 4 bytes.
- Address offset_address =
- mov_instruction_address + delta + kOffsetToLoadInstruction + 1;
- *reinterpret_cast<Object**>(offset_address) = cell;
- return true;
-}
-
-
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
-
- // If the instruction following the call is not a test eax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Extract the encoded deltas from the test eax instruction.
- Address encoded_offsets_address = test_instruction_address + 1;
- int encoded_offsets = *reinterpret_cast<int*>(encoded_offsets_address);
- int delta_to_map_check = -(encoded_offsets & 0xFFFF);
- int delta_to_record_write = encoded_offsets >> 16;
-
- // Patch the map to check. The map address is the last 4 bytes of
- // the 7-byte operand-immediate compare instruction.
- Address map_check_address = test_instruction_address + delta_to_map_check;
- Address map_address = map_check_address + 3;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // Patch the offset in the store instruction. The offset is in the
- // last 4 bytes of a six byte register-to-memory move instruction.
- Address offset_address =
- map_check_address + StoreIC::kOffsetToStoreInstruction + 2;
- // The offset should have initial value (kMaxInt - 1), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt - 1 ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- // Patch the offset in the write-barrier code. The offset is the
- // last 4 bytes of a six byte lea instruction.
- offset_address = map_check_address + delta_to_record_write + 2;
- // The offset should have initial value (kMaxInt), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- return true;
-}
-
-
-static bool PatchInlinedMapCheck(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // The keyed load has a fast inlined case if the IC call instruction
- // is immediately followed by a test instruction.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Fetch the offset from the test instruction to the map cmp
- // instruction. This offset is stored in the last 4 bytes of the 5
- // byte test instruction.
- Address delta_address = test_instruction_address + 1;
- int delta = *reinterpret_cast<int*>(delta_address);
- // Compute the map address. The map address is in the last 4 bytes
- // of the 7-byte operand-immediate compare instruction, so we add 3
- // to the offset to get the map address.
- Address map_address = test_instruction_address + delta + 3;
- // Patch the map check.
- *(reinterpret_cast<Object**>(map_address)) = map;
- return true;
-}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
-void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
// ----------- S t a t e -------------
// -- eax : key
// -- edx : receiver
@@ -1454,8 +1255,10 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
__ push(ebx); // return address
// Perform tail call to the entry.
- ExternalReference ref =
- ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
__ TailCallExternalReference(ref, 2, 1);
}
@@ -1519,12 +1322,6 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) {
}
-// The offset from the inlined patch site to the start of the inlined
-// store instruction. It is 7 bytes (test reg, imm) plus 6 bytes (jne
-// slow_label).
-const int StoreIC::kOffsetToStoreInstruction = 13;
-
-
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
@@ -1546,22 +1343,22 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
// Check that the object is a JS array.
__ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Check that elements are FixedArray.
// We rely on StoreIC_ArrayLength below to deal with all types of
// fast elements (including COW).
__ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Check that value is a smi.
__ test(value, Immediate(kSmiTagMask));
- __ j(not_zero, &miss, not_taken);
+ __ j(not_zero, &miss);
// Prepare tail call to StoreIC_ArrayLength.
__ pop(scratch);
@@ -1653,7 +1450,7 @@ void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
}
-void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
@@ -1668,8 +1465,30 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
__ push(ebx);
// Do tail-call to runtime routine.
- ExternalReference ref =
- ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ecx);
+ __ push(eax);
+ __ push(ebx); // return address
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
__ TailCallExternalReference(ref, 3, 1);
}
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 8bcce338..3e95867d 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -40,7 +40,7 @@ namespace internal {
// When invoking builtins, we need to record the safepoint in the middle of
// the invoke instruction sequence generated by the macro assembler.
-class SafepointGenerator : public PostCallGenerator {
+class SafepointGenerator : public CallWrapper {
public:
SafepointGenerator(LCodeGen* codegen,
LPointerMap* pointers,
@@ -50,7 +50,9 @@ class SafepointGenerator : public PostCallGenerator {
deoptimization_index_(deoptimization_index) {}
virtual ~SafepointGenerator() { }
- virtual void Generate() {
+ virtual void BeforeCall(int call_size) const {}
+
+ virtual void AfterCall() const {
codegen_->RecordSafepoint(pointers_, deoptimization_index_);
}
@@ -77,7 +79,7 @@ bool LCodeGen::GenerateCode() {
void LCodeGen::FinishCode(Handle<Code> code) {
ASSERT(is_done());
- code->set_stack_slots(StackSlotCount());
+ code->set_stack_slots(GetStackSlotCount());
code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
PopulateDeoptimizationData(code);
Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
@@ -126,13 +128,28 @@ bool LCodeGen::GeneratePrologue() {
}
#endif
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). ecx is zero for method calls and non-zero for function
+ // calls.
+ if (info_->is_strict_mode()) {
+ Label ok;
+ __ test(ecx, Operand(ecx));
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
+ __ mov(Operand(esp, receiver_offset),
+ Immediate(isolate()->factory()->undefined_value()));
+ __ bind(&ok);
+ }
+
__ push(ebp); // Caller's frame pointer.
__ mov(ebp, esp);
__ push(esi); // Callee's context.
__ push(edi); // Callee's JS function.
// Reserve space for the stack slots needed by the code.
- int slots = StackSlotCount();
+ int slots = GetStackSlotCount();
if (slots > 0) {
if (FLAG_debug_code) {
__ mov(Operand(eax), Immediate(slots));
@@ -254,7 +271,7 @@ bool LCodeGen::GenerateDeferredCode() {
bool LCodeGen::GenerateSafepointTable() {
ASSERT(is_done());
- safepoints_.Emit(masm(), StackSlotCount());
+ safepoints_.Emit(masm(), GetStackSlotCount());
return !is_aborted();
}
@@ -386,7 +403,7 @@ void LCodeGen::AddToTranslation(Translation* translation,
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsArgument()) {
ASSERT(is_tagged);
- int src_index = StackSlotCount() + op->index();
+ int src_index = GetStackSlotCount() + op->index();
translation->StoreStackSlot(src_index);
} else if (op->IsRegister()) {
Register reg = ToRegister(op);
@@ -426,7 +443,7 @@ void LCodeGen::CallCodeGeneric(Handle<Code> code,
// Signal that we don't inline smi code before these stubs in the
// optimizing code generator.
- if (code->kind() == Code::TYPE_RECORDING_BINARY_OP_IC ||
+ if (code->kind() == Code::BINARY_OP_IC ||
code->kind() == Code::COMPARE_IC) {
__ nop();
}
@@ -543,7 +560,7 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) {
__ mov(ebx, shared);
__ mov(eax, FieldOperand(ebx, SharedFunctionInfo::kDeoptCounterOffset));
__ sub(Operand(eax), Immediate(Smi::FromInt(1)));
- __ j(not_zero, &no_deopt);
+ __ j(not_zero, &no_deopt, Label::kNear);
if (FLAG_trap_on_deopt) __ int3();
__ mov(eax, Immediate(Smi::FromInt(FLAG_deopt_every_n_times)));
__ mov(FieldOperand(ebx, SharedFunctionInfo::kDeoptCounterOffset), eax);
@@ -564,13 +581,13 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) {
__ jmp(entry, RelocInfo::RUNTIME_ENTRY);
} else {
if (FLAG_trap_on_deopt) {
- NearLabel done;
- __ j(NegateCondition(cc), &done);
+ Label done;
+ __ j(NegateCondition(cc), &done, Label::kNear);
__ int3();
__ jmp(entry, RelocInfo::RUNTIME_ENTRY);
__ bind(&done);
} else {
- __ j(cc, entry, RelocInfo::RUNTIME_ENTRY, not_taken);
+ __ j(cc, entry, RelocInfo::RUNTIME_ENTRY);
}
}
}
@@ -689,7 +706,7 @@ void LCodeGen::DoLabel(LLabel* label) {
}
__ bind(label->label());
current_block_ = label->block_id();
- LCodeGen::DoGap(label);
+ DoGap(label);
}
@@ -715,6 +732,11 @@ void LCodeGen::DoGap(LGap* gap) {
}
+void LCodeGen::DoInstructionGap(LInstructionGap* instr) {
+ DoGap(instr);
+}
+
+
void LCodeGen::DoParameter(LParameter* instr) {
// Nothing to do.
}
@@ -780,50 +802,91 @@ void LCodeGen::DoModI(LModI* instr) {
if (divisor < 0) divisor = -divisor;
- NearLabel positive_dividend, done;
+ Label positive_dividend, done;
__ test(dividend, Operand(dividend));
- __ j(not_sign, &positive_dividend);
+ __ j(not_sign, &positive_dividend, Label::kNear);
__ neg(dividend);
__ and_(dividend, divisor - 1);
__ neg(dividend);
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- __ j(not_zero, &done);
+ __ j(not_zero, &done, Label::kNear);
DeoptimizeIf(no_condition, instr->environment());
} else {
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
}
__ bind(&positive_dividend);
__ and_(dividend, divisor - 1);
__ bind(&done);
} else {
- LOperand* right = instr->InputAt(1);
- ASSERT(ToRegister(instr->InputAt(0)).is(eax));
- ASSERT(ToRegister(instr->result()).is(edx));
+ Label done, remainder_eq_dividend, slow, do_subtraction, both_positive;
+ Register left_reg = ToRegister(instr->InputAt(0));
+ Register right_reg = ToRegister(instr->InputAt(1));
+ Register result_reg = ToRegister(instr->result());
- Register right_reg = ToRegister(right);
+ ASSERT(left_reg.is(eax));
+ ASSERT(result_reg.is(edx));
ASSERT(!right_reg.is(eax));
ASSERT(!right_reg.is(edx));
// Check for x % 0.
if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
- __ test(right_reg, ToOperand(right));
+ __ test(right_reg, Operand(right_reg));
DeoptimizeIf(zero, instr->environment());
}
+ __ test(left_reg, Operand(left_reg));
+ __ j(zero, &remainder_eq_dividend, Label::kNear);
+ __ j(sign, &slow, Label::kNear);
+
+ __ test(right_reg, Operand(right_reg));
+ __ j(not_sign, &both_positive, Label::kNear);
+ // The sign of the divisor doesn't matter.
+ __ neg(right_reg);
+
+ __ bind(&both_positive);
+ // If the dividend is smaller than the nonnegative
+ // divisor, the dividend is the result.
+ __ cmp(left_reg, Operand(right_reg));
+ __ j(less, &remainder_eq_dividend, Label::kNear);
+
+ // Check if the divisor is a PowerOfTwo integer.
+ Register scratch = ToRegister(instr->TempAt(0));
+ __ mov(scratch, right_reg);
+ __ sub(Operand(scratch), Immediate(1));
+ __ test(scratch, Operand(right_reg));
+ __ j(not_zero, &do_subtraction, Label::kNear);
+ __ and_(left_reg, Operand(scratch));
+ __ jmp(&remainder_eq_dividend, Label::kNear);
+
+ __ bind(&do_subtraction);
+ const int kUnfolds = 3;
+ // Try a few subtractions of the dividend.
+ __ mov(scratch, left_reg);
+ for (int i = 0; i < kUnfolds; i++) {
+ // Reduce the dividend by the divisor.
+ __ sub(left_reg, Operand(right_reg));
+ // Check if the dividend is less than the divisor.
+ __ cmp(left_reg, Operand(right_reg));
+ __ j(less, &remainder_eq_dividend, Label::kNear);
+ }
+ __ mov(left_reg, scratch);
+
+ // Slow case, using idiv instruction.
+ __ bind(&slow);
// Sign extend to edx.
__ cdq();
// Check for (0 % -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel positive_left;
- NearLabel done;
- __ test(eax, Operand(eax));
- __ j(not_sign, &positive_left);
+ Label positive_left;
+ Label done;
+ __ test(left_reg, Operand(left_reg));
+ __ j(not_sign, &positive_left, Label::kNear);
__ idiv(right_reg);
// Test the remainder for 0, because then the result would be -0.
- __ test(edx, Operand(edx));
- __ j(not_zero, &done);
+ __ test(result_reg, Operand(result_reg));
+ __ j(not_zero, &done, Label::kNear);
DeoptimizeIf(no_condition, instr->environment());
__ bind(&positive_left);
@@ -832,6 +895,12 @@ void LCodeGen::DoModI(LModI* instr) {
} else {
__ idiv(right_reg);
}
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&remainder_eq_dividend);
+ __ mov(result_reg, left_reg);
+
+ __ bind(&done);
}
}
@@ -854,9 +923,9 @@ void LCodeGen::DoDivI(LDivI* instr) {
// Check for (0 / -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel left_not_zero;
+ Label left_not_zero;
__ test(left_reg, Operand(left_reg));
- __ j(not_zero, &left_not_zero);
+ __ j(not_zero, &left_not_zero, Label::kNear);
__ test(right_reg, ToOperand(right));
DeoptimizeIf(sign, instr->environment());
__ bind(&left_not_zero);
@@ -864,9 +933,9 @@ void LCodeGen::DoDivI(LDivI* instr) {
// Check for (-kMinInt / -1).
if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
- NearLabel left_not_min_int;
+ Label left_not_min_int;
__ cmp(left_reg, kMinInt);
- __ j(not_zero, &left_not_min_int);
+ __ j(not_zero, &left_not_min_int, Label::kNear);
__ cmp(right_reg, -1);
DeoptimizeIf(zero, instr->environment());
__ bind(&left_not_min_int);
@@ -944,9 +1013,9 @@ void LCodeGen::DoMulI(LMulI* instr) {
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// Bail out if the result is supposed to be negative zero.
- NearLabel done;
+ Label done;
__ test(left, Operand(left));
- __ j(not_zero, &done);
+ __ j(not_zero, &done, Label::kNear);
if (right->IsConstantOperand()) {
if (ToInteger32(LConstantOperand::cast(right)) <= 0) {
DeoptimizeIf(no_condition, instr->environment());
@@ -1087,7 +1156,7 @@ void LCodeGen::DoConstantD(LConstantD* instr) {
// Use xor to produce +0.0 in a fast and compact way, but avoid to
// do so if the constant is -0.0.
if (BitCast<uint64_t, double>(v) == 0) {
- __ xorpd(res, res);
+ __ xorps(res, res);
} else {
Register temp = ToRegister(instr->TempAt(0));
uint64_t int_val = BitCast<uint64_t, double>(v);
@@ -1101,7 +1170,7 @@ void LCodeGen::DoConstantD(LConstantD* instr) {
__ Set(temp, Immediate(upper));
__ pinsrd(res, Operand(temp), 1);
} else {
- __ xorpd(res, res);
+ __ xorps(res, res);
__ Set(temp, Immediate(upper));
__ pinsrd(res, Operand(temp), 1);
}
@@ -1151,14 +1220,14 @@ void LCodeGen::DoValueOf(LValueOf* instr) {
Register result = ToRegister(instr->result());
Register map = ToRegister(instr->TempAt(0));
ASSERT(input.is(result));
- NearLabel done;
+ Label done;
// If the object is a smi return the object.
__ test(input, Immediate(kSmiTagMask));
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// If the object is not a value type, return the object.
__ CmpObjectType(input, JS_VALUE_TYPE, map);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
__ mov(result, FieldOperand(input, JSValue::kValueOffset));
__ bind(&done);
@@ -1248,7 +1317,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
ASSERT(ToRegister(instr->InputAt(1)).is(eax));
ASSERT(ToRegister(instr->result()).is(eax));
- TypeRecordingBinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
__ nop(); // Signals no inlined code.
}
@@ -1292,7 +1361,7 @@ void LCodeGen::DoBranch(LBranch* instr) {
EmitBranch(true_block, false_block, not_zero);
} else if (r.IsDouble()) {
XMMRegister reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(reg, xmm0);
EmitBranch(true_block, false_block, not_equal);
} else {
@@ -1317,10 +1386,10 @@ void LCodeGen::DoBranch(LBranch* instr) {
__ j(zero, true_label);
// Test for double values. Zero is false.
- NearLabel call_stub;
+ Label call_stub;
__ cmp(FieldOperand(reg, HeapObject::kMapOffset),
factory()->heap_number_map());
- __ j(not_equal, &call_stub);
+ __ j(not_equal, &call_stub, Label::kNear);
__ fldz();
__ fld_d(FieldOperand(reg, HeapNumber::kValueOffset));
__ FCmp();
@@ -1426,20 +1495,20 @@ void LCodeGen::DoCmpID(LCmpID* instr) {
LOperand* right = instr->InputAt(1);
LOperand* result = instr->result();
- NearLabel unordered;
+ Label unordered;
if (instr->is_double()) {
// Don't base result on EFLAGS when a NaN is involved. Instead
// jump to the unordered case, which produces a false value.
__ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right));
- __ j(parity_even, &unordered, not_taken);
+ __ j(parity_even, &unordered, Label::kNear);
} else {
EmitCmpI(left, right);
}
- NearLabel done;
+ Label done;
Condition cc = TokenToCondition(instr->op(), instr->is_double());
__ mov(ToRegister(result), factory()->true_value());
- __ j(cc, &done);
+ __ j(cc, &done, Label::kNear);
__ bind(&unordered);
__ mov(ToRegister(result), factory()->false_value());
@@ -1474,8 +1543,8 @@ void LCodeGen::DoCmpJSObjectEq(LCmpJSObjectEq* instr) {
__ cmp(left, Operand(right));
__ mov(result, factory()->true_value());
- NearLabel done;
- __ j(equal, &done);
+ Label done;
+ __ j(equal, &done, Label::kNear);
__ mov(result, factory()->false_value());
__ bind(&done);
}
@@ -1492,6 +1561,31 @@ void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
}
+void LCodeGen::DoCmpSymbolEq(LCmpSymbolEq* instr) {
+ Register left = ToRegister(instr->InputAt(0));
+ Register right = ToRegister(instr->InputAt(1));
+ Register result = ToRegister(instr->result());
+
+ Label done;
+ __ cmp(left, Operand(right));
+ __ mov(result, factory()->false_value());
+ __ j(not_equal, &done, Label::kNear);
+ __ mov(result, factory()->true_value());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
+ Register left = ToRegister(instr->InputAt(0));
+ Register right = ToRegister(instr->InputAt(1));
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+
+ __ cmp(left, Operand(right));
+ EmitBranch(true_block, false_block, equal);
+}
+
+
void LCodeGen::DoIsNull(LIsNull* instr) {
Register reg = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
@@ -1502,27 +1596,27 @@ void LCodeGen::DoIsNull(LIsNull* instr) {
__ cmp(reg, factory()->null_value());
if (instr->is_strict()) {
__ mov(result, factory()->true_value());
- NearLabel done;
- __ j(equal, &done);
+ Label done;
+ __ j(equal, &done, Label::kNear);
__ mov(result, factory()->false_value());
__ bind(&done);
} else {
- NearLabel true_value, false_value, done;
- __ j(equal, &true_value);
+ Label true_value, false_value, done;
+ __ j(equal, &true_value, Label::kNear);
__ cmp(reg, factory()->undefined_value());
- __ j(equal, &true_value);
+ __ j(equal, &true_value, Label::kNear);
__ test(reg, Immediate(kSmiTagMask));
- __ j(zero, &false_value);
+ __ j(zero, &false_value, Label::kNear);
// Check for undetectable objects by looking in the bit field in
// the map. The object has already been smi checked.
Register scratch = result;
__ mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset));
__ test(scratch, Immediate(1 << Map::kIsUndetectable));
- __ j(not_zero, &true_value);
+ __ j(not_zero, &true_value, Label::kNear);
__ bind(&false_value);
__ mov(result, factory()->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ mov(result, factory()->true_value());
__ bind(&done);
@@ -1633,8 +1727,8 @@ void LCodeGen::DoIsSmi(LIsSmi* instr) {
ASSERT(instr->hydrogen()->value()->representation().IsTagged());
__ test(input, Immediate(kSmiTagMask));
__ mov(result, factory()->true_value());
- NearLabel done;
- __ j(zero, &done);
+ Label done;
+ __ j(zero, &done, Label::kNear);
__ mov(result, factory()->false_value());
__ bind(&done);
}
@@ -1651,6 +1745,44 @@ void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
}
+void LCodeGen::DoIsUndetectable(LIsUndetectable* instr) {
+ Register input = ToRegister(instr->InputAt(0));
+ Register result = ToRegister(instr->result());
+
+ ASSERT(instr->hydrogen()->value()->representation().IsTagged());
+ Label false_label, done;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ test(input, Immediate(kSmiTagMask));
+ __ j(zero, &false_label, Label::kNear);
+ __ mov(result, FieldOperand(input, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(result, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(zero, &false_label, Label::kNear);
+ __ mov(result, factory()->true_value());
+ __ jmp(&done);
+ __ bind(&false_label);
+ __ mov(result, factory()->false_value());
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->InputAt(0));
+ Register temp = ToRegister(instr->TempAt(0));
+
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ test(input, Immediate(kSmiTagMask));
+ __ j(zero, chunk_->GetAssemblyLabel(false_block));
+ __ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
+ __ test_b(FieldOperand(temp, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ EmitBranch(true_block, false_block, not_zero);
+}
+
+
static InstanceType TestType(HHasInstanceType* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
@@ -1677,12 +1809,13 @@ void LCodeGen::DoHasInstanceType(LHasInstanceType* instr) {
ASSERT(instr->hydrogen()->value()->representation().IsTagged());
__ test(input, Immediate(kSmiTagMask));
- NearLabel done, is_false;
- __ j(zero, &is_false);
+ Label done, is_false;
+ __ j(zero, &is_false, Label::kNear);
__ CmpObjectType(input, TestType(instr->hydrogen()), result);
- __ j(NegateCondition(BranchCondition(instr->hydrogen())), &is_false);
+ __ j(NegateCondition(BranchCondition(instr->hydrogen())),
+ &is_false, Label::kNear);
__ mov(result, factory()->true_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&is_false);
__ mov(result, factory()->false_value());
__ bind(&done);
@@ -1727,8 +1860,8 @@ void LCodeGen::DoHasCachedArrayIndex(LHasCachedArrayIndex* instr) {
__ mov(result, factory()->true_value());
__ test(FieldOperand(input, String::kHashFieldOffset),
Immediate(String::kContainsCachedArrayIndexMask));
- NearLabel done;
- __ j(zero, &done);
+ Label done;
+ __ j(zero, &done, Label::kNear);
__ mov(result, factory()->false_value());
__ bind(&done);
}
@@ -1810,16 +1943,16 @@ void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
ASSERT(input.is(result));
Register temp = ToRegister(instr->TempAt(0));
Handle<String> class_name = instr->hydrogen()->class_name();
- NearLabel done;
+ Label done;
Label is_true, is_false;
EmitClassOfTest(&is_true, &is_false, class_name, input, temp, input);
- __ j(not_equal, &is_false);
+ __ j(not_equal, &is_false, Label::kNear);
__ bind(&is_true);
__ mov(result, factory()->true_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&is_false);
__ mov(result, factory()->false_value());
@@ -1867,11 +2000,11 @@ void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
InstanceofStub stub(InstanceofStub::kArgsInRegisters);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
- NearLabel true_value, done;
+ Label true_value, done;
__ test(eax, Operand(eax));
- __ j(zero, &true_value);
+ __ j(zero, &true_value, Label::kNear);
__ mov(ToRegister(instr->result()), factory()->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ mov(ToRegister(instr->result()), factory()->true_value());
__ bind(&done);
@@ -1916,17 +2049,17 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
// A Smi is not an instance of anything.
__ test(object, Immediate(kSmiTagMask));
- __ j(zero, &false_result, not_taken);
+ __ j(zero, &false_result);
// This is the inlined call site instanceof cache. The two occurences of the
// hole value will be patched to the last map/result pair generated by the
// instanceof stub.
- NearLabel cache_miss;
+ Label cache_miss;
Register map = ToRegister(instr->TempAt(0));
__ mov(map, FieldOperand(object, HeapObject::kMapOffset));
__ bind(deferred->map_check()); // Label for calculating code patching.
__ cmp(map, factory()->the_hole_value()); // Patched to cached map.
- __ j(not_equal, &cache_miss, not_taken);
+ __ j(not_equal, &cache_miss, Label::kNear);
__ mov(eax, factory()->the_hole_value()); // Patched to either true or false.
__ jmp(&done);
@@ -2018,11 +2151,11 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
if (op == Token::GT || op == Token::LTE) {
condition = ReverseCondition(condition);
}
- NearLabel true_value, done;
+ Label true_value, done;
__ test(eax, Operand(eax));
- __ j(condition, &true_value);
+ __ j(condition, &true_value, Label::kNear);
__ mov(ToRegister(instr->result()), factory()->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ mov(ToRegister(instr->result()), factory()->true_value());
__ bind(&done);
@@ -2060,7 +2193,7 @@ void LCodeGen::DoReturn(LReturn* instr) {
}
__ mov(esp, ebp);
__ pop(ebp);
- __ Ret((ParameterCount() + 1) * kPointerSize, ecx);
+ __ Ret((GetParameterCount() + 1) * kPointerSize, ecx);
}
@@ -2149,23 +2282,29 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
}
-void LCodeGen::EmitLoadField(Register result,
- Register object,
- Handle<Map> type,
- Handle<String> name) {
+void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
+ Register object,
+ Handle<Map> type,
+ Handle<String> name) {
LookupResult lookup;
type->LookupInDescriptors(NULL, *name, &lookup);
- ASSERT(lookup.IsProperty() && lookup.type() == FIELD);
- int index = lookup.GetLocalFieldIndexFromMap(*type);
- int offset = index * kPointerSize;
- if (index < 0) {
- // Negative property indices are in-object properties, indexed
- // from the end of the fixed part of the object.
- __ mov(result, FieldOperand(object, offset + type->instance_size()));
+ ASSERT(lookup.IsProperty() &&
+ (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION));
+ if (lookup.type() == FIELD) {
+ int index = lookup.GetLocalFieldIndexFromMap(*type);
+ int offset = index * kPointerSize;
+ if (index < 0) {
+ // Negative property indices are in-object properties, indexed
+ // from the end of the fixed part of the object.
+ __ mov(result, FieldOperand(object, offset + type->instance_size()));
+ } else {
+ // Non-negative property indices are in the properties array.
+ __ mov(result, FieldOperand(object, JSObject::kPropertiesOffset));
+ __ mov(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
+ }
} else {
- // Non-negative property indices are in the properties array.
- __ mov(result, FieldOperand(object, JSObject::kPropertiesOffset));
- __ mov(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
+ Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
+ LoadHeapObject(result, Handle<HeapObject>::cast(function));
}
}
@@ -2182,30 +2321,30 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
} else {
- NearLabel done;
+ Label done;
for (int i = 0; i < map_count - 1; ++i) {
Handle<Map> map = instr->hydrogen()->types()->at(i);
- NearLabel next;
+ Label next;
__ cmp(FieldOperand(object, HeapObject::kMapOffset), map);
- __ j(not_equal, &next);
- EmitLoadField(result, object, map, name);
- __ jmp(&done);
+ __ j(not_equal, &next, Label::kNear);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
+ __ jmp(&done, Label::kNear);
__ bind(&next);
}
Handle<Map> map = instr->hydrogen()->types()->last();
__ cmp(FieldOperand(object, HeapObject::kMapOffset), map);
if (instr->hydrogen()->need_generic()) {
- NearLabel generic;
- __ j(not_equal, &generic);
- EmitLoadField(result, object, map, name);
- __ jmp(&done);
+ Label generic;
+ __ j(not_equal, &generic, Label::kNear);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
+ __ jmp(&done, Label::kNear);
__ bind(&generic);
__ mov(ecx, name);
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
} else {
DeoptimizeIf(not_equal, instr->environment());
- EmitLoadField(result, object, map, name);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
}
__ bind(&done);
}
@@ -2233,10 +2372,10 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
DeoptimizeIf(not_equal, instr->environment());
// Check whether the function has an instance prototype.
- NearLabel non_instance;
+ Label non_instance;
__ test_b(FieldOperand(result, Map::kBitFieldOffset),
1 << Map::kHasNonInstancePrototype);
- __ j(not_zero, &non_instance);
+ __ j(not_zero, &non_instance, Label::kNear);
// Get the prototype or initial map from the function.
__ mov(result,
@@ -2247,13 +2386,13 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
DeoptimizeIf(equal, instr->environment());
// If the function does not have an initial map, we're done.
- NearLabel done;
+ Label done;
__ CmpObjectType(result, MAP_TYPE, temp);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
// Get the prototype from the initial map.
__ mov(result, FieldOperand(result, Map::kPrototypeOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// Non-instance prototype: Fetch prototype from constructor field
// in the function's map.
@@ -2270,13 +2409,13 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
Register input = ToRegister(instr->InputAt(0));
__ mov(result, FieldOperand(input, JSObject::kElementsOffset));
if (FLAG_debug_code) {
- NearLabel done;
+ Label done;
__ cmp(FieldOperand(result, HeapObject::kMapOffset),
Immediate(factory()->fixed_array_map()));
- __ j(equal, &done);
+ __ j(equal, &done, Label::kNear);
__ cmp(FieldOperand(result, HeapObject::kMapOffset),
Immediate(factory()->fixed_cow_array_map()));
- __ j(equal, &done);
+ __ j(equal, &done, Label::kNear);
Register temp((result.is(eax)) ? ebx : eax);
__ push(temp);
__ mov(temp, FieldOperand(result, HeapObject::kMapOffset));
@@ -2327,41 +2466,63 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
FixedArray::kHeaderSize));
// Check for the hole value.
- __ cmp(result, factory()->the_hole_value());
- DeoptimizeIf(equal, instr->environment());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ cmp(result, factory()->the_hole_value());
+ DeoptimizeIf(equal, instr->environment());
+ }
+}
+
+
+Operand LCodeGen::BuildExternalArrayOperand(LOperand* external_pointer,
+ LOperand* key,
+ ExternalArrayType array_type) {
+ Register external_pointer_reg = ToRegister(external_pointer);
+ int shift_size = ExternalArrayTypeToShiftSize(array_type);
+ if (key->IsConstantOperand()) {
+ int constant_value = ToInteger32(LConstantOperand::cast(key));
+ if (constant_value & 0xF0000000) {
+ Abort("array index constant value too big");
+ }
+ return Operand(external_pointer_reg, constant_value * (1 << shift_size));
+ } else {
+ ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size);
+ return Operand(external_pointer_reg, ToRegister(key), scale_factor, 0);
+ }
}
void LCodeGen::DoLoadKeyedSpecializedArrayElement(
LLoadKeyedSpecializedArrayElement* instr) {
- Register external_pointer = ToRegister(instr->external_pointer());
- Register key = ToRegister(instr->key());
ExternalArrayType array_type = instr->array_type();
+ Operand operand(BuildExternalArrayOperand(instr->external_pointer(),
+ instr->key(), array_type));
if (array_type == kExternalFloatArray) {
XMMRegister result(ToDoubleRegister(instr->result()));
- __ movss(result, Operand(external_pointer, key, times_4, 0));
+ __ movss(result, operand);
__ cvtss2sd(result, result);
+ } else if (array_type == kExternalDoubleArray) {
+ __ movdbl(ToDoubleRegister(instr->result()), operand);
} else {
Register result(ToRegister(instr->result()));
switch (array_type) {
case kExternalByteArray:
- __ movsx_b(result, Operand(external_pointer, key, times_1, 0));
+ __ movsx_b(result, operand);
break;
case kExternalUnsignedByteArray:
case kExternalPixelArray:
- __ movzx_b(result, Operand(external_pointer, key, times_1, 0));
+ __ movzx_b(result, operand);
break;
case kExternalShortArray:
- __ movsx_w(result, Operand(external_pointer, key, times_2, 0));
+ __ movsx_w(result, operand);
break;
case kExternalUnsignedShortArray:
- __ movzx_w(result, Operand(external_pointer, key, times_2, 0));
+ __ movzx_w(result, operand);
break;
case kExternalIntArray:
- __ mov(result, Operand(external_pointer, key, times_4, 0));
+ __ mov(result, operand);
break;
case kExternalUnsignedIntArray:
- __ mov(result, Operand(external_pointer, key, times_4, 0));
+ __ mov(result, operand);
__ test(result, Operand(result));
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
@@ -2369,6 +2530,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
DeoptimizeIf(negative, instr->environment());
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
UNREACHABLE();
break;
}
@@ -2390,16 +2552,16 @@ void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
Register result = ToRegister(instr->result());
// Check for arguments adapter frame.
- NearLabel done, adapted;
+ Label done, adapted;
__ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(result, Operand(result, StandardFrameConstants::kContextOffset));
__ cmp(Operand(result),
Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ j(equal, &adapted);
+ __ j(equal, &adapted, Label::kNear);
// No arguments adaptor frame.
__ mov(result, Operand(ebp));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// Arguments adaptor frame present.
__ bind(&adapted);
@@ -2415,12 +2577,12 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
Operand elem = ToOperand(instr->InputAt(0));
Register result = ToRegister(instr->result());
- NearLabel done;
+ Label done;
// If no arguments adaptor frame the number of arguments is fixed.
__ cmp(ebp, elem);
__ mov(result, Immediate(scope()->num_parameters()));
- __ j(equal, &done);
+ __ j(equal, &done, Label::kNear);
// Arguments adaptor frame present. Get argument length from there.
__ mov(result, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
@@ -2443,20 +2605,23 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
ASSERT(function.is(edi)); // Required by InvokeFunction.
ASSERT(ToRegister(instr->result()).is(eax));
+ // TODO(1412): This is not correct if the called function is a
+ // strict mode function or a native.
+ //
// If the receiver is null or undefined, we have to pass the global object
// as a receiver.
- NearLabel global_object, receiver_ok;
+ Label global_object, receiver_ok;
__ cmp(receiver, factory()->null_value());
- __ j(equal, &global_object);
+ __ j(equal, &global_object, Label::kNear);
__ cmp(receiver, factory()->undefined_value());
- __ j(equal, &global_object);
+ __ j(equal, &global_object, Label::kNear);
// The receiver should be a JS object.
__ test(receiver, Immediate(kSmiTagMask));
DeoptimizeIf(equal, instr->environment());
__ CmpObjectType(receiver, FIRST_JS_OBJECT_TYPE, scratch);
DeoptimizeIf(below, instr->environment());
- __ jmp(&receiver_ok);
+ __ jmp(&receiver_ok, Label::kNear);
__ bind(&global_object);
// TODO(kmillikin): We have a hydrogen value for the global object. See
@@ -2464,6 +2629,8 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
// here.
__ mov(receiver, Operand(ebp, StandardFrameConstants::kContextOffset));
__ mov(receiver, ContextOperand(receiver, Context::GLOBAL_INDEX));
+ __ mov(receiver,
+ FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
__ bind(&receiver_ok);
// Copy the arguments to this function possibly from the
@@ -2477,10 +2644,10 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
// Loop through the arguments pushing them onto the execution
// stack.
- NearLabel invoke, loop;
+ Label invoke, loop;
// length is a small non-negative integer, due to the test above.
__ test(length, Operand(length));
- __ j(zero, &invoke);
+ __ j(zero, &invoke, Label::kNear);
__ bind(&loop);
__ push(Operand(elements, length, times_pointer_size, 1 * kPointerSize));
__ dec(length);
@@ -2496,8 +2663,9 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
SafepointGenerator safepoint_generator(this,
pointers,
env->deoptimization_index());
- v8::internal::ParameterCount actual(eax);
- __ InvokeFunction(function, actual, CALL_FUNCTION, &safepoint_generator);
+ ParameterCount actual(eax);
+ __ InvokeFunction(function, actual, CALL_FUNCTION,
+ safepoint_generator, CALL_AS_METHOD);
}
@@ -2541,7 +2709,8 @@ void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
int arity,
- LInstruction* instr) {
+ LInstruction* instr,
+ CallKind call_kind) {
// Change context if needed.
bool change_context =
(info()->closure()->context() != function->context()) ||
@@ -2563,6 +2732,7 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
RecordPosition(pointers->position());
// Invoke function.
+ __ SetCallKind(ecx, call_kind);
if (*function == *info()->closure()) {
__ CallSelf();
} else {
@@ -2577,7 +2747,10 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
ASSERT(ToRegister(instr->result()).is(eax));
__ mov(edi, instr->function());
- CallKnownFunction(instr->function(), instr->arity(), instr);
+ CallKnownFunction(instr->function(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD);
}
@@ -2665,7 +2838,7 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
if (r.IsDouble()) {
XMMRegister scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- __ pxor(scratch, scratch);
+ __ xorps(scratch, scratch);
__ subsd(scratch, input_reg);
__ pand(input_reg, scratch);
} else if (r.IsInteger32()) {
@@ -2687,7 +2860,7 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
XMMRegister xmm_scratch = xmm0;
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
+ __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
__ ucomisd(input_reg, xmm_scratch);
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
@@ -2710,25 +2883,16 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
+ Label below_half, done;
// xmm_scratch = 0.5
ExternalReference one_half = ExternalReference::address_of_one_half();
__ movdbl(xmm_scratch, Operand::StaticVariable(one_half));
+ __ ucomisd(xmm_scratch, input_reg);
+ __ j(above, &below_half);
// input = input + 0.5
__ addsd(input_reg, xmm_scratch);
- // We need to return -0 for the input range [-0.5, 0[, otherwise
- // compute Math.floor(value + 0.5).
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below_equal, instr->environment());
- } else {
- // If we don't need to bailout on -0, we check only bailout
- // on negative inputs.
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below, instr->environment());
- }
// Compute Math.floor(value + 0.5).
// Use truncating instruction (OK because input is positive).
@@ -2737,6 +2901,27 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
// Overflow is signalled with minint.
__ cmp(output_reg, 0x80000000u);
DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done);
+
+ __ bind(&below_half);
+
+ // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if
+ // we can ignore the difference between a result of -0 and +0.
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // If the sign is positive, we return +0.
+ __ movmskpd(output_reg, input_reg);
+ __ test(output_reg, Immediate(1));
+ DeoptimizeIf(not_zero, instr->environment());
+ } else {
+ // If the input is >= -0.5, we return +0.
+ __ mov(output_reg, Immediate(0xBF000000));
+ __ movd(xmm_scratch, Operand(output_reg));
+ __ cvtss2sd(xmm_scratch, xmm_scratch);
+ __ ucomisd(input_reg, xmm_scratch);
+ DeoptimizeIf(below, instr->environment());
+ }
+ __ Set(output_reg, Immediate(0));
+ __ bind(&done);
}
@@ -2751,7 +2936,7 @@ void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
XMMRegister xmm_scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
- __ xorpd(xmm_scratch, xmm_scratch);
+ __ xorps(xmm_scratch, xmm_scratch);
__ addsd(input_reg, xmm_scratch); // Convert -0 to +0.
__ sqrtsd(input_reg, input_reg);
}
@@ -2820,20 +3005,20 @@ void LCodeGen::DoPower(LPower* instr) {
void LCodeGen::DoMathLog(LUnaryMathOperation* instr) {
ASSERT(instr->InputAt(0)->Equals(instr->result()));
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- NearLabel positive, done, zero, negative;
- __ xorpd(xmm0, xmm0);
+ Label positive, done, zero;
+ __ xorps(xmm0, xmm0);
__ ucomisd(input_reg, xmm0);
- __ j(above, &positive);
- __ j(equal, &zero);
+ __ j(above, &positive, Label::kNear);
+ __ j(equal, &zero, Label::kNear);
ExternalReference nan = ExternalReference::address_of_nan();
__ movdbl(input_reg, Operand::StaticVariable(nan));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&zero);
__ push(Immediate(0xFFF00000));
__ push(Immediate(0));
__ movdbl(input_reg, Operand(esp, 0));
__ add(Operand(esp), Immediate(kDoubleSize));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&positive);
__ fldln2();
__ sub(Operand(esp), Immediate(kDoubleSize));
@@ -2896,6 +3081,21 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
}
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->context()).is(esi));
+ ASSERT(ToRegister(instr->function()).is(edi));
+ ASSERT(instr->HasPointerMap());
+ ASSERT(instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator generator(this, pointers, env->deoptimization_index());
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(edi, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
+}
+
+
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
ASSERT(ToRegister(instr->key()).is(ecx));
@@ -2913,10 +3113,11 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->
- ComputeCallInitialize(arity, NOT_IN_LOOP);
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
__ mov(ecx, instr->name());
- CallCode(ic, RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
+ CallCode(ic, mode, instr, CONTEXT_ADJUSTED);
}
@@ -2925,7 +3126,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
- CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_IMPLICIT);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, CONTEXT_ADJUSTED);
__ Drop(1);
}
@@ -2936,17 +3137,18 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->
- ComputeCallInitialize(arity, NOT_IN_LOOP);
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
__ mov(ecx, instr->name());
- CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr, CONTEXT_ADJUSTED);
+ CallCode(ic, mode, instr, CONTEXT_ADJUSTED);
}
void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
ASSERT(ToRegister(instr->result()).is(eax));
__ mov(edi, instr->target());
- CallKnownFunction(instr->target(), instr->arity(), instr);
+ CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION);
}
@@ -3017,46 +3219,32 @@ void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
void LCodeGen::DoStoreKeyedSpecializedArrayElement(
LStoreKeyedSpecializedArrayElement* instr) {
- Register external_pointer = ToRegister(instr->external_pointer());
- Register key = ToRegister(instr->key());
ExternalArrayType array_type = instr->array_type();
+ Operand operand(BuildExternalArrayOperand(instr->external_pointer(),
+ instr->key(), array_type));
if (array_type == kExternalFloatArray) {
__ cvtsd2ss(xmm0, ToDoubleRegister(instr->value()));
- __ movss(Operand(external_pointer, key, times_4, 0), xmm0);
+ __ movss(operand, xmm0);
+ } else if (array_type == kExternalDoubleArray) {
+ __ movdbl(operand, ToDoubleRegister(instr->value()));
} else {
Register value = ToRegister(instr->value());
switch (array_type) {
- case kExternalPixelArray: {
- // Clamp the value to [0..255].
- Register temp = ToRegister(instr->TempAt(0));
- // The dec_b below requires that the clamped value is in a byte
- // register. eax is an arbitrary choice to satisfy this requirement, we
- // hinted the register allocator to give us eax when building the
- // instruction.
- ASSERT(temp.is(eax));
- __ mov(temp, ToRegister(instr->value()));
- NearLabel done;
- __ test(temp, Immediate(0xFFFFFF00));
- __ j(zero, &done);
- __ setcc(negative, temp); // 1 if negative, 0 if positive.
- __ dec_b(temp); // 0 if negative, 255 if positive.
- __ bind(&done);
- __ mov_b(Operand(external_pointer, key, times_1, 0), temp);
- break;
- }
+ case kExternalPixelArray:
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(external_pointer, key, times_1, 0), value);
+ __ mov_b(operand, value);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(external_pointer, key, times_2, 0), value);
+ __ mov_w(operand, value);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(external_pointer, key, times_4, 0), value);
+ __ mov(operand, value);
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
UNREACHABLE();
break;
}
@@ -3143,7 +3331,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
DeferredStringCharCodeAt* deferred =
new DeferredStringCharCodeAt(this, instr);
- NearLabel flat_string, ascii_string, done;
+ Label flat_string, ascii_string, done;
// Fetch the instance type of the receiver into result register.
__ mov(result, FieldOperand(string, HeapObject::kMapOffset));
@@ -3152,7 +3340,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
// We need special handling for non-flat strings.
STATIC_ASSERT(kSeqStringTag == 0);
__ test(result, Immediate(kStringRepresentationMask));
- __ j(zero, &flat_string);
+ __ j(zero, &flat_string, Label::kNear);
// Handle non-flat strings.
__ test(result, Immediate(kIsConsStringMask));
@@ -3179,7 +3367,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
__ bind(&flat_string);
STATIC_ASSERT(kAsciiStringTag != 0);
__ test(result, Immediate(kStringEncodingMask));
- __ j(not_zero, &ascii_string);
+ __ j(not_zero, &ascii_string, Label::kNear);
// Two-byte string.
// Load the two-byte character code into the result register.
@@ -3195,7 +3383,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
times_2,
SeqTwoByteString::kHeaderSize));
}
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// ASCII string.
// Load the byte into the result register.
@@ -3299,6 +3487,22 @@ void LCodeGen::DoStringLength(LStringLength* instr) {
}
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ if (instr->left()->IsConstantOperand()) {
+ __ push(ToImmediate(instr->left()));
+ } else {
+ __ push(ToOperand(instr->left()));
+ }
+ if (instr->right()->IsConstantOperand()) {
+ __ push(ToImmediate(instr->right()));
+ } else {
+ __ push(ToOperand(instr->right()));
+ }
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
+}
+
+
void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
LOperand* input = instr->InputAt(0);
ASSERT(input->IsRegister() || input->IsStackSlot());
@@ -3340,13 +3544,13 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
// There was overflow, so bits 30 and 31 of the original integer
// disagree. Try to allocate a heap number in new space and store
// the value in there. If that fails, call the runtime system.
- NearLabel done;
+ Label done;
__ SmiUntag(reg);
__ xor_(reg, 0x80000000);
__ cvtsi2sd(xmm0, Operand(reg));
if (FLAG_inline_new) {
__ AllocateHeapNumber(reg, tmp, no_reg, &slow);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
}
// Slow case: Call the runtime system to do the number allocation.
@@ -3429,11 +3633,11 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
XMMRegister result_reg,
bool deoptimize_on_undefined,
LEnvironment* env) {
- NearLabel load_smi, done;
+ Label load_smi, done;
// Smi check.
__ test(input_reg, Immediate(kSmiTagMask));
- __ j(zero, &load_smi, not_taken);
+ __ j(zero, &load_smi, Label::kNear);
// Heap number map check.
__ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
@@ -3441,21 +3645,22 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
if (deoptimize_on_undefined) {
DeoptimizeIf(not_equal, env);
} else {
- NearLabel heap_number;
- __ j(equal, &heap_number);
+ Label heap_number;
+ __ j(equal, &heap_number, Label::kNear);
+
__ cmp(input_reg, factory()->undefined_value());
DeoptimizeIf(not_equal, env);
// Convert undefined to NaN.
ExternalReference nan = ExternalReference::address_of_nan();
__ movdbl(result_reg, Operand::StaticVariable(nan));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&heap_number);
}
// Heap number to XMM conversion.
__ movdbl(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// Smi to XMM conversion
__ bind(&load_smi);
@@ -3477,7 +3682,7 @@ class DeferredTaggedToI: public LDeferredCode {
void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
- NearLabel done, heap_number;
+ Label done, heap_number;
Register input_reg = ToRegister(instr->InputAt(0));
// Heap number map check.
@@ -3485,18 +3690,18 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
factory()->heap_number_map());
if (instr->truncating()) {
- __ j(equal, &heap_number);
+ __ j(equal, &heap_number, Label::kNear);
// Check for undefined. Undefined is converted to zero for truncating
// conversions.
__ cmp(input_reg, factory()->undefined_value());
DeoptimizeIf(not_equal, instr->environment());
__ mov(input_reg, 0);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&heap_number);
if (CpuFeatures::IsSupported(SSE3)) {
CpuFeatures::Scope scope(SSE3);
- NearLabel convert;
+ Label convert;
// Use more powerful conversion when sse3 is available.
// Load x87 register with heap number.
__ fld_d(FieldOperand(input_reg, HeapNumber::kValueOffset));
@@ -3506,7 +3711,7 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
const uint32_t kTooBigExponent =
(HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
__ cmp(Operand(input_reg), Immediate(kTooBigExponent));
- __ j(less, &convert);
+ __ j(less, &convert, Label::kNear);
// Pop FPU stack before deoptimizing.
__ ffree(0);
__ fincstp();
@@ -3520,7 +3725,6 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
__ mov(input_reg, Operand(esp, 0)); // Low word of answer is the result.
__ add(Operand(esp), Immediate(kDoubleSize));
} else {
- NearLabel deopt;
XMMRegister xmm_temp = ToDoubleRegister(instr->TempAt(0));
__ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
__ cvttsd2si(input_reg, Operand(xmm0));
@@ -3609,8 +3813,8 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
if (CpuFeatures::IsSupported(SSE3)) {
// This will deoptimize if the exponent of the input in out of range.
CpuFeatures::Scope scope(SSE3);
- NearLabel convert, done;
- __ j(not_equal, &done);
+ Label convert, done;
+ __ j(not_equal, &done, Label::kNear);
__ sub(Operand(esp), Immediate(kDoubleSize));
__ movdbl(Operand(esp, 0), input_reg);
// Get exponent alone and check for too-big exponent.
@@ -3619,7 +3823,7 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
const uint32_t kTooBigExponent =
(HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
__ cmp(Operand(result_reg), Immediate(kTooBigExponent));
- __ j(less, &convert);
+ __ j(less, &convert, Label::kNear);
__ add(Operand(esp), Immediate(kDoubleSize));
DeoptimizeIf(no_condition, instr->environment());
__ bind(&convert);
@@ -3630,13 +3834,13 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
__ add(Operand(esp), Immediate(kDoubleSize));
__ bind(&done);
} else {
- NearLabel done;
+ Label done;
Register temp_reg = ToRegister(instr->TempAt(0));
XMMRegister xmm_scratch = xmm0;
// If cvttsd2si succeeded, we're done. Otherwise, we attempt
// manual conversion.
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
// Get high 32 bits of the input in result_reg and temp_reg.
__ pshufd(xmm_scratch, input_reg, 1);
@@ -3686,7 +3890,7 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
__ bind(&done);
}
} else {
- NearLabel done;
+ Label done;
__ cvttsd2si(result_reg, Operand(input_reg));
__ cvtsi2sd(xmm0, Operand(result_reg));
__ ucomisd(xmm0, input_reg);
@@ -3696,7 +3900,7 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
// The integer converted back is equal to the original. We
// only have to test if we got -0 as an input.
__ test(result_reg, Operand(result_reg));
- __ j(not_zero, &done);
+ __ j(not_zero, &done, Label::kNear);
__ movmskpd(result_reg, input_reg);
// Bit 0 contains the sign of the double in input_reg.
// If input was positive, we are ok and return 0, otherwise
@@ -3726,29 +3930,43 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0));
Register temp = ToRegister(instr->TempAt(0));
- InstanceType first = instr->hydrogen()->first();
- InstanceType last = instr->hydrogen()->last();
__ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
- // If there is only one type in the interval check for equality.
- if (first == last) {
- __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
- static_cast<int8_t>(first));
- DeoptimizeIf(not_equal, instr->environment());
- } else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) {
- // String has a dedicated bit in instance type.
- __ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), kIsNotStringMask);
- DeoptimizeIf(not_zero, instr->environment());
- } else {
+ if (instr->hydrogen()->is_interval_check()) {
+ InstanceType first;
+ InstanceType last;
+ instr->hydrogen()->GetCheckInterval(&first, &last);
+
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
- DeoptimizeIf(below, instr->environment());
- // Omit check for the last type.
- if (last != LAST_TYPE) {
- __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
- static_cast<int8_t>(last));
- DeoptimizeIf(above, instr->environment());
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(not_equal, instr->environment());
+ } else {
+ DeoptimizeIf(below, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
+ static_cast<int8_t>(last));
+ DeoptimizeIf(above, instr->environment());
+ }
+ }
+ } else {
+ uint8_t mask;
+ uint8_t tag;
+ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
+
+ if (IsPowerOf2(mask)) {
+ ASSERT(tag == 0 || IsPowerOf2(tag));
+ __ test_b(FieldOperand(temp, Map::kInstanceTypeOffset), mask);
+ DeoptimizeIf(tag == 0 ? not_zero : zero, instr->environment());
+ } else {
+ __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ and_(temp, mask);
+ __ cmpb(Operand(temp), tag);
+ DeoptimizeIf(not_equal, instr->environment());
}
}
}
@@ -3772,6 +3990,54 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
}
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ XMMRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ __ ClampDoubleToUint8(value_reg, xmm0, result_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register value_reg = ToRegister(instr->result());
+ __ ClampUint8(value_reg);
+}
+
+
+void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register input_reg = ToRegister(instr->unclamped());
+ Label is_smi, done, heap_number;
+
+ __ JumpIfSmi(input_reg, &is_smi);
+
+ // Check for heap number
+ __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, instr->environment());
+ __ mov(input_reg, 0);
+ __ jmp(&done, Label::kNear);
+
+ // Heap number
+ __ bind(&heap_number);
+ __ movdbl(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ ClampDoubleToUint8(xmm0, xmm1, input_reg);
+ __ jmp(&done, Label::kNear);
+
+ // smi
+ __ bind(&is_smi);
+ __ SmiUntag(input_reg);
+ __ ClampUint8(input_reg);
+
+ __ bind(&done);
+}
+
+
void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) {
if (isolate()->heap()->InNewSpace(*object)) {
Handle<JSGlobalPropertyCell> cell =
@@ -3873,7 +4139,7 @@ void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
- NearLabel materialized;
+ Label materialized;
// Registers will be used as follows:
// edi = JS function.
// ecx = literals array.
@@ -3885,7 +4151,7 @@ void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
instr->hydrogen()->literal_index() * kPointerSize;
__ mov(ebx, FieldOperand(ecx, literal_offset));
__ cmp(ebx, factory()->undefined_value());
- __ j(not_equal, &materialized);
+ __ j(not_equal, &materialized, Label::kNear);
// Create regexp literal using runtime function
// Result will be in eax.
@@ -3961,16 +4227,16 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
Register result = ToRegister(instr->result());
Label true_label;
Label false_label;
- NearLabel done;
+ Label done;
Condition final_branch_condition = EmitTypeofIs(&true_label,
&false_label,
input,
instr->type_literal());
- __ j(final_branch_condition, &true_label);
+ __ j(final_branch_condition, &true_label, Label::kNear);
__ bind(&false_label);
__ mov(result, factory()->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_label);
__ mov(result, factory()->true_value());
@@ -4064,15 +4330,14 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) {
Register result = ToRegister(instr->result());
- NearLabel true_label;
- NearLabel false_label;
- NearLabel done;
+ Label true_label;
+ Label done;
EmitIsConstructCall(result);
- __ j(equal, &true_label);
+ __ j(equal, &true_label, Label::kNear);
__ mov(result, factory()->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_label);
__ mov(result, factory()->true_value());
@@ -4096,10 +4361,10 @@ void LCodeGen::EmitIsConstructCall(Register temp) {
__ mov(temp, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
// Skip the arguments adaptor frame if it exists.
- NearLabel check_frame_marker;
+ Label check_frame_marker;
__ cmp(Operand(temp, StandardFrameConstants::kContextOffset),
Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ j(not_equal, &check_frame_marker);
+ __ j(not_equal, &check_frame_marker, Label::kNear);
__ mov(temp, Operand(temp, StandardFrameConstants::kCallerFPOffset));
// Check the marker in the calling frame.
@@ -4142,17 +4407,17 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
env->deoptimization_index());
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
__ push(Immediate(Smi::FromInt(strict_mode_flag())));
- __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, &safepoint_generator);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator);
}
void LCodeGen::DoStackCheck(LStackCheck* instr) {
// Perform stack overflow check.
- NearLabel done;
+ Label done;
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
- __ j(above_equal, &done);
+ __ j(above_equal, &done, Label::kNear);
StackCheckStub stub;
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, RESTORE_CONTEXT);
@@ -4177,6 +4442,35 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
}
+void LCodeGen::DoIn(LIn* instr) {
+ LOperand* obj = instr->object();
+ LOperand* key = instr->key();
+ if (key->IsConstantOperand()) {
+ __ push(ToImmediate(key));
+ } else {
+ __ push(ToOperand(key));
+ }
+ if (obj->IsConstantOperand()) {
+ __ push(ToImmediate(obj));
+ } else {
+ __ push(ToOperand(obj));
+ }
+ ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ // Create safepoint generator that will also ensure enough space in the
+ // reloc info for patching in deoptimization (since this is invoking a
+ // builtin)
+ SafepointGenerator safepoint_generator(this,
+ pointers,
+ env->deoptimization_index());
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator);
+}
+
+
#undef __
} } // namespace v8::internal
diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h
index bdccd3c4..1a98d8dd 100644
--- a/src/ia32/lithium-codegen-ia32.h
+++ b/src/ia32/lithium-codegen-ia32.h
@@ -105,6 +105,7 @@ class LCodeGen BASE_EMBEDDED {
// Parallel move support.
void DoParallelMove(LParallelMove* move);
+ void DoGap(LGap* instr);
// Emit frame translation commands for an environment.
void WriteTranslation(LEnvironment* environment, Translation* translation);
@@ -147,8 +148,8 @@ class LCodeGen BASE_EMBEDDED {
Register temporary,
Register temporary2);
- int StackSlotCount() const { return chunk()->spill_slot_count(); }
- int ParameterCount() const { return scope()->num_parameters(); }
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+ int GetParameterCount() const { return scope()->num_parameters(); }
void Abort(const char* format, ...);
void Comment(const char* format, ...);
@@ -207,7 +208,8 @@ class LCodeGen BASE_EMBEDDED {
// to be in edi.
void CallKnownFunction(Handle<JSFunction> function,
int arity,
- LInstruction* instr);
+ LInstruction* instr,
+ CallKind call_kind);
void LoadHeapObject(Register result, Handle<HeapObject> object);
@@ -228,6 +230,9 @@ class LCodeGen BASE_EMBEDDED {
Register ToRegister(int index) const;
XMMRegister ToDoubleRegister(int index) const;
int ToInteger32(LConstantOperand* op) const;
+ Operand BuildExternalArrayOperand(LOperand* external_pointer,
+ LOperand* key,
+ ExternalArrayType array_type);
// Specific math operations - used from DoUnaryMathOperation.
void EmitIntegerMathAbs(LUnaryMathOperation* instr);
@@ -280,10 +285,10 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
- void EmitLoadField(Register result,
- Register object,
- Handle<Map> type,
- Handle<String> name);
+ void EmitLoadFieldOrConstantFunction(Register result,
+ Register object,
+ Handle<Map> type,
+ Handle<String> name);
LChunk* const chunk_;
MacroAssembler* const masm_;
diff --git a/src/ia32/lithium-gap-resolver-ia32.cc b/src/ia32/lithium-gap-resolver-ia32.cc
index 3d1da40a..9d91c618 100644
--- a/src/ia32/lithium-gap-resolver-ia32.cc
+++ b/src/ia32/lithium-gap-resolver-ia32.cc
@@ -309,12 +309,15 @@ void LGapResolver::EmitMove(int index) {
__ mov(dst, src);
} else if (source->IsDoubleRegister()) {
- ASSERT(destination->IsDoubleRegister() ||
- destination->IsDoubleStackSlot());
XMMRegister src = cgen_->ToDoubleRegister(source);
- Operand dst = cgen_->ToOperand(destination);
- __ movdbl(dst, src);
-
+ if (destination->IsDoubleRegister()) {
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ __ movaps(dst, src);
+ } else {
+ ASSERT(destination->IsDoubleStackSlot());
+ Operand dst = cgen_->ToOperand(destination);
+ __ movdbl(dst, src);
+ }
} else if (source->IsDoubleStackSlot()) {
ASSERT(destination->IsDoubleRegister() ||
destination->IsDoubleStackSlot());
@@ -391,13 +394,19 @@ void LGapResolver::EmitSwap(int index) {
__ mov(dst, tmp1);
__ mov(src, tmp0);
}
+ } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
+ // XMM register-register swap. We rely on having xmm0
+ // available as a fixed scratch register.
+ XMMRegister src = cgen_->ToDoubleRegister(source);
+ XMMRegister dst = cgen_->ToDoubleRegister(destination);
+ __ movaps(xmm0, src);
+ __ movaps(src, dst);
+ __ movaps(dst, xmm0);
} else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
- // XMM register-register or register-memory. We rely on having xmm0
+ // XMM register-memory swap. We rely on having xmm0
// available as a fixed scratch register.
- ASSERT(source->IsDoubleRegister() || source->IsDoubleStackSlot());
- ASSERT(destination->IsDoubleRegister() ||
- destination->IsDoubleStackSlot());
+ ASSERT(source->IsDoubleStackSlot() || destination->IsDoubleStackSlot());
XMMRegister reg = cgen_->ToDoubleRegister(source->IsDoubleRegister()
? source
: destination);
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 4b10562e..91606cee 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -71,22 +71,21 @@ void LOsrEntry::MarkSpilledDoubleRegister(int allocation_index,
#ifdef DEBUG
void LInstruction::VerifyCall() {
- // Call instructions can use only fixed registers as
- // temporaries and outputs because all registers
- // are blocked by the calling convention.
- // Inputs must use a fixed register.
+ // Call instructions can use only fixed registers as temporaries and
+ // outputs because all registers are blocked by the calling convention.
+ // Inputs operands must use a fixed register or use-at-start policy or
+ // a non-register policy.
ASSERT(Output() == NULL ||
LUnallocated::cast(Output())->HasFixedPolicy() ||
!LUnallocated::cast(Output())->HasRegisterPolicy());
for (UseIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||
+ operand->IsUsedAtStart());
}
for (TempIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
}
}
#endif
@@ -240,6 +239,13 @@ void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
}
+void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_undetectable(");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
@@ -303,6 +309,15 @@ void LStoreContextSlot::PrintDataTo(StringStream* stream) {
}
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(" ");
+ InputAt(1)->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[ecx] #%d / ", arity());
}
@@ -441,7 +456,7 @@ void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
- LGap* gap = new LGap(block);
+ LInstructionGap* gap = new LInstructionGap(block);
int index = -1;
if (instr->IsControl()) {
instructions_.Add(gap);
@@ -844,24 +859,22 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
right = UseFixed(right_value, ecx);
}
- // Shift operations can only deoptimize if we do a logical shift
- // by 0 and the result cannot be truncated to int32.
- bool can_deopt = (op == Token::SHR && constant_value == 0);
- if (can_deopt) {
- bool can_truncate = true;
- for (int i = 0; i < instr->uses()->length(); i++) {
- if (!instr->uses()->at(i)->CheckFlag(HValue::kTruncatingToInt32)) {
- can_truncate = false;
+ // Shift operations can only deoptimize if we do a logical shift by 0 and
+ // the result cannot be truncated to int32.
+ bool may_deopt = (op == Token::SHR && constant_value == 0);
+ bool does_deopt = false;
+ if (may_deopt) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
break;
}
}
- can_deopt = !can_truncate;
}
- LShiftI* result = new LShiftI(op, left, right, can_deopt);
- return can_deopt
- ? AssignEnvironment(DefineSameAsFirst(result))
- : DefineSameAsFirst(result);
+ LInstruction* result =
+ DefineSameAsFirst(new LShiftI(op, left, right, does_deopt));
+ return does_deopt ? AssignEnvironment(result) : result;
}
@@ -1004,6 +1017,8 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
outer);
int argument_index = 0;
for (int i = 0; i < value_count; ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
HValue* value = hydrogen_env->values()->at(i);
LOperand* op = NULL;
if (value->IsArgumentsObject()) {
@@ -1031,106 +1046,102 @@ LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
LInstruction* LChunkBuilder::DoTest(HTest* instr) {
HValue* v = instr->value();
- if (v->EmitAtUses()) {
- if (v->IsClassOfTest()) {
- HClassOfTest* compare = HClassOfTest::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
- TempRegister(),
- TempRegister());
- } else if (v->IsCompare()) {
- HCompare* compare = HCompare::cast(v);
- Token::Value op = compare->token();
- HValue* left = compare->left();
- HValue* right = compare->right();
- Representation r = compare->GetInputRepresentation();
- if (r.IsInteger32()) {
- ASSERT(left->representation().IsInteger32());
- ASSERT(right->representation().IsInteger32());
-
- return new LCmpIDAndBranch(UseRegisterAtStart(left),
- UseOrConstantAtStart(right));
- } else if (r.IsDouble()) {
- ASSERT(left->representation().IsDouble());
- ASSERT(right->representation().IsDouble());
-
- return new LCmpIDAndBranch(UseRegisterAtStart(left),
- UseRegisterAtStart(right));
- } else {
- ASSERT(left->representation().IsTagged());
- ASSERT(right->representation().IsTagged());
- bool reversed = op == Token::GT || op == Token::LTE;
- LOperand* left_operand = UseFixed(left, reversed ? eax : edx);
- LOperand* right_operand = UseFixed(right, reversed ? edx : eax);
- LCmpTAndBranch* result = new LCmpTAndBranch(left_operand,
- right_operand);
- return MarkAsCall(result, instr);
- }
- } else if (v->IsIsSmi()) {
- HIsSmi* compare = HIsSmi::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LIsSmiAndBranch(Use(compare->value()));
- } else if (v->IsHasInstanceType()) {
- HHasInstanceType* compare = HHasInstanceType::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LHasInstanceTypeAndBranch(UseRegisterAtStart(compare->value()),
- TempRegister());
- } else if (v->IsHasCachedArrayIndex()) {
- HHasCachedArrayIndex* compare = HHasCachedArrayIndex::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LHasCachedArrayIndexAndBranch(
- UseRegisterAtStart(compare->value()));
- } else if (v->IsIsNull()) {
- HIsNull* compare = HIsNull::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- // We only need a temp register for non-strict compare.
- LOperand* temp = compare->is_strict() ? NULL : TempRegister();
- return new LIsNullAndBranch(UseRegisterAtStart(compare->value()),
- temp);
- } else if (v->IsIsObject()) {
- HIsObject* compare = HIsObject::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- LOperand* temp1 = TempRegister();
- LOperand* temp2 = TempRegister();
- return new LIsObjectAndBranch(UseRegister(compare->value()),
- temp1,
- temp2);
- } else if (v->IsCompareJSObjectEq()) {
- HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
- return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
- UseRegisterAtStart(compare->right()));
- } else if (v->IsInstanceOf()) {
- HInstanceOf* instance_of = HInstanceOf::cast(v);
- LOperand* left = UseFixed(instance_of->left(), InstanceofStub::left());
- LOperand* right = UseFixed(instance_of->right(), InstanceofStub::right());
- LOperand* context = UseFixed(instance_of->context(), esi);
- LInstanceOfAndBranch* result =
- new LInstanceOfAndBranch(context, left, right);
- return MarkAsCall(result, instr);
- } else if (v->IsTypeofIs()) {
- HTypeofIs* typeof_is = HTypeofIs::cast(v);
- return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
- } else if (v->IsIsConstructCall()) {
- return new LIsConstructCallAndBranch(TempRegister());
+ if (!v->EmitAtUses()) {
+ return new LBranch(UseRegisterAtStart(v));
+ } else if (v->IsClassOfTest()) {
+ HClassOfTest* compare = HClassOfTest::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
+ TempRegister(),
+ TempRegister());
+ } else if (v->IsCompare()) {
+ HCompare* compare = HCompare::cast(v);
+ Token::Value op = compare->token();
+ HValue* left = compare->left();
+ HValue* right = compare->right();
+ Representation r = compare->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(left->representation().IsInteger32());
+ ASSERT(right->representation().IsInteger32());
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
+ UseOrConstantAtStart(right));
+ } else if (r.IsDouble()) {
+ ASSERT(left->representation().IsDouble());
+ ASSERT(right->representation().IsDouble());
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
+ UseRegisterAtStart(right));
} else {
- if (v->IsConstant()) {
- if (HConstant::cast(v)->ToBoolean()) {
- return new LGoto(instr->FirstSuccessor()->block_id());
- } else {
- return new LGoto(instr->SecondSuccessor()->block_id());
- }
- }
- Abort("Undefined compare before branch");
- return NULL;
+ ASSERT(left->representation().IsTagged());
+ ASSERT(right->representation().IsTagged());
+ bool reversed = op == Token::GT || op == Token::LTE;
+ LOperand* left_operand = UseFixed(left, reversed ? eax : edx);
+ LOperand* right_operand = UseFixed(right, reversed ? edx : eax);
+ LCmpTAndBranch* result = new LCmpTAndBranch(left_operand, right_operand);
+ return MarkAsCall(result, instr);
}
+ } else if (v->IsIsSmi()) {
+ HIsSmi* compare = HIsSmi::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsSmiAndBranch(Use(compare->value()));
+ } else if (v->IsIsUndetectable()) {
+ HIsUndetectable* compare = HIsUndetectable::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsUndetectableAndBranch(UseRegisterAtStart(compare->value()),
+ TempRegister());
+ } else if (v->IsHasInstanceType()) {
+ HHasInstanceType* compare = HHasInstanceType::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LHasInstanceTypeAndBranch(UseRegisterAtStart(compare->value()),
+ TempRegister());
+ } else if (v->IsHasCachedArrayIndex()) {
+ HHasCachedArrayIndex* compare = HHasCachedArrayIndex::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LHasCachedArrayIndexAndBranch(
+ UseRegisterAtStart(compare->value()));
+ } else if (v->IsIsNull()) {
+ HIsNull* compare = HIsNull::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ // We only need a temp register for non-strict compare.
+ LOperand* temp = compare->is_strict() ? NULL : TempRegister();
+ return new LIsNullAndBranch(UseRegisterAtStart(compare->value()), temp);
+ } else if (v->IsIsObject()) {
+ HIsObject* compare = HIsObject::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ LOperand* temp1 = TempRegister();
+ LOperand* temp2 = TempRegister();
+ return new LIsObjectAndBranch(UseRegister(compare->value()),
+ temp1,
+ temp2);
+ } else if (v->IsCompareJSObjectEq()) {
+ HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
+ return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
+ UseRegisterAtStart(compare->right()));
+ } else if (v->IsCompareSymbolEq()) {
+ HCompareSymbolEq* compare = HCompareSymbolEq::cast(v);
+ return new LCmpSymbolEqAndBranch(UseRegisterAtStart(compare->left()),
+ UseRegisterAtStart(compare->right()));
+ } else if (v->IsInstanceOf()) {
+ HInstanceOf* instance_of = HInstanceOf::cast(v);
+ LOperand* left = UseFixed(instance_of->left(), InstanceofStub::left());
+ LOperand* right = UseFixed(instance_of->right(), InstanceofStub::right());
+ LOperand* context = UseFixed(instance_of->context(), esi);
+ LInstanceOfAndBranch* result =
+ new LInstanceOfAndBranch(context, left, right);
+ return MarkAsCall(result, instr);
+ } else if (v->IsTypeofIs()) {
+ HTypeofIs* typeof_is = HTypeofIs::cast(v);
+ return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
+ } else if (v->IsIsConstructCall()) {
+ return new LIsConstructCallAndBranch(TempRegister());
+ } else if (v->IsConstant()) {
+ HBasicBlock* successor = HConstant::cast(v)->ToBoolean()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new LGoto(successor->block_id());
+ } else {
+ Abort("Undefined compare before branch");
+ return NULL;
}
- return new LBranch(UseRegisterAtStart(v));
}
@@ -1193,7 +1204,7 @@ LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
LInstruction* LChunkBuilder::DoContext(HContext* instr) {
- return DefineAsRegister(new LContext);
+ return instr->HasNoUses() ? NULL : DefineAsRegister(new LContext);
}
@@ -1222,6 +1233,15 @@ LInstruction* LChunkBuilder::DoCallConstantFunction(
}
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* context = UseFixed(instr->context(), esi);
+ LOperand* function = UseFixed(instr->function(), edi);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new LInvokeFunction(context, function);
+ return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
if (op == kMathLog) {
@@ -1523,6 +1543,15 @@ LInstruction* LChunkBuilder::DoCompareJSObjectEq(
}
+LInstruction* LChunkBuilder::DoCompareSymbolEq(
+ HCompareSymbolEq* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ LCmpSymbolEq* result = new LCmpSymbolEq(left, right);
+ return DefineAsRegister(result);
+}
+
+
LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1547,6 +1576,14 @@ LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
}
+LInstruction* LChunkBuilder::DoIsUndetectable(HIsUndetectable* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new LIsUndetectable(value));
+}
+
+
LInstruction* LChunkBuilder::DoHasInstanceType(HHasInstanceType* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1626,6 +1663,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
}
+LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
+ // All HForceRepresentation instructions should be eliminated in the
+ // representation change phase of Hydrogen.
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoChange(HChange* instr) {
Representation from = instr->from();
Representation to = instr->to();
@@ -1727,6 +1772,27 @@ LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
}
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ if (input_rep.IsDouble()) {
+ LOperand* reg = UseRegister(value);
+ return DefineAsRegister(new LClampDToUint8(reg));
+ } else if (input_rep.IsInteger32()) {
+ LOperand* reg = UseFixed(value, eax);
+ return DefineFixed(new LClampIToUint8(reg), eax);
+ } else {
+ ASSERT(input_rep.IsTagged());
+ LOperand* reg = UseFixed(value, eax);
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve xmm1 explicitly.
+ LOperand* temp = FixedTemp(xmm1);
+ LClampTToUint8* result = new LClampTToUint8(reg, temp);
+ return AssignEnvironment(DefineFixed(result, eax));
+ }
+}
+
+
LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
return new LReturn(UseFixed(instr->value(), eax));
}
@@ -1873,11 +1939,14 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
ExternalArrayType array_type = instr->array_type();
Representation representation(instr->representation());
- ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
- (representation.IsDouble() && array_type == kExternalFloatArray));
+ ASSERT(
+ (representation.IsInteger32() && (array_type != kExternalFloatArray &&
+ array_type != kExternalDoubleArray)) ||
+ (representation.IsDouble() && (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray)));
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
- LOperand* key = UseRegister(instr->key());
+ LOperand* key = UseRegisterOrConstant(instr->key());
LLoadKeyedSpecializedArrayElement* result =
new LLoadKeyedSpecializedArrayElement(external_pointer,
key);
@@ -1923,25 +1992,20 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
Representation representation(instr->value()->representation());
ExternalArrayType array_type = instr->array_type();
- ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
- (representation.IsDouble() && array_type == kExternalFloatArray));
+ ASSERT(
+ (representation.IsInteger32() && (array_type != kExternalFloatArray &&
+ array_type != kExternalDoubleArray)) ||
+ (representation.IsDouble() && (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray)));
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
- LOperand* key = UseRegister(instr->key());
- LOperand* temp = NULL;
-
- if (array_type == kExternalPixelArray) {
- // The generated code for pixel array stores requires that the clamped value
- // is in a byte register. eax is an arbitrary choice to satisfy this
- // requirement.
- temp = FixedTemp(eax);
- }
-
+ LOperand* key = UseRegisterOrConstant(instr->key());
LOperand* val = NULL;
if (array_type == kExternalByteArray ||
- array_type == kExternalUnsignedByteArray) {
+ array_type == kExternalUnsignedByteArray ||
+ array_type == kExternalPixelArray) {
// We need a byte register in this case for the value.
val = UseFixed(instr->value(), eax);
} else {
@@ -1950,8 +2014,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
return new LStoreKeyedSpecializedArrayElement(external_pointer,
key,
- val,
- temp);
+ val);
}
@@ -2002,6 +2065,13 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
}
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new LStringAdd(left, right), eax), instr);
+}
+
+
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
@@ -2046,7 +2116,8 @@ LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
LDeleteProperty* result =
- new LDeleteProperty(Use(instr->object()), UseOrConstant(instr->key()));
+ new LDeleteProperty(UseAtStart(instr->object()),
+ UseOrConstantAtStart(instr->key()));
return MarkAsCall(DefineFixed(result, eax), instr);
}
@@ -2165,8 +2236,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->function(),
- false,
- undefined);
+ HEnvironment::LITHIUM,
+ undefined,
+ instr->call_kind());
current_block_->UpdateEnvironment(inner);
chunk_->AddInlinedClosure(instr->closure());
return NULL;
@@ -2180,6 +2252,14 @@ LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
}
+LInstruction* LChunkBuilder::DoIn(HIn* instr) {
+ LOperand* key = UseOrConstantAtStart(instr->key());
+ LOperand* object = UseOrConstantAtStart(instr->object());
+ LIn* result = new LIn(key, object);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_IA32
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index be5658b1..979c4943 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -39,12 +39,6 @@ namespace internal {
// Forward declarations.
class LCodeGen;
-#define LITHIUM_ALL_INSTRUCTION_LIST(V) \
- V(ControlInstruction) \
- V(Call) \
- LITHIUM_CONCRETE_INSTRUCTION_LIST(V)
-
-
#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
V(AccessArgumentsAt) \
V(AddI) \
@@ -73,12 +67,17 @@ class LCodeGen;
V(CheckNonSmi) \
V(CheckPrototypeMaps) \
V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
V(ClassOfTest) \
V(ClassOfTestAndBranch) \
V(CmpID) \
V(CmpIDAndBranch) \
V(CmpJSObjectEq) \
V(CmpJSObjectEqAndBranch) \
+ V(CmpSymbolEq) \
+ V(CmpSymbolEqAndBranch) \
V(CmpMapAndBranch) \
V(CmpT) \
V(CmpTAndBranch) \
@@ -93,7 +92,6 @@ class LCodeGen;
V(ExternalArrayLength) \
V(FixedArrayLength) \
V(FunctionLiteral) \
- V(Gap) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
V(GlobalReceiver) \
@@ -102,18 +100,23 @@ class LCodeGen;
V(HasCachedArrayIndexAndBranch) \
V(HasInstanceType) \
V(HasInstanceTypeAndBranch) \
+ V(In) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
V(InstanceOfKnownGlobal) \
+ V(InstructionGap) \
V(Integer32ToDouble) \
+ V(InvokeFunction) \
+ V(IsConstructCall) \
+ V(IsConstructCallAndBranch) \
V(IsNull) \
V(IsNullAndBranch) \
V(IsObject) \
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
- V(IsConstructCall) \
- V(IsConstructCallAndBranch) \
+ V(IsUndetectable) \
+ V(IsUndetectableAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
@@ -154,6 +157,7 @@ class LCodeGen;
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
@@ -169,20 +173,16 @@ class LCodeGen;
V(ValueOf)
-#define DECLARE_INSTRUCTION(type) \
- virtual bool Is##type() const { return true; } \
- static L##type* cast(LInstruction* instr) { \
- ASSERT(instr->Is##type()); \
- return reinterpret_cast<L##type*>(instr); \
+#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
+ virtual Opcode opcode() const { return LInstruction::k##type; } \
+ virtual void CompileToNative(LCodeGen* generator); \
+ virtual const char* Mnemonic() const { return mnemonic; } \
+ static L##type* cast(LInstruction* instr) { \
+ ASSERT(instr->Is##type()); \
+ return reinterpret_cast<L##type*>(instr); \
}
-#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
- virtual void CompileToNative(LCodeGen* generator); \
- virtual const char* Mnemonic() const { return mnemonic; } \
- DECLARE_INSTRUCTION(type)
-
-
#define DECLARE_HYDROGEN_ACCESSOR(type) \
H##type* hydrogen() const { \
return H##type::cast(hydrogen_value()); \
@@ -204,10 +204,25 @@ class LInstruction: public ZoneObject {
virtual void PrintDataTo(StringStream* stream) = 0;
virtual void PrintOutputOperandTo(StringStream* stream) = 0;
- // Declare virtual type testers.
-#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
- LITHIUM_ALL_INSTRUCTION_LIST(DECLARE_DO)
-#undef DECLARE_DO
+ enum Opcode {
+ // Declare a unique enum value for each instruction.
+#define DECLARE_OPCODE(type) k##type,
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE)
+ kNumberOfInstructions
+#undef DECLARE_OPCODE
+ };
+
+ virtual Opcode opcode() const = 0;
+
+ // Declare non-virtual type testers for all leaf IR classes.
+#define DECLARE_PREDICATE(type) \
+ bool Is##type() const { return opcode() == k##type; }
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE)
+#undef DECLARE_PREDICATE
+
+ // Declare virtual predicates for instructions that don't have
+ // an opcode.
+ virtual bool IsGap() const { return false; }
virtual bool IsControl() const { return false; }
virtual void SetBranchTargets(int true_block_id, int false_block_id) { }
@@ -327,16 +342,20 @@ class LTemplateInstruction: public LInstruction {
class LGap: public LTemplateInstruction<0, 0, 0> {
public:
- explicit LGap(HBasicBlock* block)
- : block_(block) {
+ explicit LGap(HBasicBlock* block) : block_(block) {
parallel_moves_[BEFORE] = NULL;
parallel_moves_[START] = NULL;
parallel_moves_[END] = NULL;
parallel_moves_[AFTER] = NULL;
}
- DECLARE_CONCRETE_INSTRUCTION(Gap, "gap")
+ // Can't use the DECLARE-macro here because of sub-classes.
+ virtual bool IsGap() const { return true; }
virtual void PrintDataTo(StringStream* stream);
+ static LGap* cast(LInstruction* instr) {
+ ASSERT(instr->IsGap());
+ return reinterpret_cast<LGap*>(instr);
+ }
bool IsRedundant() const;
@@ -366,6 +385,14 @@ class LGap: public LTemplateInstruction<0, 0, 0> {
};
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
class LGoto: public LTemplateInstruction<0, 0, 0> {
public:
LGoto(int block_id, bool include_stack_check = false)
@@ -460,7 +487,6 @@ class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
template<int I, int T>
class LControlInstruction: public LTemplateInstruction<0, I, T> {
public:
- DECLARE_INSTRUCTION(ControlInstruction)
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -647,6 +673,28 @@ class LCmpJSObjectEqAndBranch: public LControlInstruction<2, 0> {
};
+class LCmpSymbolEq: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LCmpSymbolEq(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpSymbolEq, "cmp-symbol-eq")
+};
+
+
+class LCmpSymbolEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpSymbolEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpSymbolEqAndBranch, "cmp-symbol-eq-and-branch")
+};
+
+
class LIsNull: public LTemplateInstruction<1, 1, 0> {
public:
explicit LIsNull(LOperand* value) {
@@ -724,6 +772,31 @@ class LIsSmiAndBranch: public LControlInstruction<1, 0> {
};
+class LIsUndetectable: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LIsUndetectable(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectable, "is-undetectable")
+ DECLARE_HYDROGEN_ACCESSOR(IsUndetectable)
+};
+
+
+class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
+ public:
+ explicit LIsUndetectableAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch,
+ "is-undetectable-and-branch")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
class LHasInstanceType: public LTemplateInstruction<1, 1, 0> {
public:
explicit LHasInstanceType(LOperand* value) {
@@ -1130,6 +1203,7 @@ class LArithmeticD: public LTemplateInstruction<1, 2, 0> {
Token::Value op() const { return op_; }
+ virtual Opcode opcode() const { return LInstruction::kArithmeticD; }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1146,6 +1220,7 @@ class LArithmeticT: public LTemplateInstruction<1, 2, 0> {
inputs_[1] = right;
}
+ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1450,6 +1525,25 @@ class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> {
};
+class LInvokeFunction: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LInvokeFunction(LOperand* context, LOperand* function) {
+ inputs_[0] = context;
+ inputs_[1] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ LOperand* context() { return inputs_[0]; }
+ LOperand* function() { return inputs_[1]; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
class LCallKeyed: public LTemplateInstruction<1, 2, 0> {
public:
LCallKeyed(LOperand* context, LOperand* key) {
@@ -1720,16 +1814,14 @@ class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> {
};
-class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 1> {
+class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> {
public:
LStoreKeyedSpecializedArrayElement(LOperand* external_pointer,
LOperand* key,
- LOperand* val,
- LOperand* temp) {
+ LOperand* val) {
inputs_[0] = external_pointer;
inputs_[1] = key;
inputs_[2] = val;
- temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement,
@@ -1770,6 +1862,21 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> {
};
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+};
+
+
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
@@ -1869,6 +1976,43 @@ class LCheckSmi: public LTemplateInstruction<0, 1, 0> {
};
+class LClampDToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampDToUint8(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampIToUint8(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampTToUint8(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
+};
+
+
class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
public:
explicit LCheckNonSmi(LOperand* value) {
@@ -2009,6 +2153,20 @@ class LStackCheck: public LTemplateInstruction<0, 0, 0> {
};
+class LIn: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LIn(LOperand* key, LOperand* object) {
+ inputs_[0] = key;
+ inputs_[1] = object;
+ }
+
+ LOperand* key() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(In, "in")
+};
+
+
class LChunkBuilder;
class LChunk: public ZoneObject {
public:
@@ -2232,7 +2390,6 @@ class LChunkBuilder BASE_EMBEDDED {
};
#undef DECLARE_HYDROGEN_ACCESSOR
-#undef DECLARE_INSTRUCTION
#undef DECLARE_CONCRETE_INSTRUCTION
} } // namespace v8::internal
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 13394cbc..6e66b6e8 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -73,7 +73,69 @@ void MacroAssembler::RecordWriteHelper(Register object,
shr(addr, Page::kRegionSizeLog2);
// Set dirty mark for region.
- bts(Operand(object, Page::kDirtyFlagOffset), addr);
+ // Bit tests with a memory operand should be avoided on Intel processors,
+ // as they usually have long latency and multiple uops. We load the bit base
+ // operand to a register at first and store it back after bit set.
+ mov(scratch, Operand(object, Page::kDirtyFlagOffset));
+ bts(Operand(scratch), addr);
+ mov(Operand(object, Page::kDirtyFlagOffset), scratch);
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister scratch_reg,
+ Register result_reg) {
+ Label done;
+ ExternalReference zero_ref = ExternalReference::address_of_zero();
+ movdbl(scratch_reg, Operand::StaticVariable(zero_ref));
+ Set(result_reg, Immediate(0));
+ ucomisd(input_reg, scratch_reg);
+ j(below, &done, Label::kNear);
+ ExternalReference half_ref = ExternalReference::address_of_one_half();
+ movdbl(scratch_reg, Operand::StaticVariable(half_ref));
+ addsd(scratch_reg, input_reg);
+ cvttsd2si(result_reg, Operand(scratch_reg));
+ test(result_reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ Set(result_reg, Immediate(255));
+ bind(&done);
+}
+
+
+void MacroAssembler::ClampUint8(Register reg) {
+ Label done;
+ test(reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ setcc(negative, reg); // 1 if negative, 0 if positive.
+ dec_b(reg); // 0 if negative, 255 if positive.
+ bind(&done);
+}
+
+
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cc,
+ Label* branch,
+ Label::Distance branch_near) {
+ ASSERT(cc == equal || cc == not_equal);
+ if (Serializer::enabled()) {
+ // Can't do arithmetic on external references if it might get serialized.
+ mov(scratch, Operand(object));
+ // The mask isn't really an address. We load it as an external reference in
+ // case the size of the new space is different between the snapshot maker
+ // and the running system.
+ and_(Operand(scratch),
+ Immediate(ExternalReference::new_space_mask(isolate())));
+ cmp(Operand(scratch),
+ Immediate(ExternalReference::new_space_start(isolate())));
+ j(cc, branch, branch_near);
+ } else {
+ int32_t new_space_start = reinterpret_cast<int32_t>(
+ ExternalReference::new_space_start(isolate()).address());
+ lea(scratch, Operand(object, -new_space_start));
+ and_(scratch, isolate()->heap()->NewSpaceMask());
+ j(cc, branch, branch_near);
+ }
}
@@ -83,14 +145,14 @@ void MacroAssembler::RecordWrite(Register object,
Register scratch) {
// First, check if a write barrier is even needed. The tests below
// catch stores of Smis and stores into young gen.
- NearLabel done;
+ Label done;
// Skip barrier if writing a smi.
ASSERT_EQ(0, kSmiTag);
test(value, Immediate(kSmiTagMask));
- j(zero, &done);
+ j(zero, &done, Label::kNear);
- InNewSpace(object, value, equal, &done);
+ InNewSpace(object, value, equal, &done, Label::kNear);
// The offset is relative to a tagged or untagged HeapObject pointer,
// so either offset or offset + kHeapObjectTag must be a
@@ -220,16 +282,30 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
void MacroAssembler::CheckMap(Register obj,
Handle<Map> map,
Label* fail,
- bool is_heap_object) {
- if (!is_heap_object) {
- test(obj, Immediate(kSmiTagMask));
- j(zero, fail);
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, fail);
}
cmp(FieldOperand(obj, HeapObject::kMapOffset), Immediate(map));
j(not_equal, fail);
}
+void MacroAssembler::DispatchMap(Register obj,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ cmp(FieldOperand(obj, HeapObject::kMapOffset), Immediate(map));
+ j(equal, success);
+
+ bind(&fail);
+}
+
+
Condition MacroAssembler::IsObjectStringType(Register heap_object,
Register map,
Register instance_type) {
@@ -511,9 +587,9 @@ void MacroAssembler::Throw(Register value) {
// not NULL. The frame pointer is NULL in the exception handler of
// a JS entry frame.
Set(esi, Immediate(0)); // Tentatively set context pointer to NULL.
- NearLabel skip;
+ Label skip;
cmp(ebp, 0);
- j(equal, &skip, not_taken);
+ j(equal, &skip, Label::kNear);
mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
bind(&skip);
@@ -538,12 +614,12 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
mov(esp, Operand::StaticVariable(handler_address));
// Unwind the handlers until the ENTRY handler is found.
- NearLabel loop, done;
+ Label loop, done;
bind(&loop);
// Load the type of the current stack handler.
const int kStateOffset = StackHandlerConstants::kStateOffset;
cmp(Operand(esp, kStateOffset), Immediate(StackHandler::ENTRY));
- j(equal, &done);
+ j(equal, &done, Label::kNear);
// Fetch the next handler in the list.
const int kNextOffset = StackHandlerConstants::kNextOffset;
mov(esp, Operand(esp, kNextOffset));
@@ -614,7 +690,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
// Check if both contexts are the same.
cmp(scratch, FieldOperand(holder_reg, JSGlobalProxy::kContextOffset));
- j(equal, &same_contexts, taken);
+ j(equal, &same_contexts);
// Compare security tokens, save holder_reg on the stack so we can use it
// as a temporary register.
@@ -644,7 +720,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
mov(scratch, FieldOperand(scratch, token_offset));
cmp(scratch, FieldOperand(holder_reg, token_offset));
pop(holder_reg);
- j(not_equal, miss, not_taken);
+ j(not_equal, miss);
bind(&same_contexts);
}
@@ -732,9 +808,9 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
mov(top_reg, result);
}
add(Operand(top_reg), Immediate(object_size));
- j(carry, gc_required, not_taken);
+ j(carry, gc_required);
cmp(top_reg, Operand::StaticVariable(new_space_allocation_limit));
- j(above, gc_required, not_taken);
+ j(above, gc_required);
// Update allocation top.
UpdateAllocationTopHelper(top_reg, scratch);
@@ -831,9 +907,9 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
mov(result_end, object_size);
}
add(result_end, Operand(result));
- j(carry, gc_required, not_taken);
+ j(carry, gc_required);
cmp(result_end, Operand::StaticVariable(new_space_allocation_limit));
- j(above, gc_required, not_taken);
+ j(above, gc_required);
// Tag result if requested.
if ((flags & TAG_OBJECT) != 0) {
@@ -1062,9 +1138,9 @@ void MacroAssembler::NegativeZeroTest(Register result,
Label* then_label) {
Label ok;
test(result, Operand(result));
- j(not_zero, &ok, taken);
+ j(not_zero, &ok);
test(op, Operand(op));
- j(sign, then_label, not_taken);
+ j(sign, then_label);
bind(&ok);
}
@@ -1076,10 +1152,10 @@ void MacroAssembler::NegativeZeroTest(Register result,
Label* then_label) {
Label ok;
test(result, Operand(result));
- j(not_zero, &ok, taken);
+ j(not_zero, &ok);
mov(scratch, Operand(op1));
or_(scratch, Operand(op2));
- j(sign, then_label, not_taken);
+ j(sign, then_label);
bind(&ok);
}
@@ -1090,17 +1166,17 @@ void MacroAssembler::TryGetFunctionPrototype(Register function,
Label* miss) {
// Check that the receiver isn't a smi.
test(function, Immediate(kSmiTagMask));
- j(zero, miss, not_taken);
+ j(zero, miss);
// Check that the function really is a function.
CmpObjectType(function, JS_FUNCTION_TYPE, result);
- j(not_equal, miss, not_taken);
+ j(not_equal, miss);
// Make sure that the function has an instance prototype.
Label non_instance;
movzx_b(scratch, FieldOperand(result, Map::kBitFieldOffset));
test(scratch, Immediate(1 << Map::kHasNonInstancePrototype));
- j(not_zero, &non_instance, not_taken);
+ j(not_zero, &non_instance);
// Get the prototype or initial map from the function.
mov(result,
@@ -1110,7 +1186,7 @@ void MacroAssembler::TryGetFunctionPrototype(Register function,
// simply miss the cache instead. This will allow us to allocate a
// prototype object on-demand in the runtime system.
cmp(Operand(result), Immediate(isolate()->factory()->the_hole_value()));
- j(equal, miss, not_taken);
+ j(equal, miss);
// If the function does not have an initial map, we're done.
Label done;
@@ -1131,9 +1207,9 @@ void MacroAssembler::TryGetFunctionPrototype(Register function,
}
-void MacroAssembler::CallStub(CodeStub* stub) {
+void MacroAssembler::CallStub(CodeStub* stub, unsigned ast_id) {
ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs.
- call(stub->GetCode(), RelocInfo::CODE_TARGET);
+ call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id);
}
@@ -1391,7 +1467,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function,
// Check if the result handle holds 0.
test(eax, Operand(eax));
- j(zero, &empty_handle, not_taken);
+ j(zero, &empty_handle);
// It was non-zero. Dereference to get the result value.
mov(eax, Operand(eax, 0));
bind(&prologue);
@@ -1401,7 +1477,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function,
sub(Operand::StaticVariable(level_address), Immediate(1));
Assert(above_equal, "Invalid HandleScope level");
cmp(edi, Operand::StaticVariable(limit_address));
- j(not_equal, &delete_allocated_handles, not_taken);
+ j(not_equal, &delete_allocated_handles);
bind(&leave_exit_frame);
// Check if the function scheduled an exception.
@@ -1409,7 +1485,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function,
ExternalReference::scheduled_exception_address(isolate());
cmp(Operand::StaticVariable(scheduled_exception_address),
Immediate(isolate()->factory()->the_hole_value()));
- j(not_equal, &promote_scheduled_exception, not_taken);
+ j(not_equal, &promote_scheduled_exception);
LeaveApiExitFrame();
ret(stack_space * kPointerSize);
bind(&promote_scheduled_exception);
@@ -1456,13 +1532,32 @@ MaybeObject* MacroAssembler::TryJumpToExternalReference(
}
+void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) {
+ // This macro takes the dst register to make the code more readable
+ // at the call sites. However, the dst register has to be ecx to
+ // follow the calling convention which requires the call type to be
+ // in ecx.
+ ASSERT(dst.is(ecx));
+ if (call_kind == CALL_AS_FUNCTION) {
+ // Set to some non-zero smi by updating the least significant
+ // byte.
+ mov_b(Operand(dst), 1 << kSmiTagSize);
+ } else {
+ // Set to smi zero by clearing the register.
+ xor_(dst, Operand(dst));
+ }
+}
+
+
void MacroAssembler::InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual,
Handle<Code> code_constant,
const Operand& code_operand,
- NearLabel* done,
+ Label* done,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ Label::Distance done_near,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
bool definitely_matches = false;
Label invoke;
if (expected.is_immediate()) {
@@ -1512,10 +1607,13 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
+ SetCallKind(ecx, call_kind);
call(adaptor, RelocInfo::CODE_TARGET);
- if (post_call_generator != NULL) post_call_generator->Generate();
- jmp(done);
+ call_wrapper.AfterCall();
+ jmp(done, done_near);
} else {
+ SetCallKind(ecx, call_kind);
jmp(adaptor, RelocInfo::CODE_TARGET);
}
bind(&invoke);
@@ -1527,15 +1625,20 @@ void MacroAssembler::InvokeCode(const Operand& code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
- NearLabel done;
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
+ Label done;
InvokePrologue(expected, actual, Handle<Code>::null(), code,
- &done, flag, post_call_generator);
+ &done, flag, Label::kNear, call_wrapper,
+ call_kind);
if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(code));
+ SetCallKind(ecx, call_kind);
call(code);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ call_wrapper.AfterCall();
} else {
ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(ecx, call_kind);
jmp(code);
}
bind(&done);
@@ -1547,16 +1650,20 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
const ParameterCount& actual,
RelocInfo::Mode rmode,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
- NearLabel done;
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
+ Label done;
Operand dummy(eax);
- InvokePrologue(expected, actual, code, dummy, &done,
- flag, post_call_generator);
+ InvokePrologue(expected, actual, code, dummy, &done, flag, Label::kNear,
+ call_wrapper, call_kind);
if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(code, rmode));
+ SetCallKind(ecx, call_kind);
call(code, rmode);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ call_wrapper.AfterCall();
} else {
ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(ecx, call_kind);
jmp(code, rmode);
}
bind(&done);
@@ -1566,7 +1673,8 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
void MacroAssembler::InvokeFunction(Register fun,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
ASSERT(fun.is(edi));
mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
@@ -1575,14 +1683,15 @@ void MacroAssembler::InvokeFunction(Register fun,
ParameterCount expected(ebx);
InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
- expected, actual, flag, post_call_generator);
+ expected, actual, flag, call_wrapper, call_kind);
}
void MacroAssembler::InvokeFunction(JSFunction* function,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
ASSERT(function->is_compiled());
// Get the function and setup the context.
mov(edi, Immediate(Handle<JSFunction>(function)));
@@ -1594,18 +1703,18 @@ void MacroAssembler::InvokeFunction(JSFunction* function,
// code field in the function to allow recompilation to take effect
// without changing any of the call sites.
InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
- expected, actual, flag, post_call_generator);
+ expected, actual, flag, call_wrapper, call_kind);
} else {
Handle<Code> code(function->code());
InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET,
- flag, post_call_generator);
+ flag, call_wrapper, call_kind);
}
}
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ const CallWrapper& call_wrapper) {
// Calls are not allowed in some stubs.
ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
@@ -1615,7 +1724,7 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
ParameterCount expected(0);
GetBuiltinFunction(edi, id);
InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
- expected, expected, flag, post_call_generator);
+ expected, expected, flag, call_wrapper, CALL_AS_METHOD);
}
void MacroAssembler::GetBuiltinFunction(Register target,
@@ -1681,7 +1790,7 @@ void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
mov(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
if (emit_debug_code()) {
Label ok, fail;
- CheckMap(map, isolate()->factory()->meta_map(), &fail, false);
+ CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK);
jmp(&ok);
bind(&fail);
Abort("Global functions must have initial map");
@@ -1845,7 +1954,7 @@ void MacroAssembler::AssertFastElements(Register elements) {
void MacroAssembler::Check(Condition cc, const char* msg) {
Label L;
- j(cc, &L, taken);
+ j(cc, &L);
Abort(msg);
// will not return here
bind(&L);
@@ -1894,56 +2003,14 @@ void MacroAssembler::Abort(const char* msg) {
}
-void MacroAssembler::JumpIfNotNumber(Register reg,
- TypeInfo info,
- Label* on_not_number) {
- if (emit_debug_code()) AbortIfSmi(reg);
- if (!info.IsNumber()) {
- cmp(FieldOperand(reg, HeapObject::kMapOffset),
- isolate()->factory()->heap_number_map());
- j(not_equal, on_not_number);
- }
-}
-
-
-void MacroAssembler::ConvertToInt32(Register dst,
- Register source,
- Register scratch,
- TypeInfo info,
- Label* on_not_int32) {
- if (emit_debug_code()) {
- AbortIfSmi(source);
- AbortIfNotNumber(source);
- }
- if (info.IsInteger32()) {
- cvttsd2si(dst, FieldOperand(source, HeapNumber::kValueOffset));
- } else {
- Label done;
- bool push_pop = (scratch.is(no_reg) && dst.is(source));
- ASSERT(!scratch.is(source));
- if (push_pop) {
- push(dst);
- scratch = dst;
- }
- if (scratch.is(no_reg)) scratch = dst;
- cvttsd2si(scratch, FieldOperand(source, HeapNumber::kValueOffset));
- cmp(scratch, 0x80000000u);
- if (push_pop) {
- j(not_equal, &done);
- pop(dst);
- jmp(on_not_int32);
- } else {
- j(equal, on_not_int32);
- }
-
- bind(&done);
- if (push_pop) {
- add(Operand(esp), Immediate(kPointerSize)); // Pop.
- }
- if (!scratch.is(dst)) {
- mov(dst, scratch);
- }
- }
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ mov(descriptors,
+ FieldOperand(map, Map::kInstanceDescriptorsOrBitField3Offset));
+ Label not_smi;
+ JumpIfNotSmi(descriptors, &not_smi);
+ mov(descriptors, isolate()->factory()->empty_descriptor_array());
+ bind(&not_smi);
}
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index b9862645..2ab98c5c 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -29,7 +29,7 @@
#define V8_IA32_MACRO_ASSEMBLER_IA32_H_
#include "assembler.h"
-#include "type-info.h"
+#include "v8globals.h"
namespace v8 {
namespace internal {
@@ -45,13 +45,11 @@ enum AllocationFlags {
RESULT_CONTAINS_TOP = 1 << 1
};
+
// Convenience for platform-independent signatures. We do not normally
// distinguish memory operands from other operands on ia32.
typedef Operand MemOperand;
-// Forward declaration.
-class PostCallGenerator;
-
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
public:
@@ -73,11 +71,11 @@ class MacroAssembler: public Assembler {
// Check if object is in new space.
// scratch can be object itself, but it will be clobbered.
- template <typename LabelType>
void InNewSpace(Register object,
Register scratch,
Condition cc, // equal for new space, not_equal otherwise.
- LabelType* branch);
+ Label* branch,
+ Label::Distance branch_near = Label::kFar);
// For page containing |object| mark region covering [object+offset]
// dirty. |object| is the object being stored into, |value| is the
@@ -155,37 +153,46 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// JavaScript invokes
+ // Setup call kind marking in ecx. The method takes ecx as an
+ // explicit first parameter to make the code more readable at the
+ // call sites.
+ void SetCallKind(Register dst, CallKind kind);
+
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(const Operand& code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
void InvokeCode(Handle<Code> code,
const ParameterCount& expected,
const ParameterCount& actual,
RelocInfo::Mode rmode,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
void InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
void InvokeFunction(JSFunction* function,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper = NullCallWrapper());
// Store the function for the given builtin in the target register.
void GetBuiltinFunction(Register target, Builtins::JavaScript id);
@@ -209,13 +216,21 @@ class MacroAssembler: public Assembler {
// Compare instance type for map.
void CmpInstanceType(Register map, InstanceType type);
- // Check if the map of an object is equal to a specified map and
- // branch to label if not. Skip the smi check if not required
- // (object is known to be a heap object)
+ // Check if the map of an object is equal to a specified map and branch to
+ // label if not. Skip the smi check if not required (object is known to be a
+ // heap object)
void CheckMap(Register obj,
Handle<Map> map,
Label* fail,
- bool is_heap_object);
+ SmiCheckType smi_check_type);
+
+ // Check if the map of an object is equal to a specified map and branch to a
+ // specified target if equal. Skip the smi check if not required (object is
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
// Check if the object in register heap_object is a string. Afterwards the
// register map contains the object map and the register instance_type
@@ -242,6 +257,13 @@ class MacroAssembler: public Assembler {
// jcc instructions (je, ja, jae, jb, jbe, je, and jz).
void FCmp();
+ void ClampUint8(Register reg);
+
+ void ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister scratch_reg,
+ Register result_reg);
+
+
// Smi tagging support.
void SmiTag(Register reg) {
ASSERT(kSmiTag == 0);
@@ -253,16 +275,6 @@ class MacroAssembler: public Assembler {
}
// Modifies the register even if it does not contain a Smi!
- void SmiUntag(Register reg, TypeInfo info, Label* non_smi) {
- ASSERT(kSmiTagSize == 1);
- sar(reg, kSmiTagSize);
- if (info.IsSmi()) {
- ASSERT(kSmiTag == 0);
- j(carry, non_smi);
- }
- }
-
- // Modifies the register even if it does not contain a Smi!
void SmiUntag(Register reg, Label* is_smi) {
ASSERT(kSmiTagSize == 1);
sar(reg, kSmiTagSize);
@@ -273,24 +285,15 @@ class MacroAssembler: public Assembler {
// Jump the register contains a smi.
inline void JumpIfSmi(Register value, Label* smi_label) {
test(value, Immediate(kSmiTagMask));
- j(zero, smi_label, not_taken);
+ j(zero, smi_label);
}
// Jump if register contain a non-smi.
inline void JumpIfNotSmi(Register value, Label* not_smi_label) {
test(value, Immediate(kSmiTagMask));
- j(not_zero, not_smi_label, not_taken);
+ j(not_zero, not_smi_label);
}
- // Assumes input is a heap object.
- void JumpIfNotNumber(Register reg, TypeInfo info, Label* on_not_number);
-
- // Assumes input is a heap number. Jumps on things out of range. Also jumps
- // on the min negative int32. Ignores frational parts.
- void ConvertToInt32(Register dst,
- Register src, // Can be the same as dst.
- Register scratch, // Can be no_reg or dst, but not src.
- TypeInfo info,
- Label* on_not_int32);
+ void LoadInstanceDescriptors(Register map, Register descriptors);
void LoadPowerOf2(XMMRegister dst, Register scratch, int power);
@@ -457,7 +460,7 @@ class MacroAssembler: public Assembler {
// Runtime calls
// Call a code stub. Generate the code if necessary.
- void CallStub(CodeStub* stub);
+ void CallStub(CodeStub* stub, unsigned ast_id = kNoASTId);
// Call a code stub and return the code object called. Try to generate
// the code if necessary. Do not perform a GC but instead return a retry
@@ -655,9 +658,11 @@ class MacroAssembler: public Assembler {
const ParameterCount& actual,
Handle<Code> code_constant,
const Operand& code_operand,
- NearLabel* done,
+ Label* done,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ Label::Distance done_near = Label::kFar,
+ const CallWrapper& call_wrapper = NullCallWrapper(),
+ CallKind call_kind = CALL_AS_METHOD);
// Activation support.
void EnterFrame(StackFrame::Type type);
@@ -692,33 +697,6 @@ class MacroAssembler: public Assembler {
};
-template <typename LabelType>
-void MacroAssembler::InNewSpace(Register object,
- Register scratch,
- Condition cc,
- LabelType* branch) {
- ASSERT(cc == equal || cc == not_equal);
- if (Serializer::enabled()) {
- // Can't do arithmetic on external references if it might get serialized.
- mov(scratch, Operand(object));
- // The mask isn't really an address. We load it as an external reference in
- // case the size of the new space is different between the snapshot maker
- // and the running system.
- and_(Operand(scratch),
- Immediate(ExternalReference::new_space_mask(isolate())));
- cmp(Operand(scratch),
- Immediate(ExternalReference::new_space_start(isolate())));
- j(cc, branch);
- } else {
- int32_t new_space_start = reinterpret_cast<int32_t>(
- ExternalReference::new_space_start(isolate()).address());
- lea(scratch, Operand(object, -new_space_start));
- and_(scratch, isolate()->heap()->NewSpaceMask());
- j(cc, branch);
- }
-}
-
-
// The code patcher is used to patch (typically) small parts of code e.g. for
// debugging and other types of instrumentation. When using the code patcher
// the exact number of bytes specified must be emitted. Is not legal to emit
@@ -739,17 +717,6 @@ class CodePatcher {
};
-// Helper class for generating code or data associated with the code
-// right after a call instruction. As an example this can be used to
-// generate safepoint data after calls for crankshaft.
-class PostCallGenerator {
- public:
- PostCallGenerator() { }
- virtual ~PostCallGenerator() { }
- virtual void Generate() = 0;
-};
-
-
// -----------------------------------------------------------------------------
// Static helper functions.
diff --git a/src/ia32/regexp-macro-assembler-ia32.cc b/src/ia32/regexp-macro-assembler-ia32.cc
index 5b2f208d..8db2e9b1 100644
--- a/src/ia32/regexp-macro-assembler-ia32.cc
+++ b/src/ia32/regexp-macro-assembler-ia32.cc
@@ -305,7 +305,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
// The length of a capture should not be negative. This can only happen
// if the end of the capture is unrecorded, or at a point earlier than
// the start of the capture.
- BranchOrBacktrack(less, on_no_match, not_taken);
+ BranchOrBacktrack(less, on_no_match);
// If length is zero, either the capture is empty or it is completely
// uncaptured. In either case succeed immediately.
@@ -348,7 +348,7 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase(
__ add(Operand(edi), Immediate(1));
// Compare to end of match, and loop if not done.
__ cmp(edi, Operand(ebx));
- __ j(below, &loop, taken);
+ __ j(below, &loop);
__ jmp(&success);
__ bind(&fail);
@@ -687,11 +687,11 @@ Handle<HeapObject> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
__ mov(ecx, esp);
__ sub(ecx, Operand::StaticVariable(stack_limit));
// Handle it if the stack pointer is already below the stack limit.
- __ j(below_equal, &stack_limit_hit, not_taken);
+ __ j(below_equal, &stack_limit_hit);
// Check if there is room for the variable number of registers above
// the stack limit.
__ cmp(ecx, num_registers_ * kPointerSize);
- __ j(above_equal, &stack_ok, taken);
+ __ j(above_equal, &stack_ok);
// Exit with OutOfMemory exception. There is not enough space on the stack
// for our working registers.
__ mov(eax, EXCEPTION);
@@ -971,9 +971,9 @@ void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) {
}
void RegExpMacroAssemblerIA32::SetCurrentPositionFromEnd(int by) {
- NearLabel after_position;
+ Label after_position;
__ cmp(edi, -by * char_size());
- __ j(greater_equal, &after_position);
+ __ j(greater_equal, &after_position, Label::kNear);
__ mov(edi, -by * char_size());
// On RegExp code entry (where this operation is used), the character before
// the current position is expected to be already loaded.
@@ -1142,8 +1142,7 @@ void RegExpMacroAssemblerIA32::CheckPosition(int cp_offset,
void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition,
- Label* to,
- Hint hint) {
+ Label* to) {
if (condition < 0) { // No condition
if (to == NULL) {
Backtrack();
@@ -1153,10 +1152,10 @@ void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition,
return;
}
if (to == NULL) {
- __ j(condition, &backtrack_label_, hint);
+ __ j(condition, &backtrack_label_);
return;
}
- __ j(condition, to, hint);
+ __ j(condition, to);
}
@@ -1209,7 +1208,7 @@ void RegExpMacroAssemblerIA32::CheckPreemption() {
ExternalReference stack_limit =
ExternalReference::address_of_stack_limit(masm_->isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
- __ j(above, &no_preempt, taken);
+ __ j(above, &no_preempt);
SafeCall(&check_preempt_label_);
diff --git a/src/ia32/regexp-macro-assembler-ia32.h b/src/ia32/regexp-macro-assembler-ia32.h
index 70606da1..21c86d05 100644
--- a/src/ia32/regexp-macro-assembler-ia32.h
+++ b/src/ia32/regexp-macro-assembler-ia32.h
@@ -168,7 +168,7 @@ class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler {
// Equivalent to a conditional branch to the label, unless the label
// is NULL, in which case it is a conditional Backtrack.
- void BranchOrBacktrack(Condition condition, Label* to, Hint hint = no_hint);
+ void BranchOrBacktrack(Condition condition, Label* to);
// Call and return internally in the generated code in a way that
// is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
diff --git a/src/ia32/simulator-ia32.h b/src/ia32/simulator-ia32.h
index cb660cd3..13ddf35c 100644
--- a/src/ia32/simulator-ia32.h
+++ b/src/ia32/simulator-ia32.h
@@ -56,7 +56,9 @@ typedef int (*regexp_matcher)(String*, int, const byte*,
// just use the C stack limit.
class SimulatorStack : public v8::internal::AllStatic {
public:
- static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
+ USE(isolate);
return c_limit;
}
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index 27d28868..550a6ffd 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -57,7 +57,7 @@ static void ProbeTable(Isolate* isolate,
// Check that the key in the entry matches the name.
__ cmp(name, Operand::StaticArray(offset, times_2, key_offset));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Check that the flags match what we're looking for.
__ mov(offset, FieldOperand(extra, Code::kFlagsOffset));
@@ -76,7 +76,7 @@ static void ProbeTable(Isolate* isolate,
// Check that the key in the entry matches the name.
__ cmp(name, Operand::StaticArray(offset, times_2, key_offset));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Get the code entry from the cache.
__ mov(offset, Operand::StaticArray(offset, times_2, value_offset));
@@ -107,18 +107,17 @@ static void ProbeTable(Isolate* isolate,
// must always call a backup property check that is complete.
// This function is safe to call if the receiver has fast properties.
// Name must be a symbol and receiver must be a heap object.
-static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
- Label* miss_label,
- Register receiver,
- String* name,
- Register r0,
- Register r1) {
+static MaybeObject* GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ String* name,
+ Register r0,
+ Register r1) {
ASSERT(name->IsSymbol());
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->negative_lookups(), 1);
__ IncrementCounter(counters->negative_lookups_miss(), 1);
- Label done;
__ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset));
const int kInterceptorOrAccessCheckNeededMask =
@@ -127,11 +126,11 @@ static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
// Bail out if the receiver has a named interceptor or requires access checks.
__ test_b(FieldOperand(r0, Map::kBitFieldOffset),
kInterceptorOrAccessCheckNeededMask);
- __ j(not_zero, miss_label, not_taken);
+ __ j(not_zero, miss_label);
// Check that receiver is a JSObject.
__ CmpInstanceType(r0, FIRST_JS_OBJECT_TYPE);
- __ j(below, miss_label, not_taken);
+ __ j(below, miss_label);
// Load properties array.
Register properties = r0;
@@ -142,64 +141,20 @@ static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
Immediate(masm->isolate()->factory()->hash_table_map()));
__ j(not_equal, miss_label);
- // Compute the capacity mask.
- const int kCapacityOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kCapacityIndex * kPointerSize;
-
- // Generate an unrolled loop that performs a few probes before
- // giving up.
- static const int kProbes = 4;
- const int kElementsStartOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kElementsStartIndex * kPointerSize;
-
- // If names of slots in range from 1 to kProbes - 1 for the hash value are
- // not equal to the name and kProbes-th slot is not used (its name is the
- // undefined value), it guarantees the hash table doesn't contain the
- // property. It's true even if some slots represent deleted properties
- // (their names are the null value).
- for (int i = 0; i < kProbes; i++) {
- // r0 points to properties hash.
- // Compute the masked index: (hash + i + i * i) & mask.
- Register index = r1;
- // Capacity is smi 2^n.
- __ mov(index, FieldOperand(properties, kCapacityOffset));
- __ dec(index);
- __ and_(Operand(index),
- Immediate(Smi::FromInt(name->Hash() +
- StringDictionary::GetProbeOffset(i))));
-
- // Scale the index by multiplying by the entry size.
- ASSERT(StringDictionary::kEntrySize == 3);
- __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
-
- Register entity_name = r1;
- // Having undefined at this place means the name is not contained.
- ASSERT_EQ(kSmiTagSize, 1);
- __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
- kElementsStartOffset - kHeapObjectTag));
- __ cmp(entity_name, masm->isolate()->factory()->undefined_value());
- if (i != kProbes - 1) {
- __ j(equal, &done, taken);
-
- // Stop if found the property.
- __ cmp(entity_name, Handle<String>(name));
- __ j(equal, miss_label, not_taken);
-
- // Check if the entry name is not a symbol.
- __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
- __ test_b(FieldOperand(entity_name, Map::kInstanceTypeOffset),
- kIsSymbolMask);
- __ j(zero, miss_label, not_taken);
- } else {
- // Give up probing if still not found the undefined value.
- __ j(not_equal, miss_label, not_taken);
- }
- }
+ Label done;
+ MaybeObject* result =
+ StringDictionaryLookupStub::GenerateNegativeLookup(masm,
+ miss_label,
+ &done,
+ properties,
+ name,
+ r1);
+ if (result->IsFailure()) return result;
__ bind(&done);
__ DecrementCounter(counters->negative_lookups_miss(), 1);
+
+ return result;
}
@@ -234,7 +189,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
// Get the map of the receiver and compute the hash.
__ mov(scratch, FieldOperand(name, String::kHashFieldOffset));
@@ -295,11 +250,11 @@ void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
Label* miss_label) {
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss_label, not_taken);
+ __ j(zero, miss_label);
// Check that the object is a JS array.
__ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
- __ j(not_equal, miss_label, not_taken);
+ __ j(not_equal, miss_label);
// Load length directly from the JS array.
__ mov(eax, FieldOperand(receiver, JSArray::kLengthOffset));
@@ -316,14 +271,14 @@ static void GenerateStringCheck(MacroAssembler* masm,
Label* non_string_object) {
// Check that the object isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, smi, not_taken);
+ __ j(zero, smi);
// Check that the object is a string.
__ mov(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
ASSERT(kNotStringTag != 0);
__ test(scratch, Immediate(kNotStringTag));
- __ j(not_zero, non_string_object, not_taken);
+ __ j(not_zero, non_string_object);
}
@@ -348,7 +303,7 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
// Check if the object is a JSValue wrapper.
__ bind(&check_wrapper);
__ cmp(scratch1, JS_VALUE_TYPE);
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
// Check if the wrapped value is a string and load the length
// directly if it is.
@@ -533,10 +488,12 @@ class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
const ParameterCount& arguments,
- Register name)
+ Register name,
+ Code::ExtraICState extra_ic_state)
: stub_compiler_(stub_compiler),
arguments_(arguments),
- name_(name) {}
+ name_(name),
+ extra_ic_state_(extra_ic_state) {}
MaybeObject* Compile(MacroAssembler* masm,
JSObject* object,
@@ -553,7 +510,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
CallOptimization optimization(lookup);
@@ -661,8 +618,11 @@ class CallInterceptorCompiler BASE_EMBEDDED {
GenerateFastApiCall(masm, optimization, arguments_.immediate());
if (result->IsFailure()) return result;
} else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
__ InvokeFunction(optimization.constant_function(), arguments_,
- JUMP_FUNCTION);
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
}
// Deferred code for fast API call case---clean preallocated space.
@@ -741,6 +701,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
StubCompiler* stub_compiler_;
const ParameterCount& arguments_;
Register name_;
+ Code::ExtraICState extra_ic_state_;
};
@@ -758,6 +719,14 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
}
+void StubCompiler::GenerateKeyedLoadMissForceGeneric(MacroAssembler* masm) {
+ Code* code = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ Handle<Code> ic(code);
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+}
+
+
// Both name_reg and receiver_reg are preserved on jumps to miss_label,
// but may be destroyed if store is successful.
void StubCompiler::GenerateStoreField(MacroAssembler* masm,
@@ -770,12 +739,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
Label* miss_label) {
// Check that the object isn't a smi.
__ test(receiver_reg, Immediate(kSmiTagMask));
- __ j(zero, miss_label, not_taken);
+ __ j(zero, miss_label);
// Check that the map of the object hasn't changed.
__ cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset),
Immediate(Handle<Map>(object->map())));
- __ j(not_equal, miss_label, not_taken);
+ __ j(not_equal, miss_label);
// Perform global security token check if needed.
if (object->IsJSGlobalProxy()) {
@@ -865,7 +834,7 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell(
__ cmp(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)),
Immediate(masm->isolate()->factory()->the_hole_value()));
}
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
return cell;
}
@@ -951,12 +920,17 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
ASSERT(current->property_dictionary()->FindEntry(name) ==
StringDictionary::kNotFound);
- GenerateDictionaryNegativeLookup(masm(),
- miss,
- reg,
- name,
- scratch1,
- scratch2);
+ MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch1,
+ scratch2);
+ if (negative_lookup->IsFailure()) {
+ set_failure(Failure::cast(negative_lookup));
+ return reg;
+ }
+
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // from now the object is in holder_reg
__ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
@@ -965,7 +939,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
__ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
__ cmp(Operand(scratch1), Immediate(Handle<Map>(current->map())));
// Branch on the result of the map check.
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
@@ -985,7 +959,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
__ cmp(FieldOperand(reg, HeapObject::kMapOffset),
Immediate(Handle<Map>(current->map())));
// Branch on the result of the map check.
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
// Check access rights to the global object. This has to happen
// after the map check so that we know that the object is
// actually a global object.
@@ -1012,7 +986,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
// Check the holder map.
__ cmp(FieldOperand(reg, HeapObject::kMapOffset),
Immediate(Handle<Map>(holder->map())));
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
// Perform security check for access to the global object.
ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
@@ -1047,7 +1021,7 @@ void StubCompiler::GenerateLoadField(JSObject* object,
Label* miss) {
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
// Check the prototype chain.
Register reg =
@@ -1072,7 +1046,7 @@ MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object,
Label* miss) {
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
// Check that the maps haven't changed.
Register reg =
@@ -1139,7 +1113,7 @@ void StubCompiler::GenerateLoadConstant(JSObject* object,
Label* miss) {
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
// Check that the maps haven't changed.
CheckPrototypes(object, receiver, holder,
@@ -1166,7 +1140,7 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
@@ -1295,7 +1269,7 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
if (kind_ == Code::KEYED_CALL_IC) {
__ cmp(Operand(ecx), Immediate(Handle<String>(name)));
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
}
}
@@ -1317,7 +1291,7 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
// the receiver cannot be a smi.
if (object != holder) {
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
}
// Check that the maps haven't changed.
@@ -1344,17 +1318,17 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ test(edi, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, miss);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
// Check the shared function info. Make sure it hasn't changed.
__ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
Immediate(Handle<SharedFunctionInfo>(function->shared())));
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
} else {
__ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, miss);
}
}
@@ -1362,7 +1336,8 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
MaybeObject* CallStubCompiler::GenerateMissBranch() {
MaybeObject* maybe_obj =
isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
- kind_);
+ kind_,
+ extra_ic_state_);
Object* obj;
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
__ jmp(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
@@ -1392,7 +1367,7 @@ MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField(
// Check that the receiver isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
// Do the right check and compute the holder register.
Register reg = CheckPrototypes(object, edx, holder, ebx, eax, edi,
@@ -1402,9 +1377,9 @@ MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField(
// Check that the function really is a function.
__ test(edi, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.
@@ -1414,7 +1389,11 @@ MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField(
}
// Invoke the function.
- __ InvokeFunction(edi, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
// Handle call cache miss.
__ bind(&miss);
@@ -1689,7 +1668,9 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
Label index_out_of_range;
Label* index_out_of_range_label = &index_out_of_range;
- if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
index_out_of_range_label = &miss;
}
@@ -1773,7 +1754,9 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
Label index_out_of_range;
Label* index_out_of_range_label = &index_out_of_range;
- if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
index_out_of_range_label = &miss;
}
@@ -1896,7 +1879,11 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall(
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
__ bind(&miss);
// ecx: function name.
@@ -1964,7 +1951,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
// Check if the argument is a heap number and load its value into xmm0.
Label slow;
- __ CheckMap(eax, factory()->heap_number_map(), &slow, true);
+ __ CheckMap(eax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
__ movdbl(xmm0, FieldOperand(eax, HeapNumber::kValueOffset));
// Check if the argument is strictly positive. Note this also
@@ -2026,7 +2013,8 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ bind(&miss);
// ecx: function name.
@@ -2108,7 +2096,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
// Check if the argument is a heap number and load its exponent and
// sign into ebx.
__ bind(&not_smi);
- __ CheckMap(eax, factory()->heap_number_map(), &slow, true);
+ __ CheckMap(eax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
__ mov(ebx, FieldOperand(eax, HeapNumber::kExponentOffset));
// Check the sign of the argument. If the argument is positive,
@@ -2131,7 +2119,8 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ bind(&miss);
// ecx: function name.
@@ -2155,6 +2144,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall(
// repatch it to global receiver.
if (object->IsGlobalObject()) return heap()->undefined_value();
if (cell != NULL) return heap()->undefined_value();
+ if (!object->IsJSObject()) return heap()->undefined_value();
int depth = optimization.GetPrototypeDepthOfExpectedType(
JSObject::cast(object), holder);
if (depth == kInvalidProtoDepth) return heap()->undefined_value();
@@ -2169,7 +2159,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall(
// Check that the receiver isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss_before_stack_reserved, not_taken);
+ __ j(zero, &miss_before_stack_reserved);
Counters* counters = isolate()->counters();
__ IncrementCounter(counters->call_const(), 1);
@@ -2204,11 +2194,12 @@ MaybeObject* CallStubCompiler::CompileFastApiCall(
}
-MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
- JSObject* holder,
- JSFunction* function,
- String* name,
- CheckType check) {
+MaybeObject* CallStubCompiler::CompileCallConstant(
+ Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@@ -2237,7 +2228,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
// Check that the receiver isn't a smi.
if (check != NUMBER_CHECK) {
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
}
// Make sure that it's okay not to patch the on stack receiver
@@ -2269,7 +2260,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
} else {
// Check that the object is a string or a symbol.
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, eax);
- __ j(above_equal, &miss, not_taken);
+ __ j(above_equal, &miss);
// Check that the maps starting from the prototype haven't changed.
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::STRING_FUNCTION_INDEX, eax, &miss);
@@ -2287,9 +2278,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
Label fast;
// Check that the object is a smi or a heap number.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &fast, taken);
+ __ j(zero, &fast);
__ CmpObjectType(edx, HEAP_NUMBER_TYPE, eax);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
__ bind(&fast);
// Check that the maps starting from the prototype haven't changed.
GenerateDirectLoadGlobalFunctionPrototype(
@@ -2309,9 +2300,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
Label fast;
// Check that the object is a boolean.
__ cmp(edx, factory()->true_value());
- __ j(equal, &fast, taken);
+ __ j(equal, &fast);
__ cmp(edx, factory()->false_value());
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
__ bind(&fast);
// Check that the maps starting from the prototype haven't changed.
GenerateDirectLoadGlobalFunctionPrototype(
@@ -2326,7 +2317,11 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
UNREACHABLE();
}
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
// Handle call cache miss.
__ bind(&miss);
@@ -2361,7 +2356,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Get the receiver from the stack.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
- CallInterceptorCompiler compiler(this, arguments(), ecx);
+ CallInterceptorCompiler compiler(this, arguments(), ecx, extra_ic_state_);
MaybeObject* result = compiler.Compile(masm(),
object,
holder,
@@ -2379,9 +2374,9 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Check that the function really is a function.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
__ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.
@@ -2392,7 +2387,11 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Invoke the function.
__ mov(edi, eax);
- __ InvokeFunction(edi, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(edi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
// Handle load cache miss.
__ bind(&miss);
@@ -2404,11 +2403,12 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
}
-MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object,
- GlobalObject* holder,
- JSGlobalPropertyCell* cell,
- JSFunction* function,
- String* name) {
+MaybeObject* CallStubCompiler::CompileCallGlobal(
+ JSObject* object,
+ GlobalObject* holder,
+ JSGlobalPropertyCell* cell,
+ JSFunction* function,
+ String* name) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
@@ -2451,16 +2451,21 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object,
__ IncrementCounter(counters->call_global_inline(), 1);
ASSERT(function->is_compiled());
ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
if (V8::UseCrankshaft()) {
// TODO(kasperl): For now, we always call indirectly through the
// code field in the function to allow recompilation to take effect
// without changing any of the call sites.
__ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
- expected, arguments(), JUMP_FUNCTION);
+ expected, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
} else {
Handle<Code> code(function->code());
__ InvokeCode(code, expected, arguments(),
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ RelocInfo::CODE_TARGET, JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
}
// Handle call cache miss.
@@ -2518,12 +2523,12 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object,
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
// Check that the map of the object hasn't changed.
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(Handle<Map>(object->map())));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Perform global security token check if needed.
if (object->IsJSGlobalProxy()) {
@@ -2568,12 +2573,12 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver,
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
// Check that the map of the object hasn't changed.
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(Handle<Map>(receiver->map())));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Perform global security token check if needed.
if (receiver->IsJSGlobalProxy()) {
@@ -2620,7 +2625,7 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
// Check that the map of the global has not changed.
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(Handle<Map>(object->map())));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Compute the cell operand to use.
@@ -2673,7 +2678,7 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
// Check that the name has not changed.
__ cmp(Operand(ecx), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
// Generate store field code. Trashes the name register.
GenerateStoreField(masm(),
@@ -2694,60 +2699,58 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
}
-MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
- JSObject* receiver) {
+MaybeObject* KeyedStoreStubCompiler::CompileStoreFastElement(
+ Map* receiver_map) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- Label miss;
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ MaybeObject* maybe_stub =
+ KeyedStoreFastElementStub(is_js_array).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(edx,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
- // Check that the receiver isn't a smi.
- __ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Check that the map matches.
- __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
- Immediate(Handle<Map>(receiver->map())));
- __ j(not_equal, &miss, not_taken);
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
- // Check that the key is a smi.
- __ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &miss, not_taken);
- // Get the elements array and make sure it is a fast element array, not 'cow'.
- __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
- __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
- Immediate(factory()->fixed_array_map()));
- __ j(not_equal, &miss, not_taken);
+MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(edx, &miss);
- // Check that the key is within bounds.
- if (receiver->IsJSArray()) {
- __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
- __ j(above_equal, &miss, not_taken);
- } else {
- __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // Compare smis.
- __ j(above_equal, &miss, not_taken);
+ Register map_reg = ebx;
+ __ mov(map_reg, FieldOperand(edx, HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map(receiver_maps->at(current));
+ __ cmp(map_reg, map);
+ __ j(equal, Handle<Code>(handler_ics->at(current)));
}
-
- // Do the store and update the write barrier. Make sure to preserve
- // the value in register eax.
- __ mov(edx, Operand(eax));
- __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax);
- __ RecordWrite(edi, 0, edx, ecx);
-
- // Done.
- __ ret(0);
-
- // Handle store cache miss.
__ bind(&miss);
- Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
- __ jmp(ic, RelocInfo::CODE_TARGET);
+ Handle<Code> miss_ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ jmp(miss_ic, RelocInfo::CODE_TARGET);
// Return the generated code.
- return GetCode(NORMAL, NULL);
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -2763,7 +2766,7 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name,
// Check that the receiver isn't a smi.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
ASSERT(last->IsGlobalObject() || last->HasFastProperties());
@@ -2916,7 +2919,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
// the receiver cannot be a smi.
if (object != holder) {
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss);
}
// Check that the maps haven't changed.
@@ -2933,7 +2936,7 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
// Check for deleted property if property can actually be deleted.
if (!is_dont_delete) {
__ cmp(ebx, factory()->the_hole_value());
- __ j(equal, &miss, not_taken);
+ __ j(equal, &miss);
} else if (FLAG_debug_code) {
__ cmp(ebx, factory()->the_hole_value());
__ Check(not_equal, "DontDelete cells can't contain the hole");
@@ -2969,7 +2972,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name,
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
GenerateLoadField(receiver, holder, edx, ebx, ecx, edi, index, name, &miss);
@@ -2999,7 +3002,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback(
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
MaybeObject* result = GenerateLoadCallback(receiver, holder, edx, eax, ebx,
ecx, edi, callback, name, &miss);
@@ -3034,7 +3037,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name,
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
GenerateLoadConstant(receiver, holder, edx, ebx, ecx, edi,
value, name, &miss);
@@ -3062,7 +3065,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
LookupResult lookup;
LookupPostInterceptor(holder, name, &lookup);
@@ -3098,7 +3101,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
GenerateLoadArrayLength(masm(), edx, ecx, &miss);
__ bind(&miss);
@@ -3123,7 +3126,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss, true);
__ bind(&miss);
@@ -3148,7 +3151,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss);
GenerateLoadFunctionPrototype(masm(), edx, ecx, ebx, &miss);
__ bind(&miss);
@@ -3160,48 +3163,52 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadFastElement(Map* receiver_map) {
// ----------- S t a t e -------------
// -- eax : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- Label miss;
+ MaybeObject* maybe_stub = KeyedLoadFastElementStub().TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(edx,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
- // Check that the receiver isn't a smi.
- __ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
-
- // Check that the map matches.
- __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
- Immediate(Handle<Map>(receiver->map())));
- __ j(not_equal, &miss, not_taken);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
- // Check that the key is a smi.
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &miss, not_taken);
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
- // Get the elements array.
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- __ AssertFastElements(ecx);
- // Check that the key is within bounds.
- __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset));
- __ j(above_equal, &miss, not_taken);
+MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(edx, &miss);
- // Load the result and make sure it's not the hole.
- __ mov(ebx, Operand(ecx, eax, times_2,
- FixedArray::kHeaderSize - kHeapObjectTag));
- __ cmp(ebx, factory()->the_hole_value());
- __ j(equal, &miss, not_taken);
- __ mov(eax, ebx);
- __ ret(0);
+ Register map_reg = ebx;
+ __ mov(map_reg, FieldOperand(edx, HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map(receiver_maps->at(current));
+ __ cmp(map_reg, map);
+ __ j(equal, Handle<Code>(handler_ics->at(current)));
+ }
__ bind(&miss);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
- return GetCode(NORMAL, NULL);
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -3222,7 +3229,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
__ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kDebugInfoOffset));
__ cmp(ebx, factory()->undefined_value());
- __ j(not_equal, &generic_stub_call, not_taken);
+ __ j(not_equal, &generic_stub_call);
#endif
// Load the initial map and verify that it is in fact a map.
@@ -3344,36 +3351,82 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
- JSObject*receiver, ExternalArrayType array_type, Code::Flags flags) {
+MaybeObject* ExternalArrayLoadStubCompiler::CompileLoad(
+ JSObject*receiver, ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- eax : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- Label slow, failed_allocation;
+ MaybeObject* maybe_stub =
+ KeyedLoadExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(edx,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Check that the object isn't a smi.
- __ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &slow, not_taken);
+ // Return the generated code.
+ return GetCode();
+}
+
+
+MaybeObject* ExternalArrayStoreStubCompiler::CompileStore(
+ JSObject* receiver, ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ MaybeObject* maybe_stub =
+ KeyedStoreExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(edx,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ return GetCode();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void KeyedLoadStubCompiler::GenerateLoadExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic, failed_allocation, slow;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
// Check that the key is a smi.
__ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &slow, not_taken);
-
- // Check that the map matches.
- __ CheckMap(edx, Handle<Map>(receiver->map()), &slow, false);
- __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
+ __ j(not_zero, &miss_force_generic);
- // eax: key, known to be a smi.
- // edx: receiver, known to be a JSObject.
- // ebx: elements object, known to be an external array.
// Check that the index is in range.
__ mov(ecx, eax);
__ SmiUntag(ecx); // Untag the index.
+ __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
__ cmp(ecx, FieldOperand(ebx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
- __ j(above_equal, &slow);
+ __ j(above_equal, &miss_force_generic);
__ mov(ebx, FieldOperand(ebx, ExternalArray::kExternalPointerOffset));
// ebx: base pointer of external storage
switch (array_type) {
@@ -3397,6 +3450,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
case kExternalFloatArray:
__ fld_s(Operand(ebx, ecx, times_4, 0));
break;
+ case kExternalDoubleArray:
+ __ fld_d(Operand(ebx, ecx, times_8, 0));
+ break;
default:
UNREACHABLE();
break;
@@ -3454,7 +3510,8 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ mov(eax, ecx);
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ ret(0);
- } else if (array_type == kExternalFloatArray) {
+ } else if (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray) {
// For the floating-point array type, we need to always allocate a
// HeapNumber.
__ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation);
@@ -3476,47 +3533,48 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
// Slow case: Jump to runtime.
__ bind(&slow);
- Counters* counters = isolate()->counters();
+ Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->keyed_load_external_array_slow(), 1);
+
// ----------- S t a t e -------------
// -- eax : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- __ pop(ebx);
- __ push(edx); // receiver
- __ push(eax); // name
- __ push(ebx); // return address
+ Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Slow();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Perform tail call to the entry.
- __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+ // ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
- // Return the generated code.
- return GetCode(flags);
+ // Miss case: Jump to runtime.
+ __ bind(&miss_force_generic);
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
+ __ jmp(miss_ic, RelocInfo::CODE_TARGET);
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
- JSObject* receiver, ExternalArrayType array_type, Code::Flags flags) {
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
// ----------- S t a t e -------------
- // -- eax : value
- // -- ecx : key
+ // -- eax : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- Label slow, check_heap_number;
-
- // Check that the object isn't a smi.
- __ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &slow);
+ Label miss_force_generic, slow, check_heap_number;
- // Check that the map matches.
- __ CheckMap(edx, Handle<Map>(receiver->map()), &slow, false);
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
// Check that the key is a smi.
__ test(ecx, Immediate(kSmiTagMask));
- __ j(not_zero, &slow);
+ __ j(not_zero, &miss_force_generic);
// Check that the index is in range.
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
@@ -3547,9 +3605,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
switch (array_type) {
case kExternalPixelArray:
{ // Clamp the value to [0..255].
- NearLabel done;
+ Label done;
__ test(ecx, Immediate(0xFFFFFF00));
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
__ setcc(negative, ecx); // 1 if negative, 0 if positive.
__ dec_b(ecx); // 0 if negative, 255 if positive.
__ bind(&done);
@@ -3569,11 +3627,16 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
__ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
// Need to perform int-to-float conversion.
__ push(ecx);
__ fild_s(Operand(esp, 0));
__ pop(ecx);
- __ fstp_s(Operand(edi, ebx, times_4, 0));
+ if (array_type == kExternalFloatArray) {
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
+ } else { // array_type == kExternalDoubleArray.
+ __ fstp_d(Operand(edi, ebx, times_8, 0));
+ }
break;
default:
UNREACHABLE();
@@ -3590,7 +3653,7 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
// edi: elements array
// ebx: untagged index
__ cmp(FieldOperand(eax, HeapObject::kMapOffset),
- Immediate(factory()->heap_number_map()));
+ Immediate(masm->isolate()->factory()->heap_number_map()));
__ j(not_equal, &slow);
// The WebGL specification leaves the behavior of storing NaN and
@@ -3603,6 +3666,10 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ fstp_s(Operand(edi, ebx, times_4, 0));
__ ret(0);
+ } else if (array_type == kExternalDoubleArray) {
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ fstp_d(Operand(edi, ebx, times_8, 0));
+ __ ret(0);
} else {
// Perform float-to-int conversion with truncation (round-to-zero)
// behavior.
@@ -3621,9 +3688,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
switch (array_type) {
case kExternalPixelArray:
{ // Clamp the value to [0..255].
- NearLabel done;
+ Label done;
__ test(ecx, Immediate(0xFFFFFF00));
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
__ setcc(negative, ecx); // 1 if negative, 0 if positive.
__ dec_b(ecx); // 0 if negative, 255 if positive.
__ bind(&done);
@@ -3681,6 +3748,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
// Slow case: call runtime.
__ bind(&slow);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_store_external_array_slow(), 1);
+
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
@@ -3688,19 +3758,109 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
// -- esp[0] : return address
// -----------------------------------
- __ pop(ebx);
- __ push(edx);
- __ push(ecx);
- __ push(eax);
- __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes
- __ push(Immediate(Smi::FromInt(
- Code::ExtractExtraICStateFromFlags(flags) & kStrictMode)));
- __ push(ebx); // return address
+ Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_Slow();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ __ bind(&miss_force_generic);
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ jmp(miss_ic, RelocInfo::CODE_TARGET);
+}
+
+
+
+
+void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &miss_force_generic);
+
+ // Get the elements array.
+ __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
+ __ AssertFastElements(ecx);
+
+ // Check that the key is within bounds.
+ __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset));
+ __ j(above_equal, &miss_force_generic);
+
+ // Load the result and make sure it's not the hole.
+ __ mov(ebx, Operand(ecx, eax, times_2,
+ FixedArray::kHeaderSize - kHeapObjectTag));
+ __ cmp(ebx, masm->isolate()->factory()->the_hole_value());
+ __ j(equal, &miss_force_generic);
+ __ mov(eax, ebx);
+ __ ret(0);
+
+ __ bind(&miss_force_generic);
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
+ __ jmp(miss_ic, RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
+ bool is_js_array) {
+ // ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(not_zero, &miss_force_generic);
- // Do tail-call to runtime routine.
- __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ cmp(FieldOperand(edi, HeapObject::kMapOffset),
+ Immediate(masm->isolate()->factory()->fixed_array_map()));
+ __ j(not_equal, &miss_force_generic);
+
+ if (is_js_array) {
+ // Check that the key is within bounds.
+ __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
+ __ j(above_equal, &miss_force_generic);
+ } else {
+ // Check that the key is within bounds.
+ __ cmp(ecx, FieldOperand(edi, FixedArray::kLengthOffset)); // smis.
+ __ j(above_equal, &miss_force_generic);
+ }
+
+ // Do the store and update the write barrier. Make sure to preserve
+ // the value in register eax.
+ __ mov(edx, Operand(eax));
+ __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax);
+ __ RecordWrite(edi, 0, edx, ecx);
+
+ // Done.
+ __ ret(0);
- return GetCode(flags);
+ // Handle store cache miss, replacing the ic with the generic stub.
+ __ bind(&miss_force_generic);
+ Handle<Code> ic_force_generic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
}
diff --git a/src/ic.cc b/src/ic.cc
index 99eb21fa..0e87e512 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -67,7 +67,33 @@ void IC::TraceIC(const char* type,
State new_state = StateFrom(new_target,
HEAP->undefined_value(),
HEAP->undefined_value());
- PrintF("[%s (%c->%c)%s", type,
+ PrintF("[%s in ", type);
+ StackFrameIterator it;
+ while (it.frame()->fp() != this->fp()) it.Advance();
+ StackFrame* raw_frame = it.frame();
+ if (raw_frame->is_internal()) {
+ Isolate* isolate = new_target->GetIsolate();
+ Code* apply_builtin = isolate->builtins()->builtin(
+ Builtins::kFunctionApply);
+ if (raw_frame->unchecked_code() == apply_builtin) {
+ PrintF("apply from ");
+ it.Advance();
+ raw_frame = it.frame();
+ }
+ }
+ if (raw_frame->is_java_script()) {
+ JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
+ Code* js_code = frame->unchecked_code();
+ // Find the function on the stack and both the active code for the
+ // function and the original code.
+ JSFunction* function = JSFunction::cast(frame->function());
+ function->PrintName();
+ int code_offset = address() - js_code->instruction_start();
+ PrintF("+%d", code_offset);
+ } else {
+ PrintF("<unknown>");
+ }
+ PrintF(" (%c->%c)%s",
TransitionMarkFromState(old_state),
TransitionMarkFromState(new_state),
extra_info);
@@ -274,15 +300,14 @@ void IC::Clear(Address address) {
switch (target->kind()) {
case Code::LOAD_IC: return LoadIC::Clear(address, target);
case Code::KEYED_LOAD_IC:
- case Code::KEYED_EXTERNAL_ARRAY_LOAD_IC:
return KeyedLoadIC::Clear(address, target);
case Code::STORE_IC: return StoreIC::Clear(address, target);
case Code::KEYED_STORE_IC:
- case Code::KEYED_EXTERNAL_ARRAY_STORE_IC:
return KeyedStoreIC::Clear(address, target);
case Code::CALL_IC: return CallIC::Clear(address, target);
case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
- case Code::TYPE_RECORDING_BINARY_OP_IC:
+ case Code::UNARY_OP_IC:
+ case Code::BINARY_OP_IC:
case Code::COMPARE_IC:
// Clearing these is tricky and does not
// make any performance difference.
@@ -293,65 +318,36 @@ void IC::Clear(Address address) {
void CallICBase::Clear(Address address, Code* target) {
+ bool contextual = CallICBase::Contextual::decode(target->extra_ic_state());
State state = target->ic_state();
if (state == UNINITIALIZED) return;
Code* code =
Isolate::Current()->stub_cache()->FindCallInitialize(
target->arguments_count(),
target->ic_in_loop(),
+ contextual ? RelocInfo::CODE_TARGET_CONTEXT : RelocInfo::CODE_TARGET,
target->kind());
SetTargetAtAddress(address, code);
}
-void KeyedLoadIC::ClearInlinedVersion(Address address) {
- // Insert null as the map to check for to make sure the map check fails
- // sending control flow to the IC instead of the inlined version.
- PatchInlinedLoad(address, HEAP->null_value());
-}
-
-
void KeyedLoadIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
// Make sure to also clear the map used in inline fast cases. If we
// do not clear these maps, cached code can keep objects alive
// through the embedded maps.
- ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub());
}
-void LoadIC::ClearInlinedVersion(Address address) {
- // Reset the map check of the inlined inobject property load (if
- // present) to guarantee failure by holding an invalid map (the null
- // value). The offset can be patched to anything.
- Heap* heap = HEAP;
- PatchInlinedLoad(address, heap->null_value(), 0);
- PatchInlinedContextualLoad(address,
- heap->null_value(),
- heap->null_value(),
- true);
-}
-
-
void LoadIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
- ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub());
}
-void StoreIC::ClearInlinedVersion(Address address) {
- // Reset the map check of the inlined inobject property store (if
- // present) to guarantee failure by holding an invalid map (the null
- // value). The offset can be patched to anything.
- PatchInlinedStore(address, HEAP->null_value(), 0);
-}
-
-
void StoreIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
- ClearInlinedVersion(address);
SetTargetAtAddress(address,
(target->extra_ic_state() == kStrictMode)
? initialize_stub_strict()
@@ -359,21 +355,6 @@ void StoreIC::Clear(Address address, Code* target) {
}
-void KeyedStoreIC::ClearInlinedVersion(Address address) {
- // Insert null as the elements map to check for. This will make
- // sure that the elements fast-case map check fails so that control
- // flows to the IC instead of the inlined version.
- PatchInlinedStore(address, HEAP->null_value());
-}
-
-
-void KeyedStoreIC::RestoreInlinedVersion(Address address) {
- // Restore the fast-case elements map check so that the inlined
- // version can be used again.
- PatchInlinedStore(address, HEAP->fixed_array_map());
-}
-
-
void KeyedStoreIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
SetTargetAtAddress(address,
@@ -595,7 +576,7 @@ bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
ASSERT(string == args[0] || string == JSValue::cast(args[0])->value());
// If we're in the default (fastest) state and the index is
// out of bounds, update the state to record this fact.
- if (*extra_ic_state == DEFAULT_STRING_STUB &&
+ if (StringStubState::decode(*extra_ic_state) == DEFAULT_STRING_STUB &&
argc >= 1 && args[1]->IsNumber()) {
double index;
if (args[1]->IsSmi()) {
@@ -605,7 +586,9 @@ bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
index = DoubleToInteger(HeapNumber::cast(args[1])->value());
}
if (index < 0 || index >= string->length()) {
- *extra_ic_state = STRING_INDEX_OUT_OF_BOUNDS;
+ *extra_ic_state =
+ StringStubState::update(*extra_ic_state,
+ STRING_INDEX_OUT_OF_BOUNDS);
return true;
}
}
@@ -633,6 +616,7 @@ MaybeObject* CallICBase::ComputeMonomorphicStub(
maybe_code = isolate()->stub_cache()->ComputeCallField(argc,
in_loop,
kind_,
+ extra_ic_state,
*name,
*object,
lookup->holder(),
@@ -668,6 +652,7 @@ MaybeObject* CallICBase::ComputeMonomorphicStub(
maybe_code = isolate()->stub_cache()->ComputeCallGlobal(argc,
in_loop,
kind_,
+ extra_ic_state,
*name,
*receiver,
global,
@@ -682,6 +667,7 @@ MaybeObject* CallICBase::ComputeMonomorphicStub(
maybe_code = isolate()->stub_cache()->ComputeCallNormal(argc,
in_loop,
kind_,
+ extra_ic_state,
*name,
*receiver);
}
@@ -692,6 +678,7 @@ MaybeObject* CallICBase::ComputeMonomorphicStub(
maybe_code = isolate()->stub_cache()->ComputeCallInterceptor(
argc,
kind_,
+ extra_ic_state,
*name,
*object,
lookup->holder());
@@ -730,9 +717,11 @@ void CallICBase::UpdateCaches(LookupResult* lookup,
// This is the first time we execute this inline cache.
// Set the target to the pre monomorphic stub to delay
// setting the monomorphic state.
- maybe_code = isolate()->stub_cache()->ComputeCallPreMonomorphic(argc,
- in_loop,
- kind_);
+ maybe_code =
+ isolate()->stub_cache()->ComputeCallPreMonomorphic(argc,
+ in_loop,
+ kind_,
+ extra_ic_state);
} else if (state == MONOMORPHIC) {
if (kind_ == Code::CALL_IC &&
TryUpdateExtraICState(lookup, object, &extra_ic_state)) {
@@ -752,9 +741,11 @@ void CallICBase::UpdateCaches(LookupResult* lookup,
object,
name);
} else {
- maybe_code = isolate()->stub_cache()->ComputeCallMegamorphic(argc,
- in_loop,
- kind_);
+ maybe_code =
+ isolate()->stub_cache()->ComputeCallMegamorphic(argc,
+ in_loop,
+ kind_,
+ extra_ic_state);
}
} else {
maybe_code = ComputeMonomorphicStub(lookup,
@@ -812,7 +803,7 @@ MaybeObject* KeyedCallIC::LoadFunction(State state,
int argc = target()->arguments_count();
InLoopFlag in_loop = target()->ic_in_loop();
MaybeObject* maybe_code = isolate()->stub_cache()->ComputeCallMegamorphic(
- argc, in_loop, Code::KEYED_CALL_IC);
+ argc, in_loop, Code::KEYED_CALL_IC, Code::kNoExtraICState);
Object* code;
if (maybe_code->ToObject(&code)) {
set_target(Code::cast(code));
@@ -873,9 +864,6 @@ MaybeObject* LoadIC::Load(State state,
#endif
if (state == PREMONOMORPHIC) {
if (object->IsString()) {
- Map* map = HeapObject::cast(*object)->map();
- const int offset = String::kLengthOffset;
- PatchInlinedLoad(address(), map, offset);
set_target(isolate()->builtins()->builtin(
Builtins::kLoadIC_StringLength));
} else {
@@ -903,9 +891,6 @@ MaybeObject* LoadIC::Load(State state,
if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
#endif
if (state == PREMONOMORPHIC) {
- Map* map = HeapObject::cast(*object)->map();
- const int offset = JSArray::kLengthOffset;
- PatchInlinedLoad(address(), map, offset);
set_target(isolate()->builtins()->builtin(
Builtins::kLoadIC_ArrayLength));
} else {
@@ -948,70 +933,14 @@ MaybeObject* LoadIC::Load(State state,
LOG(isolate(), SuspectReadEvent(*name, *object));
}
- bool can_be_inlined_precheck =
- FLAG_use_ic &&
- lookup.IsProperty() &&
- lookup.IsCacheable() &&
- lookup.holder() == *object &&
- !object->IsAccessCheckNeeded();
-
- bool can_be_inlined =
- can_be_inlined_precheck &&
- state == PREMONOMORPHIC &&
- lookup.type() == FIELD;
-
- bool can_be_inlined_contextual =
- can_be_inlined_precheck &&
- state == UNINITIALIZED &&
- lookup.holder()->IsGlobalObject() &&
- lookup.type() == NORMAL;
-
- if (can_be_inlined) {
- Map* map = lookup.holder()->map();
- // Property's index in the properties array. If negative we have
- // an inobject property.
- int index = lookup.GetFieldIndex() - map->inobject_properties();
- if (index < 0) {
- // Index is an offset from the end of the object.
- int offset = map->instance_size() + (index * kPointerSize);
- if (PatchInlinedLoad(address(), map, offset)) {
- set_target(megamorphic_stub());
- TRACE_IC_NAMED("[LoadIC : inline patch %s]\n", name);
- return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
- } else {
- TRACE_IC_NAMED("[LoadIC : no inline patch %s (patching failed)]\n",
- name);
- }
- } else {
- TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inobject)]\n", name);
- }
- } else if (can_be_inlined_contextual) {
- Map* map = lookup.holder()->map();
- JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(
- lookup.holder()->property_dictionary()->ValueAt(
- lookup.GetDictionaryEntry()));
- if (PatchInlinedContextualLoad(address(),
- map,
- cell,
- lookup.IsDontDelete())) {
- set_target(megamorphic_stub());
- TRACE_IC_NAMED("[LoadIC : inline contextual patch %s]\n", name);
- ASSERT(cell->value() != isolate()->heap()->the_hole_value());
- return cell->value();
- }
- } else {
- if (FLAG_use_ic && state == PREMONOMORPHIC) {
- TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inlinable)]\n", name);
- }
- }
-
// Update inline cache and stub cache.
if (FLAG_use_ic) {
UpdateCaches(&lookup, state, object, name);
}
PropertyAttributes attr;
- if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
+ if (lookup.IsProperty() &&
+ (lookup.type() == INTERCEPTOR || lookup.type() == HANDLER)) {
// Get the property.
Object* result;
{ MaybeObject* maybe_result =
@@ -1139,9 +1068,49 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
}
+String* KeyedLoadIC::GetStubNameForCache(IC::State ic_state) {
+ if (ic_state == MONOMORPHIC) {
+ return isolate()->heap()->KeyedLoadSpecializedMonomorphic_symbol();
+ } else {
+ ASSERT(ic_state == MEGAMORPHIC);
+ return isolate()->heap()->KeyedLoadSpecializedPolymorphic_symbol();
+ }
+}
+
+
+MaybeObject* KeyedLoadIC::GetFastElementStubWithoutMapCheck(
+ bool is_js_array) {
+ return KeyedLoadFastElementStub().TryGetCode();
+}
+
+
+MaybeObject* KeyedLoadIC::GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type) {
+ return KeyedLoadExternalArrayStub(array_type).TryGetCode();
+}
+
+
+MaybeObject* KeyedLoadIC::ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode) {
+ Object* object;
+ KeyedLoadStubCompiler compiler;
+ MaybeObject* maybe_code = compiler.CompileLoadMegamorphic(receiver_maps,
+ targets);
+ if (!maybe_code->ToObject(&object)) return maybe_code;
+ isolate()->counters()->keyed_load_polymorphic_stubs()->Increment();
+ PROFILE(isolate(), CodeCreateEvent(
+ Logger::KEYED_LOAD_MEGAMORPHIC_IC_TAG,
+ Code::cast(object), 0));
+ return object;
+}
+
+
MaybeObject* KeyedLoadIC::Load(State state,
Handle<Object> object,
- Handle<Object> key) {
+ Handle<Object> key,
+ bool force_generic_stub) {
// Check for values that can be converted into a symbol.
// TODO(1295): Remove this code.
HandleScope scope(isolate());
@@ -1267,47 +1236,32 @@ MaybeObject* KeyedLoadIC::Load(State state,
if (use_ic) {
Code* stub = generic_stub();
- if (state == UNINITIALIZED) {
+ if (!force_generic_stub) {
if (object->IsString() && key->IsNumber()) {
- stub = string_stub();
+ if (state == UNINITIALIZED) {
+ stub = string_stub();
+ }
} else if (object->IsJSObject()) {
- Handle<JSObject> receiver = Handle<JSObject>::cast(object);
- if (receiver->HasExternalArrayElements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedLoadOrStoreExternalArray(
- *receiver, false, kNonStrictMode);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
- } else if (receiver->HasIndexedInterceptor()) {
+ JSObject* receiver = JSObject::cast(*object);
+ if (receiver->HasIndexedInterceptor()) {
stub = indexed_interceptor_stub();
- } else if (key->IsSmi() &&
- receiver->map()->has_fast_elements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedLoadSpecialized(*receiver);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
+ } else if (key->IsSmi()) {
+ MaybeObject* maybe_stub = ComputeStub(receiver,
+ false,
+ kNonStrictMode,
+ stub);
+ stub = maybe_stub->IsFailure() ?
+ NULL : Code::cast(maybe_stub->ToObjectUnchecked());
}
}
}
if (stub != NULL) set_target(stub);
+ }
#ifdef DEBUG
- TraceIC("KeyedLoadIC", key, state, target());
+ TraceIC("KeyedLoadIC", key, state, target());
#endif // DEBUG
- // For JSObjects with fast elements that are not value wrappers
- // and that do not have indexed interceptors, we initialize the
- // inlined fast case (if present) by patching the inlined map
- // check.
- if (object->IsJSObject() &&
- !object->IsJSValue() &&
- !JSObject::cast(*object)->HasIndexedInterceptor() &&
- JSObject::cast(*object)->HasFastElements()) {
- Map* map = JSObject::cast(*object)->map();
- PatchInlinedLoad(address(), map);
- }
- }
-
// Get the property.
return Runtime::GetObjectProperty(isolate(), object, key);
}
@@ -1471,57 +1425,7 @@ MaybeObject* StoreIC::Store(State state,
LookupResult lookup;
if (LookupForWrite(*receiver, *name, &lookup)) {
- bool can_be_inlined =
- state == UNINITIALIZED &&
- lookup.IsProperty() &&
- lookup.holder() == *receiver &&
- lookup.type() == FIELD &&
- !receiver->IsAccessCheckNeeded();
-
- if (can_be_inlined) {
- Map* map = lookup.holder()->map();
- // Property's index in the properties array. If negative we have
- // an inobject property.
- int index = lookup.GetFieldIndex() - map->inobject_properties();
- if (index < 0) {
- // Index is an offset from the end of the object.
- int offset = map->instance_size() + (index * kPointerSize);
- if (PatchInlinedStore(address(), map, offset)) {
- set_target((strict_mode == kStrictMode)
- ? megamorphic_stub_strict()
- : megamorphic_stub());
-#ifdef DEBUG
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : inline patch %s]\n", *name->ToCString());
- }
-#endif
- return receiver->SetProperty(*name, *value, NONE, strict_mode);
-#ifdef DEBUG
-
- } else {
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : no inline patch %s (patching failed)]\n",
- *name->ToCString());
- }
- }
- } else {
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : no inline patch %s (not inobject)]\n",
- *name->ToCString());
- }
- }
- } else {
- if (state == PREMONOMORPHIC) {
- if (FLAG_trace_ic) {
- PrintF("[StoreIC : no inline patch %s (not inlinable)]\n",
- *name->ToCString());
-#endif
- }
- }
- }
-
- // If no inlined store ic was patched, generate a stub for this
- // store.
+ // Generate a stub for this store.
UpdateCaches(&lookup, state, strict_mode, receiver, name, value);
} else {
// Strict mode doesn't allow setting non-existent global property
@@ -1653,11 +1557,256 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
}
+static bool AddOneReceiverMapIfMissing(MapList* receiver_maps,
+ Map* new_receiver_map) {
+ for (int current = 0; current < receiver_maps->length(); ++current) {
+ if (receiver_maps->at(current) == new_receiver_map) {
+ return false;
+ }
+ }
+ receiver_maps->Add(new_receiver_map);
+ return true;
+}
+
+
+void KeyedIC::GetReceiverMapsForStub(Code* stub, MapList* result) {
+ ASSERT(stub->is_inline_cache_stub());
+ if (stub == string_stub()) {
+ return result->Add(isolate()->heap()->string_map());
+ } else if (stub->is_keyed_load_stub() || stub->is_keyed_store_stub()) {
+ if (stub->ic_state() == MONOMORPHIC) {
+ result->Add(Map::cast(stub->FindFirstMap()));
+ } else {
+ ASSERT(stub->ic_state() == MEGAMORPHIC);
+ AssertNoAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(stub, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ ASSERT(object->IsMap());
+ result->Add(Map::cast(object));
+ }
+ }
+ }
+}
+
+
+MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode,
+ Code* generic_stub) {
+ State ic_state = target()->ic_state();
+ Code* monomorphic_stub;
+ // Always compute the MONOMORPHIC stub, even if the MEGAMORPHIC stub ends up
+ // being used. This is necessary because the megamorphic stub needs to have
+ // access to more information than what is stored in the receiver map in some
+ // cases (external arrays need the array type from the MONOMORPHIC stub).
+ MaybeObject* maybe_stub = ComputeMonomorphicStub(receiver,
+ is_store,
+ strict_mode,
+ generic_stub);
+ if (!maybe_stub->To(&monomorphic_stub)) return maybe_stub;
+
+ if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
+ return monomorphic_stub;
+ }
+ ASSERT(target() != generic_stub);
+
+ // Don't handle megamorphic property accesses for INTERCEPTORS or CALLBACKS
+ // via megamorphic stubs, since they don't have a map in their relocation info
+ // and so the stubs can't be harvested for the object needed for a map check.
+ if (target()->type() != NORMAL) {
+ return generic_stub;
+ }
+
+ // Determine the list of receiver maps that this call site has seen,
+ // adding the map that was just encountered.
+ MapList target_receiver_maps;
+ GetReceiverMapsForStub(target(), &target_receiver_maps);
+ if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver->map())) {
+ // If the miss wasn't due to an unseen map, a MEGAMORPHIC stub
+ // won't help, use the generic stub.
+ return generic_stub;
+ }
+
+ // TODO(1385): Currently MEGAMORPHIC stubs are cached in the receiver map stub
+ // cache, but that can put receiver types together from unrelated call sites
+ // into the same stub--they always handle the union of all receiver maps seen
+ // at all call sites involving the receiver map. This is only an
+ // approximation: ideally, there would be a global cache that mapped sets of
+ // receiver maps to MEGAMORPHIC stubs. The complexity of the MEGAMORPHIC stub
+ // computation also leads to direct manipulation of the stub cache from the IC
+ // code, which the global cache solution would avoid.
+ Code::Kind kind = this->kind();
+ Code::Flags flags = Code::ComputeFlags(kind,
+ NOT_IN_LOOP,
+ MEGAMORPHIC,
+ strict_mode);
+ String* megamorphic_name = GetStubNameForCache(MEGAMORPHIC);
+ Object* maybe_cached_stub = receiver->map()->FindInCodeCache(megamorphic_name,
+ flags);
+
+ // Create a set of all receiver maps that have been seen at the IC call site
+ // and those seen by the MEGAMORPHIC cached stub, if that's the stub that's
+ // been selected.
+ MapList receiver_maps;
+ if (!maybe_cached_stub->IsUndefined()) {
+ GetReceiverMapsForStub(Code::cast(maybe_cached_stub), &receiver_maps);
+ }
+ bool added_map = false;
+ for (int i = 0; i < target_receiver_maps.length(); ++i) {
+ if (AddOneReceiverMapIfMissing(&receiver_maps,
+ target_receiver_maps.at(i))) {
+ added_map = true;
+ }
+ }
+ ASSERT(receiver_maps.length() > 0);
+
+ // If the maximum number of receiver maps has been exceeded, use the Generic
+ // version of the IC.
+ if (receiver_maps.length() > KeyedIC::kMaxKeyedPolymorphism) {
+ return generic_stub;
+ }
+
+ // If no maps have been seen at the call site that aren't in the cached
+ // stub, then use it.
+ if (!added_map) {
+ ASSERT(!maybe_cached_stub->IsUndefined());
+ ASSERT(maybe_cached_stub->IsCode());
+ return Code::cast(maybe_cached_stub);
+ }
+
+ // Lookup all of the receiver maps in the cache, they should all already
+ // have MONOMORPHIC stubs.
+ CodeList handler_ics(KeyedIC::kMaxKeyedPolymorphism);
+ for (int current = 0; current < receiver_maps.length(); ++current) {
+ Map* receiver_map(receiver_maps.at(current));
+ MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck(
+ receiver_map,
+ strict_mode,
+ generic_stub);
+ Code* cached_stub;
+ if (!maybe_cached_stub->To(&cached_stub)) {
+ return maybe_cached_stub;
+ }
+ handler_ics.Add(cached_stub);
+ }
+
+ Code* stub;
+ // Build the MEGAMORPHIC stub.
+ maybe_stub = ConstructMegamorphicStub(&receiver_maps,
+ &handler_ics,
+ strict_mode);
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+
+ MaybeObject* maybe_update = receiver->UpdateMapCodeCache(
+ megamorphic_name,
+ stub);
+ if (maybe_update->IsFailure()) return maybe_update;
+ return stub;
+}
+
+
+MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck(
+ Map* receiver_map,
+ StrictModeFlag strict_mode,
+ Code* generic_stub) {
+ if ((receiver_map->instance_type() & kNotStringTag) == 0) {
+ ASSERT(string_stub() != NULL);
+ return string_stub();
+ } else if (receiver_map->has_external_array_elements()) {
+ // Determine the array type from the default MONOMORPHIC already generated
+ // stub. There is no other way to determine the type of the external array
+ // directly from the receiver type.
+ Code::Kind kind = this->kind();
+ Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
+ NORMAL,
+ strict_mode);
+ String* monomorphic_name = GetStubNameForCache(MONOMORPHIC);
+ Object* maybe_default_stub = receiver_map->FindInCodeCache(monomorphic_name,
+ flags);
+ if (maybe_default_stub->IsUndefined()) {
+ return generic_stub;
+ }
+ Code* default_stub = Code::cast(maybe_default_stub);
+ return GetExternalArrayStubWithoutMapCheck(
+ default_stub->external_array_type());
+ } else if (receiver_map->has_fast_elements()) {
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ return GetFastElementStubWithoutMapCheck(is_js_array);
+ } else {
+ return generic_stub;
+ }
+}
+
+
+MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode,
+ Code* generic_stub) {
+ Code* result = NULL;
+ if (receiver->HasExternalArrayElements()) {
+ MaybeObject* maybe_stub =
+ isolate()->stub_cache()->ComputeKeyedLoadOrStoreExternalArray(
+ receiver, is_store, strict_mode);
+ if (!maybe_stub->To(&result)) return maybe_stub;
+ } else if (receiver->map()->has_fast_elements()) {
+ MaybeObject* maybe_stub =
+ isolate()->stub_cache()->ComputeKeyedLoadOrStoreFastElement(
+ receiver, is_store, strict_mode);
+ if (!maybe_stub->To(&result)) return maybe_stub;
+ } else {
+ result = generic_stub;
+ }
+ return result;
+}
+
+
+String* KeyedStoreIC::GetStubNameForCache(IC::State ic_state) {
+ if (ic_state == MONOMORPHIC) {
+ return isolate()->heap()->KeyedStoreSpecializedMonomorphic_symbol();
+ } else {
+ ASSERT(ic_state == MEGAMORPHIC);
+ return isolate()->heap()->KeyedStoreSpecializedPolymorphic_symbol();
+ }
+}
+
+
+MaybeObject* KeyedStoreIC::GetFastElementStubWithoutMapCheck(
+ bool is_js_array) {
+ return KeyedStoreFastElementStub(is_js_array).TryGetCode();
+}
+
+
+MaybeObject* KeyedStoreIC::GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type) {
+ return KeyedStoreExternalArrayStub(array_type).TryGetCode();
+}
+
+
+MaybeObject* KeyedStoreIC::ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode) {
+ Object* object;
+ KeyedStoreStubCompiler compiler(strict_mode);
+ MaybeObject* maybe_code = compiler.CompileStoreMegamorphic(receiver_maps,
+ targets);
+ if (!maybe_code->ToObject(&object)) return maybe_code;
+ isolate()->counters()->keyed_store_polymorphic_stubs()->Increment();
+ PROFILE(isolate(), CodeCreateEvent(
+ Logger::KEYED_STORE_MEGAMORPHIC_IC_TAG,
+ Code::cast(object), 0));
+ return object;
+}
+
+
MaybeObject* KeyedStoreIC::Store(State state,
StrictModeFlag strict_mode,
Handle<Object> object,
Handle<Object> key,
- Handle<Object> value) {
+ Handle<Object> value,
+ bool force_generic) {
if (key->IsSymbol()) {
Handle<String> name = Handle<String>::cast(key);
@@ -1699,29 +1848,27 @@ MaybeObject* KeyedStoreIC::Store(State state,
ASSERT(!(use_ic && object->IsJSGlobalProxy()));
if (use_ic) {
- Code* stub =
- (strict_mode == kStrictMode) ? generic_stub_strict() : generic_stub();
- if (state == UNINITIALIZED) {
- if (object->IsJSObject()) {
- Handle<JSObject> receiver = Handle<JSObject>::cast(object);
- if (receiver->HasExternalArrayElements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedLoadOrStoreExternalArray(
- *receiver, true, strict_mode);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
- } else if (key->IsSmi() && receiver->map()->has_fast_elements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedStoreSpecialized(
- *receiver, strict_mode);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
- }
+ Code* stub = (strict_mode == kStrictMode)
+ ? generic_stub_strict()
+ : generic_stub();
+ if (!force_generic) {
+ if (object->IsJSObject() && key->IsSmi()) {
+ JSObject* receiver = JSObject::cast(*object);
+ MaybeObject* maybe_stub = ComputeStub(receiver,
+ true,
+ strict_mode,
+ stub);
+ stub = maybe_stub->IsFailure() ?
+ NULL : Code::cast(maybe_stub->ToObjectUnchecked());
}
}
if (stub != NULL) set_target(stub);
}
+#ifdef DEBUG
+ TraceIC("KeyedStoreIC", key, state, target());
+#endif
+
// Set the property.
return Runtime::SetObjectProperty(
isolate(), object , key, value, NONE, strict_mode);
@@ -1890,7 +2037,16 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss) {
ASSERT(args.length() == 2);
KeyedLoadIC ic(isolate);
IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
- return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1), false);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissForceGeneric) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 2);
+ KeyedLoadIC ic(isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1), true);
}
@@ -1974,22 +2130,123 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Miss) {
static_cast<StrictModeFlag>(extra_ic_state & kStrictMode),
args.at<Object>(0),
args.at<Object>(1),
- args.at<Object>(2));
+ args.at<Object>(2),
+ false);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Slow) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(isolate);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ Handle<Object> object = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(1);
+ Handle<Object> value = args.at<Object>(2);
+ StrictModeFlag strict_mode =
+ static_cast<StrictModeFlag>(extra_ic_state & kStrictMode);
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ NONE,
+ strict_mode);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissForceGeneric) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ static_cast<StrictModeFlag>(extra_ic_state & kStrictMode),
+ args.at<Object>(0),
+ args.at<Object>(1),
+ args.at<Object>(2),
+ true);
}
-void TRBinaryOpIC::patch(Code* code) {
+void UnaryOpIC::patch(Code* code) {
set_target(code);
}
-const char* TRBinaryOpIC::GetName(TypeInfo type_info) {
+const char* UnaryOpIC::GetName(TypeInfo type_info) {
+ switch (type_info) {
+ case UNINITIALIZED: return "Uninitialized";
+ case SMI: return "Smi";
+ case HEAP_NUMBER: return "HeapNumbers";
+ case GENERIC: return "Generic";
+ default: return "Invalid";
+ }
+}
+
+
+UnaryOpIC::State UnaryOpIC::ToState(TypeInfo type_info) {
+ switch (type_info) {
+ case UNINITIALIZED:
+ return ::v8::internal::UNINITIALIZED;
+ case SMI:
+ case HEAP_NUMBER:
+ return MONOMORPHIC;
+ case GENERIC:
+ return MEGAMORPHIC;
+ }
+ UNREACHABLE();
+ return ::v8::internal::UNINITIALIZED;
+}
+
+UnaryOpIC::TypeInfo UnaryOpIC::GetTypeInfo(Handle<Object> operand) {
+ ::v8::internal::TypeInfo operand_type =
+ ::v8::internal::TypeInfo::TypeFromValue(operand);
+ if (operand_type.IsSmi()) {
+ return SMI;
+ } else if (operand_type.IsNumber()) {
+ return HEAP_NUMBER;
+ } else {
+ return GENERIC;
+ }
+}
+
+
+UnaryOpIC::TypeInfo UnaryOpIC::ComputeNewType(
+ UnaryOpIC::TypeInfo current_type,
+ UnaryOpIC::TypeInfo previous_type) {
+ switch (previous_type) {
+ case UnaryOpIC::UNINITIALIZED:
+ return current_type;
+ case UnaryOpIC::SMI:
+ return (current_type == UnaryOpIC::GENERIC)
+ ? UnaryOpIC::GENERIC
+ : UnaryOpIC::HEAP_NUMBER;
+ case UnaryOpIC::HEAP_NUMBER:
+ return UnaryOpIC::GENERIC;
+ case UnaryOpIC::GENERIC:
+ // We should never do patching if we are in GENERIC state.
+ UNREACHABLE();
+ return UnaryOpIC::GENERIC;
+ }
+ UNREACHABLE();
+ return UnaryOpIC::GENERIC;
+}
+
+
+void BinaryOpIC::patch(Code* code) {
+ set_target(code);
+}
+
+
+const char* BinaryOpIC::GetName(TypeInfo type_info) {
switch (type_info) {
case UNINITIALIZED: return "Uninitialized";
case SMI: return "SMI";
case INT32: return "Int32s";
case HEAP_NUMBER: return "HeapNumbers";
case ODDBALL: return "Oddball";
+ case BOTH_STRING: return "BothStrings";
case STRING: return "Strings";
case GENERIC: return "Generic";
default: return "Invalid";
@@ -1997,7 +2254,7 @@ const char* TRBinaryOpIC::GetName(TypeInfo type_info) {
}
-TRBinaryOpIC::State TRBinaryOpIC::ToState(TypeInfo type_info) {
+BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
switch (type_info) {
case UNINITIALIZED:
return ::v8::internal::UNINITIALIZED;
@@ -2005,6 +2262,7 @@ TRBinaryOpIC::State TRBinaryOpIC::ToState(TypeInfo type_info) {
case INT32:
case HEAP_NUMBER:
case ODDBALL:
+ case BOTH_STRING:
case STRING:
return MONOMORPHIC;
case GENERIC:
@@ -2015,18 +2273,23 @@ TRBinaryOpIC::State TRBinaryOpIC::ToState(TypeInfo type_info) {
}
-TRBinaryOpIC::TypeInfo TRBinaryOpIC::JoinTypes(TRBinaryOpIC::TypeInfo x,
- TRBinaryOpIC::TypeInfo y) {
+BinaryOpIC::TypeInfo BinaryOpIC::JoinTypes(BinaryOpIC::TypeInfo x,
+ BinaryOpIC::TypeInfo y) {
if (x == UNINITIALIZED) return y;
if (y == UNINITIALIZED) return x;
- if (x == STRING && y == STRING) return STRING;
- if (x == STRING || y == STRING) return GENERIC;
- if (x >= y) return x;
+ if (x == y) return x;
+ if (x == BOTH_STRING && y == STRING) return STRING;
+ if (x == STRING && y == BOTH_STRING) return STRING;
+ if (x == STRING || x == BOTH_STRING || y == STRING || y == BOTH_STRING) {
+ return GENERIC;
+ }
+ if (x > y) return x;
return y;
}
-TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
- Handle<Object> right) {
+
+BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Handle<Object> left,
+ Handle<Object> right) {
::v8::internal::TypeInfo left_type =
::v8::internal::TypeInfo::TypeFromValue(left);
::v8::internal::TypeInfo right_type =
@@ -2046,9 +2309,11 @@ TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
return HEAP_NUMBER;
}
- if (left_type.IsString() || right_type.IsString()) {
- // Patching for fast string ADD makes sense even if only one of the
- // arguments is a string.
+ // Patching for fast string ADD makes sense even if only one of the
+ // arguments is a string.
+ if (left_type.IsString()) {
+ return right_type.IsString() ? BOTH_STRING : STRING;
+ } else if (right_type.IsString()) {
return STRING;
}
@@ -2062,12 +2327,67 @@ TRBinaryOpIC::TypeInfo TRBinaryOpIC::GetTypeInfo(Handle<Object> left,
// defined in code-stubs-<arch>.cc
// Only needed to remove dependency of ic.cc on code-stubs-<arch>.h.
-Handle<Code> GetTypeRecordingBinaryOpStub(int key,
- TRBinaryOpIC::TypeInfo type_info,
- TRBinaryOpIC::TypeInfo result_type);
+Handle<Code> GetUnaryOpStub(int key, UnaryOpIC::TypeInfo type_info);
+
+
+RUNTIME_FUNCTION(MaybeObject*, UnaryOp_Patch) {
+ ASSERT(args.length() == 4);
+
+ HandleScope scope(isolate);
+ Handle<Object> operand = args.at<Object>(0);
+ int key = Smi::cast(args[1])->value();
+ Token::Value op = static_cast<Token::Value>(Smi::cast(args[2])->value());
+ UnaryOpIC::TypeInfo previous_type =
+ static_cast<UnaryOpIC::TypeInfo>(Smi::cast(args[3])->value());
+ UnaryOpIC::TypeInfo type = UnaryOpIC::GetTypeInfo(operand);
+ type = UnaryOpIC::ComputeNewType(type, previous_type);
+
+ Handle<Code> code = GetUnaryOpStub(key, type);
+ if (!code.is_null()) {
+ if (FLAG_trace_ic) {
+ PrintF("[UnaryOpIC (%s->%s)#%s]\n",
+ UnaryOpIC::GetName(previous_type),
+ UnaryOpIC::GetName(type),
+ Token::Name(op));
+ }
+ UnaryOpIC ic(isolate);
+ ic.patch(*code);
+ }
-RUNTIME_FUNCTION(MaybeObject*, TypeRecordingBinaryOp_Patch) {
+ Handle<JSBuiltinsObject> builtins = Handle<JSBuiltinsObject>(
+ isolate->thread_local_top()->context_->builtins(), isolate);
+ Object* builtin = NULL; // Initialization calms down the compiler.
+ switch (op) {
+ case Token::SUB:
+ builtin = builtins->javascript_builtin(Builtins::UNARY_MINUS);
+ break;
+ case Token::BIT_NOT:
+ builtin = builtins->javascript_builtin(Builtins::BIT_NOT);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ Handle<JSFunction> builtin_function(JSFunction::cast(builtin), isolate);
+
+ bool caught_exception;
+ Handle<Object> result = Execution::Call(builtin_function, operand, 0, NULL,
+ &caught_exception);
+ if (caught_exception) {
+ return Failure::Exception();
+ }
+ return *result;
+}
+
+// defined in code-stubs-<arch>.cc
+// Only needed to remove dependency of ic.cc on code-stubs-<arch>.h.
+Handle<Code> GetBinaryOpStub(int key,
+ BinaryOpIC::TypeInfo type_info,
+ BinaryOpIC::TypeInfo result_type);
+
+
+RUNTIME_FUNCTION(MaybeObject*, BinaryOp_Patch) {
ASSERT(args.length() == 5);
HandleScope scope(isolate);
@@ -2075,48 +2395,50 @@ RUNTIME_FUNCTION(MaybeObject*, TypeRecordingBinaryOp_Patch) {
Handle<Object> right = args.at<Object>(1);
int key = Smi::cast(args[2])->value();
Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
- TRBinaryOpIC::TypeInfo previous_type =
- static_cast<TRBinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
-
- TRBinaryOpIC::TypeInfo type = TRBinaryOpIC::GetTypeInfo(left, right);
- type = TRBinaryOpIC::JoinTypes(type, previous_type);
- TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED;
- if (type == TRBinaryOpIC::STRING && op != Token::ADD) {
- type = TRBinaryOpIC::GENERIC;
- }
- if (type == TRBinaryOpIC::SMI &&
- previous_type == TRBinaryOpIC::SMI) {
- if (op == Token::DIV || op == Token::MUL || kSmiValueSize == 32) {
+ BinaryOpIC::TypeInfo previous_type =
+ static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
+
+ BinaryOpIC::TypeInfo type = BinaryOpIC::GetTypeInfo(left, right);
+ type = BinaryOpIC::JoinTypes(type, previous_type);
+ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED;
+ if ((type == BinaryOpIC::STRING || type == BinaryOpIC::BOTH_STRING) &&
+ op != Token::ADD) {
+ type = BinaryOpIC::GENERIC;
+ }
+ if (type == BinaryOpIC::SMI && previous_type == BinaryOpIC::SMI) {
+ if (op == Token::DIV ||
+ op == Token::MUL ||
+ op == Token::SHR ||
+ kSmiValueSize == 32) {
// Arithmetic on two Smi inputs has yielded a heap number.
// That is the only way to get here from the Smi stub.
// With 32-bit Smis, all overflows give heap numbers, but with
// 31-bit Smis, most operations overflow to int32 results.
- result_type = TRBinaryOpIC::HEAP_NUMBER;
+ result_type = BinaryOpIC::HEAP_NUMBER;
} else {
// Other operations on SMIs that overflow yield int32s.
- result_type = TRBinaryOpIC::INT32;
+ result_type = BinaryOpIC::INT32;
}
}
- if (type == TRBinaryOpIC::INT32 &&
- previous_type == TRBinaryOpIC::INT32) {
+ if (type == BinaryOpIC::INT32 && previous_type == BinaryOpIC::INT32) {
// We must be here because an operation on two INT32 types overflowed.
- result_type = TRBinaryOpIC::HEAP_NUMBER;
+ result_type = BinaryOpIC::HEAP_NUMBER;
}
- Handle<Code> code = GetTypeRecordingBinaryOpStub(key, type, result_type);
+ Handle<Code> code = GetBinaryOpStub(key, type, result_type);
if (!code.is_null()) {
if (FLAG_trace_ic) {
- PrintF("[TypeRecordingBinaryOpIC (%s->(%s->%s))#%s]\n",
- TRBinaryOpIC::GetName(previous_type),
- TRBinaryOpIC::GetName(type),
- TRBinaryOpIC::GetName(result_type),
+ PrintF("[BinaryOpIC (%s->(%s->%s))#%s]\n",
+ BinaryOpIC::GetName(previous_type),
+ BinaryOpIC::GetName(type),
+ BinaryOpIC::GetName(result_type),
Token::Name(op));
}
- TRBinaryOpIC ic(isolate);
+ BinaryOpIC ic(isolate);
ic.patch(*code);
// Activate inlined smi code.
- if (previous_type == TRBinaryOpIC::UNINITIALIZED) {
+ if (previous_type == BinaryOpIC::UNINITIALIZED) {
PatchInlinedSmiCode(ic.address());
}
}
@@ -2198,6 +2520,8 @@ const char* CompareIC::GetStateName(State state) {
case SMIS: return "SMIS";
case HEAP_NUMBERS: return "HEAP_NUMBERS";
case OBJECTS: return "OBJECTS";
+ case SYMBOLS: return "SYMBOLS";
+ case STRINGS: return "STRINGS";
case GENERIC: return "GENERIC";
default:
UNREACHABLE();
@@ -2210,12 +2534,18 @@ CompareIC::State CompareIC::TargetState(State state,
bool has_inlined_smi_code,
Handle<Object> x,
Handle<Object> y) {
- if (!has_inlined_smi_code && state != UNINITIALIZED) return GENERIC;
+ if (!has_inlined_smi_code && state != UNINITIALIZED && state != SYMBOLS) {
+ return GENERIC;
+ }
if (state == UNINITIALIZED && x->IsSmi() && y->IsSmi()) return SMIS;
if ((state == UNINITIALIZED || (state == SMIS && has_inlined_smi_code)) &&
x->IsNumber() && y->IsNumber()) return HEAP_NUMBERS;
if (op_ != Token::EQ && op_ != Token::EQ_STRICT) return GENERIC;
if (state == UNINITIALIZED &&
+ x->IsSymbol() && y->IsSymbol()) return SYMBOLS;
+ if ((state == UNINITIALIZED || state == SYMBOLS) &&
+ x->IsString() && y->IsString()) return STRINGS;
+ if (state == UNINITIALIZED &&
x->IsJSObject() && y->IsJSObject()) return OBJECTS;
return GENERIC;
}
diff --git a/src/ic.h b/src/ic.h
index 911cbd83..25d09860 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -29,6 +29,7 @@
#define V8_IC_H_
#include "macro-assembler.h"
+#include "type-info.h"
namespace v8 {
namespace internal {
@@ -39,12 +40,15 @@ namespace internal {
#define IC_UTIL_LIST(ICU) \
ICU(LoadIC_Miss) \
ICU(KeyedLoadIC_Miss) \
+ ICU(KeyedLoadIC_MissForceGeneric) \
ICU(CallIC_Miss) \
ICU(KeyedCallIC_Miss) \
ICU(StoreIC_Miss) \
ICU(StoreIC_ArrayLength) \
ICU(SharedStoreIC_ExtendStorage) \
ICU(KeyedStoreIC_Miss) \
+ ICU(KeyedStoreIC_MissForceGeneric) \
+ ICU(KeyedStoreIC_Slow) \
/* Utilities for IC stubs. */ \
ICU(LoadCallbackProperty) \
ICU(StoreCallbackProperty) \
@@ -53,7 +57,8 @@ namespace internal {
ICU(LoadPropertyWithInterceptorForCall) \
ICU(KeyedLoadPropertyWithInterceptor) \
ICU(StoreInterceptorProperty) \
- ICU(TypeRecordingBinaryOp_Patch) \
+ ICU(UnaryOp_Patch) \
+ ICU(BinaryOp_Patch) \
ICU(CompareIC_Miss)
//
// IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC,
@@ -141,11 +146,11 @@ class IC {
void set_target(Code* code) { SetTargetAtAddress(address(), code); }
#ifdef DEBUG
- static void TraceIC(const char* type,
- Handle<Object> name,
- State old_state,
- Code* new_target,
- const char* extra_info = "");
+ void TraceIC(const char* type,
+ Handle<Object> name,
+ State old_state,
+ Code* new_target,
+ const char* extra_info = "");
#endif
Failure* TypeError(const char* type,
@@ -190,6 +195,10 @@ class IC_Utility {
class CallICBase: public IC {
+ public:
+ class Contextual: public BitField<bool, 0, 1> {};
+ class StringStubState: public BitField<StringStubFeedback, 1, 1> {};
+
protected:
CallICBase(Code::Kind kind, Isolate* isolate)
: IC(EXTRA_CALL_FRAME, isolate), kind_(kind) {}
@@ -230,6 +239,7 @@ class CallICBase: public IC {
void ReceiverToObjectIfRequired(Handle<Object> callee, Handle<Object> object);
static void Clear(Address address, Code* target);
+
friend class IC;
};
@@ -241,11 +251,17 @@ class CallIC: public CallICBase {
}
// Code generator routines.
- static void GenerateInitialize(MacroAssembler* masm, int argc) {
- GenerateMiss(masm, argc);
- }
- static void GenerateMiss(MacroAssembler* masm, int argc);
- static void GenerateMegamorphic(MacroAssembler* masm, int argc);
+ static void GenerateInitialize(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
+ GenerateMiss(masm, argc, extra_ic_state);
+ }
+ static void GenerateMiss(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state);
+ static void GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state);
static void GenerateNormal(MacroAssembler* masm, int argc);
};
@@ -296,14 +312,6 @@ class LoadIC: public IC {
bool support_wrappers);
static void GenerateFunctionPrototype(MacroAssembler* masm);
- // Clear the use of the inlined version.
- static void ClearInlinedVersion(Address address);
-
- // The offset from the inlined patch site to the start of the
- // inlined load instruction. It is architecture-dependent, and not
- // used on ARM.
- static const int kOffsetToLoadInstruction;
-
private:
// Update the inline cache and the global stub cache based on the
// lookup result.
@@ -328,42 +336,82 @@ class LoadIC: public IC {
static void Clear(Address address, Code* target);
- static bool PatchInlinedLoad(Address address, Object* map, int index);
+ friend class IC;
+};
- static bool PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete);
- friend class IC;
+class KeyedIC: public IC {
+ public:
+ explicit KeyedIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {}
+ virtual ~KeyedIC() {}
+
+ static const int kMaxKeyedPolymorphism = 4;
+
+ virtual MaybeObject* GetFastElementStubWithoutMapCheck(
+ bool is_js_array) = 0;
+
+ virtual MaybeObject* GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type) = 0;
+
+ protected:
+ virtual Code* string_stub() {
+ return NULL;
+ }
+
+ virtual Code::Kind kind() const = 0;
+
+ virtual String* GetStubNameForCache(IC::State ic_state) = 0;
+
+ MaybeObject* ComputeStub(JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode,
+ Code* default_stub);
+
+ virtual MaybeObject* ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode) = 0;
+
+ private:
+ void GetReceiverMapsForStub(Code* stub, MapList* result);
+
+ MaybeObject* ComputeMonomorphicStubWithoutMapCheck(
+ Map* receiver_map,
+ StrictModeFlag strict_mode,
+ Code* generic_stub);
+
+ MaybeObject* ComputeMonomorphicStub(JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode,
+ Code* default_stub);
};
-class KeyedLoadIC: public IC {
+class KeyedLoadIC: public KeyedIC {
public:
- explicit KeyedLoadIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {
+ explicit KeyedLoadIC(Isolate* isolate) : KeyedIC(isolate) {
ASSERT(target()->is_keyed_load_stub());
}
MUST_USE_RESULT MaybeObject* Load(State state,
Handle<Object> object,
- Handle<Object> key);
+ Handle<Object> key,
+ bool force_generic_stub);
// Code generator routines.
- static void GenerateMiss(MacroAssembler* masm);
+ static void GenerateMiss(MacroAssembler* masm, bool force_generic);
static void GenerateRuntimeGetProperty(MacroAssembler* masm);
- static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
+ static void GenerateInitialize(MacroAssembler* masm) {
+ GenerateMiss(masm, false);
+ }
static void GeneratePreMonomorphic(MacroAssembler* masm) {
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
}
static void GenerateGeneric(MacroAssembler* masm);
static void GenerateString(MacroAssembler* masm);
static void GenerateIndexedInterceptor(MacroAssembler* masm);
- // Clear the use of the inlined version.
- static void ClearInlinedVersion(Address address);
-
// Bit mask to be tested against bit field for the cases when
// generic stub should go into slow case.
// Access check is necessary explicitly since generic stub does not perform
@@ -371,6 +419,27 @@ class KeyedLoadIC: public IC {
static const int kSlowCaseBitFieldMask =
(1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor);
+ virtual MaybeObject* GetFastElementStubWithoutMapCheck(
+ bool is_js_array);
+
+ virtual MaybeObject* GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type);
+
+ protected:
+ virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; }
+
+ virtual String* GetStubNameForCache(IC::State ic_state);
+
+ virtual MaybeObject* ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode);
+
+ virtual Code* string_stub() {
+ return isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_String);
+ }
+
private:
// Update the inline cache.
void UpdateCaches(LookupResult* lookup,
@@ -395,11 +464,6 @@ class KeyedLoadIC: public IC {
return isolate()->builtins()->builtin(
Builtins::kKeyedLoadIC_PreMonomorphic);
}
- Code* string_stub() {
- return isolate()->builtins()->builtin(
- Builtins::kKeyedLoadIC_String);
- }
-
Code* indexed_interceptor_stub() {
return isolate()->builtins()->builtin(
Builtins::kKeyedLoadIC_IndexedInterceptor);
@@ -407,10 +471,6 @@ class KeyedLoadIC: public IC {
static void Clear(Address address, Code* target);
- // Support for patching the map that is checked in an inlined
- // version of keyed load.
- static bool PatchInlinedLoad(Address address, Object* map);
-
friend class IC;
};
@@ -437,13 +497,6 @@ class StoreIC: public IC {
static void GenerateGlobalProxy(MacroAssembler* masm,
StrictModeFlag strict_mode);
- // Clear the use of an inlined version.
- static void ClearInlinedVersion(Address address);
-
- // The offset from the inlined patch site to the start of the
- // inlined store instruction.
- static const int kOffsetToStoreInstruction;
-
private:
// Update the inline cache and the global stub cache based on the
// lookup result.
@@ -489,38 +542,50 @@ class StoreIC: public IC {
static void Clear(Address address, Code* target);
- // Support for patching the index and the map that is checked in an
- // inlined version of the named store.
- static bool PatchInlinedStore(Address address, Object* map, int index);
-
friend class IC;
};
-class KeyedStoreIC: public IC {
+class KeyedStoreIC: public KeyedIC {
public:
- explicit KeyedStoreIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
+ explicit KeyedStoreIC(Isolate* isolate) : KeyedIC(isolate) {
+ ASSERT(target()->is_keyed_store_stub());
+ }
MUST_USE_RESULT MaybeObject* Store(State state,
- StrictModeFlag strict_mode,
+ StrictModeFlag strict_mode,
Handle<Object> object,
Handle<Object> name,
- Handle<Object> value);
+ Handle<Object> value,
+ bool force_generic);
// Code generators for stub routines. Only called once at startup.
- static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
- static void GenerateMiss(MacroAssembler* masm);
+ static void GenerateInitialize(MacroAssembler* masm) {
+ GenerateMiss(masm, false);
+ }
+ static void GenerateMiss(MacroAssembler* masm, bool force_generic);
+ static void GenerateSlow(MacroAssembler* masm);
static void GenerateRuntimeSetProperty(MacroAssembler* masm,
StrictModeFlag strict_mode);
static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode);
- // Clear the inlined version so the IC is always hit.
- static void ClearInlinedVersion(Address address);
+ virtual MaybeObject* GetFastElementStubWithoutMapCheck(
+ bool is_js_array);
- // Restore the inlined version so the fast case can get hit.
- static void RestoreInlinedVersion(Address address);
+ virtual MaybeObject* GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type);
- private:
+ protected:
+ virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; }
+
+ virtual String* GetStubNameForCache(IC::State ic_state);
+
+ virtual MaybeObject* ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode);
+
+ private:
// Update the inline cache.
void UpdateCaches(LookupResult* lookup,
State state,
@@ -564,20 +629,38 @@ class KeyedStoreIC: public IC {
static void Clear(Address address, Code* target);
- // Support for patching the map that is checked in an inlined
- // version of keyed store.
- // The address is the patch point for the IC call
- // (Assembler::kCallTargetAddressOffset before the end of
- // the call/return address).
- // The map is the new map that the inlined code should check against.
- static bool PatchInlinedStore(Address address, Object* map);
-
friend class IC;
};
+class UnaryOpIC: public IC {
+ public:
+
+ // sorted: increasingly more unspecific (ignoring UNINITIALIZED)
+ // TODO(svenpanne) Using enums+switch is an antipattern, use a class instead.
+ enum TypeInfo {
+ UNINITIALIZED,
+ SMI,
+ HEAP_NUMBER,
+ GENERIC
+ };
+
+ explicit UnaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
+
+ void patch(Code* code);
+
+ static const char* GetName(TypeInfo type_info);
+
+ static State ToState(TypeInfo type_info);
+
+ static TypeInfo GetTypeInfo(Handle<Object> operand);
+
+ static TypeInfo ComputeNewType(TypeInfo type, TypeInfo previous);
+};
+
+
// Type Recording BinaryOpIC, that records the types of the inputs and outputs.
-class TRBinaryOpIC: public IC {
+class BinaryOpIC: public IC {
public:
enum TypeInfo {
@@ -586,11 +669,12 @@ class TRBinaryOpIC: public IC {
INT32,
HEAP_NUMBER,
ODDBALL,
+ BOTH_STRING, // Only used for addition operation.
STRING, // Only used for addition operation. At least one string operand.
GENERIC
};
- explicit TRBinaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
+ explicit BinaryOpIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
void patch(Code* code);
@@ -610,6 +694,8 @@ class CompareIC: public IC {
UNINITIALIZED,
SMIS,
HEAP_NUMBERS,
+ SYMBOLS,
+ STRINGS,
OBJECTS,
GENERIC
};
@@ -642,7 +728,7 @@ class CompareIC: public IC {
Token::Value op_;
};
-// Helper for TRBinaryOpIC and CompareIC.
+// Helper for BinaryOpIC and CompareIC.
void PatchInlinedSmiCode(Address address);
} } // namespace v8::internal
diff --git a/src/mips/register-allocator-mips.h b/src/isolate-inl.h
index c4489231..aa6b5372 100644
--- a/src/mips/register-allocator-mips.h
+++ b/src/isolate-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,23 +25,26 @@
// (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_MIPS_REGISTER_ALLOCATOR_MIPS_H_
-#define V8_MIPS_REGISTER_ALLOCATOR_MIPS_H_
+#ifndef V8_ISOLATE_INL_H_
+#define V8_ISOLATE_INL_H_
-#include "mips/constants-mips.h"
+#include "isolate.h"
+
+#include "debug.h"
namespace v8 {
namespace internal {
-class RegisterAllocatorConstants : public AllStatic {
- public:
- // No registers are currently managed by the register allocator on MIPS.
- static const int kNumRegisters = 0;
- static const int kInvalidRegister = -1;
-};
+bool Isolate::DebuggerHasBreakPoints() {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ return debug()->has_break_points();
+#else
+ return false;
+#endif
+}
-} } // namespace v8::internal
-#endif // V8_MIPS_REGISTER_ALLOCATOR_MIPS_H_
+} } // namespace v8::internal
+#endif // V8_ISOLATE_INL_H_
diff --git a/src/isolate.cc b/src/isolate.cc
index 5b3438f9..a7bf7d9b 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -40,6 +40,7 @@
#include "isolate.h"
#include "lithium-allocator.h"
#include "log.h"
+#include "messages.h"
#include "regexp-stack.h"
#include "runtime-profiler.h"
#include "scanner.h"
@@ -49,6 +50,7 @@
#include "spaces.h"
#include "stub-cache.h"
#include "version.h"
+#include "vm-state-inl.h"
namespace v8 {
@@ -61,6 +63,7 @@ int ThreadId::AllocateThreadId() {
return new_id;
}
+
int ThreadId::GetCurrentThreadId() {
int thread_id = Thread::GetThreadLocalInt(Isolate::thread_id_key_);
if (thread_id == 0) {
@@ -71,6 +74,52 @@ int ThreadId::GetCurrentThreadId() {
}
+ThreadLocalTop::ThreadLocalTop() {
+ InitializeInternal();
+}
+
+
+void ThreadLocalTop::InitializeInternal() {
+ c_entry_fp_ = 0;
+ handler_ = 0;
+#ifdef USE_SIMULATOR
+ simulator_ = NULL;
+#endif
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ js_entry_sp_ = NULL;
+ external_callback_ = NULL;
+#endif
+#ifdef ENABLE_VMSTATE_TRACKING
+ current_vm_state_ = EXTERNAL;
+#endif
+ try_catch_handler_address_ = NULL;
+ context_ = NULL;
+ thread_id_ = ThreadId::Invalid();
+ external_caught_exception_ = false;
+ failed_access_check_callback_ = NULL;
+ save_context_ = NULL;
+ catcher_ = NULL;
+}
+
+
+void ThreadLocalTop::Initialize() {
+ InitializeInternal();
+#ifdef USE_SIMULATOR
+#ifdef V8_TARGET_ARCH_ARM
+ simulator_ = Simulator::current(isolate_);
+#elif V8_TARGET_ARCH_MIPS
+ simulator_ = Simulator::current(isolate_);
+#endif
+#endif
+ thread_id_ = ThreadId::Current();
+}
+
+
+v8::TryCatch* ThreadLocalTop::TryCatchHandler() {
+ return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address());
+}
+
+
// Create a dummy thread that will wait forever on a semaphore. The only
// purpose for this thread is to have some stack area to save essential data
// into for use by a stacks only core dump (aka minidump).
@@ -312,6 +361,17 @@ Isolate::PerIsolateThreadData*
}
+Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThisThread() {
+ ThreadId thread_id = ThreadId::Current();
+ PerIsolateThreadData* per_thread = NULL;
+ {
+ ScopedLock lock(process_wide_mutex_);
+ per_thread = thread_data_table_->Lookup(this, thread_id);
+ }
+ return per_thread;
+}
+
+
void Isolate::EnsureDefaultIsolate() {
ScopedLock lock(process_wide_mutex_);
if (default_isolate_ == NULL) {
@@ -323,14 +383,19 @@ void Isolate::EnsureDefaultIsolate() {
}
// Can't use SetIsolateThreadLocals(default_isolate_, NULL) here
// becase a non-null thread data may be already set.
- Thread::SetThreadLocal(isolate_key_, default_isolate_);
+ if (Thread::GetThreadLocal(isolate_key_) == NULL) {
+ Thread::SetThreadLocal(isolate_key_, default_isolate_);
+ }
+ CHECK(default_isolate_->PreInit());
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debugger* Isolate::GetDefaultIsolateDebugger() {
EnsureDefaultIsolate();
return default_isolate_->debugger();
}
+#endif
StackGuard* Isolate::GetDefaultIsolateStackGuard() {
@@ -357,6 +422,892 @@ Isolate* Isolate::GetDefaultIsolateForLocking() {
}
+Address Isolate::get_address_from_id(Isolate::AddressId id) {
+ return isolate_addresses_[id];
+}
+
+
+char* Isolate::Iterate(ObjectVisitor* v, char* thread_storage) {
+ ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(thread_storage);
+ Iterate(v, thread);
+ return thread_storage + sizeof(ThreadLocalTop);
+}
+
+
+void Isolate::IterateThread(ThreadVisitor* v) {
+ v->VisitThread(this, thread_local_top());
+}
+
+
+void Isolate::IterateThread(ThreadVisitor* v, char* t) {
+ ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(t);
+ v->VisitThread(this, thread);
+}
+
+
+void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
+ // Visit the roots from the top for a given thread.
+ Object* pending;
+ // The pending exception can sometimes be a failure. We can't show
+ // that to the GC, which only understands objects.
+ if (thread->pending_exception_->ToObject(&pending)) {
+ v->VisitPointer(&pending);
+ thread->pending_exception_ = pending; // In case GC updated it.
+ }
+ v->VisitPointer(&(thread->pending_message_obj_));
+ v->VisitPointer(BitCast<Object**>(&(thread->pending_message_script_)));
+ v->VisitPointer(BitCast<Object**>(&(thread->context_)));
+ Object* scheduled;
+ if (thread->scheduled_exception_->ToObject(&scheduled)) {
+ v->VisitPointer(&scheduled);
+ thread->scheduled_exception_ = scheduled;
+ }
+
+ for (v8::TryCatch* block = thread->TryCatchHandler();
+ block != NULL;
+ block = TRY_CATCH_FROM_ADDRESS(block->next_)) {
+ v->VisitPointer(BitCast<Object**>(&(block->exception_)));
+ v->VisitPointer(BitCast<Object**>(&(block->message_)));
+ }
+
+ // Iterate over pointers on native execution stack.
+ for (StackFrameIterator it(this, thread); !it.done(); it.Advance()) {
+ it.frame()->Iterate(v);
+ }
+}
+
+
+void Isolate::Iterate(ObjectVisitor* v) {
+ ThreadLocalTop* current_t = thread_local_top();
+ Iterate(v, current_t);
+}
+
+
+void Isolate::RegisterTryCatchHandler(v8::TryCatch* that) {
+ // The ARM simulator has a separate JS stack. We therefore register
+ // the C++ try catch handler with the simulator and get back an
+ // address that can be used for comparisons with addresses into the
+ // JS stack. When running without the simulator, the address
+ // returned will be the address of the C++ try catch handler itself.
+ Address address = reinterpret_cast<Address>(
+ SimulatorStack::RegisterCTryCatch(reinterpret_cast<uintptr_t>(that)));
+ thread_local_top()->set_try_catch_handler_address(address);
+}
+
+
+void Isolate::UnregisterTryCatchHandler(v8::TryCatch* that) {
+ ASSERT(thread_local_top()->TryCatchHandler() == that);
+ thread_local_top()->set_try_catch_handler_address(
+ reinterpret_cast<Address>(that->next_));
+ thread_local_top()->catcher_ = NULL;
+ SimulatorStack::UnregisterCTryCatch();
+}
+
+
+Handle<String> Isolate::StackTraceString() {
+ if (stack_trace_nesting_level_ == 0) {
+ stack_trace_nesting_level_++;
+ HeapStringAllocator allocator;
+ StringStream::ClearMentionedObjectCache();
+ StringStream accumulator(&allocator);
+ incomplete_message_ = &accumulator;
+ PrintStack(&accumulator);
+ Handle<String> stack_trace = accumulator.ToString();
+ incomplete_message_ = NULL;
+ stack_trace_nesting_level_ = 0;
+ return stack_trace;
+ } else if (stack_trace_nesting_level_ == 1) {
+ stack_trace_nesting_level_++;
+ OS::PrintError(
+ "\n\nAttempt to print stack while printing stack (double fault)\n");
+ OS::PrintError(
+ "If you are lucky you may find a partial stack dump on stdout.\n\n");
+ incomplete_message_->OutputToStdOut();
+ return factory()->empty_symbol();
+ } else {
+ OS::Abort();
+ // Unreachable
+ return factory()->empty_symbol();
+ }
+}
+
+
+Handle<JSArray> Isolate::CaptureCurrentStackTrace(
+ int frame_limit, StackTrace::StackTraceOptions options) {
+ // Ensure no negative values.
+ int limit = Max(frame_limit, 0);
+ Handle<JSArray> stack_trace = factory()->NewJSArray(frame_limit);
+
+ Handle<String> column_key = factory()->LookupAsciiSymbol("column");
+ Handle<String> line_key = factory()->LookupAsciiSymbol("lineNumber");
+ Handle<String> script_key = factory()->LookupAsciiSymbol("scriptName");
+ Handle<String> name_or_source_url_key =
+ factory()->LookupAsciiSymbol("nameOrSourceURL");
+ Handle<String> script_name_or_source_url_key =
+ factory()->LookupAsciiSymbol("scriptNameOrSourceURL");
+ Handle<String> function_key = factory()->LookupAsciiSymbol("functionName");
+ Handle<String> eval_key = factory()->LookupAsciiSymbol("isEval");
+ Handle<String> constructor_key =
+ factory()->LookupAsciiSymbol("isConstructor");
+
+ StackTraceFrameIterator it(this);
+ int frames_seen = 0;
+ while (!it.done() && (frames_seen < limit)) {
+ JavaScriptFrame* frame = it.frame();
+ // Set initial size to the maximum inlining level + 1 for the outermost
+ // function.
+ List<FrameSummary> frames(Compiler::kMaxInliningLevels + 1);
+ frame->Summarize(&frames);
+ for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
+ // Create a JSObject to hold the information for the StackFrame.
+ Handle<JSObject> stackFrame = factory()->NewJSObject(object_function());
+
+ Handle<JSFunction> fun = frames[i].function();
+ Handle<Script> script(Script::cast(fun->shared()->script()));
+
+ if (options & StackTrace::kLineNumber) {
+ int script_line_offset = script->line_offset()->value();
+ int position = frames[i].code()->SourcePosition(frames[i].pc());
+ int line_number = GetScriptLineNumber(script, position);
+ // line_number is already shifted by the script_line_offset.
+ int relative_line_number = line_number - script_line_offset;
+ if (options & StackTrace::kColumnOffset && relative_line_number >= 0) {
+ Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
+ int start = (relative_line_number == 0) ? 0 :
+ Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
+ int column_offset = position - start;
+ if (relative_line_number == 0) {
+ // For the case where the code is on the same line as the script
+ // tag.
+ column_offset += script->column_offset()->value();
+ }
+ SetLocalPropertyNoThrow(stackFrame, column_key,
+ Handle<Smi>(Smi::FromInt(column_offset + 1)));
+ }
+ SetLocalPropertyNoThrow(stackFrame, line_key,
+ Handle<Smi>(Smi::FromInt(line_number + 1)));
+ }
+
+ if (options & StackTrace::kScriptName) {
+ Handle<Object> script_name(script->name(), this);
+ SetLocalPropertyNoThrow(stackFrame, script_key, script_name);
+ }
+
+ if (options & StackTrace::kScriptNameOrSourceURL) {
+ Handle<Object> script_name(script->name(), this);
+ Handle<JSValue> script_wrapper = GetScriptWrapper(script);
+ Handle<Object> property = GetProperty(script_wrapper,
+ name_or_source_url_key);
+ ASSERT(property->IsJSFunction());
+ Handle<JSFunction> method = Handle<JSFunction>::cast(property);
+ bool caught_exception;
+ Handle<Object> result = Execution::TryCall(method, script_wrapper, 0,
+ NULL, &caught_exception);
+ if (caught_exception) {
+ result = factory()->undefined_value();
+ }
+ SetLocalPropertyNoThrow(stackFrame, script_name_or_source_url_key,
+ result);
+ }
+
+ if (options & StackTrace::kFunctionName) {
+ Handle<Object> fun_name(fun->shared()->name(), this);
+ if (fun_name->ToBoolean()->IsFalse()) {
+ fun_name = Handle<Object>(fun->shared()->inferred_name(), this);
+ }
+ SetLocalPropertyNoThrow(stackFrame, function_key, fun_name);
+ }
+
+ if (options & StackTrace::kIsEval) {
+ int type = Smi::cast(script->compilation_type())->value();
+ Handle<Object> is_eval = (type == Script::COMPILATION_TYPE_EVAL) ?
+ factory()->true_value() : factory()->false_value();
+ SetLocalPropertyNoThrow(stackFrame, eval_key, is_eval);
+ }
+
+ if (options & StackTrace::kIsConstructor) {
+ Handle<Object> is_constructor = (frames[i].is_constructor()) ?
+ factory()->true_value() : factory()->false_value();
+ SetLocalPropertyNoThrow(stackFrame, constructor_key, is_constructor);
+ }
+
+ FixedArray::cast(stack_trace->elements())->set(frames_seen, *stackFrame);
+ frames_seen++;
+ }
+ it.Advance();
+ }
+
+ stack_trace->set_length(Smi::FromInt(frames_seen));
+ return stack_trace;
+}
+
+
+void Isolate::PrintStack() {
+ if (stack_trace_nesting_level_ == 0) {
+ stack_trace_nesting_level_++;
+
+ StringAllocator* allocator;
+ if (preallocated_message_space_ == NULL) {
+ allocator = new HeapStringAllocator();
+ } else {
+ allocator = preallocated_message_space_;
+ }
+
+ StringStream::ClearMentionedObjectCache();
+ StringStream accumulator(allocator);
+ incomplete_message_ = &accumulator;
+ PrintStack(&accumulator);
+ accumulator.OutputToStdOut();
+ accumulator.Log();
+ incomplete_message_ = NULL;
+ stack_trace_nesting_level_ = 0;
+ if (preallocated_message_space_ == NULL) {
+ // Remove the HeapStringAllocator created above.
+ delete allocator;
+ }
+ } else if (stack_trace_nesting_level_ == 1) {
+ stack_trace_nesting_level_++;
+ OS::PrintError(
+ "\n\nAttempt to print stack while printing stack (double fault)\n");
+ OS::PrintError(
+ "If you are lucky you may find a partial stack dump on stdout.\n\n");
+ incomplete_message_->OutputToStdOut();
+ }
+}
+
+
+static void PrintFrames(StringStream* accumulator,
+ StackFrame::PrintMode mode) {
+ StackFrameIterator it;
+ for (int i = 0; !it.done(); it.Advance()) {
+ it.frame()->Print(accumulator, mode, i++);
+ }
+}
+
+
+void Isolate::PrintStack(StringStream* accumulator) {
+ if (!IsInitialized()) {
+ accumulator->Add(
+ "\n==== Stack trace is not available ==========================\n\n");
+ accumulator->Add(
+ "\n==== Isolate for the thread is not initialized =============\n\n");
+ return;
+ }
+ // The MentionedObjectCache is not GC-proof at the moment.
+ AssertNoAllocation nogc;
+ ASSERT(StringStream::IsMentionedObjectCacheClear());
+
+ // Avoid printing anything if there are no frames.
+ if (c_entry_fp(thread_local_top()) == 0) return;
+
+ accumulator->Add(
+ "\n==== Stack trace ============================================\n\n");
+ PrintFrames(accumulator, StackFrame::OVERVIEW);
+
+ accumulator->Add(
+ "\n==== Details ================================================\n\n");
+ PrintFrames(accumulator, StackFrame::DETAILS);
+
+ accumulator->PrintMentionedObjectCache();
+ accumulator->Add("=====================\n\n");
+}
+
+
+void Isolate::SetFailedAccessCheckCallback(
+ v8::FailedAccessCheckCallback callback) {
+ thread_local_top()->failed_access_check_callback_ = callback;
+}
+
+
+void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
+ if (!thread_local_top()->failed_access_check_callback_) return;
+
+ ASSERT(receiver->IsAccessCheckNeeded());
+ ASSERT(context());
+
+ // Get the data object from access check info.
+ JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ if (!constructor->shared()->IsApiFunction()) return;
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
+ if (data_obj == heap_.undefined_value()) return;
+
+ HandleScope scope;
+ Handle<JSObject> receiver_handle(receiver);
+ Handle<Object> data(AccessCheckInfo::cast(data_obj)->data());
+ thread_local_top()->failed_access_check_callback_(
+ v8::Utils::ToLocal(receiver_handle),
+ type,
+ v8::Utils::ToLocal(data));
+}
+
+
+enum MayAccessDecision {
+ YES, NO, UNKNOWN
+};
+
+
+static MayAccessDecision MayAccessPreCheck(Isolate* isolate,
+ JSObject* receiver,
+ v8::AccessType type) {
+ // During bootstrapping, callback functions are not enabled yet.
+ if (isolate->bootstrapper()->IsActive()) return YES;
+
+ if (receiver->IsJSGlobalProxy()) {
+ Object* receiver_context = JSGlobalProxy::cast(receiver)->context();
+ if (!receiver_context->IsContext()) return NO;
+
+ // Get the global context of current top context.
+ // avoid using Isolate::global_context() because it uses Handle.
+ Context* global_context = isolate->context()->global()->global_context();
+ if (receiver_context == global_context) return YES;
+
+ if (Context::cast(receiver_context)->security_token() ==
+ global_context->security_token())
+ return YES;
+ }
+
+ return UNKNOWN;
+}
+
+
+bool Isolate::MayNamedAccess(JSObject* receiver, Object* key,
+ v8::AccessType type) {
+ ASSERT(receiver->IsAccessCheckNeeded());
+
+ // The callers of this method are not expecting a GC.
+ AssertNoAllocation no_gc;
+
+ // Skip checks for hidden properties access. Note, we do not
+ // require existence of a context in this case.
+ if (key == heap_.hidden_symbol()) return true;
+
+ // Check for compatibility between the security tokens in the
+ // current lexical context and the accessed object.
+ ASSERT(context());
+
+ MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
+ if (decision != UNKNOWN) return decision == YES;
+
+ // Get named access check callback
+ JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ if (!constructor->shared()->IsApiFunction()) return false;
+
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
+ if (data_obj == heap_.undefined_value()) return false;
+
+ Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
+ v8::NamedSecurityCallback callback =
+ v8::ToCData<v8::NamedSecurityCallback>(fun_obj);
+
+ if (!callback) return false;
+
+ HandleScope scope(this);
+ Handle<JSObject> receiver_handle(receiver, this);
+ Handle<Object> key_handle(key, this);
+ Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
+ LOG(this, ApiNamedSecurityCheck(key));
+ bool result = false;
+ {
+ // Leaving JavaScript.
+ VMState state(this, EXTERNAL);
+ result = callback(v8::Utils::ToLocal(receiver_handle),
+ v8::Utils::ToLocal(key_handle),
+ type,
+ v8::Utils::ToLocal(data));
+ }
+ return result;
+}
+
+
+bool Isolate::MayIndexedAccess(JSObject* receiver,
+ uint32_t index,
+ v8::AccessType type) {
+ ASSERT(receiver->IsAccessCheckNeeded());
+ // Check for compatibility between the security tokens in the
+ // current lexical context and the accessed object.
+ ASSERT(context());
+
+ MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
+ if (decision != UNKNOWN) return decision == YES;
+
+ // Get indexed access check callback
+ JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ if (!constructor->shared()->IsApiFunction()) return false;
+
+ Object* data_obj =
+ constructor->shared()->get_api_func_data()->access_check_info();
+ if (data_obj == heap_.undefined_value()) return false;
+
+ Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();
+ v8::IndexedSecurityCallback callback =
+ v8::ToCData<v8::IndexedSecurityCallback>(fun_obj);
+
+ if (!callback) return false;
+
+ HandleScope scope(this);
+ Handle<JSObject> receiver_handle(receiver, this);
+ Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
+ LOG(this, ApiIndexedSecurityCheck(index));
+ bool result = false;
+ {
+ // Leaving JavaScript.
+ VMState state(this, EXTERNAL);
+ result = callback(v8::Utils::ToLocal(receiver_handle),
+ index,
+ type,
+ v8::Utils::ToLocal(data));
+ }
+ return result;
+}
+
+
+const char* const Isolate::kStackOverflowMessage =
+ "Uncaught RangeError: Maximum call stack size exceeded";
+
+
+Failure* Isolate::StackOverflow() {
+ HandleScope scope;
+ Handle<String> key = factory()->stack_overflow_symbol();
+ Handle<JSObject> boilerplate =
+ Handle<JSObject>::cast(GetProperty(js_builtins_object(), key));
+ Handle<Object> exception = Copy(boilerplate);
+ // TODO(1240995): To avoid having to call JavaScript code to compute
+ // the message for stack overflow exceptions which is very likely to
+ // double fault with another stack overflow exception, we use a
+ // precomputed message.
+ DoThrow(*exception, NULL);
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::TerminateExecution() {
+ DoThrow(heap_.termination_exception(), NULL);
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::Throw(Object* exception, MessageLocation* location) {
+ DoThrow(exception, location);
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::ReThrow(MaybeObject* exception, MessageLocation* location) {
+ bool can_be_caught_externally = false;
+ bool catchable_by_javascript = is_catchable_by_javascript(exception);
+ ShouldReportException(&can_be_caught_externally, catchable_by_javascript);
+
+ thread_local_top()->catcher_ = can_be_caught_externally ?
+ try_catch_handler() : NULL;
+
+ // Set the exception being re-thrown.
+ set_pending_exception(exception);
+ if (exception->IsFailure()) return exception->ToFailureUnchecked();
+ return Failure::Exception();
+}
+
+
+Failure* Isolate::ThrowIllegalOperation() {
+ return Throw(heap_.illegal_access_symbol());
+}
+
+
+void Isolate::ScheduleThrow(Object* exception) {
+ // When scheduling a throw we first throw the exception to get the
+ // error reporting if it is uncaught before rescheduling it.
+ Throw(exception);
+ thread_local_top()->scheduled_exception_ = pending_exception();
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+}
+
+
+Failure* Isolate::PromoteScheduledException() {
+ MaybeObject* thrown = scheduled_exception();
+ clear_scheduled_exception();
+ // Re-throw the exception to avoid getting repeated error reporting.
+ return ReThrow(thrown);
+}
+
+
+void Isolate::PrintCurrentStackTrace(FILE* out) {
+ StackTraceFrameIterator it(this);
+ while (!it.done()) {
+ HandleScope scope;
+ // Find code position if recorded in relocation info.
+ JavaScriptFrame* frame = it.frame();
+ int pos = frame->LookupCode()->SourcePosition(frame->pc());
+ Handle<Object> pos_obj(Smi::FromInt(pos));
+ // Fetch function and receiver.
+ Handle<JSFunction> fun(JSFunction::cast(frame->function()));
+ Handle<Object> recv(frame->receiver());
+ // Advance to the next JavaScript frame and determine if the
+ // current frame is the top-level frame.
+ it.Advance();
+ Handle<Object> is_top_level = it.done()
+ ? factory()->true_value()
+ : factory()->false_value();
+ // Generate and print stack trace line.
+ Handle<String> line =
+ Execution::GetStackTraceLine(recv, fun, pos_obj, is_top_level);
+ if (line->length() > 0) {
+ line->PrintOn(out);
+ fprintf(out, "\n");
+ }
+ }
+}
+
+
+void Isolate::ComputeLocation(MessageLocation* target) {
+ *target = MessageLocation(Handle<Script>(heap_.empty_script()), -1, -1);
+ StackTraceFrameIterator it(this);
+ if (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ JSFunction* fun = JSFunction::cast(frame->function());
+ Object* script = fun->shared()->script();
+ if (script->IsScript() &&
+ !(Script::cast(script)->source()->IsUndefined())) {
+ int pos = frame->LookupCode()->SourcePosition(frame->pc());
+ // Compute the location from the function and the reloc info.
+ Handle<Script> casted_script(Script::cast(script));
+ *target = MessageLocation(casted_script, pos, pos + 1);
+ }
+ }
+}
+
+
+bool Isolate::ShouldReportException(bool* can_be_caught_externally,
+ bool catchable_by_javascript) {
+ // Find the top-most try-catch handler.
+ StackHandler* handler =
+ StackHandler::FromAddress(Isolate::handler(thread_local_top()));
+ while (handler != NULL && !handler->is_try_catch()) {
+ handler = handler->next();
+ }
+
+ // Get the address of the external handler so we can compare the address to
+ // determine which one is closer to the top of the stack.
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+
+ // The exception has been externally caught if and only if there is
+ // an external handler which is on top of the top-most try-catch
+ // handler.
+ *can_be_caught_externally = external_handler_address != NULL &&
+ (handler == NULL || handler->address() > external_handler_address ||
+ !catchable_by_javascript);
+
+ if (*can_be_caught_externally) {
+ // Only report the exception if the external handler is verbose.
+ return try_catch_handler()->is_verbose_;
+ } else {
+ // Report the exception if it isn't caught by JavaScript code.
+ return handler == NULL;
+ }
+}
+
+
+void Isolate::DoThrow(MaybeObject* exception, MessageLocation* location) {
+ ASSERT(!has_pending_exception());
+
+ HandleScope scope;
+ Object* exception_object = Smi::FromInt(0);
+ bool is_object = exception->ToObject(&exception_object);
+ Handle<Object> exception_handle(exception_object);
+
+ // Determine reporting and whether the exception is caught externally.
+ bool catchable_by_javascript = is_catchable_by_javascript(exception);
+ // Only real objects can be caught by JS.
+ ASSERT(!catchable_by_javascript || is_object);
+ bool can_be_caught_externally = false;
+ bool should_report_exception =
+ ShouldReportException(&can_be_caught_externally, catchable_by_javascript);
+ bool report_exception = catchable_by_javascript && should_report_exception;
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Notify debugger of exception.
+ if (catchable_by_javascript) {
+ debugger_->OnException(exception_handle, report_exception);
+ }
+#endif
+
+ // Generate the message.
+ Handle<Object> message_obj;
+ MessageLocation potential_computed_location;
+ bool try_catch_needs_message =
+ can_be_caught_externally &&
+ try_catch_handler()->capture_message_;
+ if (report_exception || try_catch_needs_message) {
+ if (location == NULL) {
+ // If no location was specified we use a computed one instead
+ ComputeLocation(&potential_computed_location);
+ location = &potential_computed_location;
+ }
+ if (!bootstrapper()->IsActive()) {
+ // It's not safe to try to make message objects or collect stack
+ // traces while the bootstrapper is active since the infrastructure
+ // may not have been properly initialized.
+ Handle<String> stack_trace;
+ if (FLAG_trace_exception) stack_trace = StackTraceString();
+ Handle<JSArray> stack_trace_object;
+ if (report_exception && capture_stack_trace_for_uncaught_exceptions_) {
+ stack_trace_object = CaptureCurrentStackTrace(
+ stack_trace_for_uncaught_exceptions_frame_limit_,
+ stack_trace_for_uncaught_exceptions_options_);
+ }
+ ASSERT(is_object); // Can't use the handle unless there's a real object.
+ message_obj = MessageHandler::MakeMessageObject("uncaught_exception",
+ location, HandleVector<Object>(&exception_handle, 1), stack_trace,
+ stack_trace_object);
+ }
+ }
+
+ // Save the message for reporting if the the exception remains uncaught.
+ thread_local_top()->has_pending_message_ = report_exception;
+ if (!message_obj.is_null()) {
+ thread_local_top()->pending_message_obj_ = *message_obj;
+ if (location != NULL) {
+ thread_local_top()->pending_message_script_ = *location->script();
+ thread_local_top()->pending_message_start_pos_ = location->start_pos();
+ thread_local_top()->pending_message_end_pos_ = location->end_pos();
+ }
+ }
+
+ // Do not forget to clean catcher_ if currently thrown exception cannot
+ // be caught. If necessary, ReThrow will update the catcher.
+ thread_local_top()->catcher_ = can_be_caught_externally ?
+ try_catch_handler() : NULL;
+
+ // NOTE: Notifying the debugger or generating the message
+ // may have caused new exceptions. For now, we just ignore
+ // that and set the pending exception to the original one.
+ if (is_object) {
+ set_pending_exception(*exception_handle);
+ } else {
+ // Failures are not on the heap so they neither need nor work with handles.
+ ASSERT(exception_handle->IsFailure());
+ set_pending_exception(exception);
+ }
+}
+
+
+bool Isolate::IsExternallyCaught() {
+ ASSERT(has_pending_exception());
+
+ if ((thread_local_top()->catcher_ == NULL) ||
+ (try_catch_handler() != thread_local_top()->catcher_)) {
+ // When throwing the exception, we found no v8::TryCatch
+ // which should care about this exception.
+ return false;
+ }
+
+ if (!is_catchable_by_javascript(pending_exception())) {
+ return true;
+ }
+
+ // Get the address of the external handler so we can compare the address to
+ // determine which one is closer to the top of the stack.
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+ ASSERT(external_handler_address != NULL);
+
+ // The exception has been externally caught if and only if there is
+ // an external handler which is on top of the top-most try-finally
+ // handler.
+ // There should be no try-catch blocks as they would prohibit us from
+ // finding external catcher in the first place (see catcher_ check above).
+ //
+ // Note, that finally clause would rethrow an exception unless it's
+ // aborted by jumps in control flow like return, break, etc. and we'll
+ // have another chances to set proper v8::TryCatch.
+ StackHandler* handler =
+ StackHandler::FromAddress(Isolate::handler(thread_local_top()));
+ while (handler != NULL && handler->address() < external_handler_address) {
+ ASSERT(!handler->is_try_catch());
+ if (handler->is_try_finally()) return false;
+
+ handler = handler->next();
+ }
+
+ return true;
+}
+
+
+void Isolate::ReportPendingMessages() {
+ ASSERT(has_pending_exception());
+ PropagatePendingExceptionToExternalTryCatch();
+
+ // If the pending exception is OutOfMemoryException set out_of_memory in
+ // the global context. Note: We have to mark the global context here
+ // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
+ // set it.
+ HandleScope scope;
+ if (thread_local_top_.pending_exception_ == Failure::OutOfMemoryException()) {
+ context()->mark_out_of_memory();
+ } else if (thread_local_top_.pending_exception_ ==
+ heap()->termination_exception()) {
+ // Do nothing: if needed, the exception has been already propagated to
+ // v8::TryCatch.
+ } else {
+ if (thread_local_top_.has_pending_message_) {
+ thread_local_top_.has_pending_message_ = false;
+ if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
+ HandleScope scope;
+ Handle<Object> message_obj(thread_local_top_.pending_message_obj_);
+ if (thread_local_top_.pending_message_script_ != NULL) {
+ Handle<Script> script(thread_local_top_.pending_message_script_);
+ int start_pos = thread_local_top_.pending_message_start_pos_;
+ int end_pos = thread_local_top_.pending_message_end_pos_;
+ MessageLocation location(script, start_pos, end_pos);
+ MessageHandler::ReportMessage(this, &location, message_obj);
+ } else {
+ MessageHandler::ReportMessage(this, NULL, message_obj);
+ }
+ }
+ }
+ }
+ clear_pending_message();
+}
+
+
+void Isolate::TraceException(bool flag) {
+ FLAG_trace_exception = flag; // TODO(isolates): This is an unfortunate use.
+}
+
+
+bool Isolate::OptionalRescheduleException(bool is_bottom_call) {
+ ASSERT(has_pending_exception());
+ PropagatePendingExceptionToExternalTryCatch();
+
+ // Allways reschedule out of memory exceptions.
+ if (!is_out_of_memory()) {
+ bool is_termination_exception =
+ pending_exception() == heap_.termination_exception();
+
+ // Do not reschedule the exception if this is the bottom call.
+ bool clear_exception = is_bottom_call;
+
+ if (is_termination_exception) {
+ if (is_bottom_call) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ return false;
+ }
+ } else if (thread_local_top()->external_caught_exception_) {
+ // If the exception is externally caught, clear it if there are no
+ // JavaScript frames on the way to the C++ frame that has the
+ // external handler.
+ ASSERT(thread_local_top()->try_catch_handler_address() != NULL);
+ Address external_handler_address =
+ thread_local_top()->try_catch_handler_address();
+ JavaScriptFrameIterator it;
+ if (it.done() || (it.frame()->sp() > external_handler_address)) {
+ clear_exception = true;
+ }
+ }
+
+ // Clear the exception if needed.
+ if (clear_exception) {
+ thread_local_top()->external_caught_exception_ = false;
+ clear_pending_exception();
+ return false;
+ }
+ }
+
+ // Reschedule the exception.
+ thread_local_top()->scheduled_exception_ = pending_exception();
+ clear_pending_exception();
+ return true;
+}
+
+
+void Isolate::SetCaptureStackTraceForUncaughtExceptions(
+ bool capture,
+ int frame_limit,
+ StackTrace::StackTraceOptions options) {
+ capture_stack_trace_for_uncaught_exceptions_ = capture;
+ stack_trace_for_uncaught_exceptions_frame_limit_ = frame_limit;
+ stack_trace_for_uncaught_exceptions_options_ = options;
+}
+
+
+bool Isolate::is_out_of_memory() {
+ if (has_pending_exception()) {
+ MaybeObject* e = pending_exception();
+ if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
+ return true;
+ }
+ }
+ if (has_scheduled_exception()) {
+ MaybeObject* e = scheduled_exception();
+ if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+Handle<Context> Isolate::global_context() {
+ GlobalObject* global = thread_local_top()->context_->global();
+ return Handle<Context>(global->global_context());
+}
+
+
+Handle<Context> Isolate::GetCallingGlobalContext() {
+ JavaScriptFrameIterator it;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (debug_->InDebugger()) {
+ while (!it.done()) {
+ JavaScriptFrame* frame = it.frame();
+ Context* context = Context::cast(frame->context());
+ if (context->global_context() == *debug_->debug_context()) {
+ it.Advance();
+ } else {
+ break;
+ }
+ }
+ }
+#endif // ENABLE_DEBUGGER_SUPPORT
+ if (it.done()) return Handle<Context>::null();
+ JavaScriptFrame* frame = it.frame();
+ Context* context = Context::cast(frame->context());
+ return Handle<Context>(context->global_context());
+}
+
+
+char* Isolate::ArchiveThread(char* to) {
+ if (RuntimeProfiler::IsEnabled() && current_vm_state() == JS) {
+ RuntimeProfiler::IsolateExitedJS(this);
+ }
+ memcpy(to, reinterpret_cast<char*>(thread_local_top()),
+ sizeof(ThreadLocalTop));
+ InitializeThreadLocal();
+ return to + sizeof(ThreadLocalTop);
+}
+
+
+char* Isolate::RestoreThread(char* from) {
+ memcpy(reinterpret_cast<char*>(thread_local_top()), from,
+ sizeof(ThreadLocalTop));
+ // This might be just paranoia, but it seems to be needed in case a
+ // thread_local_top_ is restored on a separate OS thread.
+#ifdef USE_SIMULATOR
+#ifdef V8_TARGET_ARCH_ARM
+ thread_local_top()->simulator_ = Simulator::current(this);
+#elif V8_TARGET_ARCH_MIPS
+ thread_local_top()->simulator_ = Simulator::current(this);
+#endif
+#endif
+ if (RuntimeProfiler::IsEnabled() && current_vm_state() == JS) {
+ RuntimeProfiler::IsolateEnteredJS(this);
+ }
+ return from + sizeof(ThreadLocalTop);
+}
+
+
Isolate::ThreadDataTable::ThreadDataTable()
: list_(NULL) {
}
@@ -417,15 +1368,11 @@ Isolate::Isolate()
bootstrapper_(NULL),
runtime_profiler_(NULL),
compilation_cache_(NULL),
- counters_(NULL),
+ counters_(new Counters()),
code_range_(NULL),
- // Must be initialized early to allow v8::SetResourceConstraints calls.
break_access_(OS::CreateMutex()),
- debugger_initialized_(false),
- // Must be initialized early to allow v8::Debug calls.
- debugger_access_(OS::CreateMutex()),
- logger_(NULL),
- stats_table_(NULL),
+ logger_(new Logger()),
+ stats_table_(new StatsTable()),
stub_cache_(NULL),
deoptimizer_data_(NULL),
capture_stack_trace_for_uncaught_exceptions_(false),
@@ -450,7 +1397,8 @@ Isolate::Isolate()
string_tracker_(NULL),
regexp_stack_(NULL),
frame_element_constant_list_(0),
- result_constant_list_(0) {
+ result_constant_list_(0),
+ embedder_data_(NULL) {
TRACE_ISOLATE(constructor);
memset(isolate_addresses_, 0,
@@ -460,6 +1408,11 @@ Isolate::Isolate()
zone_.isolate_ = this;
stack_guard_.isolate_ = this;
+ // ThreadManager is initialized early to support locking an isolate
+ // before it is entered.
+ thread_manager_ = new ThreadManager();
+ thread_manager_->isolate_ = this;
+
#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__)
simulator_initialized_ = false;
@@ -467,9 +1420,6 @@ Isolate::Isolate()
simulator_redirection_ = NULL;
#endif
- thread_manager_ = new ThreadManager();
- thread_manager_->isolate_ = this;
-
#ifdef DEBUG
// heap_histograms_ initializes itself.
memset(&js_spill_information_, 0, sizeof(js_spill_information_));
@@ -555,7 +1505,7 @@ void Isolate::Deinit() {
logger_->TearDown();
// The default isolate is re-initializable due to legacy API.
- state_ = UNINITIALIZED;
+ state_ = PREINITIALIZED;
}
}
@@ -642,7 +1592,67 @@ Isolate::~Isolate() {
}
+bool Isolate::PreInit() {
+ if (state_ != UNINITIALIZED) return true;
+
+ TRACE_ISOLATE(preinit);
+
+ ASSERT(Isolate::Current() == this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ debug_ = new Debug(this);
+ debugger_ = new Debugger();
+ debugger_->isolate_ = this;
+#endif
+
+ memory_allocator_ = new MemoryAllocator();
+ memory_allocator_->isolate_ = this;
+ code_range_ = new CodeRange();
+ code_range_->isolate_ = this;
+
+ // Safe after setting Heap::isolate_, initializing StackGuard and
+ // ensuring that Isolate::Current() == this.
+ heap_.SetStackLimits();
+
+#ifdef DEBUG
+ DisallowAllocationFailure disallow_allocation_failure;
+#endif
+
+#define C(name) isolate_addresses_[Isolate::k_##name] = \
+ reinterpret_cast<Address>(name());
+ ISOLATE_ADDRESS_LIST(C)
+ ISOLATE_ADDRESS_LIST_PROF(C)
+#undef C
+
+ string_tracker_ = new StringTracker();
+ string_tracker_->isolate_ = this;
+ compilation_cache_ = new CompilationCache(this);
+ transcendental_cache_ = new TranscendentalCache();
+ keyed_lookup_cache_ = new KeyedLookupCache();
+ context_slot_cache_ = new ContextSlotCache();
+ descriptor_lookup_cache_ = new DescriptorLookupCache();
+ unicode_cache_ = new UnicodeCache();
+ pc_to_code_cache_ = new PcToCodeCache(this);
+ write_input_buffer_ = new StringInputBuffer();
+ global_handles_ = new GlobalHandles(this);
+ bootstrapper_ = new Bootstrapper();
+ handle_scope_implementer_ = new HandleScopeImplementer(this);
+ stub_cache_ = new StubCache(this);
+ ast_sentinels_ = new AstSentinels();
+ regexp_stack_ = new RegExpStack();
+ regexp_stack_->isolate_ = this;
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ producer_heap_profile_ = new ProducerHeapProfile();
+ producer_heap_profile_->isolate_ = this;
+#endif
+
+ state_ = PREINITIALIZED;
+ return true;
+}
+
+
void Isolate::InitializeThreadLocal() {
+ thread_local_top_.isolate_ = this;
thread_local_top_.Initialize();
clear_pending_exception();
clear_pending_message();
@@ -677,77 +1687,19 @@ void Isolate::PropagatePendingExceptionToExternalTryCatch() {
}
-void Isolate::InitializeLoggingAndCounters() {
- if (logger_ == NULL) {
- logger_ = new Logger;
- }
- if (counters_ == NULL) {
- counters_ = new Counters;
- }
-}
-
-
-void Isolate::InitializeDebugger() {
-#ifdef ENABLE_DEBUGGER_SUPPORT
- ScopedLock lock(debugger_access_);
- if (NoBarrier_Load(&debugger_initialized_)) return;
- InitializeLoggingAndCounters();
- debug_ = new Debug(this);
- debugger_ = new Debugger(this);
- Release_Store(&debugger_initialized_, true);
-#endif
-}
-
-
bool Isolate::Init(Deserializer* des) {
ASSERT(state_ != INITIALIZED);
- ASSERT(Isolate::Current() == this);
+
TRACE_ISOLATE(init);
+ bool create_heap_objects = des == NULL;
+
#ifdef DEBUG
// The initialization process does not handle memory exhaustion.
DisallowAllocationFailure disallow_allocation_failure;
#endif
- InitializeLoggingAndCounters();
-
- InitializeDebugger();
-
- memory_allocator_ = new MemoryAllocator(this);
- code_range_ = new CodeRange(this);
-
- // Safe after setting Heap::isolate_, initializing StackGuard and
- // ensuring that Isolate::Current() == this.
- heap_.SetStackLimits();
-
-#define C(name) isolate_addresses_[Isolate::k_##name] = \
- reinterpret_cast<Address>(name());
- ISOLATE_ADDRESS_LIST(C)
- ISOLATE_ADDRESS_LIST_PROF(C)
-#undef C
-
- string_tracker_ = new StringTracker();
- string_tracker_->isolate_ = this;
- compilation_cache_ = new CompilationCache(this);
- transcendental_cache_ = new TranscendentalCache();
- keyed_lookup_cache_ = new KeyedLookupCache();
- context_slot_cache_ = new ContextSlotCache();
- descriptor_lookup_cache_ = new DescriptorLookupCache();
- unicode_cache_ = new UnicodeCache();
- pc_to_code_cache_ = new PcToCodeCache(this);
- write_input_buffer_ = new StringInputBuffer();
- global_handles_ = new GlobalHandles(this);
- bootstrapper_ = new Bootstrapper();
- handle_scope_implementer_ = new HandleScopeImplementer();
- stub_cache_ = new StubCache(this);
- ast_sentinels_ = new AstSentinels();
- regexp_stack_ = new RegExpStack();
- regexp_stack_->isolate_ = this;
-
-#ifdef ENABLE_LOGGING_AND_PROFILING
- producer_heap_profile_ = new ProducerHeapProfile();
- producer_heap_profile_->isolate_ = this;
-#endif
+ if (state_ == UNINITIALIZED && !PreInit()) return false;
// Enable logging before setting up the heap
logger_->Setup();
@@ -758,7 +1710,7 @@ bool Isolate::Init(Deserializer* des) {
// Initialize other runtime facilities
#if defined(USE_SIMULATOR)
#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
- Simulator::Initialize();
+ Simulator::Initialize(this);
#endif
#endif
@@ -770,8 +1722,7 @@ bool Isolate::Init(Deserializer* des) {
stack_guard_.InitThread(lock);
}
- // Setup the object heap.
- const bool create_heap_objects = (des == NULL);
+ // Setup the object heap
ASSERT(!heap_.HasBeenSetup());
if (!heap_.Setup(create_heap_objects)) {
V8::SetFatalError();
@@ -820,7 +1771,7 @@ bool Isolate::Init(Deserializer* des) {
// If we are deserializing, log non-function code objects and compiled
// functions found in the snapshot.
- if (des != NULL && FLAG_log_code) {
+ if (des != NULL && (FLAG_log_code || FLAG_ll_prof)) {
HandleScope scope;
LOG(this, LogCodeObjects());
LOG(this, LogCompiledFunctions());
@@ -831,16 +1782,6 @@ bool Isolate::Init(Deserializer* des) {
}
-// Initialized lazily to allow early
-// v8::V8::SetAddHistogramSampleFunction calls.
-StatsTable* Isolate::stats_table() {
- if (stats_table_ == NULL) {
- stats_table_ = new StatsTable;
- }
- return stats_table_;
-}
-
-
void Isolate::Enter() {
Isolate* current_isolate = NULL;
PerIsolateThreadData* current_data = CurrentPerIsolateThreadData();
@@ -880,6 +1821,8 @@ void Isolate::Enter() {
SetIsolateThreadLocals(this, data);
+ CHECK(PreInit());
+
// In case it's the first time some thread enters the isolate.
set_thread_id(data->thread_id());
}
diff --git a/src/isolate.h b/src/isolate.h
index 167c8ef0..0d36b3f7 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -224,6 +224,7 @@ class ThreadLocalTop BASE_EMBEDDED {
ASSERT(try_catch_handler_address_ == NULL);
}
+ Isolate* isolate_;
// The context where the current execution method is created and for variable
// lookups.
Context* context_;
@@ -267,9 +268,6 @@ class ThreadLocalTop BASE_EMBEDDED {
// Call back function to report unsafe JS accesses.
v8::FailedAccessCheckCallback failed_access_check_callback_;
- // Whether out of memory exceptions should be ignored.
- bool ignore_out_of_memory_;
-
private:
void InitializeInternal();
@@ -297,7 +295,6 @@ class HashMap;
#ifdef ENABLE_DEBUGGER_SUPPORT
#define ISOLATE_DEBUGGER_INIT_LIST(V) \
- V(uint64_t, enabled_cpu_features, 0) \
V(v8::Debug::EventCallback, debug_event_callback, NULL) \
V(DebuggerAgent*, debugger_agent_instance, NULL)
#else
@@ -349,6 +346,7 @@ typedef List<HeapObject*, PreallocatedStorage> DebugObjectCache;
/* A previously allocated buffer of kMinimalBufferSize bytes, or NULL. */ \
V(byte*, assembler_spare_buffer, NULL) \
V(FatalErrorCallback, exception_behavior, NULL) \
+ V(AllowCodeGenerationFromStringsCallback, allow_code_gen_callback, NULL) \
V(v8::Debug::MessageHandler, message_handler, NULL) \
/* To distinguish the function templates, so that we can find them in the */ \
/* function cache of the global context. */ \
@@ -373,6 +371,7 @@ typedef List<HeapObject*, PreallocatedStorage> DebugObjectCache;
V(unsigned, ast_node_count, 0) \
/* SafeStackFrameIterator activations count. */ \
V(int, safe_stack_iterator_counter, 0) \
+ V(uint64_t, enabled_cpu_features, 0) \
ISOLATE_PLATFORM_INIT_LIST(V) \
ISOLATE_LOGGING_INIT_LIST(V) \
ISOLATE_DEBUGGER_INIT_LIST(V)
@@ -469,13 +468,6 @@ class Isolate {
return reinterpret_cast<Isolate*>(Thread::GetThreadLocal(isolate_key_));
}
- // Usually called by Init(), but can be called early e.g. to allow
- // testing components that require logging but not the whole
- // isolate.
- //
- // Safe to call more than once.
- void InitializeLoggingAndCounters();
-
bool Init(Deserializer* des);
bool IsInitialized() { return state_ == INITIALIZED; }
@@ -496,9 +488,15 @@ class Isolate {
// Safe to call multiple times.
static void EnsureDefaultIsolate();
+ // Find the PerThread for this particular (isolate, thread) combination
+ // If one does not yet exist, return null.
+ PerIsolateThreadData* FindPerThreadDataForThisThread();
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Get the debugger from the default isolate. Preinitializes the
// default isolate if needed.
static Debugger* GetDefaultIsolateDebugger();
+#endif
// Get the stack guard from the default isolate. Preinitializes the
// default isolate if needed.
@@ -522,12 +520,10 @@ class Isolate {
// switched to non-legacy behavior).
static void EnterDefaultIsolate();
+ // Debug.
// Mutex for serializing access to break control structures.
Mutex* break_access() { return break_access_; }
- // Mutex for serializing access to debugger.
- Mutex* debugger_access() { return debugger_access_; }
-
Address get_address_from_id(AddressId id);
// Access to top context (where the current function object was created).
@@ -688,12 +684,6 @@ class Isolate {
// Tells whether the current context has experienced an out of memory
// exception.
bool is_out_of_memory();
- bool ignore_out_of_memory() {
- return thread_local_top_.ignore_out_of_memory_;
- }
- void set_ignore_out_of_memory(bool value) {
- thread_local_top_.ignore_out_of_memory_ = value;
- }
void PrintCurrentStackTrace(FILE* out);
void PrintStackTrace(FILE* out, char* thread_data);
@@ -802,24 +792,14 @@ class Isolate {
#undef GLOBAL_CONTEXT_FIELD_ACCESSOR
Bootstrapper* bootstrapper() { return bootstrapper_; }
- Counters* counters() {
- // Call InitializeLoggingAndCounters() if logging is needed before
- // the isolate is fully initialized.
- ASSERT(counters_ != NULL);
- return counters_;
- }
+ Counters* counters() { return counters_; }
CodeRange* code_range() { return code_range_; }
RuntimeProfiler* runtime_profiler() { return runtime_profiler_; }
CompilationCache* compilation_cache() { return compilation_cache_; }
- Logger* logger() {
- // Call InitializeLoggingAndCounters() if logging is needed before
- // the isolate is fully initialized.
- ASSERT(logger_ != NULL);
- return logger_;
- }
+ Logger* logger() { return logger_; }
StackGuard* stack_guard() { return &stack_guard_; }
Heap* heap() { return &heap_; }
- StatsTable* stats_table();
+ StatsTable* stats_table() { return stats_table_; }
StubCache* stub_cache() { return stub_cache_; }
DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }
ThreadLocalTop* thread_local_top() { return &thread_local_top_; }
@@ -897,14 +877,6 @@ class Isolate {
RuntimeState* runtime_state() { return &runtime_state_; }
- StringInputBuffer* liveedit_compare_substrings_buf1() {
- return &liveedit_compare_substrings_buf1_;
- }
-
- StringInputBuffer* liveedit_compare_substrings_buf2() {
- return &liveedit_compare_substrings_buf2_;
- }
-
StaticResource<SafeStringInputBuffer>* compiler_safe_string_input_buffer() {
return &compiler_safe_string_input_buffer_;
}
@@ -936,16 +908,12 @@ class Isolate {
void PreallocatedStorageInit(size_t size);
#ifdef ENABLE_DEBUGGER_SUPPORT
- Debugger* debugger() {
- if (!NoBarrier_Load(&debugger_initialized_)) InitializeDebugger();
- return debugger_;
- }
- Debug* debug() {
- if (!NoBarrier_Load(&debugger_initialized_)) InitializeDebugger();
- return debug_;
- }
+ Debugger* debugger() { return debugger_; }
+ Debug* debug() { return debug_; }
#endif
+ inline bool DebuggerHasBreakPoints();
+
#ifdef ENABLE_LOGGING_AND_PROFILING
ProducerHeapProfile* producer_heap_profile() {
return producer_heap_profile_;
@@ -1026,6 +994,9 @@ class Isolate {
void ResetEagerOptimizingData();
+ void SetData(void* data) { embedder_data_ = data; }
+ void* GetData() { return embedder_data_; }
+
private:
Isolate();
@@ -1079,6 +1050,8 @@ class Isolate {
static Isolate* default_isolate_;
static ThreadDataTable* thread_data_table_;
+ bool PreInit();
+
void Deinit();
static void SetIsolateThreadLocals(Isolate* isolate,
@@ -1086,6 +1059,7 @@ class Isolate {
enum State {
UNINITIALIZED, // Some components may not have been allocated.
+ PREINITIALIZED, // Components have been allocated but not initialized.
INITIALIZED // All components are fully initialized.
};
@@ -1100,7 +1074,7 @@ class Isolate {
// If one does not yet exist, allocate a new one.
PerIsolateThreadData* FindOrAllocatePerThreadDataForThisThread();
- // PreInits and returns a default isolate. Needed when a new thread tries
+// PreInits and returns a default isolate. Needed when a new thread tries
// to create a Locker for the first time (the lock itself is in the isolate).
static Isolate* GetDefaultIsolateForLocking();
@@ -1129,8 +1103,6 @@ class Isolate {
void PropagatePendingExceptionToExternalTryCatch();
- void InitializeDebugger();
-
int stack_trace_nesting_level_;
StringStream* incomplete_message_;
// The preallocated memory thread singleton.
@@ -1144,8 +1116,6 @@ class Isolate {
Counters* counters_;
CodeRange* code_range_;
Mutex* break_access_;
- Atomic32 debugger_initialized_;
- Mutex* debugger_access_;
Heap heap_;
Logger* logger_;
StackGuard stack_guard_;
@@ -1175,8 +1145,6 @@ class Isolate {
ThreadManager* thread_manager_;
AstSentinels* ast_sentinels_;
RuntimeState runtime_state_;
- StringInputBuffer liveedit_compare_substrings_buf1_;
- StringInputBuffer liveedit_compare_substrings_buf2_;
StaticResource<SafeStringInputBuffer> compiler_safe_string_input_buffer_;
Builtins builtins_;
StringTracker* string_tracker_;
@@ -1191,6 +1159,7 @@ class Isolate {
unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize_mapping_;
ZoneObjectList frame_element_constant_list_;
ZoneObjectList result_constant_list_;
+ void* embedder_data_;
#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__)
@@ -1238,10 +1207,13 @@ class Isolate {
friend class ExecutionAccess;
friend class IsolateInitializer;
+ friend class ThreadManager;
+ friend class Simulator;
+ friend class StackGuard;
friend class ThreadId;
- friend class TestMemoryAllocatorScope;
friend class v8::Isolate;
friend class v8::Locker;
+ friend class v8::Unlocker;
DISALLOW_COPY_AND_ASSIGN(Isolate);
};
@@ -1399,20 +1371,6 @@ inline void Context::mark_out_of_memory() {
}
-// Temporary macro to be used to flag definitions that are indeed static
-// and not per-isolate. (It would be great to be able to grep for [static]!)
-#define RLYSTC static
-
-
-// Temporary macro to be used to flag classes that should be static.
-#define STATIC_CLASS class
-
-
-// Temporary macro to be used to flag classes that are completely converted
-// to be isolate-friendly. Their mix of static/nonstatic methods/fields is
-// correct.
-#define ISOLATED_CLASS class
-
} } // namespace v8::internal
// TODO(isolates): Get rid of these -inl.h includes and place them only where
diff --git a/src/json-parser.cc b/src/json-parser.cc
new file mode 100644
index 00000000..b7f57c2c
--- /dev/null
+++ b/src/json-parser.cc
@@ -0,0 +1,513 @@
+// Copyright 2011 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "char-predicates-inl.h"
+#include "conversions.h"
+#include "json-parser.h"
+#include "messages.h"
+#include "spaces.h"
+
+namespace v8 {
+namespace internal {
+
+
+Handle<Object> JsonParser::ParseJson(Handle<String> source) {
+ isolate_ = source->map()->isolate();
+ source_ = Handle<String>(source->TryFlattenGetString());
+ source_length_ = source_->length() - 1;
+
+ // Optimized fast case where we only have ascii characters.
+ if (source_->IsSeqAsciiString()) {
+ is_sequential_ascii_ = true;
+ seq_source_ = Handle<SeqAsciiString>::cast(source_);
+ } else {
+ is_sequential_ascii_ = false;
+ }
+
+ // Set initial position right before the string.
+ position_ = -1;
+ // Advance to the first character (posibly EOS)
+ Advance();
+ Next();
+ Handle<Object> result = ParseJsonValue();
+ if (result.is_null() || Next() != Token::EOS) {
+ // Parse failed. Scanner's current token is the unexpected token.
+ Token::Value token = current_.token;
+
+ const char* message;
+ const char* name_opt = NULL;
+
+ switch (token) {
+ case Token::EOS:
+ message = "unexpected_eos";
+ break;
+ case Token::NUMBER:
+ message = "unexpected_token_number";
+ break;
+ case Token::STRING:
+ message = "unexpected_token_string";
+ break;
+ case Token::IDENTIFIER:
+ case Token::FUTURE_RESERVED_WORD:
+ message = "unexpected_token_identifier";
+ break;
+ default:
+ message = "unexpected_token";
+ name_opt = Token::String(token);
+ ASSERT(name_opt != NULL);
+ break;
+ }
+
+ Factory* factory = isolate()->factory();
+ MessageLocation location(factory->NewScript(source),
+ current_.beg_pos,
+ current_.end_pos);
+ Handle<JSArray> array;
+ if (name_opt == NULL) {
+ array = factory->NewJSArray(0);
+ } else {
+ Handle<String> name = factory->NewStringFromUtf8(CStrVector(name_opt));
+ Handle<FixedArray> element = factory->NewFixedArray(1);
+ element->set(0, *name);
+ array = factory->NewJSArrayWithElements(element);
+ }
+ Handle<Object> result = factory->NewSyntaxError(message, array);
+ isolate()->Throw(*result, &location);
+ return Handle<Object>::null();
+ }
+ return result;
+}
+
+
+// Parse any JSON value.
+Handle<Object> JsonParser::ParseJsonValue() {
+ Token::Value token = Next();
+ switch (token) {
+ case Token::STRING:
+ return GetString(false);
+ case Token::NUMBER:
+ return isolate()->factory()->NewNumber(number_);
+ case Token::FALSE_LITERAL:
+ return isolate()->factory()->false_value();
+ case Token::TRUE_LITERAL:
+ return isolate()->factory()->true_value();
+ case Token::NULL_LITERAL:
+ return isolate()->factory()->null_value();
+ case Token::LBRACE:
+ return ParseJsonObject();
+ case Token::LBRACK:
+ return ParseJsonArray();
+ default:
+ return ReportUnexpectedToken();
+ }
+}
+
+
+// Parse a JSON object. Scanner must be right after '{' token.
+Handle<Object> JsonParser::ParseJsonObject() {
+ Handle<JSFunction> object_constructor(
+ isolate()->global_context()->object_function());
+ Handle<JSObject> json_object =
+ isolate()->factory()->NewJSObject(object_constructor);
+
+ if (Peek() == Token::RBRACE) {
+ Next();
+ } else {
+ do {
+ if (Next() != Token::STRING) {
+ return ReportUnexpectedToken();
+ }
+ Handle<String> key = GetString(true);
+ if (Next() != Token::COLON) {
+ return ReportUnexpectedToken();
+ }
+
+ Handle<Object> value = ParseJsonValue();
+ if (value.is_null()) return Handle<Object>::null();
+
+ uint32_t index;
+ if (key->AsArrayIndex(&index)) {
+ SetOwnElement(json_object, index, value, kNonStrictMode);
+ } else if (key->Equals(isolate()->heap()->Proto_symbol())) {
+ SetPrototype(json_object, value);
+ } else {
+ SetLocalPropertyIgnoreAttributes(json_object, key, value, NONE);
+ }
+ } while (Next() == Token::COMMA);
+ if (current_.token != Token::RBRACE) {
+ return ReportUnexpectedToken();
+ }
+ }
+ return json_object;
+}
+
+// Parse a JSON array. Scanner must be right after '[' token.
+Handle<Object> JsonParser::ParseJsonArray() {
+ ZoneScope zone_scope(isolate(), DELETE_ON_EXIT);
+ ZoneList<Handle<Object> > elements(4);
+
+ Token::Value token = Peek();
+ if (token == Token::RBRACK) {
+ Next();
+ } else {
+ do {
+ Handle<Object> element = ParseJsonValue();
+ if (element.is_null()) return Handle<Object>::null();
+ elements.Add(element);
+ token = Next();
+ } while (token == Token::COMMA);
+ if (token != Token::RBRACK) {
+ return ReportUnexpectedToken();
+ }
+ }
+
+ // Allocate a fixed array with all the elements.
+ Handle<FixedArray> fast_elements =
+ isolate()->factory()->NewFixedArray(elements.length());
+
+ for (int i = 0, n = elements.length(); i < n; i++) {
+ fast_elements->set(i, *elements[i]);
+ }
+
+ return isolate()->factory()->NewJSArrayWithElements(fast_elements);
+}
+
+
+Token::Value JsonParser::Next() {
+ current_ = next_;
+ ScanJson();
+ return current_.token;
+}
+
+void JsonParser::ScanJson() {
+ if (source_->IsSeqAsciiString()) {
+ is_sequential_ascii_ = true;
+ } else {
+ is_sequential_ascii_ = false;
+ }
+
+ Token::Value token;
+ do {
+ // Remember the position of the next token
+ next_.beg_pos = position_;
+ switch (c0_) {
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ Advance();
+ token = Token::WHITESPACE;
+ break;
+ case '{':
+ Advance();
+ token = Token::LBRACE;
+ break;
+ case '}':
+ Advance();
+ token = Token::RBRACE;
+ break;
+ case '[':
+ Advance();
+ token = Token::LBRACK;
+ break;
+ case ']':
+ Advance();
+ token = Token::RBRACK;
+ break;
+ case ':':
+ Advance();
+ token = Token::COLON;
+ break;
+ case ',':
+ Advance();
+ token = Token::COMMA;
+ break;
+ case '"':
+ token = ScanJsonString();
+ break;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ token = ScanJsonNumber();
+ break;
+ case 't':
+ token = ScanJsonIdentifier("true", Token::TRUE_LITERAL);
+ break;
+ case 'f':
+ token = ScanJsonIdentifier("false", Token::FALSE_LITERAL);
+ break;
+ case 'n':
+ token = ScanJsonIdentifier("null", Token::NULL_LITERAL);
+ break;
+ default:
+ if (c0_ < 0) {
+ Advance();
+ token = Token::EOS;
+ } else {
+ Advance();
+ token = Token::ILLEGAL;
+ }
+ }
+ } while (token == Token::WHITESPACE);
+
+ next_.end_pos = position_;
+ next_.token = token;
+}
+
+
+Token::Value JsonParser::ScanJsonIdentifier(const char* text,
+ Token::Value token) {
+ while (*text != '\0') {
+ if (c0_ != *text) return Token::ILLEGAL;
+ Advance();
+ text++;
+ }
+ return token;
+}
+
+
+Token::Value JsonParser::ScanJsonNumber() {
+ bool negative = false;
+
+ if (c0_ == '-') {
+ Advance();
+ negative = true;
+ }
+ if (c0_ == '0') {
+ Advance();
+ // Prefix zero is only allowed if it's the only digit before
+ // a decimal point or exponent.
+ if ('0' <= c0_ && c0_ <= '9') return Token::ILLEGAL;
+ } else {
+ int i = 0;
+ int digits = 0;
+ if (c0_ < '1' || c0_ > '9') return Token::ILLEGAL;
+ do {
+ i = i * 10 + c0_ - '0';
+ digits++;
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ if (c0_ != '.' && c0_ != 'e' && c0_ != 'E' && digits < 10) {
+ number_ = (negative ? -i : i);
+ return Token::NUMBER;
+ }
+ }
+ if (c0_ == '.') {
+ Advance();
+ if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL;
+ do {
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ }
+ if (AsciiAlphaToLower(c0_) == 'e') {
+ Advance();
+ if (c0_ == '-' || c0_ == '+') Advance();
+ if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL;
+ do {
+ Advance();
+ } while (c0_ >= '0' && c0_ <= '9');
+ }
+ if (is_sequential_ascii_) {
+ Vector<const char> chars(seq_source_->GetChars() + next_.beg_pos,
+ position_ - next_.beg_pos);
+ number_ = StringToDouble(isolate()->unicode_cache(),
+ chars,
+ NO_FLAGS, // Hex, octal or trailing junk.
+ OS::nan_value());
+ } else {
+ Vector<char> buffer = Vector<char>::New(position_ - next_.beg_pos);
+ String::WriteToFlat(*source_, buffer.start(), next_.beg_pos, position_);
+ Vector<const char> result =
+ Vector<const char>(reinterpret_cast<const char*>(buffer.start()),
+ position_ - next_.beg_pos);
+ number_ = StringToDouble(isolate()->unicode_cache(),
+ result,
+ NO_FLAGS, // Hex, octal or trailing junk.
+ 0.0);
+ buffer.Dispose();
+ }
+ return Token::NUMBER;
+}
+
+Token::Value JsonParser::SlowScanJsonString() {
+ // The currently scanned ascii characters.
+ Handle<String> ascii(isolate()->factory()->NewSubString(source_,
+ next_.beg_pos + 1,
+ position_));
+ Handle<String> two_byte =
+ isolate()->factory()->NewRawTwoByteString(kInitialSpecialStringSize,
+ NOT_TENURED);
+ Handle<SeqTwoByteString> seq_two_byte =
+ Handle<SeqTwoByteString>::cast(two_byte);
+
+ int allocation_count = 1;
+ int count = 0;
+
+ while (c0_ != '"') {
+ // Create new seq string
+ if (count >= kInitialSpecialStringSize * allocation_count) {
+ allocation_count = allocation_count * 2;
+ int new_size = allocation_count * kInitialSpecialStringSize;
+ Handle<String> new_two_byte =
+ isolate()->factory()->NewRawTwoByteString(new_size,
+ NOT_TENURED);
+ uc16* char_start =
+ Handle<SeqTwoByteString>::cast(new_two_byte)->GetChars();
+ String::WriteToFlat(*seq_two_byte, char_start, 0, count);
+ seq_two_byte = Handle<SeqTwoByteString>::cast(new_two_byte);
+ }
+
+ // Check for control character (0x00-0x1f) or unterminated string (<0).
+ if (c0_ < 0x20) return Token::ILLEGAL;
+ if (c0_ != '\\') {
+ seq_two_byte->SeqTwoByteStringSet(count++, c0_);
+ Advance();
+ } else {
+ Advance();
+ switch (c0_) {
+ case '"':
+ case '\\':
+ case '/':
+ seq_two_byte->SeqTwoByteStringSet(count++, c0_);
+ break;
+ case 'b':
+ seq_two_byte->SeqTwoByteStringSet(count++, '\x08');
+ break;
+ case 'f':
+ seq_two_byte->SeqTwoByteStringSet(count++, '\x0c');
+ break;
+ case 'n':
+ seq_two_byte->SeqTwoByteStringSet(count++, '\x0a');
+ break;
+ case 'r':
+ seq_two_byte->SeqTwoByteStringSet(count++, '\x0d');
+ break;
+ case 't':
+ seq_two_byte->SeqTwoByteStringSet(count++, '\x09');
+ break;
+ case 'u': {
+ uc32 value = 0;
+ for (int i = 0; i < 4; i++) {
+ Advance();
+ int digit = HexValue(c0_);
+ if (digit < 0) {
+ return Token::ILLEGAL;
+ }
+ value = value * 16 + digit;
+ }
+ seq_two_byte->SeqTwoByteStringSet(count++, value);
+ break;
+ }
+ default:
+ return Token::ILLEGAL;
+ }
+ Advance();
+ }
+ }
+ // Advance past the last '"'.
+ ASSERT_EQ('"', c0_);
+ Advance();
+
+ // Shrink the the string to our length.
+ if (isolate()->heap()->InNewSpace(*seq_two_byte)) {
+ isolate()->heap()->new_space()->
+ ShrinkStringAtAllocationBoundary<SeqTwoByteString>(*seq_two_byte,
+ count);
+ } else {
+ int string_size = SeqTwoByteString::SizeFor(count);
+ int allocated_string_size =
+ SeqTwoByteString::SizeFor(kInitialSpecialStringSize * allocation_count);
+ int delta = allocated_string_size - string_size;
+ Address start_filler_object = seq_two_byte->address() + string_size;
+ seq_two_byte->set_length(count);
+ isolate()->heap()->CreateFillerObjectAt(start_filler_object, delta);
+ }
+ string_val_ = isolate()->factory()->NewConsString(ascii, seq_two_byte);
+ return Token::STRING;
+}
+
+
+Token::Value JsonParser::ScanJsonString() {
+ ASSERT_EQ('"', c0_);
+ // Set string_val to null. If string_val is not set we assume an
+ // ascii string begining at next_.beg_pos + 1 to next_.end_pos - 1.
+ string_val_ = Handle<String>::null();
+ Advance();
+ // Fast case for ascii only without escape characters.
+ while (c0_ != '"') {
+ // Check for control character (0x00-0x1f) or unterminated string (<0).
+ if (c0_ < 0x20) return Token::ILLEGAL;
+ if (c0_ != '\\' && c0_ < kMaxAsciiCharCode) {
+ Advance();
+ } else {
+ return SlowScanJsonString();
+ }
+ }
+ ASSERT_EQ('"', c0_);
+ // Advance past the last '"'.
+ Advance();
+ return Token::STRING;
+}
+
+Handle<String> JsonParser::GetString() {
+ return GetString(false);
+}
+
+Handle<String> JsonParser::GetSymbol() {
+ Handle<String> result = GetString(true);
+ if (result->IsSymbol()) return result;
+ return isolate()->factory()->LookupSymbol(result);
+}
+
+Handle<String> JsonParser::GetString(bool hint_symbol) {
+ // We have a non ascii string, return that.
+ if (!string_val_.is_null()) return string_val_;
+
+ if (is_sequential_ascii_ && hint_symbol) {
+ Handle<SeqAsciiString> seq = Handle<SeqAsciiString>::cast(source_);
+ // The current token includes the '"' in both ends.
+ int length = current_.end_pos - current_.beg_pos - 2;
+ return isolate()->factory()->LookupAsciiSymbol(seq_source_,
+ current_.beg_pos + 1,
+ length);
+ }
+ // The current token includes the '"' in both ends.
+ return isolate()->factory()->NewSubString(
+ source_, current_.beg_pos + 1, current_.end_pos - 1);
+}
+
+} } // namespace v8::internal
diff --git a/src/json-parser.h b/src/json-parser.h
new file mode 100644
index 00000000..5903d215
--- /dev/null
+++ b/src/json-parser.h
@@ -0,0 +1,161 @@
+// Copyright 2011 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_JSON_PARSER_H_
+#define V8_JSON_PARSER_H_
+
+#include "token.h"
+
+namespace v8 {
+namespace internal {
+
+// A simple json parser.
+class JsonParser BASE_EMBEDDED {
+ public:
+ static Handle<Object> Parse(Handle<String> source) {
+ return JsonParser().ParseJson(source);
+ }
+
+ static const int kEndOfString = -1;
+
+ private:
+ // Parse a string containing a single JSON value.
+ Handle<Object> ParseJson(Handle<String> source);
+
+ inline void Advance() {
+ if (position_ >= source_length_) {
+ position_++;
+ c0_ = kEndOfString;
+ } else if (is_sequential_ascii_) {
+ position_++;
+ c0_ = seq_source_->SeqAsciiStringGet(position_);
+ } else {
+ position_++;
+ c0_ = source_->Get(position_);
+ }
+ }
+
+ inline Isolate* isolate() { return isolate_; }
+
+ // Get the string for the current string token.
+ Handle<String> GetString(bool hint_symbol);
+ Handle<String> GetString();
+ Handle<String> GetSymbol();
+
+ // Scan a single JSON token. The JSON lexical grammar is specified in the
+ // ECMAScript 5 standard, section 15.12.1.1.
+ // Recognizes all of the single-character tokens directly, or calls a function
+ // to scan a number, string or identifier literal.
+ // The only allowed whitespace characters between tokens are tab,
+ // carriage-return, newline and space.
+ void ScanJson();
+
+ // A JSON string (production JSONString) is subset of valid JavaScript string
+ // literals. The string must only be double-quoted (not single-quoted), and
+ // the only allowed backslash-escapes are ", /, \, b, f, n, r, t and
+ // four-digit hex escapes (uXXXX). Any other use of backslashes is invalid.
+ Token::Value ScanJsonString();
+ // Slow version for unicode support, uses the first ascii_count characters,
+ // as first part of a ConsString
+ Token::Value SlowScanJsonString();
+
+ // A JSON number (production JSONNumber) is a subset of the valid JavaScript
+ // decimal number literals.
+ // It includes an optional minus sign, must have at least one
+ // digit before and after a decimal point, may not have prefixed zeros (unless
+ // the integer part is zero), and may include an exponent part (e.g., "e-10").
+ // Hexadecimal and octal numbers are not allowed.
+ Token::Value ScanJsonNumber();
+
+ // Used to recognizes one of the literals "true", "false", or "null". These
+ // are the only valid JSON identifiers (productions JSONBooleanLiteral,
+ // JSONNullLiteral).
+ Token::Value ScanJsonIdentifier(const char* text, Token::Value token);
+
+ // Parse a single JSON value from input (grammar production JSONValue).
+ // A JSON value is either a (double-quoted) string literal, a number literal,
+ // one of "true", "false", or "null", or an object or array literal.
+ Handle<Object> ParseJsonValue();
+
+ // Parse a JSON object literal (grammar production JSONObject).
+ // An object literal is a squiggly-braced and comma separated sequence
+ // (possibly empty) of key/value pairs, where the key is a JSON string
+ // literal, the value is a JSON value, and the two are separated by a colon.
+ // A JSON array dosn't allow numbers and identifiers as keys, like a
+ // JavaScript array.
+ Handle<Object> ParseJsonObject();
+
+ // Parses a JSON array literal (grammar production JSONArray). An array
+ // literal is a square-bracketed and comma separated sequence (possibly empty)
+ // of JSON values.
+ // A JSON array doesn't allow leaving out values from the sequence, nor does
+ // it allow a terminal comma, like a JavaScript array does.
+ Handle<Object> ParseJsonArray();
+
+
+ // Mark that a parsing error has happened at the current token, and
+ // return a null handle. Primarily for readability.
+ Handle<Object> ReportUnexpectedToken() { return Handle<Object>::null(); }
+
+ // Peek at the next token.
+ Token::Value Peek() { return next_.token; }
+ // Scan the next token and return the token scanned on the last call.
+ Token::Value Next();
+
+ struct TokenInfo {
+ TokenInfo() : token(Token::ILLEGAL),
+ beg_pos(0),
+ end_pos(0) { }
+ Token::Value token;
+ int beg_pos;
+ int end_pos;
+ };
+
+ static const int kInitialSpecialStringSize = 1024;
+
+
+ private:
+ Handle<String> source_;
+ int source_length_;
+ Handle<SeqAsciiString> seq_source_;
+
+ bool is_sequential_ascii_;
+ // Current and next token
+ TokenInfo current_;
+ TokenInfo next_;
+ Isolate* isolate_;
+ uc32 c0_;
+ int position_;
+
+
+ Handle<String> string_val_;
+ double number_;
+};
+
+} } // namespace v8::internal
+
+#endif // V8_JSON_PARSER_H_
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index 66b63325..e7aa8602 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -127,7 +127,7 @@ Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re,
return re;
}
pattern = FlattenGetString(pattern);
- CompilationZoneScope zone_scope(DELETE_ON_EXIT);
+ CompilationZoneScope zone_scope(isolate, DELETE_ON_EXIT);
PostponeInterruptsScope postpone(isolate);
RegExpCompileData parse_result;
FlatStringReader reader(isolate, pattern);
@@ -295,23 +295,62 @@ bool RegExpImpl::EnsureCompiledIrregexp(Handle<JSRegExp> re, bool is_ascii) {
#else // V8_INTERPRETED_REGEXP (RegExp native code)
if (compiled_code->IsCode()) return true;
#endif
+ // We could potentially have marked this as flushable, but have kept
+ // a saved version if we did not flush it yet.
+ Object* saved_code = re->DataAt(JSRegExp::saved_code_index(is_ascii));
+ if (saved_code->IsCode()) {
+ // Reinstate the code in the original place.
+ re->SetDataAt(JSRegExp::code_index(is_ascii), saved_code);
+ ASSERT(compiled_code->IsSmi());
+ return true;
+ }
return CompileIrregexp(re, is_ascii);
}
+static bool CreateRegExpErrorObjectAndThrow(Handle<JSRegExp> re,
+ bool is_ascii,
+ Handle<String> error_message,
+ Isolate* isolate) {
+ Factory* factory = isolate->factory();
+ Handle<FixedArray> elements = factory->NewFixedArray(2);
+ elements->set(0, re->Pattern());
+ elements->set(1, *error_message);
+ Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
+ Handle<Object> regexp_err =
+ factory->NewSyntaxError("malformed_regexp", array);
+ isolate->Throw(*regexp_err);
+ return false;
+}
+
+
bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) {
// Compile the RegExp.
Isolate* isolate = re->GetIsolate();
- CompilationZoneScope zone_scope(DELETE_ON_EXIT);
+ CompilationZoneScope zone_scope(isolate, DELETE_ON_EXIT);
PostponeInterruptsScope postpone(isolate);
+ // If we had a compilation error the last time this is saved at the
+ // saved code index.
Object* entry = re->DataAt(JSRegExp::code_index(is_ascii));
- if (entry->IsJSObject()) {
- // If it's a JSObject, a previous compilation failed and threw this object.
- // Re-throw the object without trying again.
- isolate->Throw(entry);
+ // When arriving here entry can only be a smi, either representing an
+ // uncompiled regexp, a previous compilation error, or code that has
+ // been flushed.
+ ASSERT(entry->IsSmi());
+ int entry_value = Smi::cast(entry)->value();
+ ASSERT(entry_value == JSRegExp::kUninitializedValue ||
+ entry_value == JSRegExp::kCompilationErrorValue ||
+ (entry_value < JSRegExp::kCodeAgeMask && entry_value >= 0));
+
+ if (entry_value == JSRegExp::kCompilationErrorValue) {
+ // A previous compilation failed and threw an error which we store in
+ // the saved code index (we store the error message, not the actual
+ // error). Recreate the error object and throw it.
+ Object* error_string = re->DataAt(JSRegExp::saved_code_index(is_ascii));
+ ASSERT(error_string->IsString());
+ Handle<String> error_message(String::cast(error_string));
+ CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
return false;
}
- ASSERT(entry->IsTheHole());
JSRegExp::Flags flags = re->GetFlags();
@@ -340,17 +379,9 @@ bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) {
is_ascii);
if (result.error_message != NULL) {
// Unable to compile regexp.
- Factory* factory = isolate->factory();
- Handle<FixedArray> elements = factory->NewFixedArray(2);
- elements->set(0, *pattern);
Handle<String> error_message =
- factory->NewStringFromUtf8(CStrVector(result.error_message));
- elements->set(1, *error_message);
- Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
- Handle<Object> regexp_err =
- factory->NewSyntaxError("malformed_regexp", array);
- isolate->Throw(*regexp_err);
- re->SetDataAt(JSRegExp::code_index(is_ascii), *regexp_err);
+ isolate->factory()->NewStringFromUtf8(CStrVector(result.error_message));
+ CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
return false;
}
@@ -460,6 +491,7 @@ RegExpImpl::IrregexpResult RegExpImpl::IrregexpExecOnce(
ASSERT(output.length() >= (IrregexpNumberOfCaptures(*irregexp) + 1) * 2);
do {
bool is_ascii = subject->IsAsciiRepresentation();
+ EnsureCompiledIrregexp(regexp, is_ascii);
Handle<Code> code(IrregexpNativeCode(*irregexp, is_ascii), isolate);
NativeRegExpMacroAssembler::Result res =
NativeRegExpMacroAssembler::Match(code,
@@ -810,6 +842,11 @@ class RegExpCompiler {
inline bool ignore_case() { return ignore_case_; }
inline bool ascii() { return ascii_; }
+ int current_expansion_factor() { return current_expansion_factor_; }
+ void set_current_expansion_factor(int value) {
+ current_expansion_factor_ = value;
+ }
+
static const int kNoRegister = -1;
private:
EndNode* accept_;
@@ -820,6 +857,7 @@ class RegExpCompiler {
bool ignore_case_;
bool ascii_;
bool reg_exp_too_big_;
+ int current_expansion_factor_;
};
@@ -847,7 +885,8 @@ RegExpCompiler::RegExpCompiler(int capture_count, bool ignore_case, bool ascii)
recursion_depth_(0),
ignore_case_(ignore_case),
ascii_(ascii),
- reg_exp_too_big_(false) {
+ reg_exp_too_big_(false),
+ current_expansion_factor_(1) {
accept_ = new EndNode(EndNode::ACCEPT);
ASSERT(next_register_ - 1 <= RegExpMacroAssembler::kMaxRegister);
}
@@ -3727,6 +3766,44 @@ RegExpNode* RegExpQuantifier::ToNode(RegExpCompiler* compiler,
}
+// Scoped object to keep track of how much we unroll quantifier loops in the
+// regexp graph generator.
+class RegExpExpansionLimiter {
+ public:
+ static const int kMaxExpansionFactor = 6;
+ RegExpExpansionLimiter(RegExpCompiler* compiler, int factor)
+ : compiler_(compiler),
+ saved_expansion_factor_(compiler->current_expansion_factor()),
+ ok_to_expand_(saved_expansion_factor_ <= kMaxExpansionFactor) {
+ ASSERT(factor > 0);
+ if (ok_to_expand_) {
+ if (factor > kMaxExpansionFactor) {
+ // Avoid integer overflow of the current expansion factor.
+ ok_to_expand_ = false;
+ compiler->set_current_expansion_factor(kMaxExpansionFactor + 1);
+ } else {
+ int new_factor = saved_expansion_factor_ * factor;
+ ok_to_expand_ = (new_factor <= kMaxExpansionFactor);
+ compiler->set_current_expansion_factor(new_factor);
+ }
+ }
+ }
+
+ ~RegExpExpansionLimiter() {
+ compiler_->set_current_expansion_factor(saved_expansion_factor_);
+ }
+
+ bool ok_to_expand() { return ok_to_expand_; }
+
+ private:
+ RegExpCompiler* compiler_;
+ int saved_expansion_factor_;
+ bool ok_to_expand_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RegExpExpansionLimiter);
+};
+
+
RegExpNode* RegExpQuantifier::ToNode(int min,
int max,
bool is_greedy,
@@ -3766,38 +3843,46 @@ RegExpNode* RegExpQuantifier::ToNode(int min,
} else if (FLAG_regexp_optimization && !needs_capture_clearing) {
// Only unroll if there are no captures and the body can't be
// empty.
- if (min > 0 && min <= kMaxUnrolledMinMatches) {
- int new_max = (max == kInfinity) ? max : max - min;
- // Recurse once to get the loop or optional matches after the fixed ones.
- RegExpNode* answer = ToNode(
- 0, new_max, is_greedy, body, compiler, on_success, true);
- // Unroll the forced matches from 0 to min. This can cause chains of
- // TextNodes (which the parser does not generate). These should be
- // combined if it turns out they hinder good code generation.
- for (int i = 0; i < min; i++) {
- answer = body->ToNode(compiler, answer);
+ {
+ RegExpExpansionLimiter limiter(
+ compiler, min + ((max != min) ? 1 : 0));
+ if (min > 0 && min <= kMaxUnrolledMinMatches && limiter.ok_to_expand()) {
+ int new_max = (max == kInfinity) ? max : max - min;
+ // Recurse once to get the loop or optional matches after the fixed
+ // ones.
+ RegExpNode* answer = ToNode(
+ 0, new_max, is_greedy, body, compiler, on_success, true);
+ // Unroll the forced matches from 0 to min. This can cause chains of
+ // TextNodes (which the parser does not generate). These should be
+ // combined if it turns out they hinder good code generation.
+ for (int i = 0; i < min; i++) {
+ answer = body->ToNode(compiler, answer);
+ }
+ return answer;
}
- return answer;
}
- if (max <= kMaxUnrolledMaxMatches) {
- ASSERT(min == 0);
- // Unroll the optional matches up to max.
- RegExpNode* answer = on_success;
- for (int i = 0; i < max; i++) {
- ChoiceNode* alternation = new ChoiceNode(2);
- if (is_greedy) {
- alternation->AddAlternative(GuardedAlternative(body->ToNode(compiler,
- answer)));
- alternation->AddAlternative(GuardedAlternative(on_success));
- } else {
- alternation->AddAlternative(GuardedAlternative(on_success));
- alternation->AddAlternative(GuardedAlternative(body->ToNode(compiler,
- answer)));
+ if (max <= kMaxUnrolledMaxMatches && min == 0) {
+ ASSERT(max > 0); // Due to the 'if' above.
+ RegExpExpansionLimiter limiter(compiler, max);
+ if (limiter.ok_to_expand()) {
+ // Unroll the optional matches up to max.
+ RegExpNode* answer = on_success;
+ for (int i = 0; i < max; i++) {
+ ChoiceNode* alternation = new ChoiceNode(2);
+ if (is_greedy) {
+ alternation->AddAlternative(
+ GuardedAlternative(body->ToNode(compiler, answer)));
+ alternation->AddAlternative(GuardedAlternative(on_success));
+ } else {
+ alternation->AddAlternative(GuardedAlternative(on_success));
+ alternation->AddAlternative(
+ GuardedAlternative(body->ToNode(compiler, answer)));
+ }
+ answer = alternation;
+ if (not_at_start) alternation->set_not_at_start();
}
- answer = alternation;
- if (not_at_start) alternation->set_not_at_start();
+ return answer;
}
- return answer;
}
}
bool has_min = min > 0;
@@ -4123,12 +4208,6 @@ void CharacterRange::Split(ZoneList<CharacterRange>* base,
}
-static void AddUncanonicals(Isolate* isolate,
- ZoneList<CharacterRange>* ranges,
- int bottom,
- int top);
-
-
void CharacterRange::AddCaseEquivalents(ZoneList<CharacterRange>* ranges,
bool is_ascii) {
Isolate* isolate = Isolate::Current();
@@ -4289,101 +4368,6 @@ SetRelation CharacterRange::WordCharacterRelation(
}
-static void AddUncanonicals(Isolate* isolate,
- ZoneList<CharacterRange>* ranges,
- int bottom,
- int top) {
- unibrow::uchar chars[unibrow::Ecma262UnCanonicalize::kMaxWidth];
- // Zones with no case mappings. There is a DEBUG-mode loop to assert that
- // this table is correct.
- // 0x0600 - 0x0fff
- // 0x1100 - 0x1cff
- // 0x2000 - 0x20ff
- // 0x2200 - 0x23ff
- // 0x2500 - 0x2bff
- // 0x2e00 - 0xa5ff
- // 0xa800 - 0xfaff
- // 0xfc00 - 0xfeff
- const int boundary_count = 18;
- int boundaries[] = {
- 0x600, 0x1000, 0x1100, 0x1d00, 0x2000, 0x2100, 0x2200, 0x2400, 0x2500,
- 0x2c00, 0x2e00, 0xa600, 0xa800, 0xfb00, 0xfc00, 0xff00};
-
- // Special ASCII rule from spec can save us some work here.
- if (bottom == 0x80 && top == 0xffff) return;
-
- if (top <= boundaries[0]) {
- CharacterRange range(bottom, top);
- range.AddCaseEquivalents(ranges, false);
- return;
- }
-
- // Split up very large ranges. This helps remove ranges where there are no
- // case mappings.
- for (int i = 0; i < boundary_count; i++) {
- if (bottom < boundaries[i] && top >= boundaries[i]) {
- AddUncanonicals(isolate, ranges, bottom, boundaries[i] - 1);
- AddUncanonicals(isolate, ranges, boundaries[i], top);
- return;
- }
- }
-
- // If we are completely in a zone with no case mappings then we are done.
- for (int i = 0; i < boundary_count; i += 2) {
- if (bottom >= boundaries[i] && top < boundaries[i + 1]) {
-#ifdef DEBUG
- for (int j = bottom; j <= top; j++) {
- unsigned current_char = j;
- int length = isolate->jsregexp_uncanonicalize()->get(current_char,
- '\0', chars);
- for (int k = 0; k < length; k++) {
- ASSERT(chars[k] == current_char);
- }
- }
-#endif
- return;
- }
- }
-
- // Step through the range finding equivalent characters.
- ZoneList<unibrow::uchar> *characters = new ZoneList<unibrow::uchar>(100);
- for (int i = bottom; i <= top; i++) {
- int length = isolate->jsregexp_uncanonicalize()->get(i, '\0', chars);
- for (int j = 0; j < length; j++) {
- uc32 chr = chars[j];
- if (chr != i && (chr < bottom || chr > top)) {
- characters->Add(chr);
- }
- }
- }
-
- // Step through the equivalent characters finding simple ranges and
- // adding ranges to the character class.
- if (characters->length() > 0) {
- int new_from = characters->at(0);
- int new_to = new_from;
- for (int i = 1; i < characters->length(); i++) {
- int chr = characters->at(i);
- if (chr == new_to + 1) {
- new_to++;
- } else {
- if (new_to == new_from) {
- ranges->Add(CharacterRange::Singleton(new_from));
- } else {
- ranges->Add(CharacterRange(new_from, new_to));
- }
- new_from = new_to = chr;
- }
- }
- if (new_to == new_from) {
- ranges->Add(CharacterRange::Singleton(new_from));
- } else {
- ranges->Add(CharacterRange(new_from, new_to));
- }
- }
-}
-
-
ZoneList<CharacterRange>* CharacterSet::ranges() {
if (ranges_ == NULL) {
ranges_ = new ZoneList<CharacterRange>(2);
diff --git a/src/jsregexp.h b/src/jsregexp.h
index d56b650a..58958d85 100644
--- a/src/jsregexp.h
+++ b/src/jsregexp.h
@@ -28,6 +28,7 @@
#ifndef V8_JSREGEXP_H_
#define V8_JSREGEXP_H_
+#include "allocation.h"
#include "macro-assembler.h"
#include "zone-inl.h"
diff --git a/src/list-inl.h b/src/list-inl.h
index eeaea65f..8ef7514f 100644
--- a/src/list-inl.h
+++ b/src/list-inl.h
@@ -46,10 +46,16 @@ void List<T, P>::Add(const T& element) {
template<typename T, class P>
void List<T, P>::AddAll(const List<T, P>& other) {
- int result_length = length_ + other.length_;
+ AddAll(other.ToVector());
+}
+
+
+template<typename T, class P>
+void List<T, P>::AddAll(const Vector<T>& other) {
+ int result_length = length_ + other.length();
if (capacity_ < result_length) Resize(result_length);
- for (int i = 0; i < other.length_; i++) {
- data_[length_ + i] = other.data_[i];
+ for (int i = 0; i < other.length(); i++) {
+ data_[length_ + i] = other.at(i);
}
length_ = result_length;
}
diff --git a/src/list.h b/src/list.h
index 9a2e6989..ca2b7bce 100644
--- a/src/list.h
+++ b/src/list.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,6 +28,8 @@
#ifndef V8_LIST_H_
#define V8_LIST_H_
+#include "utils.h"
+
namespace v8 {
namespace internal {
@@ -80,7 +82,7 @@ class List {
INLINE(int length() const) { return length_; }
INLINE(int capacity() const) { return capacity_; }
- Vector<T> ToVector() { return Vector<T>(data_, length_); }
+ Vector<T> ToVector() const { return Vector<T>(data_, length_); }
Vector<const T> ToConstVector() { return Vector<const T>(data_, length_); }
@@ -91,6 +93,9 @@ class List {
// Add all the elements from the argument list to this list.
void AddAll(const List<T, P>& other);
+ // Add all the elements from the vector to this list.
+ void AddAll(const Vector<T>& other);
+
// Inserts the element at the specific index.
void InsertAt(int index, const T& element);
@@ -159,6 +164,11 @@ class List {
DISALLOW_COPY_AND_ASSIGN(List);
};
+class Map;
+class Code;
+typedef List<Map*> MapList;
+typedef List<Code*> CodeList;
+
} } // namespace v8::internal
#endif // V8_LIST_H_
diff --git a/src/lithium-allocator.cc b/src/lithium-allocator.cc
index f62a7dbc..50ed1227 100644
--- a/src/lithium-allocator.cc
+++ b/src/lithium-allocator.cc
@@ -1029,6 +1029,22 @@ void LAllocator::ResolvePhis(HBasicBlock* block) {
chunk_->AddGapMove(cur_block->last_instruction_index() - 1,
operand,
phi_operand);
+
+ // We are going to insert a move before the branch instruction.
+ // Some branch instructions (e.g. loops' back edges)
+ // can potentially cause a GC so they have a pointer map.
+ // By inserting a move we essentially create a copy of a
+ // value which is invisible to PopulatePointerMaps(), because we store
+ // it into a location different from the operand of a live range
+ // covering a branch instruction.
+ // Thus we need to manually record a pointer.
+ if (phi->representation().IsTagged()) {
+ LInstruction* branch =
+ InstructionAt(cur_block->last_instruction_index());
+ if (branch->HasPointerMap()) {
+ branch->pointer_map()->RecordPointer(phi_operand);
+ }
+ }
}
LiveRange* live_range = LiveRangeFor(phi->id());
@@ -1116,7 +1132,7 @@ void LAllocator::ResolveControlFlow(LiveRange* range,
// We are going to insert a move before the branch instruction.
// Some branch instructions (e.g. loops' back edges)
// can potentially cause a GC so they have a pointer map.
- // By insterting a move we essentially create a copy of a
+ // By inserting a move we essentially create a copy of a
// value which is invisible to PopulatePointerMaps(), because we store
// it into a location different from the operand of a live range
// covering a branch instruction.
@@ -2013,12 +2029,12 @@ LifetimePosition LAllocator::FindOptimalSplitPos(LifetimePosition start,
// We have no choice
if (start_instr == end_instr) return end;
- HBasicBlock* end_block = GetBlock(start);
- HBasicBlock* start_block = GetBlock(end);
+ HBasicBlock* start_block = GetBlock(start);
+ HBasicBlock* end_block = GetBlock(end);
if (end_block == start_block) {
- // The interval is split in the same basic block. Split at latest possible
- // position.
+ // The interval is split in the same basic block. Split at the latest
+ // possible position.
return end;
}
@@ -2029,7 +2045,9 @@ LifetimePosition LAllocator::FindOptimalSplitPos(LifetimePosition start,
block = block->parent_loop_header();
}
- if (block == end_block) return end;
+ // We did not find any suitable outer loop. Split at the latest possible
+ // position unless end_block is a loop header itself.
+ if (block == end_block && !end_block->IsLoopHeader()) return end;
return LifetimePosition::FromInstructionIndex(
block->first_instruction_index());
diff --git a/src/lithium-allocator.h b/src/lithium-allocator.h
index f109c454..d4565587 100644
--- a/src/lithium-allocator.h
+++ b/src/lithium-allocator.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,7 +30,7 @@
#include "v8.h"
-#include "data-flow.h"
+#include "allocation.h"
#include "lithium.h"
#include "zone.h"
diff --git a/src/lithium.cc b/src/lithium.cc
index aeac2db5..62b263be 100644
--- a/src/lithium.cc
+++ b/src/lithium.cc
@@ -166,4 +166,25 @@ void LPointerMap::PrintTo(StringStream* stream) {
}
+int ExternalArrayTypeToShiftSize(ExternalArrayType type) {
+ switch (type) {
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ case kExternalPixelArray:
+ return 0;
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ return 1;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ case kExternalFloatArray:
+ return 2;
+ case kExternalDoubleArray:
+ return 3;
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+
} } // namespace v8::internal
diff --git a/src/lithium.h b/src/lithium.h
index 280da472..ffc236dd 100644
--- a/src/lithium.h
+++ b/src/lithium.h
@@ -28,6 +28,7 @@
#ifndef V8_LITHIUM_H_
#define V8_LITHIUM_H_
+#include "allocation.h"
#include "hydrogen.h"
#include "safepoint-table.h"
@@ -588,6 +589,10 @@ class DeepIterator BASE_EMBEDDED {
ShallowIterator current_iterator_;
};
+
+int ExternalArrayTypeToShiftSize(ExternalArrayType type);
+
+
} } // namespace v8::internal
#endif // V8_LITHIUM_H_
diff --git a/src/liveedit.cc b/src/liveedit.cc
index 14667660..e89cae3a 100644
--- a/src/liveedit.cc
+++ b/src/liveedit.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,8 +30,8 @@
#include "liveedit.h"
-#include "compiler.h"
#include "compilation-cache.h"
+#include "compiler.h"
#include "debug.h"
#include "deoptimizer.h"
#include "global-handles.h"
@@ -268,17 +268,10 @@ void Comparator::CalculateDifference(Comparator::Input* input,
}
-static bool CompareSubstrings(Isolate* isolate, Handle<String> s1, int pos1,
+static bool CompareSubstrings(Handle<String> s1, int pos1,
Handle<String> s2, int pos2, int len) {
- StringInputBuffer& buf1 = *isolate->liveedit_compare_substrings_buf1();
- StringInputBuffer& buf2 = *isolate->liveedit_compare_substrings_buf2();
- buf1.Reset(*s1);
- buf1.Seek(pos1);
- buf2.Reset(*s2);
- buf2.Seek(pos2);
for (int i = 0; i < len; i++) {
- ASSERT(buf1.has_more() && buf2.has_more());
- if (buf1.GetNext() != buf2.GetNext()) {
+ if (s1->Get(i + pos1) != s2->Get(i + pos2)) {
return false;
}
}
@@ -410,9 +403,9 @@ class LineEndsWrapper {
// Represents 2 strings as 2 arrays of lines.
class LineArrayCompareInput : public Comparator::Input {
public:
- LineArrayCompareInput(Isolate* isolate, Handle<String> s1, Handle<String> s2,
+ LineArrayCompareInput(Handle<String> s1, Handle<String> s2,
LineEndsWrapper line_ends1, LineEndsWrapper line_ends2)
- : isolate_(isolate), s1_(s1), s2_(s2), line_ends1_(line_ends1),
+ : s1_(s1), s2_(s2), line_ends1_(line_ends1),
line_ends2_(line_ends2) {
}
int getLength1() {
@@ -431,12 +424,11 @@ class LineArrayCompareInput : public Comparator::Input {
if (len1 != len2) {
return false;
}
- return CompareSubstrings(isolate_, s1_, line_start1, s2_, line_start2,
+ return CompareSubstrings(s1_, line_start1, s2_, line_start2,
len1);
}
private:
- Isolate* isolate_;
Handle<String> s1_;
Handle<String> s2_;
LineEndsWrapper line_ends1_;
@@ -492,11 +484,13 @@ class TokenizingLineArrayCompareOutput : public Comparator::Output {
Handle<JSArray> LiveEdit::CompareStrings(Handle<String> s1,
Handle<String> s2) {
+ s1 = FlattenGetString(s1);
+ s2 = FlattenGetString(s2);
+
LineEndsWrapper line_ends1(s1);
LineEndsWrapper line_ends2(s2);
- LineArrayCompareInput
- input(Isolate::Current(), s1, s2, line_ends1, line_ends2);
+ LineArrayCompareInput input(s1, s2, line_ends1, line_ends2);
TokenizingLineArrayCompareOutput output(line_ends1, line_ends2, s1, s2);
Comparator::CalculateDifference(&input, &output);
@@ -533,12 +527,12 @@ static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
// Wraps any object into a OpaqueReference, that will hide the object
// from JavaScript.
-static Handle<JSValue> WrapInJSValue(Object* object) {
+static Handle<JSValue> WrapInJSValue(Handle<Object> object) {
Handle<JSFunction> constructor =
Isolate::Current()->opaque_reference_function();
Handle<JSValue> result =
Handle<JSValue>::cast(FACTORY->NewJSObject(constructor));
- result->set_value(object);
+ result->set_value(*object);
return result;
}
@@ -605,17 +599,17 @@ class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
}
void SetFunctionCode(Handle<Code> function_code,
Handle<Object> code_scope_info) {
- Handle<JSValue> code_wrapper = WrapInJSValue(*function_code);
+ Handle<JSValue> code_wrapper = WrapInJSValue(function_code);
this->SetField(kCodeOffset_, code_wrapper);
- Handle<JSValue> scope_wrapper = WrapInJSValue(*code_scope_info);
+ Handle<JSValue> scope_wrapper = WrapInJSValue(code_scope_info);
this->SetField(kCodeScopeInfoOffset_, scope_wrapper);
}
void SetOuterScopeInfo(Handle<Object> scope_info_array) {
this->SetField(kOuterScopeInfoOffset_, scope_info_array);
}
void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
- Handle<JSValue> info_holder = WrapInJSValue(*info);
+ Handle<JSValue> info_holder = WrapInJSValue(info);
this->SetField(kSharedFunctionInfoOffset_, info_holder);
}
int GetParentIndex() {
@@ -672,7 +666,7 @@ class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
Handle<SharedFunctionInfo> info) {
HandleScope scope;
this->SetField(kFunctionNameOffset_, name);
- Handle<JSValue> info_holder = WrapInJSValue(*info);
+ Handle<JSValue> info_holder = WrapInJSValue(info);
this->SetField(kSharedInfoOffset_, info_holder);
this->SetSmiValueField(kStartPositionOffset_, start_position);
this->SetSmiValueField(kEndPositionOffset_, end_position);
@@ -820,7 +814,7 @@ class FunctionInfoListener {
JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
Handle<String> source) {
Isolate* isolate = Isolate::Current();
- CompilationZoneScope zone_scope(DELETE_ON_EXIT);
+ CompilationZoneScope zone_scope(isolate, DELETE_ON_EXIT);
FunctionInfoListener listener;
Handle<Object> original_source = Handle<Object>(script->source());
@@ -914,7 +908,7 @@ static void ReplaceCodeObject(Code* original, Code* substitution) {
AssertNoAllocation no_allocations_please;
// A zone scope for ReferenceCollectorVisitor.
- ZoneScope scope(DELETE_ON_EXIT);
+ ZoneScope scope(Isolate::Current(), DELETE_ON_EXIT);
ReferenceCollectorVisitor visitor(original);
@@ -1411,6 +1405,9 @@ static const char* DropFrames(Vector<StackFrame*> frames,
Builtins::kFrameDropper_LiveEdit)) {
// OK, we can drop our own code.
*mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
+ } else if (pre_top_frame_code ==
+ isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) {
+ *mode = Debug::FRAME_DROPPED_IN_RETURN_CALL;
} else if (pre_top_frame_code->kind() == Code::STUB &&
pre_top_frame_code->major_key()) {
// Entry from our unit tests, it's fine, we support this case.
@@ -1461,8 +1458,9 @@ static bool IsDropableFrame(StackFrame* frame) {
// removing all listed function if possible and if do_drop is true.
static const char* DropActivationsInActiveThread(
Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) {
- Debug* debug = Isolate::Current()->debug();
- ZoneScope scope(DELETE_ON_EXIT);
+ Isolate* isolate = Isolate::Current();
+ Debug* debug = isolate->debug();
+ ZoneScope scope(isolate, DELETE_ON_EXIT);
Vector<StackFrame*> frames = CreateStackMap();
int array_len = Smi::cast(shared_info_array->length())->value();
@@ -1682,7 +1680,7 @@ void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
}
-bool LiveEditFunctionTracker::IsActive() {
+bool LiveEditFunctionTracker::IsActive(Isolate* isolate) {
return false;
}
diff --git a/src/liveedit.h b/src/liveedit.h
index 36c2c760..60e6238c 100644
--- a/src/liveedit.h
+++ b/src/liveedit.h
@@ -49,6 +49,7 @@
// instantiate newly compiled functions.
+#include "allocation.h"
#include "compiler.h"
namespace v8 {
diff --git a/src/liveobjectlist.cc b/src/liveobjectlist.cc
index 5795a6b0..29a9b017 100644
--- a/src/liveobjectlist.cc
+++ b/src/liveobjectlist.cc
@@ -118,7 +118,7 @@ typedef int (*RawComparer)(const void*, const void*);
v(Code, "meta: Code") \
v(Map, "meta: Map") \
v(Oddball, "Oddball") \
- v(Proxy, "meta: Proxy") \
+ v(Foreign, "meta: Foreign") \
v(SharedFunctionInfo, "meta: SharedFunctionInfo") \
v(Struct, "meta: Struct") \
\
diff --git a/src/log-utils.cc b/src/log-utils.cc
index a854ade0..1bba7cd5 100644
--- a/src/log-utils.cc
+++ b/src/log-utils.cc
@@ -126,7 +126,7 @@ Log::Log(Logger* logger)
: write_to_file_(false),
is_stopped_(false),
output_handle_(NULL),
- output_code_handle_(NULL),
+ ll_output_handle_(NULL),
output_buffer_(NULL),
mutex_(NULL),
message_buffer_(NULL),
@@ -168,7 +168,7 @@ void Log::Initialize() {
bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
|| FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
- || FLAG_log_regexp || FLAG_log_state_changes;
+ || FLAG_log_regexp || FLAG_log_state_changes || FLAG_ll_prof;
bool open_log_file = start_logging || FLAG_prof_lazy;
@@ -233,7 +233,12 @@ void Log::OpenStdout() {
}
-static const char kCodeLogExt[] = ".code";
+// Extension added to V8 log file name to get the low-level log name.
+static const char kLowLevelLogExt[] = ".ll";
+
+// File buffer size of the low-level log. We don't use the default to
+// minimize the associated overhead.
+static const int kLowLevelLogBufferSize = 2 * MB;
void Log::OpenFile(const char* name) {
@@ -241,14 +246,13 @@ void Log::OpenFile(const char* name) {
output_handle_ = OS::FOpen(name, OS::LogFileOpenMode);
write_to_file_ = true;
if (FLAG_ll_prof) {
- // Open a file for logging the contents of code objects so that
- // they can be disassembled later.
- size_t name_len = strlen(name);
- ScopedVector<char> code_name(
- static_cast<int>(name_len + sizeof(kCodeLogExt)));
- memcpy(code_name.start(), name, name_len);
- memcpy(code_name.start() + name_len, kCodeLogExt, sizeof(kCodeLogExt));
- output_code_handle_ = OS::FOpen(code_name.start(), OS::LogFileOpenMode);
+ // Open the low-level log file.
+ size_t len = strlen(name);
+ ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLowLevelLogExt)));
+ memcpy(ll_name.start(), name, len);
+ memcpy(ll_name.start() + len, kLowLevelLogExt, sizeof(kLowLevelLogExt));
+ ll_output_handle_ = OS::FOpen(ll_name.start(), OS::LogFileOpenMode);
+ setvbuf(ll_output_handle_, NULL, _IOFBF, kLowLevelLogBufferSize);
}
}
@@ -266,8 +270,8 @@ void Log::Close() {
if (write_to_file_) {
if (output_handle_ != NULL) fclose(output_handle_);
output_handle_ = NULL;
- if (output_code_handle_ != NULL) fclose(output_code_handle_);
- output_code_handle_ = NULL;
+ if (ll_output_handle_ != NULL) fclose(ll_output_handle_);
+ ll_output_handle_ = NULL;
} else {
delete output_buffer_;
output_buffer_ = NULL;
@@ -361,6 +365,7 @@ void LogMessageBuilder::AppendAddress(Address addr) {
void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
+ if (str == NULL) return;
AssertNoAllocation no_heap_allocation; // Ensure string stay valid.
int len = str->length();
if (len > 0x1000)
diff --git a/src/log-utils.h b/src/log-utils.h
index 255c73c9..81bbf779 100644
--- a/src/log-utils.h
+++ b/src/log-utils.h
@@ -28,6 +28,8 @@
#ifndef V8_LOG_UTILS_H_
#define V8_LOG_UTILS_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
@@ -154,8 +156,8 @@ class Log {
// mutex_ should be acquired before using output_handle_ or output_buffer_.
FILE* output_handle_;
- // Used when low-level profiling is active to save code object contents.
- FILE* output_code_handle_;
+ // Used when low-level profiling is active.
+ FILE* ll_output_handle_;
LogDynamicBuffer* output_buffer_;
diff --git a/src/log.cc b/src/log.cc
index 3ce20722..0474ce0c 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -334,10 +334,187 @@ void Profiler::Run() {
}
+// Low-level profiling event structures.
+
+struct LowLevelCodeCreateStruct {
+ static const char kTag = 'C';
+
+ int32_t name_size;
+ Address code_address;
+ int32_t code_size;
+};
+
+
+struct LowLevelCodeMoveStruct {
+ static const char kTag = 'M';
+
+ Address from_address;
+ Address to_address;
+};
+
+
+struct LowLevelCodeDeleteStruct {
+ static const char kTag = 'D';
+
+ Address address;
+};
+
+
+struct LowLevelSnapshotPositionStruct {
+ static const char kTag = 'P';
+
+ Address address;
+ int32_t position;
+};
+
+
+static const char kCodeMovingGCTag = 'G';
+
+
//
// Logger class implementation.
//
+class Logger::NameMap {
+ public:
+ NameMap() : impl_(&PointerEquals) {}
+
+ ~NameMap() {
+ for (HashMap::Entry* p = impl_.Start(); p != NULL; p = impl_.Next(p)) {
+ DeleteArray(static_cast<const char*>(p->value));
+ }
+ }
+
+ void Insert(Address code_address, const char* name, int name_size) {
+ HashMap::Entry* entry = FindOrCreateEntry(code_address);
+ if (entry->value == NULL) {
+ entry->value = CopyName(name, name_size);
+ }
+ }
+
+ const char* Lookup(Address code_address) {
+ HashMap::Entry* entry = FindEntry(code_address);
+ return (entry != NULL) ? static_cast<const char*>(entry->value) : NULL;
+ }
+
+ void Remove(Address code_address) {
+ HashMap::Entry* entry = FindEntry(code_address);
+ if (entry != NULL) DeleteArray(static_cast<const char*>(entry->value));
+ RemoveEntry(entry);
+ }
+
+ void Move(Address from, Address to) {
+ if (from == to) return;
+ HashMap::Entry* from_entry = FindEntry(from);
+ ASSERT(from_entry != NULL);
+ void* value = from_entry->value;
+ RemoveEntry(from_entry);
+ HashMap::Entry* to_entry = FindOrCreateEntry(to);
+ ASSERT(to_entry->value == NULL);
+ to_entry->value = value;
+ }
+
+ private:
+ static bool PointerEquals(void* lhs, void* rhs) {
+ return lhs == rhs;
+ }
+
+ static char* CopyName(const char* name, int name_size) {
+ char* result = NewArray<char>(name_size + 1);
+ for (int i = 0; i < name_size; ++i) {
+ char c = name[i];
+ if (c == '\0') c = ' ';
+ result[i] = c;
+ }
+ result[name_size] = '\0';
+ return result;
+ }
+
+ HashMap::Entry* FindOrCreateEntry(Address code_address) {
+ return impl_.Lookup(code_address, ComputePointerHash(code_address), true);
+ }
+
+ HashMap::Entry* FindEntry(Address code_address) {
+ return impl_.Lookup(code_address, ComputePointerHash(code_address), false);
+ }
+
+ void RemoveEntry(HashMap::Entry* entry) {
+ impl_.Remove(entry->key, entry->hash);
+ }
+
+ HashMap impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(NameMap);
+};
+
+
+class Logger::NameBuffer {
+ public:
+ NameBuffer() { Reset(); }
+
+ void Reset() {
+ utf8_pos_ = 0;
+ }
+
+ void AppendString(String* str) {
+ if (str == NULL) return;
+ if (str->HasOnlyAsciiChars()) {
+ int utf8_length = Min(str->length(), kUtf8BufferSize - utf8_pos_);
+ String::WriteToFlat(str, utf8_buffer_ + utf8_pos_, 0, utf8_length);
+ utf8_pos_ += utf8_length;
+ return;
+ }
+ int uc16_length = Min(str->length(), kUc16BufferSize);
+ String::WriteToFlat(str, uc16_buffer_, 0, uc16_length);
+ for (int i = 0; i < uc16_length && utf8_pos_ < kUtf8BufferSize; ++i) {
+ uc16 c = uc16_buffer_[i];
+ if (c <= String::kMaxAsciiCharCodeU) {
+ utf8_buffer_[utf8_pos_++] = static_cast<char>(c);
+ } else {
+ int char_length = unibrow::Utf8::Length(c);
+ if (utf8_pos_ + char_length > kUtf8BufferSize) break;
+ unibrow::Utf8::Encode(utf8_buffer_ + utf8_pos_, c);
+ utf8_pos_ += char_length;
+ }
+ }
+ }
+
+ void AppendBytes(const char* bytes, int size) {
+ size = Min(size, kUtf8BufferSize - utf8_pos_);
+ memcpy(utf8_buffer_ + utf8_pos_, bytes, size);
+ utf8_pos_ += size;
+ }
+
+ void AppendBytes(const char* bytes) {
+ AppendBytes(bytes, StrLength(bytes));
+ }
+
+ void AppendByte(char c) {
+ if (utf8_pos_ >= kUtf8BufferSize) return;
+ utf8_buffer_[utf8_pos_++] = c;
+ }
+
+ void AppendInt(int n) {
+ Vector<char> buffer(utf8_buffer_ + utf8_pos_, kUtf8BufferSize - utf8_pos_);
+ int size = OS::SNPrintF(buffer, "%d", n);
+ if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
+ utf8_pos_ += size;
+ }
+ }
+
+ const char* get() { return utf8_buffer_; }
+ int size() const { return utf8_pos_; }
+
+ private:
+ static const int kUtf8BufferSize = 512;
+ static const int kUc16BufferSize = 128;
+
+ int utf8_pos_;
+ char utf8_buffer_[kUtf8BufferSize];
+ uc16 uc16_buffer_[kUc16BufferSize];
+};
+
+
Logger::Logger()
: ticker_(NULL),
profiler_(NULL),
@@ -347,6 +524,8 @@ Logger::Logger()
cpu_profiler_nesting_(0),
heap_profiler_nesting_(0),
log_(new Log(this)),
+ name_buffer_(new NameBuffer),
+ address_to_name_map_(NULL),
is_initialized_(false),
last_address_(NULL),
prev_sp_(NULL),
@@ -355,10 +534,14 @@ Logger::Logger()
prev_code_(NULL) {
}
+
Logger::~Logger() {
+ delete address_to_name_map_;
+ delete name_buffer_;
delete log_;
}
+
#define DECLARE_EVENT(ignore1, name) name,
static const char* const kLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)
@@ -735,7 +918,20 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
const char* comment) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code) return;
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof || Serializer::enabled()) {
+ name_buffer_->Reset();
+ name_buffer_->AppendBytes(kLogEventsNames[tag]);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendBytes(comment);
+ }
+ if (FLAG_ll_prof) {
+ LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (Serializer::enabled()) {
+ RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
@@ -749,7 +945,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
msg.Append(*p);
}
msg.Append('"');
- LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@@ -760,13 +955,30 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (name != NULL) {
- SmartPointer<char> str =
- name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
- CodeCreateEvent(tag, code, *str);
- } else {
- CodeCreateEvent(tag, code, "");
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof || Serializer::enabled()) {
+ name_buffer_->Reset();
+ name_buffer_->AppendBytes(kLogEventsNames[tag]);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendString(name);
}
+ if (FLAG_ll_prof) {
+ LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (Serializer::enabled()) {
+ RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (!FLAG_log_code) return;
+ LogMessageBuilder msg(this);
+ msg.Append("%s,%s,",
+ kLogEventsNames[CODE_CREATION_EVENT],
+ kLogEventsNames[tag]);
+ msg.AppendAddress(code->address());
+ msg.Append(",%d,\"", code->ExecutableSize());
+ msg.AppendDetailed(name, false);
+ msg.Append('"');
+ msg.Append('\n');
+ msg.WriteToLogFile();
#endif
}
@@ -788,7 +1000,21 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
SharedFunctionInfo* shared,
String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code) return;
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof || Serializer::enabled()) {
+ name_buffer_->Reset();
+ name_buffer_->AppendBytes(kLogEventsNames[tag]);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendBytes(ComputeMarker(code));
+ name_buffer_->AppendString(name);
+ }
+ if (FLAG_ll_prof) {
+ LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (Serializer::enabled()) {
+ RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (!FLAG_log_code) return;
if (code == Isolate::Current()->builtins()->builtin(
Builtins::kLazyCompile))
return;
@@ -803,7 +1029,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
msg.Append(",%d,\"%s\",", code->ExecutableSize(), *str);
msg.AppendAddress(shared->address());
msg.Append(",%s", ComputeMarker(code));
- LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@@ -818,7 +1043,25 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
SharedFunctionInfo* shared,
String* source, int line) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code) return;
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof || Serializer::enabled()) {
+ name_buffer_->Reset();
+ name_buffer_->AppendBytes(kLogEventsNames[tag]);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendBytes(ComputeMarker(code));
+ name_buffer_->AppendString(shared->DebugName());
+ name_buffer_->AppendByte(' ');
+ name_buffer_->AppendString(source);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendInt(line);
+ }
+ if (FLAG_ll_prof) {
+ LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (Serializer::enabled()) {
+ RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
SmartPointer<char> name =
shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
@@ -835,7 +1078,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
line);
msg.AppendAddress(shared->address());
msg.Append(",%s", ComputeMarker(code));
- LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@@ -844,14 +1086,26 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code) return;
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof || Serializer::enabled()) {
+ name_buffer_->Reset();
+ name_buffer_->AppendBytes(kLogEventsNames[tag]);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendInt(args_count);
+ }
+ if (FLAG_ll_prof) {
+ LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (Serializer::enabled()) {
+ RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]);
msg.AppendAddress(code->address());
msg.Append(",%d,\"args_count: %d\"", code->ExecutableSize(), args_count);
- LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@@ -860,10 +1114,8 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
void Logger::CodeMovingGCEvent() {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return;
- LogMessageBuilder msg(this);
- msg.Append("%s\n", kLogEventsNames[CODE_MOVING_GC]);
- msg.WriteToLogFile();
+ if (!log_->IsEnabled() || !FLAG_ll_prof) return;
+ LowLevelLogWriteBytes(&kCodeMovingGCTag, sizeof(kCodeMovingGCTag));
OS::SignalCodeMovingGC();
#endif
}
@@ -871,7 +1123,20 @@ void Logger::CodeMovingGCEvent() {
void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code) return;
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof || Serializer::enabled()) {
+ name_buffer_->Reset();
+ name_buffer_->AppendBytes(kLogEventsNames[REG_EXP_TAG]);
+ name_buffer_->AppendByte(':');
+ name_buffer_->AppendString(source);
+ }
+ if (FLAG_ll_prof) {
+ LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (Serializer::enabled()) {
+ RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
+ }
+ if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
@@ -880,7 +1145,6 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
msg.Append(",%d,\"", code->ExecutableSize());
msg.AppendDetailed(source, false);
msg.Append('\"');
- LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@@ -889,6 +1153,11 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
void Logger::CodeMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof) LowLevelCodeMoveEvent(from, to);
+ if (Serializer::enabled() && address_to_name_map_ != NULL) {
+ address_to_name_map_->Move(from, to);
+ }
MoveEventInternal(CODE_MOVE_EVENT, from, to);
#endif
}
@@ -896,6 +1165,11 @@ void Logger::CodeMoveEvent(Address from, Address to) {
void Logger::CodeDeleteEvent(Address from) {
#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof) LowLevelCodeDeleteEvent(from);
+ if (Serializer::enabled() && address_to_name_map_ != NULL) {
+ address_to_name_map_->Remove(from);
+ }
DeleteEventInternal(CODE_DELETE_EVENT, from);
#endif
}
@@ -903,7 +1177,21 @@ void Logger::CodeDeleteEvent(Address from) {
void Logger::SnapshotPositionEvent(Address addr, int pos) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_snapshot_positions) return;
+ if (!log_->IsEnabled()) return;
+ if (FLAG_ll_prof) LowLevelSnapshotPositionEvent(addr, pos);
+ if (Serializer::enabled() && address_to_name_map_ != NULL) {
+ const char* code_name = address_to_name_map_->Lookup(addr);
+ if (code_name == NULL) return; // Not a code object.
+ LogMessageBuilder msg(this);
+ msg.Append("%s,%d,\"", kLogEventsNames[SNAPSHOT_CODE_NAME_EVENT], pos);
+ for (const char* p = code_name; *p != '\0'; ++p) {
+ if (*p == '"') msg.Append('\\');
+ msg.Append(*p);
+ }
+ msg.Append("\"\n");
+ msg.WriteToLogFile();
+ }
+ if (!FLAG_log_snapshot_positions) return;
LogMessageBuilder msg(this);
msg.Append("%s,", kLogEventsNames[SNAPSHOT_POSITION_EVENT]);
msg.AppendAddress(addr);
@@ -1308,7 +1596,7 @@ static int EnumerateCompiledFunctions(Handle<SharedFunctionInfo>* sfis,
void Logger::LogCodeObject(Object* object) {
- if (FLAG_log_code) {
+ if (FLAG_log_code || FLAG_ll_prof) {
Code* code_object = Code::cast(object);
LogEventsAndTags tag = Logger::STUB_TAG;
const char* description = "Unknown code from the snapshot";
@@ -1316,7 +1604,8 @@ void Logger::LogCodeObject(Object* object) {
case Code::FUNCTION:
case Code::OPTIMIZED_FUNCTION:
return; // We log this later using LogCompiledFunctions.
- case Code::TYPE_RECORDING_BINARY_OP_IC: // fall through
+ case Code::UNARY_OP_IC: // fall through
+ case Code::BINARY_OP_IC: // fall through
case Code::COMPARE_IC: // fall through
case Code::STUB:
description =
@@ -1333,10 +1622,6 @@ void Logger::LogCodeObject(Object* object) {
description = "A keyed load IC from the snapshot";
tag = Logger::KEYED_LOAD_IC_TAG;
break;
- case Code::KEYED_EXTERNAL_ARRAY_LOAD_IC:
- description = "A keyed external array load IC from the snapshot";
- tag = Logger::KEYED_EXTERNAL_ARRAY_LOAD_IC_TAG;
- break;
case Code::LOAD_IC:
description = "A load IC from the snapshot";
tag = Logger::LOAD_IC_TAG;
@@ -1349,10 +1634,6 @@ void Logger::LogCodeObject(Object* object) {
description = "A keyed store IC from the snapshot";
tag = Logger::KEYED_STORE_IC_TAG;
break;
- case Code::KEYED_EXTERNAL_ARRAY_STORE_IC:
- description = "A keyed external array store IC from the snapshot";
- tag = Logger::KEYED_EXTERNAL_ARRAY_STORE_IC_TAG;
- break;
case Code::CALL_IC:
description = "A call IC from the snapshot";
tag = Logger::CALL_IC_TAG;
@@ -1369,7 +1650,7 @@ void Logger::LogCodeObject(Object* object) {
void Logger::LogCodeInfo() {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!log_->IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return;
+ if (!log_->IsEnabled() || !FLAG_ll_prof) return;
#if V8_TARGET_ARCH_IA32
const char arch[] = "ia32";
#elif V8_TARGET_ARCH_X64
@@ -1379,21 +1660,69 @@ void Logger::LogCodeInfo() {
#else
const char arch[] = "unknown";
#endif
- LogMessageBuilder msg(this);
- msg.Append("code-info,%s,%d\n", arch, Code::kHeaderSize);
- msg.WriteToLogFile();
+ LowLevelLogWriteBytes(arch, sizeof(arch));
#endif // ENABLE_LOGGING_AND_PROFILING
}
-void Logger::LowLevelCodeCreateEvent(Code* code, LogMessageBuilder* msg) {
- if (!FLAG_ll_prof || log_->output_code_handle_ == NULL) return;
- int pos = static_cast<int>(ftell(log_->output_code_handle_));
- size_t rv = fwrite(code->instruction_start(), 1, code->instruction_size(),
- log_->output_code_handle_);
- ASSERT(static_cast<size_t>(code->instruction_size()) == rv);
+void Logger::RegisterSnapshotCodeName(Code* code,
+ const char* name,
+ int name_size) {
+ ASSERT(Serializer::enabled());
+ if (address_to_name_map_ == NULL) {
+ address_to_name_map_ = new NameMap;
+ }
+ address_to_name_map_->Insert(code->address(), name, name_size);
+}
+
+
+void Logger::LowLevelCodeCreateEvent(Code* code,
+ const char* name,
+ int name_size) {
+ if (log_->ll_output_handle_ == NULL) return;
+ LowLevelCodeCreateStruct event;
+ event.name_size = name_size;
+ event.code_address = code->instruction_start();
+ ASSERT(event.code_address == code->address() + Code::kHeaderSize);
+ event.code_size = code->instruction_size();
+ LowLevelLogWriteStruct(event);
+ LowLevelLogWriteBytes(name, name_size);
+ LowLevelLogWriteBytes(
+ reinterpret_cast<const char*>(code->instruction_start()),
+ code->instruction_size());
+}
+
+
+void Logger::LowLevelCodeMoveEvent(Address from, Address to) {
+ if (log_->ll_output_handle_ == NULL) return;
+ LowLevelCodeMoveStruct event;
+ event.from_address = from + Code::kHeaderSize;
+ event.to_address = to + Code::kHeaderSize;
+ LowLevelLogWriteStruct(event);
+}
+
+
+void Logger::LowLevelCodeDeleteEvent(Address from) {
+ if (log_->ll_output_handle_ == NULL) return;
+ LowLevelCodeDeleteStruct event;
+ event.address = from + Code::kHeaderSize;
+ LowLevelLogWriteStruct(event);
+}
+
+
+void Logger::LowLevelSnapshotPositionEvent(Address addr, int pos) {
+ if (log_->ll_output_handle_ == NULL) return;
+ LowLevelSnapshotPositionStruct event;
+ event.address = addr + Code::kHeaderSize;
+ event.position = pos;
+ LowLevelLogWriteStruct(event);
+}
+
+
+void Logger::LowLevelLogWriteBytes(const char* bytes, int size) {
+ size_t rv = fwrite(bytes, 1, size, log_->ll_output_handle_);
+ ASSERT(static_cast<size_t>(size) == rv);
USE(rv);
- msg->Append(",%d", pos);
}
@@ -1496,7 +1825,6 @@ bool Logger::Setup() {
// --ll-prof implies --log-code and --log-snapshot-positions.
if (FLAG_ll_prof) {
- FLAG_log_code = true;
FLAG_log_snapshot_positions = true;
}
@@ -1523,7 +1851,7 @@ bool Logger::Setup() {
bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
|| FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
- || FLAG_log_regexp || FLAG_log_state_changes;
+ || FLAG_log_regexp || FLAG_log_state_changes || FLAG_ll_prof;
if (start_logging) {
logging_nesting_ = 1;
diff --git a/src/log.h b/src/log.h
index 1fa86d24..93f3fe7b 100644
--- a/src/log.h
+++ b/src/log.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,6 +28,7 @@
#ifndef V8_LOG_H_
#define V8_LOG_H_
+#include "allocation.h"
#include "platform.h"
#include "log-utils.h"
@@ -69,11 +70,12 @@ namespace internal {
// tick profiler requires code events, so --prof implies --log-code.
// Forward declarations.
-class Ticker;
+class HashMap;
+class LogMessageBuilder;
class Profiler;
class Semaphore;
class SlidingStateWindow;
-class LogMessageBuilder;
+class Ticker;
#undef LOG
#ifdef ENABLE_LOGGING_AND_PROFILING
@@ -88,48 +90,51 @@ class LogMessageBuilder;
#define LOG(isolate, Call) ((void) 0)
#endif
-#define LOG_EVENTS_AND_TAGS_LIST(V) \
- V(CODE_CREATION_EVENT, "code-creation") \
- V(CODE_MOVE_EVENT, "code-move") \
- V(CODE_DELETE_EVENT, "code-delete") \
- V(CODE_MOVING_GC, "code-moving-gc") \
- V(SHARED_FUNC_MOVE_EVENT, "sfi-move") \
- V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
- V(TICK_EVENT, "tick") \
- V(REPEAT_META_EVENT, "repeat") \
- V(BUILTIN_TAG, "Builtin") \
- V(CALL_DEBUG_BREAK_TAG, "CallDebugBreak") \
- V(CALL_DEBUG_PREPARE_STEP_IN_TAG, "CallDebugPrepareStepIn") \
- V(CALL_IC_TAG, "CallIC") \
- V(CALL_INITIALIZE_TAG, "CallInitialize") \
- V(CALL_MEGAMORPHIC_TAG, "CallMegamorphic") \
- V(CALL_MISS_TAG, "CallMiss") \
- V(CALL_NORMAL_TAG, "CallNormal") \
- V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic") \
- V(KEYED_CALL_DEBUG_BREAK_TAG, "KeyedCallDebugBreak") \
- V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG, \
- "KeyedCallDebugPrepareStepIn") \
- V(KEYED_CALL_IC_TAG, "KeyedCallIC") \
- V(KEYED_CALL_INITIALIZE_TAG, "KeyedCallInitialize") \
- V(KEYED_CALL_MEGAMORPHIC_TAG, "KeyedCallMegamorphic") \
- V(KEYED_CALL_MISS_TAG, "KeyedCallMiss") \
- V(KEYED_CALL_NORMAL_TAG, "KeyedCallNormal") \
- V(KEYED_CALL_PRE_MONOMORPHIC_TAG, "KeyedCallPreMonomorphic") \
- V(CALLBACK_TAG, "Callback") \
- V(EVAL_TAG, "Eval") \
- V(FUNCTION_TAG, "Function") \
- V(KEYED_LOAD_IC_TAG, "KeyedLoadIC") \
- V(KEYED_EXTERNAL_ARRAY_LOAD_IC_TAG, "KeyedExternalArrayLoadIC") \
- V(KEYED_STORE_IC_TAG, "KeyedStoreIC") \
- V(KEYED_EXTERNAL_ARRAY_STORE_IC_TAG, "KeyedExternalArrayStoreIC")\
- V(LAZY_COMPILE_TAG, "LazyCompile") \
- V(LOAD_IC_TAG, "LoadIC") \
- V(REG_EXP_TAG, "RegExp") \
- V(SCRIPT_TAG, "Script") \
- V(STORE_IC_TAG, "StoreIC") \
- V(STUB_TAG, "Stub") \
- V(NATIVE_FUNCTION_TAG, "Function") \
- V(NATIVE_LAZY_COMPILE_TAG, "LazyCompile") \
+#define LOG_EVENTS_AND_TAGS_LIST(V) \
+ V(CODE_CREATION_EVENT, "code-creation") \
+ V(CODE_MOVE_EVENT, "code-move") \
+ V(CODE_DELETE_EVENT, "code-delete") \
+ V(CODE_MOVING_GC, "code-moving-gc") \
+ V(SHARED_FUNC_MOVE_EVENT, "sfi-move") \
+ V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
+ V(SNAPSHOT_CODE_NAME_EVENT, "snapshot-code-name") \
+ V(TICK_EVENT, "tick") \
+ V(REPEAT_META_EVENT, "repeat") \
+ V(BUILTIN_TAG, "Builtin") \
+ V(CALL_DEBUG_BREAK_TAG, "CallDebugBreak") \
+ V(CALL_DEBUG_PREPARE_STEP_IN_TAG, "CallDebugPrepareStepIn") \
+ V(CALL_IC_TAG, "CallIC") \
+ V(CALL_INITIALIZE_TAG, "CallInitialize") \
+ V(CALL_MEGAMORPHIC_TAG, "CallMegamorphic") \
+ V(CALL_MISS_TAG, "CallMiss") \
+ V(CALL_NORMAL_TAG, "CallNormal") \
+ V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic") \
+ V(KEYED_CALL_DEBUG_BREAK_TAG, "KeyedCallDebugBreak") \
+ V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG, \
+ "KeyedCallDebugPrepareStepIn") \
+ V(KEYED_CALL_IC_TAG, "KeyedCallIC") \
+ V(KEYED_CALL_INITIALIZE_TAG, "KeyedCallInitialize") \
+ V(KEYED_CALL_MEGAMORPHIC_TAG, "KeyedCallMegamorphic") \
+ V(KEYED_CALL_MISS_TAG, "KeyedCallMiss") \
+ V(KEYED_CALL_NORMAL_TAG, "KeyedCallNormal") \
+ V(KEYED_CALL_PRE_MONOMORPHIC_TAG, "KeyedCallPreMonomorphic") \
+ V(CALLBACK_TAG, "Callback") \
+ V(EVAL_TAG, "Eval") \
+ V(FUNCTION_TAG, "Function") \
+ V(KEYED_LOAD_IC_TAG, "KeyedLoadIC") \
+ V(KEYED_LOAD_MEGAMORPHIC_IC_TAG, "KeyedLoadMegamorphicIC") \
+ V(KEYED_EXTERNAL_ARRAY_LOAD_IC_TAG, "KeyedExternalArrayLoadIC") \
+ V(KEYED_STORE_IC_TAG, "KeyedStoreIC") \
+ V(KEYED_STORE_MEGAMORPHIC_IC_TAG, "KeyedStoreMegamorphicIC") \
+ V(KEYED_EXTERNAL_ARRAY_STORE_IC_TAG, "KeyedExternalArrayStoreIC") \
+ V(LAZY_COMPILE_TAG, "LazyCompile") \
+ V(LOAD_IC_TAG, "LoadIC") \
+ V(REG_EXP_TAG, "RegExp") \
+ V(SCRIPT_TAG, "Script") \
+ V(STORE_IC_TAG, "StoreIC") \
+ V(STUB_TAG, "Stub") \
+ V(NATIVE_FUNCTION_TAG, "Function") \
+ V(NATIVE_LAZY_COMPILE_TAG, "LazyCompile") \
V(NATIVE_SCRIPT_TAG, "Script")
// Note that 'NATIVE_' cases for functions and scripts are mapped onto
// original tags when writing to the log.
@@ -306,6 +311,9 @@ class Logger {
void LogFailure();
private:
+ class NameBuffer;
+ class NameMap;
+
Logger();
~Logger();
@@ -332,8 +340,26 @@ class Logger {
// Emits general information about generated code.
void LogCodeInfo();
- // Handles code creation when low-level profiling is active.
- void LowLevelCodeCreateEvent(Code* code, LogMessageBuilder* msg);
+ void RegisterSnapshotCodeName(Code* code, const char* name, int name_size);
+
+ // Low-level logging support.
+
+ void LowLevelCodeCreateEvent(Code* code, const char* name, int name_size);
+
+ void LowLevelCodeMoveEvent(Address from, Address to);
+
+ void LowLevelCodeDeleteEvent(Address from);
+
+ void LowLevelSnapshotPositionEvent(Address addr, int pos);
+
+ void LowLevelLogWriteBytes(const char* bytes, int size);
+
+ template <typename T>
+ void LowLevelLogWriteStruct(const T& s) {
+ char tag = T::kTag;
+ LowLevelLogWriteBytes(reinterpret_cast<const char*>(&tag), sizeof(tag));
+ LowLevelLogWriteBytes(reinterpret_cast<const char*>(&s), sizeof(s));
+ }
// Emits a profiler tick event. Used by the profiler thread.
void TickEvent(TickSample* sample, bool overflow);
@@ -385,6 +411,10 @@ class Logger {
Log* log_;
+ NameBuffer* name_buffer_;
+
+ NameMap* address_to_name_map_;
+
// Guards against multiple calls to TearDown() that can happen in some tests.
// 'true' between Setup() and TearDown().
bool is_initialized_;
diff --git a/src/macros.py b/src/macros.py
index 69f36c09..28d501f3 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -127,7 +127,7 @@ macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
macro TO_UINT32(arg) = (arg >>> 0);
macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
macro TO_NUMBER_INLINE(arg) = (IS_NUMBER(%IS_VAR(arg)) ? arg : NonNumberToNumber(arg));
-
+macro TO_OBJECT_INLINE(arg) = (IS_SPEC_OBJECT(%IS_VAR(arg)) ? arg : ToObject(arg));
# Macros implemented in Python.
python macro CHAR_CODE(str) = ord(str[1]);
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 68a50622..94399051 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -305,13 +305,11 @@ class CodeFlusher {
*GetNextCandidateField(candidate) = next_candidate;
}
- STATIC_ASSERT(kPointerSize <= Code::kHeaderSize - Code::kHeaderPaddingStart);
-
static SharedFunctionInfo** GetNextCandidateField(
SharedFunctionInfo* candidate) {
Code* code = candidate->unchecked_code();
return reinterpret_cast<SharedFunctionInfo**>(
- code->address() + Code::kHeaderPaddingStart);
+ code->address() + Code::kNextCodeFlushingCandidateOffset);
}
static SharedFunctionInfo* GetNextCandidate(SharedFunctionInfo* candidate) {
@@ -424,6 +422,9 @@ class StaticMarkingVisitor : public StaticVisitorBase {
table_.Register(kVisitJSFunction,
&VisitJSFunctionAndFlushCode);
+ table_.Register(kVisitJSRegExp,
+ &VisitRegExpAndFlushCode);
+
table_.Register(kVisitPropertyCell,
&FixedBodyVisitor<StaticMarkingVisitor,
JSGlobalPropertyCell::BodyDescriptor,
@@ -459,7 +460,7 @@ class StaticMarkingVisitor : public StaticVisitorBase {
static inline void VisitCodeTarget(Heap* heap, RelocInfo* rinfo) {
ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
Code* code = Code::GetCodeFromTargetAddress(rinfo->target_address());
- if (FLAG_cleanup_ics_at_gc && code->is_inline_cache_stub()) {
+ if (FLAG_cleanup_code_caches_at_gc && code->is_inline_cache_stub()) {
IC::Clear(rinfo->pc());
// Please note targets for cleared inline cached do not have to be
// marked since they are contained in HEAP->non_monomorphic_cache().
@@ -564,6 +565,8 @@ class StaticMarkingVisitor : public StaticVisitorBase {
// flushed.
static const int kCodeAgeThreshold = 5;
+ static const int kRegExpCodeThreshold = 5;
+
inline static bool HasSourceCode(Heap* heap, SharedFunctionInfo* info) {
Object* undefined = heap->raw_unchecked_undefined_value();
return (info->script() != undefined) &&
@@ -699,6 +702,68 @@ class StaticMarkingVisitor : public StaticVisitorBase {
}
+ static void UpdateRegExpCodeAgeAndFlush(Heap* heap,
+ JSRegExp* re,
+ bool is_ascii) {
+ // Make sure that the fixed array is in fact initialized on the RegExp.
+ // We could potentially trigger a GC when initializing the RegExp.
+ if (SafeMap(re->data())->instance_type() != FIXED_ARRAY_TYPE) return;
+
+ // Make sure this is a RegExp that actually contains code.
+ if (re->TypeTagUnchecked() != JSRegExp::IRREGEXP) return;
+
+ Object* code = re->DataAtUnchecked(JSRegExp::code_index(is_ascii));
+ if (!code->IsSmi() && SafeMap(code)->instance_type() == CODE_TYPE) {
+ // Save a copy that can be reinstated if we need the code again.
+ re->SetDataAtUnchecked(JSRegExp::saved_code_index(is_ascii),
+ code,
+ heap);
+ // Set a number in the 0-255 range to guarantee no smi overflow.
+ re->SetDataAtUnchecked(JSRegExp::code_index(is_ascii),
+ Smi::FromInt(heap->sweep_generation() & 0xff),
+ heap);
+ } else if (code->IsSmi()) {
+ int value = Smi::cast(code)->value();
+ // The regexp has not been compiled yet or there was a compilation error.
+ if (value == JSRegExp::kUninitializedValue ||
+ value == JSRegExp::kCompilationErrorValue) {
+ return;
+ }
+
+ // Check if we should flush now.
+ if (value == ((heap->sweep_generation() - kRegExpCodeThreshold) & 0xff)) {
+ re->SetDataAtUnchecked(JSRegExp::code_index(is_ascii),
+ Smi::FromInt(JSRegExp::kUninitializedValue),
+ heap);
+ re->SetDataAtUnchecked(JSRegExp::saved_code_index(is_ascii),
+ Smi::FromInt(JSRegExp::kUninitializedValue),
+ heap);
+ }
+ }
+ }
+
+
+ // Works by setting the current sweep_generation (as a smi) in the
+ // code object place in the data array of the RegExp and keeps a copy
+ // around that can be reinstated if we reuse the RegExp before flushing.
+ // If we did not use the code for kRegExpCodeThreshold mark sweep GCs
+ // we flush the code.
+ static void VisitRegExpAndFlushCode(Map* map, HeapObject* object) {
+ Heap* heap = map->heap();
+ MarkCompactCollector* collector = heap->mark_compact_collector();
+ if (!collector->is_code_flushing_enabled()) {
+ VisitJSRegExpFields(map, object);
+ return;
+ }
+ JSRegExp* re = reinterpret_cast<JSRegExp*>(object);
+ // Flush code or set age on both ascii and two byte code.
+ UpdateRegExpCodeAgeAndFlush(heap, re, true);
+ UpdateRegExpCodeAgeAndFlush(heap, re, false);
+ // Visit the fields of the RegExp, including the updated FixedArray.
+ VisitJSRegExpFields(map, object);
+ }
+
+
static void VisitSharedFunctionInfoAndFlushCode(Map* map,
HeapObject* object) {
MarkCompactCollector* collector = map->heap()->mark_compact_collector();
@@ -829,6 +894,15 @@ class StaticMarkingVisitor : public StaticVisitorBase {
// Don't visit the next function list field as it is a weak reference.
}
+ static inline void VisitJSRegExpFields(Map* map,
+ HeapObject* object) {
+ int last_property_offset =
+ JSRegExp::kSize + kPointerSize * map->inobject_properties();
+ VisitPointers(map->heap(),
+ SLOT_ADDR(object, JSRegExp::kPropertiesOffset),
+ SLOT_ADDR(object, last_property_offset));
+ }
+
static void VisitSharedFunctionInfoFields(Heap* heap,
HeapObject* object,
@@ -1058,7 +1132,7 @@ void MarkCompactCollector::MarkUnmarkedObject(HeapObject* object) {
ASSERT(HEAP->Contains(object));
if (object->IsMap()) {
Map* map = Map::cast(object);
- if (FLAG_cleanup_caches_in_maps_at_gc) {
+ if (FLAG_cleanup_code_caches_at_gc) {
map->ClearCodeCache(heap());
}
SetMark(map);
@@ -1083,8 +1157,13 @@ void MarkCompactCollector::MarkMapContents(Map* map) {
FixedArray* prototype_transitions = map->unchecked_prototype_transitions();
if (!prototype_transitions->IsMarked()) SetMark(prototype_transitions);
- MarkDescriptorArray(reinterpret_cast<DescriptorArray*>(
- *HeapObject::RawField(map, Map::kInstanceDescriptorsOffset)));
+ Object* raw_descriptor_array =
+ *HeapObject::RawField(map,
+ Map::kInstanceDescriptorsOrBitField3Offset);
+ if (!raw_descriptor_array->IsSmi()) {
+ MarkDescriptorArray(
+ reinterpret_cast<DescriptorArray*>(raw_descriptor_array));
+ }
// Mark the Object* fields of the Map.
// Since the descriptor array has been marked already, it is fine
@@ -2055,7 +2134,7 @@ static void SweepNewSpace(Heap* heap, NewSpace* space) {
}
// Update roots.
- heap->IterateRoots(&updating_visitor, VISIT_ALL_IN_SCAVENGE);
+ heap->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE);
LiveObjectList::IterateElements(&updating_visitor);
// Update pointers in old spaces.
diff --git a/src/messages.cc b/src/messages.cc
index abc25377..4cbf0af7 100644
--- a/src/messages.cc
+++ b/src/messages.cc
@@ -125,13 +125,13 @@ void MessageHandler::ReportMessage(Isolate* isolate,
HandleScope scope;
if (global_listeners.get(i)->IsUndefined()) continue;
v8::NeanderObject listener(JSObject::cast(global_listeners.get(i)));
- Handle<Proxy> callback_obj(Proxy::cast(listener.get(0)));
+ Handle<Foreign> callback_obj(Foreign::cast(listener.get(0)));
v8::MessageCallback callback =
- FUNCTION_CAST<v8::MessageCallback>(callback_obj->proxy());
+ FUNCTION_CAST<v8::MessageCallback>(callback_obj->address());
Handle<Object> callback_data(listener.get(1));
{
// Do not allow exceptions to propagate.
- v8::TryCatch tryCatch;
+ v8::TryCatch try_catch;
callback(api_message_obj, v8::Utils::ToLocal(callback_data));
}
if (isolate->has_scheduled_exception()) {
diff --git a/src/messages.js b/src/messages.js
index d8810dce..75ea99dc 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -57,11 +57,13 @@ function FormatString(format, message) {
for (var i = 0; i < format.length; i++) {
var str = format[i];
for (arg_num = 0; arg_num < kReplacementMarkers.length; arg_num++) {
- if (format[i] !== kReplacementMarkers[arg_num]) continue;
- try {
- str = ToDetailString(args[arg_num]);
- } catch (e) {
- str = "#<error>";
+ if (str == kReplacementMarkers[arg_num]) {
+ try {
+ str = ToDetailString(args[arg_num]);
+ } catch (e) {
+ str = "#<error>";
+ }
+ break;
}
}
result += str;
@@ -100,7 +102,8 @@ function ToStringCheckErrorObject(obj) {
function ToDetailString(obj) {
- if (obj != null && IS_OBJECT(obj) && obj.toString === $Object.prototype.toString) {
+ if (obj != null && IS_OBJECT(obj) &&
+ obj.toString === $Object.prototype.toString) {
var constructor = obj.constructor;
if (!constructor) return ToStringCheckErrorObject(obj);
var constructorName = constructor.name;
@@ -142,6 +145,7 @@ function FormatMessage(message) {
kMessages = {
// Error
cyclic_proto: ["Cyclic __proto__ value"],
+ code_gen_from_strings: ["Code generation from strings disallowed for this context"],
// TypeError
unexpected_token: ["Unexpected token ", "%0"],
unexpected_token_number: ["Unexpected number"],
@@ -191,6 +195,7 @@ function FormatMessage(message) {
redefine_disallowed: ["Cannot redefine property: ", "%0"],
define_disallowed: ["Cannot define property, object is not extensible: ", "%0"],
non_extensible_proto: ["%0", " is not extensible"],
+ handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
// RangeError
invalid_array_length: ["Invalid array length"],
stack_overflow: ["Maximum call stack size exceeded"],
@@ -206,6 +211,7 @@ function FormatMessage(message) {
invalid_json: ["String '", "%0", "' is not valid JSON"],
circular_structure: ["Converting circular structure to JSON"],
obj_ctor_property_non_object: ["Object.", "%0", " called on non-object"],
+ called_on_null_or_undefined: ["%0", " called on null or undefined"],
array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
illegal_access: ["Illegal access"],
@@ -233,11 +239,10 @@ function FormatMessage(message) {
strict_function: ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
strict_read_only_property: ["Cannot assign to read only property '", "%0", "' of ", "%1"],
strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"],
- strict_arguments_callee: ["Cannot access property 'callee' of strict mode arguments"],
- strict_arguments_caller: ["Cannot access property 'caller' of strict mode arguments"],
- strict_function_caller: ["Cannot access property 'caller' of a strict mode function"],
- strict_function_arguments: ["Cannot access property 'arguments' of a strict mode function"],
+ strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
strict_caller: ["Illegal access to a strict mode caller function."],
+ cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
+ redef_external_array_element: ["Cannot redefine a property of an object with external array elements"],
};
}
var message_type = %MessageGetType(message);
@@ -550,6 +555,7 @@ function SourceLocation(script, position, line, column, start, end) {
this.end = end;
}
+SourceLocation.prototype.__proto__ = null;
const kLineLengthLimit = 78;
@@ -639,6 +645,7 @@ function SourceSlice(script, from_line, to_line, from_position, to_position) {
this.to_position = to_position;
}
+SourceSlice.prototype.__proto__ = null;
/**
* Get the source text for a SourceSlice
@@ -700,23 +707,28 @@ function CallSite(receiver, fun, pos) {
this.pos = pos;
}
+CallSite.prototype.__proto__ = null;
+
CallSite.prototype.getThis = function () {
return this.receiver;
};
CallSite.prototype.getTypeName = function () {
var constructor = this.receiver.constructor;
- if (!constructor)
+ if (!constructor) {
return %_CallFunction(this.receiver, ObjectToString);
+ }
var constructorName = constructor.name;
- if (!constructorName)
+ if (!constructorName) {
return %_CallFunction(this.receiver, ObjectToString);
+ }
return constructorName;
};
CallSite.prototype.isToplevel = function () {
- if (this.receiver == null)
+ if (this.receiver == null) {
return true;
+ }
return IS_GLOBAL(this.receiver);
};
@@ -749,8 +761,9 @@ CallSite.prototype.getFunctionName = function () {
}
// Maybe this is an evaluation?
var script = %FunctionGetScript(this.fun);
- if (script && script.compilation_type == COMPILATION_TYPE_EVAL)
+ if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
return "eval";
+ }
return null;
};
@@ -772,13 +785,15 @@ CallSite.prototype.getMethodName = function () {
this.receiver.__lookupSetter__(prop) === this.fun ||
(!this.receiver.__lookupGetter__(prop) && this.receiver[prop] === this.fun)) {
// If we find more than one match bail out to avoid confusion.
- if (name)
+ if (name) {
return null;
+ }
name = prop;
}
}
- if (name)
+ if (name) {
return name;
+ }
return null;
};
@@ -788,8 +803,9 @@ CallSite.prototype.getFileName = function () {
};
CallSite.prototype.getLineNumber = function () {
- if (this.pos == -1)
+ if (this.pos == -1) {
return null;
+ }
var script = %FunctionGetScript(this.fun);
var location = null;
if (script) {
@@ -799,8 +815,9 @@ CallSite.prototype.getLineNumber = function () {
};
CallSite.prototype.getColumnNumber = function () {
- if (this.pos == -1)
+ if (this.pos == -1) {
return null;
+ }
var script = %FunctionGetScript(this.fun);
var location = null;
if (script) {
@@ -820,15 +837,17 @@ CallSite.prototype.getPosition = function () {
CallSite.prototype.isConstructor = function () {
var constructor = this.receiver ? this.receiver.constructor : null;
- if (!constructor)
+ if (!constructor) {
return false;
+ }
return this.fun === constructor;
};
function FormatEvalOrigin(script) {
var sourceURL = script.nameOrSourceURL();
- if (sourceURL)
+ if (sourceURL) {
return sourceURL;
+ }
var eval_origin = "eval at ";
if (script.eval_from_function_name) {
@@ -1023,8 +1042,9 @@ function DefineError(f) {
function captureStackTrace(obj, cons_opt) {
var stackTraceLimit = $Error.stackTraceLimit;
if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
- if (stackTraceLimit < 0 || stackTraceLimit > 10000)
+ if (stackTraceLimit < 0 || stackTraceLimit > 10000) {
stackTraceLimit = 10000;
+ }
var raw_stack = %CollectStackTrace(cons_opt
? cons_opt
: captureStackTrace, stackTraceLimit);
@@ -1071,6 +1091,10 @@ function errorToStringDetectCycle() {
}
function errorToString() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Error.prototype.toString"]);
+ }
// This helper function is needed because access to properties on
// the builtins object do not work inside of a catch clause.
function isCyclicErrorMarker(o) { return o === cyclic_error_marker; }
@@ -1080,8 +1104,10 @@ function errorToString() {
} catch(e) {
// If this error message was encountered already return the empty
// string for it instead of recursively formatting it.
- if (isCyclicErrorMarker(e)) return '';
- else throw e;
+ if (isCyclicErrorMarker(e)) {
+ return '';
+ }
+ throw e;
}
}
diff --git a/src/mips/assembler-mips-inl.h b/src/mips/assembler-mips-inl.h
index f7453d16..e787fed7 100644
--- a/src/mips/assembler-mips-inl.h
+++ b/src/mips/assembler-mips-inl.h
@@ -30,7 +30,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#ifndef V8_MIPS_ASSEMBLER_MIPS_INL_H_
@@ -45,7 +45,7 @@ namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
-// Operand and MemOperand
+// Operand and MemOperand.
Operand::Operand(int32_t immediate, RelocInfo::Mode rmode) {
rm_ = no_reg;
@@ -80,7 +80,7 @@ bool Operand::is_reg() const {
// -----------------------------------------------------------------------------
-// RelocInfo
+// RelocInfo.
void RelocInfo::apply(intptr_t delta) {
// On MIPS we do not use pc relative addressing, so we don't need to patch the
@@ -95,24 +95,8 @@ Address RelocInfo::target_address() {
Address RelocInfo::target_address_address() {
- ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY
- || rmode_ == EMBEDDED_OBJECT
- || rmode_ == EXTERNAL_REFERENCE);
- // Read the address of the word containing the target_address in an
- // instruction stream.
- // The only architecture-independent user of this function is the serializer.
- // The serializer uses it to find out how many raw bytes of instruction to
- // output before the next target.
- // For an instructions like LUI/ORI where the target bits are mixed into the
- // instruction bits, the size of the target will be zero, indicating that the
- // serializer should not step forward in memory after a target is resolved
- // and written. In this case the target_address_address function should
- // return the end of the instructions to be patched, allowing the
- // deserializer to deserialize the instructions as raw bytes and put them in
- // place, ready to be patched with the target. In our case, that is the
- // address of the instruction that follows LUI/ORI instruction pair.
- return reinterpret_cast<Address>(
- pc_ + Assembler::kInstructionsFor32BitConstant * Assembler::kInstrSize);
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
+ return reinterpret_cast<Address>(pc_);
}
@@ -144,12 +128,9 @@ Object** RelocInfo::target_object_address() {
// Provide a "natural pointer" to the embedded object,
// which can be de-referenced during heap iteration.
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
- // TODO(mips): Commenting out, to simplify arch-independent changes.
- // GC won't work like this, but this commit is for asm/disasm/sim.
- // reconstructed_obj_ptr_ =
- // reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
- // return &reconstructed_obj_ptr_;
- return NULL;
+ reconstructed_obj_ptr_ =
+ reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
+ return &reconstructed_obj_ptr_;
}
@@ -161,11 +142,8 @@ void RelocInfo::set_target_object(Object* target) {
Address* RelocInfo::target_reference_address() {
ASSERT(rmode_ == EXTERNAL_REFERENCE);
- // TODO(mips): Commenting out, to simplify arch-independent changes.
- // GC won't work like this, but this commit is for asm/disasm/sim.
- // reconstructed_adr_ptr_ = Assembler::target_address_at(pc_);
- // return &reconstructed_adr_ptr_;
- return NULL;
+ reconstructed_adr_ptr_ = Assembler::target_address_at(pc_);
+ return &reconstructed_adr_ptr_;
}
@@ -251,26 +229,23 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
void RelocInfo::Visit(ObjectVisitor* visitor) {
RelocInfo::Mode mode = rmode();
if (mode == RelocInfo::EMBEDDED_OBJECT) {
- // RelocInfo is needed when pointer must be updated/serialized, such as
- // UpdatingVisitor in mark-compact.cc or Serializer in serialize.cc.
- // It is ignored by visitors that do not need it.
- // Commenting out, to simplify arch-independednt changes.
- // GC won't work like this, but this commit is for asm/disasm/sim.
- // visitor->VisitPointer(target_object_address(), this);
+ Object** p = target_object_address();
+ Object* orig = *p;
+ visitor->VisitPointer(p);
+ if (*p != orig) {
+ set_target_object(*p);
+ }
} else if (RelocInfo::IsCodeTarget(mode)) {
visitor->VisitCodeTarget(this);
+ } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
+ visitor->VisitGlobalPropertyCell(this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
- // RelocInfo is needed when external-references must be serialized by
- // Serializer Visitor in serialize.cc. It is ignored by visitors that
- // do not need it.
- // Commenting out, to simplify arch-independednt changes.
- // Serializer won't work like this, but this commit is for asm/disasm/sim.
- // visitor->VisitExternalReference(target_reference_address(), this);
+ visitor->VisitExternalReference(target_reference_address());
#ifdef ENABLE_DEBUGGER_SUPPORT
// TODO(isolates): Get a cached isolate below.
} else if (((RelocInfo::IsJSReturn(mode) &&
IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
+ (RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence())) &&
Isolate::Current()->debug()->has_break_points()) {
visitor->VisitDebugTarget(this);
@@ -287,7 +262,9 @@ void RelocInfo::Visit(Heap* heap) {
if (mode == RelocInfo::EMBEDDED_OBJECT) {
StaticVisitor::VisitPointer(heap, target_object_address());
} else if (RelocInfo::IsCodeTarget(mode)) {
- StaticVisitor::VisitCodeTarget(this);
+ StaticVisitor::VisitCodeTarget(heap, this);
+ } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
+ StaticVisitor::VisitGlobalPropertyCell(heap, this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
StaticVisitor::VisitExternalReference(target_reference_address());
#ifdef ENABLE_DEBUGGER_SUPPORT
@@ -296,7 +273,7 @@ void RelocInfo::Visit(Heap* heap) {
IsPatchedReturnSequence()) ||
(RelocInfo::IsDebugBreakSlot(mode) &&
IsPatchedDebugBreakSlotSequence()))) {
- StaticVisitor::VisitDebugTarget(this);
+ StaticVisitor::VisitDebugTarget(heap, this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
StaticVisitor::VisitRuntimeEntry(this);
@@ -305,7 +282,7 @@ void RelocInfo::Visit(Heap* heap) {
// -----------------------------------------------------------------------------
-// Assembler
+// Assembler.
void Assembler::CheckBuffer() {
diff --git a/src/mips/assembler-mips.cc b/src/mips/assembler-mips.cc
index 7d00da1f..2e10904c 100644
--- a/src/mips/assembler-mips.cc
+++ b/src/mips/assembler-mips.cc
@@ -30,7 +30,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#include "v8.h"
@@ -43,13 +43,17 @@
namespace v8 {
namespace internal {
-CpuFeatures::CpuFeatures()
- : supported_(0),
- enabled_(0),
- found_by_runtime_probing_(0) {
-}
+#ifdef DEBUG
+bool CpuFeatures::initialized_ = false;
+#endif
+unsigned CpuFeatures::supported_ = 0;
+unsigned CpuFeatures::found_by_runtime_probing_ = 0;
-void CpuFeatures::Probe(bool portable) {
+void CpuFeatures::Probe() {
+ ASSERT(!initialized_);
+#ifdef DEBUG
+ initialized_ = true;
+#endif
// If the compiler is allowed to use fpu then we can use fpu too in our
// code generation.
#if !defined(__mips__)
@@ -58,7 +62,7 @@ void CpuFeatures::Probe(bool portable) {
supported_ |= 1u << FPU;
}
#else
- if (portable && Serializer::enabled()) {
+ if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
return; // No features if we might serialize.
}
@@ -69,8 +73,6 @@ void CpuFeatures::Probe(bool portable) {
supported_ |= 1u << FPU;
found_by_runtime_probing_ |= 1u << FPU;
}
-
- if (!portable) found_by_runtime_probing_ = 0;
#endif
}
@@ -235,12 +237,10 @@ const Instr kLwSwOffsetMask = kImm16Mask;
static const int kMinimalBufferSize = 4 * KB;
-Assembler::Assembler(void* buffer, int buffer_size)
- : AssemblerBase(Isolate::Current()),
+Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
+ : AssemblerBase(arg_isolate),
positions_recorder_(this),
- allow_peephole_optimization_(false) {
- // BUG(3245989): disable peephole optimization if crankshaft is enabled.
- allow_peephole_optimization_ = FLAG_peephole_optimization;
+ emit_debug_code_(FLAG_debug_code) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@@ -276,13 +276,17 @@ Assembler::Assembler(void* buffer, int buffer_size)
no_trampoline_pool_before_ = 0;
trampoline_pool_blocked_nesting_ = 0;
next_buffer_check_ = kMaxBranchOffset - kTrampolineSize;
+ internal_trampoline_exception_ = false;
+ last_bound_pos_ = 0;
+
+ ast_id_for_reloc_info_ = kNoASTId;
}
Assembler::~Assembler() {
if (own_buffer_) {
if (isolate()->assembler_spare_buffer() == NULL &&
- buffer_size_ == kMinimalBufferSize) {
+ buffer_size_ == kMinimalBufferSize) {
isolate()->set_assembler_spare_buffer(buffer_);
} else {
DeleteArray(buffer_);
@@ -316,13 +320,82 @@ void Assembler::CodeTargetAlign() {
}
-Register Assembler::GetRt(Instr instr) {
+Register Assembler::GetRtReg(Instr instr) {
Register rt;
- rt.code_ = (instr & kRtMask) >> kRtShift;
+ rt.code_ = (instr & kRtFieldMask) >> kRtShift;
return rt;
}
+Register Assembler::GetRsReg(Instr instr) {
+ Register rs;
+ rs.code_ = (instr & kRsFieldMask) >> kRsShift;
+ return rs;
+}
+
+
+Register Assembler::GetRdReg(Instr instr) {
+ Register rd;
+ rd.code_ = (instr & kRdFieldMask) >> kRdShift;
+ return rd;
+}
+
+
+uint32_t Assembler::GetRt(Instr instr) {
+ return (instr & kRtFieldMask) >> kRtShift;
+}
+
+
+uint32_t Assembler::GetRtField(Instr instr) {
+ return instr & kRtFieldMask;
+}
+
+
+uint32_t Assembler::GetRs(Instr instr) {
+ return (instr & kRsFieldMask) >> kRsShift;
+}
+
+
+uint32_t Assembler::GetRsField(Instr instr) {
+ return instr & kRsFieldMask;
+}
+
+
+uint32_t Assembler::GetRd(Instr instr) {
+ return (instr & kRdFieldMask) >> kRdShift;
+}
+
+
+uint32_t Assembler::GetRdField(Instr instr) {
+ return instr & kRdFieldMask;
+}
+
+
+uint32_t Assembler::GetSa(Instr instr) {
+ return (instr & kSaFieldMask) >> kSaShift;
+}
+
+
+uint32_t Assembler::GetSaField(Instr instr) {
+ return instr & kSaFieldMask;
+}
+
+
+uint32_t Assembler::GetOpcodeField(Instr instr) {
+ return instr & kOpcodeMask;
+}
+
+
+uint32_t Assembler::GetImmediate16(Instr instr) {
+ return instr & kImm16Mask;
+}
+
+
+uint32_t Assembler::GetLabelConst(Instr instr) {
+ return instr & ~kImm16Mask;
+}
+
+
bool Assembler::IsPop(Instr instr) {
return (instr & ~kRtMask) == kPopRegPattern;
}
@@ -374,10 +447,10 @@ const int kEndOfChain = -4;
bool Assembler::IsBranch(Instr instr) {
- uint32_t opcode = ((instr & kOpcodeMask));
- uint32_t rt_field = ((instr & kRtFieldMask));
- uint32_t rs_field = ((instr & kRsFieldMask));
- uint32_t label_constant = (instr & ~kImm16Mask);
+ uint32_t opcode = GetOpcodeField(instr);
+ uint32_t rt_field = GetRtField(instr);
+ uint32_t rs_field = GetRsField(instr);
+ uint32_t label_constant = GetLabelConst(instr);
// Checks if the instruction is a branch.
return opcode == BEQ ||
opcode == BNE ||
@@ -386,7 +459,7 @@ bool Assembler::IsBranch(Instr instr) {
opcode == BEQL ||
opcode == BNEL ||
opcode == BLEZL ||
- opcode == BGTZL||
+ opcode == BGTZL ||
(opcode == REGIMM && (rt_field == BLTZ || rt_field == BGEZ ||
rt_field == BLTZAL || rt_field == BGEZAL)) ||
(opcode == COP1 && rs_field == BC1) || // Coprocessor branch.
@@ -394,13 +467,23 @@ bool Assembler::IsBranch(Instr instr) {
}
+bool Assembler::IsBeq(Instr instr) {
+ return GetOpcodeField(instr) == BEQ;
+}
+
+
+bool Assembler::IsBne(Instr instr) {
+ return GetOpcodeField(instr) == BNE;
+}
+
+
bool Assembler::IsNop(Instr instr, unsigned int type) {
// See Assembler::nop(type).
ASSERT(type < 32);
- uint32_t opcode = ((instr & kOpcodeMask));
- uint32_t rt = ((instr & kRtFieldMask) >> kRtShift);
- uint32_t rs = ((instr & kRsFieldMask) >> kRsShift);
- uint32_t sa = ((instr & kSaFieldMask) >> kSaShift);
+ uint32_t opcode = GetOpcodeField(instr);
+ uint32_t rt = GetRt(instr);
+ uint32_t rs = GetRs(instr);
+ uint32_t sa = GetSa(instr);
// nop(type) == sll(zero_reg, zero_reg, type);
// Technically all these values will be 0 but
@@ -465,6 +548,11 @@ Instr Assembler::SetAddImmediateOffset(Instr instr, int16_t offset) {
}
+bool Assembler::IsAndImmediate(Instr instr) {
+ return GetOpcodeField(instr) == ANDI;
+}
+
+
int Assembler::target_at(int32_t pos) {
Instr instr = instr_at(pos);
if ((instr & ~kImm16Mask) == 0) {
@@ -546,6 +634,10 @@ void Assembler::bind_to(Label* L, int pos) {
if (dist > kMaxBranchOffset) {
do {
int32_t trampoline_pos = get_trampoline_entry(fixup_pos);
+ if (kInvalidSlotPos == trampoline_pos) {
+ // Internal error.
+ return;
+ }
ASSERT((trampoline_pos - fixup_pos) <= kMaxBranchOffset);
target_at_put(fixup_pos, trampoline_pos);
fixup_pos = trampoline_pos;
@@ -554,6 +646,10 @@ void Assembler::bind_to(Label* L, int pos) {
} else if (dist < -kMaxBranchOffset) {
do {
int32_t trampoline_pos = get_trampoline_entry(fixup_pos, false);
+ if (kInvalidSlotPos == trampoline_pos) {
+ // Internal error.
+ return;
+ }
ASSERT((trampoline_pos - fixup_pos) >= -kMaxBranchOffset);
target_at_put(fixup_pos, trampoline_pos);
fixup_pos = trampoline_pos;
@@ -652,7 +748,7 @@ void Assembler::GenInstrRegister(Opcode opcode,
FPURegister fd,
SecondaryField func) {
ASSERT(fd.is_valid() && fs.is_valid() && ft.is_valid());
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
Instr instr = opcode | fmt | (ft.code() << kFtShift) | (fs.code() << kFsShift)
| (fd.code() << kFdShift) | func;
emit(instr);
@@ -666,7 +762,7 @@ void Assembler::GenInstrRegister(Opcode opcode,
FPURegister fd,
SecondaryField func) {
ASSERT(fd.is_valid() && fs.is_valid() && rt.is_valid());
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
Instr instr = opcode | fmt | (rt.code() << kRtShift)
| (fs.code() << kFsShift) | (fd.code() << kFdShift) | func;
emit(instr);
@@ -679,7 +775,7 @@ void Assembler::GenInstrRegister(Opcode opcode,
FPUControlRegister fs,
SecondaryField func) {
ASSERT(fs.is_valid() && rt.is_valid());
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
Instr instr =
opcode | fmt | (rt.code() << kRtShift) | (fs.code() << kFsShift) | func;
emit(instr);
@@ -714,7 +810,7 @@ void Assembler::GenInstrImmediate(Opcode opcode,
FPURegister ft,
int32_t j) {
ASSERT(rs.is_valid() && ft.is_valid() && (is_int16(j) || is_uint16(j)));
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
Instr instr = opcode | (rs.code() << kRsShift) | (ft.code() << kFtShift)
| (j & kImm16Mask);
emit(instr);
@@ -760,23 +856,28 @@ int32_t Assembler::get_label_entry(int32_t pos, bool next_pool) {
// Returns the next free trampoline entry from the next trampoline pool.
int32_t Assembler::get_trampoline_entry(int32_t pos, bool next_pool) {
int trampoline_count = trampolines_.length();
- int32_t trampoline_entry = 0;
+ int32_t trampoline_entry = kInvalidSlotPos;
ASSERT(trampoline_count > 0);
- if (next_pool) {
- for (int i = 0; i < trampoline_count; i++) {
- if (trampolines_[i].start() > pos) {
- trampoline_entry = trampolines_[i].take_slot();
- break;
+ if (!internal_trampoline_exception_) {
+ if (next_pool) {
+ for (int i = 0; i < trampoline_count; i++) {
+ if (trampolines_[i].start() > pos) {
+ trampoline_entry = trampolines_[i].take_slot();
+ break;
+ }
}
- }
- } else { // Caller needs a trampoline entry from the previous pool.
- for (int i = trampoline_count-1; i >= 0; i--) {
- if (trampolines_[i].end() < pos) {
- trampoline_entry = trampolines_[i].take_slot();
- break;
+ } else { // Caller needs a trampoline entry from the previous pool.
+ for (int i = trampoline_count-1; i >= 0; i--) {
+ if (trampolines_[i].end() < pos) {
+ trampoline_entry = trampolines_[i].take_slot();
+ break;
+ }
}
}
+ if (kInvalidSlotPos == trampoline_entry) {
+ internal_trampoline_exception_ = true;
+ }
}
return trampoline_entry;
}
@@ -792,6 +893,10 @@ int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
if (dist > kMaxBranchOffset) {
do {
int32_t trampoline_pos = get_trampoline_entry(target_pos);
+ if (kInvalidSlotPos == trampoline_pos) {
+ // Internal error.
+ return 0;
+ }
ASSERT((trampoline_pos - target_pos) > 0);
ASSERT((trampoline_pos - target_pos) <= kMaxBranchOffset);
target_at_put(trampoline_pos, target_pos);
@@ -801,6 +906,10 @@ int32_t Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
} else if (dist < -kMaxBranchOffset) {
do {
int32_t trampoline_pos = get_trampoline_entry(target_pos, false);
+ if (kInvalidSlotPos == trampoline_pos) {
+ // Internal error.
+ return 0;
+ }
ASSERT((target_pos - trampoline_pos) > 0);
ASSERT((target_pos - trampoline_pos) <= kMaxBranchOffset);
target_at_put(trampoline_pos, target_pos);
@@ -979,157 +1088,6 @@ void Assembler::addu(Register rd, Register rs, Register rt) {
void Assembler::addiu(Register rd, Register rs, int32_t j) {
GenInstrImmediate(ADDIU, rs, rd, j);
-
- // Eliminate pattern: push(r), pop().
- // addiu(sp, sp, Operand(-kPointerSize));
- // sw(src, MemOperand(sp, 0);
- // addiu(sp, sp, Operand(kPointerSize));
- // Both instructions can be eliminated.
- if (can_peephole_optimize(3) &&
- // Pattern.
- instr_at(pc_ - 1 * kInstrSize) == kPopInstruction &&
- (instr_at(pc_ - 2 * kInstrSize) & ~kRtMask) == kPushRegPattern &&
- (instr_at(pc_ - 3 * kInstrSize)) == kPushInstruction) {
- pc_ -= 3 * kInstrSize;
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push(reg)/pop() eliminated\n", pc_offset());
- }
- }
-
- // Eliminate pattern: push(ry), pop(rx).
- // addiu(sp, sp, -kPointerSize)
- // sw(ry, MemOperand(sp, 0)
- // lw(rx, MemOperand(sp, 0)
- // addiu(sp, sp, kPointerSize);
- // Both instructions can be eliminated if ry = rx.
- // If ry != rx, a register copy from ry to rx is inserted
- // after eliminating the push and the pop instructions.
- if (can_peephole_optimize(4)) {
- Instr pre_push_sp_set = instr_at(pc_ - 4 * kInstrSize);
- Instr push_instr = instr_at(pc_ - 3 * kInstrSize);
- Instr pop_instr = instr_at(pc_ - 2 * kInstrSize);
- Instr post_pop_sp_set = instr_at(pc_ - 1 * kInstrSize);
-
- if (IsPush(push_instr) &&
- IsPop(pop_instr) && pre_push_sp_set == kPushInstruction &&
- post_pop_sp_set == kPopInstruction) {
- if ((pop_instr & kRtMask) != (push_instr & kRtMask)) {
- // For consecutive push and pop on different registers,
- // we delete both the push & pop and insert a register move.
- // push ry, pop rx --> mov rx, ry.
- Register reg_pushed, reg_popped;
- reg_pushed = GetRt(push_instr);
- reg_popped = GetRt(pop_instr);
- pc_ -= 4 * kInstrSize;
- // Insert a mov instruction, which is better than a pair of push & pop.
- or_(reg_popped, reg_pushed, zero_reg);
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop (diff reg) replaced by a reg move\n",
- pc_offset());
- }
- } else {
- // For consecutive push and pop on the same register,
- // both the push and the pop can be deleted.
- pc_ -= 4 * kInstrSize;
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop (same reg) eliminated\n", pc_offset());
- }
- }
- }
- }
-
- if (can_peephole_optimize(5)) {
- Instr pre_push_sp_set = instr_at(pc_ - 5 * kInstrSize);
- Instr mem_write_instr = instr_at(pc_ - 4 * kInstrSize);
- Instr lw_instr = instr_at(pc_ - 3 * kInstrSize);
- Instr mem_read_instr = instr_at(pc_ - 2 * kInstrSize);
- Instr post_pop_sp_set = instr_at(pc_ - 1 * kInstrSize);
-
- if (IsPush(mem_write_instr) &&
- pre_push_sp_set == kPushInstruction &&
- IsPop(mem_read_instr) &&
- post_pop_sp_set == kPopInstruction) {
- if ((IsLwRegFpOffset(lw_instr) ||
- IsLwRegFpNegOffset(lw_instr))) {
- if ((mem_write_instr & kRtMask) ==
- (mem_read_instr & kRtMask)) {
- // Pattern: push & pop from/to same register,
- // with a fp+offset lw in between.
- //
- // The following:
- // addiu sp, sp, -4
- // sw rx, [sp, #0]!
- // lw rz, [fp, #-24]
- // lw rx, [sp, 0],
- // addiu sp, sp, 4
- //
- // Becomes:
- // if(rx == rz)
- // delete all
- // else
- // lw rz, [fp, #-24]
-
- if ((mem_write_instr & kRtMask) == (lw_instr & kRtMask)) {
- pc_ -= 5 * kInstrSize;
- } else {
- pc_ -= 5 * kInstrSize;
- // Reinsert back the lw rz.
- emit(lw_instr);
- }
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop -dead ldr fp+offset in middle\n", pc_offset());
- }
- } else {
- // Pattern: push & pop from/to different registers
- // with a fp + offset lw in between.
- //
- // The following:
- // addiu sp, sp ,-4
- // sw rx, [sp, 0]
- // lw rz, [fp, #-24]
- // lw ry, [sp, 0]
- // addiu sp, sp, 4
- //
- // Becomes:
- // if(ry == rz)
- // mov ry, rx;
- // else if(rx != rz)
- // lw rz, [fp, #-24]
- // mov ry, rx
- // else if((ry != rz) || (rx == rz)) becomes:
- // mov ry, rx
- // lw rz, [fp, #-24]
-
- Register reg_pushed, reg_popped;
- if ((mem_read_instr & kRtMask) == (lw_instr & kRtMask)) {
- reg_pushed = GetRt(mem_write_instr);
- reg_popped = GetRt(mem_read_instr);
- pc_ -= 5 * kInstrSize;
- or_(reg_popped, reg_pushed, zero_reg); // Move instruction.
- } else if ((mem_write_instr & kRtMask)
- != (lw_instr & kRtMask)) {
- reg_pushed = GetRt(mem_write_instr);
- reg_popped = GetRt(mem_read_instr);
- pc_ -= 5 * kInstrSize;
- emit(lw_instr);
- or_(reg_popped, reg_pushed, zero_reg); // Move instruction.
- } else if (((mem_read_instr & kRtMask)
- != (lw_instr & kRtMask)) ||
- ((mem_write_instr & kRtMask)
- == (lw_instr & kRtMask)) ) {
- reg_pushed = GetRt(mem_write_instr);
- reg_popped = GetRt(mem_read_instr);
- pc_ -= 5 * kInstrSize;
- or_(reg_popped, reg_pushed, zero_reg); // Move instruction.
- emit(lw_instr);
- }
- if (FLAG_print_peephole_optimization) {
- PrintF("%x push/pop (ldr fp+off in middle)\n", pc_offset());
- }
- }
- }
- }
- }
}
@@ -1317,54 +1275,6 @@ void Assembler::lw(Register rd, const MemOperand& rs) {
LoadRegPlusOffsetToAt(rs);
GenInstrImmediate(LW, at, rd, 0); // Equiv to lw(rd, MemOperand(at, 0));
}
-
- if (can_peephole_optimize(2)) {
- Instr sw_instr = instr_at(pc_ - 2 * kInstrSize);
- Instr lw_instr = instr_at(pc_ - 1 * kInstrSize);
-
- if ((IsSwRegFpOffset(sw_instr) &&
- IsLwRegFpOffset(lw_instr)) ||
- (IsSwRegFpNegOffset(sw_instr) &&
- IsLwRegFpNegOffset(lw_instr))) {
- if ((lw_instr & kLwSwInstrArgumentMask) ==
- (sw_instr & kLwSwInstrArgumentMask)) {
- // Pattern: Lw/sw same fp+offset, same register.
- //
- // The following:
- // sw rx, [fp, #-12]
- // lw rx, [fp, #-12]
- //
- // Becomes:
- // sw rx, [fp, #-12]
-
- pc_ -= 1 * kInstrSize;
- if (FLAG_print_peephole_optimization) {
- PrintF("%x sw/lw (fp + same offset), same reg\n", pc_offset());
- }
- } else if ((lw_instr & kLwSwOffsetMask) ==
- (sw_instr & kLwSwOffsetMask)) {
- // Pattern: Lw/sw same fp+offset, different register.
- //
- // The following:
- // sw rx, [fp, #-12]
- // lw ry, [fp, #-12]
- //
- // Becomes:
- // sw rx, [fp, #-12]
- // mov ry, rx
-
- Register reg_stored, reg_loaded;
- reg_stored = GetRt(sw_instr);
- reg_loaded = GetRt(lw_instr);
- pc_ -= 1 * kInstrSize;
- // Insert a mov instruction, which is better than lw.
- or_(reg_loaded, reg_stored, zero_reg); // Move instruction.
- if (FLAG_print_peephole_optimization) {
- PrintF("%x sw/lw (fp + same offset), diff reg \n", pc_offset());
- }
- }
- }
- }
}
@@ -1405,23 +1315,6 @@ void Assembler::sw(Register rd, const MemOperand& rs) {
LoadRegPlusOffsetToAt(rs);
GenInstrImmediate(SW, at, rd, 0); // Equiv to sw(rd, MemOperand(at, 0));
}
-
- // Eliminate pattern: pop(), push(r).
- // addiu sp, sp, Operand(kPointerSize);
- // addiu sp, sp, Operand(-kPointerSize);
- // -> sw r, MemOpernad(sp, 0);
- if (can_peephole_optimize(3) &&
- // Pattern.
- instr_at(pc_ - 1 * kInstrSize) ==
- (kPushRegPattern | (rd.code() << kRtShift)) &&
- instr_at(pc_ - 2 * kInstrSize) == kPushInstruction &&
- instr_at(pc_ - 3 * kInstrSize) == kPopInstruction) {
- pc_ -= 3 * kInstrSize;
- GenInstrImmediate(SW, rs.rm(), rd, rs.offset_);
- if (FLAG_print_peephole_optimization) {
- PrintF("%x pop()/push(reg) eliminated\n", pc_offset());
- }
- }
}
@@ -1545,14 +1438,14 @@ void Assembler::movn(Register rd, Register rs, Register rt) {
void Assembler::movt(Register rd, Register rs, uint16_t cc) {
Register rt;
- rt.code_ = (cc & 0x0003) << 2 | 1;
+ rt.code_ = (cc & 0x0007) << 2 | 1;
GenInstrRegister(SPECIAL, rs, rt, rd, 0, MOVCI);
}
void Assembler::movf(Register rd, Register rs, uint16_t cc) {
Register rt;
- rt.code_ = (cc & 0x0003) << 2 | 0;
+ rt.code_ = (cc & 0x0007) << 2 | 0;
GenInstrRegister(SPECIAL, rs, rt, rd, 0, MOVCI);
}
@@ -1816,7 +1709,7 @@ void Assembler::cvt_d_s(FPURegister fd, FPURegister fs) {
// Conditions.
void Assembler::c(FPUCondition cond, SecondaryField fmt,
FPURegister fs, FPURegister ft, uint16_t cc) {
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
ASSERT(is_uint3(cc));
ASSERT((fmt & ~(31 << kRsShift)) == 0);
Instr instr = COP1 | fmt | ft.code() << 16 | fs.code() << kFsShift
@@ -1827,7 +1720,7 @@ void Assembler::c(FPUCondition cond, SecondaryField fmt,
void Assembler::fcmp(FPURegister src1, const double src2,
FPUCondition cond) {
- ASSERT(isolate()->cpu_features()->IsSupported(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
ASSERT(src2 == 0.0);
mtc1(zero_reg, f14);
cvt_d_w(f14, f14);
@@ -1836,7 +1729,7 @@ void Assembler::fcmp(FPURegister src1, const double src2,
void Assembler::bc1f(int16_t offset, uint16_t cc) {
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
ASSERT(is_uint3(cc));
Instr instr = COP1 | BC1 | cc << 18 | 0 << 16 | (offset & kImm16Mask);
emit(instr);
@@ -1844,7 +1737,7 @@ void Assembler::bc1f(int16_t offset, uint16_t cc) {
void Assembler::bc1t(int16_t offset, uint16_t cc) {
- ASSERT(isolate()->cpu_features()->IsEnabled(FPU));
+ ASSERT(CpuFeatures::IsEnabled(FPU));
ASSERT(is_uint3(cc));
Instr instr = COP1 | BC1 | cc << 18 | 1 << 16 | (offset & kImm16Mask);
emit(instr);
@@ -1949,7 +1842,14 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
return;
}
ASSERT(buffer_space() >= kMaxRelocSize); // Too late to grow buffer here.
- reloc_info_writer.Write(&rinfo);
+ if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
+ ASSERT(ast_id_for_reloc_info_ != kNoASTId);
+ RelocInfo reloc_info_with_ast_id(pc_, rmode, ast_id_for_reloc_info_);
+ ast_id_for_reloc_info_ = kNoASTId;
+ reloc_info_writer.Write(&reloc_info_with_ast_id);
+ } else {
+ reloc_info_writer.Write(&rinfo);
+ }
}
}
@@ -2017,72 +1917,39 @@ void Assembler::CheckTrampolinePool(bool force_emit) {
Address Assembler::target_address_at(Address pc) {
Instr instr1 = instr_at(pc);
Instr instr2 = instr_at(pc + kInstrSize);
- // Check we have 2 instructions generated by li.
- ASSERT(((instr1 & kOpcodeMask) == LUI && (instr2 & kOpcodeMask) == ORI) ||
- ((instr1 == nopInstr) && ((instr2 & kOpcodeMask) == ADDI ||
- (instr2 & kOpcodeMask) == ORI ||
- (instr2 & kOpcodeMask) == LUI)));
- // Interpret these 2 instructions.
- if (instr1 == nopInstr) {
- if ((instr2 & kOpcodeMask) == ADDI) {
- return reinterpret_cast<Address>(((instr2 & kImm16Mask) << 16) >> 16);
- } else if ((instr2 & kOpcodeMask) == ORI) {
- return reinterpret_cast<Address>(instr2 & kImm16Mask);
- } else if ((instr2 & kOpcodeMask) == LUI) {
- return reinterpret_cast<Address>((instr2 & kImm16Mask) << 16);
- }
- } else if ((instr1 & kOpcodeMask) == LUI && (instr2 & kOpcodeMask) == ORI) {
- // 32 bit value.
+ // Interpret 2 instructions generated by li: lui/ori
+ if ((GetOpcodeField(instr1) == LUI) && (GetOpcodeField(instr2) == ORI)) {
+ // Assemble the 32 bit value.
return reinterpret_cast<Address>(
- (instr1 & kImm16Mask) << 16 | (instr2 & kImm16Mask));
+ (GetImmediate16(instr1) << 16) | GetImmediate16(instr2));
}
- // We should never get here.
+ // We should never get here, force a bad address if we do.
UNREACHABLE();
return (Address)0x0;
}
void Assembler::set_target_address_at(Address pc, Address target) {
- // On MIPS we need to patch the code to generate.
+ // On MIPS we patch the address into lui/ori instruction pair.
- // First check we have a li.
+ // First check we have an li (lui/ori pair).
Instr instr2 = instr_at(pc + kInstrSize);
#ifdef DEBUG
Instr instr1 = instr_at(pc);
// Check we have indeed the result from a li with MustUseReg true.
- CHECK(((instr1 & kOpcodeMask) == LUI && (instr2 & kOpcodeMask) == ORI) ||
- ((instr1 == 0) && ((instr2 & kOpcodeMask)== ADDIU ||
- (instr2 & kOpcodeMask)== ORI ||
- (instr2 & kOpcodeMask)== LUI)));
+ CHECK((GetOpcodeField(instr1) == LUI && GetOpcodeField(instr2) == ORI));
#endif
- uint32_t rt_code = (instr2 & kRtFieldMask);
+ uint32_t rt_code = GetRtField(instr2);
uint32_t* p = reinterpret_cast<uint32_t*>(pc);
uint32_t itarget = reinterpret_cast<uint32_t>(target);
- if (is_int16(itarget)) {
- // nop.
- // addiu rt zero_reg j.
- *p = nopInstr;
- *(p+1) = ADDIU | rt_code | (itarget & kImm16Mask);
- } else if (!(itarget & kHiMask)) {
- // nop.
- // ori rt zero_reg j.
- *p = nopInstr;
- *(p+1) = ORI | rt_code | (itarget & kImm16Mask);
- } else if (!(itarget & kImm16Mask)) {
- // nop.
- // lui rt (kHiMask & itarget) >> kLuiShift.
- *p = nopInstr;
- *(p+1) = LUI | rt_code | ((itarget & kHiMask) >> kLuiShift);
- } else {
- // lui rt (kHiMask & itarget) >> kLuiShift.
- // ori rt rt, (kImm16Mask & itarget).
- *p = LUI | rt_code | ((itarget & kHiMask) >> kLuiShift);
- *(p+1) = ORI | rt_code | (rt_code << 5) | (itarget & kImm16Mask);
- }
+ // lui rt, high-16.
+ // ori rt rt, low-16.
+ *p = LUI | rt_code | ((itarget & kHiMask) >> kLuiShift);
+ *(p+1) = ORI | rt_code | (rt_code << 5) | (itarget & kImm16Mask);
CPU::FlushICache(pc, 2 * sizeof(int32_t));
}
diff --git a/src/mips/assembler-mips.h b/src/mips/assembler-mips.h
index 5a6e2715..a1673934 100644
--- a/src/mips/assembler-mips.h
+++ b/src/mips/assembler-mips.h
@@ -30,7 +30,7 @@
// The original source code covered by the above license above has been
// modified significantly by Google Inc.
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
#ifndef V8_MIPS_ASSEMBLER_MIPS_H_
@@ -67,12 +67,13 @@ namespace internal {
// -----------------------------------------------------------------------------
-// Implementation of Register and FPURegister
+// Implementation of Register and FPURegister.
// Core register.
struct Register {
static const int kNumRegisters = v8::internal::kNumRegisters;
- static const int kNumAllocatableRegisters = 14; // v0 through t7
+ static const int kNumAllocatableRegisters = 14; // v0 through t7.
+ static const int kSizeInBytes = 4;
static int ToAllocationIndex(Register reg) {
return reg.code() - 2; // zero_reg and 'at' are skipped.
@@ -267,9 +268,6 @@ const FPURegister f31 = { 31 };
// FPU (coprocessor 1) control registers.
// Currently only FCSR (#31) is implemented.
struct FPUControlRegister {
- static const int kFCSRRegister = 31;
- static const int kInvalidFPUControlRegister = -1;
-
bool is_valid() const { return code_ == kFCSRRegister; }
bool is(FPUControlRegister creg) const { return code_ == creg.code_; }
int code() const {
@@ -288,7 +286,7 @@ struct FPUControlRegister {
int code_;
};
-const FPUControlRegister no_fpucreg = { -1 };
+const FPUControlRegister no_fpucreg = { kInvalidFPUControlRegister };
const FPUControlRegister FCSR = { kFCSRRegister };
@@ -318,7 +316,7 @@ class Operand BASE_EMBEDDED {
private:
Register rm_;
- int32_t imm32_; // Valid if rm_ == no_reg
+ int32_t imm32_; // Valid if rm_ == no_reg.
RelocInfo::Mode rmode_;
friend class Assembler;
@@ -342,58 +340,98 @@ class MemOperand : public Operand {
// CpuFeatures keeps track of which features are supported by the target CPU.
// Supported features must be enabled by a Scope before use.
-class CpuFeatures {
+class CpuFeatures : public AllStatic {
public:
// Detect features of the target CPU. Set safe defaults if the serializer
// is enabled (snapshots must be portable).
- void Probe(bool portable);
+ static void Probe();
// Check whether a feature is supported by the target CPU.
- bool IsSupported(CpuFeature f) const {
+ static bool IsSupported(CpuFeature f) {
+ ASSERT(initialized_);
if (f == FPU && !FLAG_enable_fpu) return false;
return (supported_ & (1u << f)) != 0;
}
+
+#ifdef DEBUG
// Check whether a feature is currently enabled.
- bool IsEnabled(CpuFeature f) const {
- return (enabled_ & (1u << f)) != 0;
+ static bool IsEnabled(CpuFeature f) {
+ ASSERT(initialized_);
+ Isolate* isolate = Isolate::UncheckedCurrent();
+ if (isolate == NULL) {
+ // When no isolate is available, work as if we're running in
+ // release mode.
+ return IsSupported(f);
+ }
+ unsigned enabled = static_cast<unsigned>(isolate->enabled_cpu_features());
+ return (enabled & (1u << f)) != 0;
}
+#endif
// Enable a specified feature within a scope.
class Scope BASE_EMBEDDED {
#ifdef DEBUG
public:
- explicit Scope(CpuFeature f)
- : cpu_features_(Isolate::Current()->cpu_features()),
- isolate_(Isolate::Current()) {
- ASSERT(cpu_features_->IsSupported(f));
+ explicit Scope(CpuFeature f) {
+ unsigned mask = 1u << f;
+ ASSERT(CpuFeatures::IsSupported(f));
ASSERT(!Serializer::enabled() ||
- (cpu_features_->found_by_runtime_probing_ & (1u << f)) == 0);
- old_enabled_ = cpu_features_->enabled_;
- cpu_features_->enabled_ |= 1u << f;
+ (CpuFeatures::found_by_runtime_probing_ & mask) == 0);
+ isolate_ = Isolate::UncheckedCurrent();
+ old_enabled_ = 0;
+ if (isolate_ != NULL) {
+ old_enabled_ = static_cast<unsigned>(isolate_->enabled_cpu_features());
+ isolate_->set_enabled_cpu_features(old_enabled_ | mask);
+ }
}
~Scope() {
- ASSERT_EQ(Isolate::Current(), isolate_);
- cpu_features_->enabled_ = old_enabled_;
- }
+ ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
+ if (isolate_ != NULL) {
+ isolate_->set_enabled_cpu_features(old_enabled_);
+ }
+ }
private:
- unsigned old_enabled_;
- CpuFeatures* cpu_features_;
Isolate* isolate_;
+ unsigned old_enabled_;
#else
public:
explicit Scope(CpuFeature f) {}
#endif
};
- private:
- CpuFeatures();
+ class TryForceFeatureScope BASE_EMBEDDED {
+ public:
+ explicit TryForceFeatureScope(CpuFeature f)
+ : old_supported_(CpuFeatures::supported_) {
+ if (CanForce()) {
+ CpuFeatures::supported_ |= (1u << f);
+ }
+ }
- unsigned supported_;
- unsigned enabled_;
- unsigned found_by_runtime_probing_;
+ ~TryForceFeatureScope() {
+ if (CanForce()) {
+ CpuFeatures::supported_ = old_supported_;
+ }
+ }
- friend class Isolate;
+ private:
+ static bool CanForce() {
+ // It's only safe to temporarily force support of CPU features
+ // when there's only a single isolate, which is guaranteed when
+ // the serializer is enabled.
+ return Serializer::enabled();
+ }
+
+ const unsigned old_supported_;
+ };
+
+ private:
+#ifdef DEBUG
+ static bool initialized_;
+#endif
+ static unsigned supported_;
+ static unsigned found_by_runtime_probing_;
DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
};
@@ -414,7 +452,7 @@ class Assembler : public AssemblerBase {
// for code generation and assumes its size to be buffer_size. If the buffer
// is too small, a fatal error occurs. No deallocation of the buffer is done
// upon destruction of the assembler.
- Assembler(void* buffer, int buffer_size);
+ Assembler(Isolate* isolate, void* buffer, int buffer_size);
~Assembler();
// Overrides the default provided by FLAG_debug_code.
@@ -439,10 +477,10 @@ class Assembler : public AssemblerBase {
//
// Note: The same Label can be used for forward and backward branches
// but it may be bound only once.
- void bind(Label* L); // binds an unbound label L to the current code position
+ void bind(Label* L); // Binds an unbound label L to current code position.
- // Returns the branch offset to the given label from the current code position
- // Links the label to the current position if it is still unbound
+ // Returns the branch offset to the given label from the current code
+ // position. Links the label to the current position if it is still unbound.
// Manages the jump elimination optimization if the second parameter is true.
int32_t branch_offset(Label* L, bool jump_elimination_allowed);
int32_t shifted_branch_offset(Label* L, bool jump_elimination_allowed) {
@@ -541,14 +579,14 @@ class Assembler : public AssemblerBase {
FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED
};
- // type == 0 is the default non-marking type.
+ // Type == 0 is the default non-marking type.
void nop(unsigned int type = 0) {
ASSERT(type < 32);
sll(zero_reg, zero_reg, type, true);
}
- //------- Branch and jump instructions --------
+ // --------Branch-and-jump-instructions----------
// We don't use likely variant of instructions.
void b(int16_t offset);
void b(Label* L) { b(branch_offset(L, false)>>2); }
@@ -571,7 +609,7 @@ class Assembler : public AssemblerBase {
}
// Never use the int16_t b(l)cond version with a branch offset
- // instead of using the Label* version. See Twiki for infos.
+ // instead of using the Label* version.
// Jump targets must be in the current 256 MB-aligned region. ie 28 bits.
void j(int32_t target);
@@ -761,6 +799,10 @@ class Assembler : public AssemblerBase {
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
+ // Record the AST id of the CallIC being compiled, so that it can be placed
+ // in the relocation information.
+ void RecordAstId(unsigned ast_id) { ast_id_for_reloc_info_ = ast_id; }
+
// Record a comment relocation entry that can be used by a disassembler.
// Use --code-comments to enable.
void RecordComment(const char* msg);
@@ -774,12 +816,6 @@ class Assembler : public AssemblerBase {
PositionsRecorder* positions_recorder() { return &positions_recorder_; }
- bool can_peephole_optimize(int instructions) {
- if (!allow_peephole_optimization_) return false;
- if (last_bound_pos_ > pc_offset() - instructions * kInstrSize) return false;
- return reloc_info_writer.last_pc() <= pc_ - instructions * kInstrSize;
- }
-
// Postpone the generation of the trampoline pool for the specified number of
// instructions.
void BlockTrampolinePoolFor(int instructions);
@@ -804,6 +840,8 @@ class Assembler : public AssemblerBase {
// Check if an instruction is a branch of some kind.
static bool IsBranch(Instr instr);
+ static bool IsBeq(Instr instr);
+ static bool IsBne(Instr instr);
static bool IsNop(Instr instr, unsigned int type);
static bool IsPop(Instr instr);
@@ -813,7 +851,21 @@ class Assembler : public AssemblerBase {
static bool IsLwRegFpNegOffset(Instr instr);
static bool IsSwRegFpNegOffset(Instr instr);
- static Register GetRt(Instr instr);
+ static Register GetRtReg(Instr instr);
+ static Register GetRsReg(Instr instr);
+ static Register GetRdReg(Instr instr);
+
+ static uint32_t GetRt(Instr instr);
+ static uint32_t GetRtField(Instr instr);
+ static uint32_t GetRs(Instr instr);
+ static uint32_t GetRsField(Instr instr);
+ static uint32_t GetRd(Instr instr);
+ static uint32_t GetRdField(Instr instr);
+ static uint32_t GetSa(Instr instr);
+ static uint32_t GetSaField(Instr instr);
+ static uint32_t GetOpcodeField(Instr instr);
+ static uint32_t GetImmediate16(Instr instr);
+ static uint32_t GetLabelConst(Instr instr);
static int32_t GetBranchOffset(Instr instr);
static bool IsLw(Instr instr);
@@ -825,9 +877,16 @@ class Assembler : public AssemblerBase {
static bool IsAddImmediate(Instr instr);
static Instr SetAddImmediateOffset(Instr instr, int16_t offset);
+ static bool IsAndImmediate(Instr instr);
+
void CheckTrampolinePool(bool force_emit = false);
protected:
+ // Relocation for a type-recording IC has the AST id added to it. This
+ // member variable is a way to pass the information from the call site to
+ // the relocation info.
+ unsigned ast_id_for_reloc_info_;
+
bool emit_debug_code() const { return emit_debug_code_; }
int32_t buffer_space() const { return reloc_info_writer.pos() - pc_; }
@@ -861,6 +920,10 @@ class Assembler : public AssemblerBase {
return trampoline_pool_blocked_nesting_ > 0;
}
+ bool has_exception() const {
+ return internal_trampoline_exception_;
+ }
+
private:
// Code buffer:
// The buffer into which code and relocation info are generated.
@@ -1005,10 +1068,18 @@ class Assembler : public AssemblerBase {
return end_;
}
int take_slot() {
- int trampoline_slot = next_slot_;
- ASSERT(free_slot_count_ > 0);
- free_slot_count_--;
- next_slot_ += 2 * kInstrSize;
+ int trampoline_slot = kInvalidSlotPos;
+ if (free_slot_count_ <= 0) {
+ // We have run out of space on trampolines.
+ // Make sure we fail in debug mode, so we become aware of each case
+ // when this happens.
+ ASSERT(0);
+ // Internal exception will be caught.
+ } else {
+ trampoline_slot = next_slot_;
+ free_slot_count_--;
+ next_slot_ += 2*kInstrSize;
+ }
return trampoline_slot;
}
int take_label() {
@@ -1038,8 +1109,10 @@ class Assembler : public AssemblerBase {
static const int kMaxBranchOffset = (1 << (18 - 1)) - 1;
static const int kMaxDistBetweenPools =
kMaxBranchOffset - 2 * kTrampolineSize;
+ static const int kInvalidSlotPos = -1;
List<Trampoline> trampolines_;
+ bool internal_trampoline_exception_;
friend class RegExpMacroAssemblerMIPS;
friend class RelocInfo;
@@ -1047,7 +1120,6 @@ class Assembler : public AssemblerBase {
friend class BlockTrampolinePoolScope;
PositionsRecorder positions_recorder_;
- bool allow_peephole_optimization_;
bool emit_debug_code_;
friend class PositionsRecorder;
friend class EnsureSpace;
diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc
index b4bab8ef..e22259d7 100644
--- a/src/mips/builtins-mips.cc
+++ b/src/mips/builtins-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -31,7 +31,7 @@
#if defined(V8_TARGET_ARCH_MIPS)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "deoptimizer.h"
#include "full-codegen.h"
@@ -47,97 +47,1577 @@ namespace internal {
void Builtins::Generate_Adaptor(MacroAssembler* masm,
CFunctionId id,
BuiltinExtraArguments extra_args) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments excluding receiver
+ // -- a1 : called function (only guaranteed when
+ // -- extra_args requires it)
+ // -- cp : context
+ // -- sp[0] : last argument
+ // -- ...
+ // -- sp[4 * (argc - 1)] : first argument
+ // -- sp[4 * agrc] : receiver
+ // -----------------------------------
+
+ // Insert extra arguments.
+ int num_extra_args = 0;
+ if (extra_args == NEEDS_CALLED_FUNCTION) {
+ num_extra_args = 1;
+ __ push(a1);
+ } else {
+ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ }
+
+ // JumpToExternalReference expects a0 to contain the number of arguments
+ // including the receiver and the extra arguments.
+ __ Addu(a0, a0, Operand(num_extra_args + 1));
+ __ JumpToExternalReference(ExternalReference(id, masm->isolate()));
+}
+
+
+// Load the built-in Array function from the current context.
+static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) {
+ // Load the global context.
+
+ __ lw(result, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ lw(result,
+ FieldMemOperand(result, GlobalObject::kGlobalContextOffset));
+ // Load the Array function from the global context.
+ __ lw(result,
+ MemOperand(result,
+ Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
+}
+
+
+// This constant has the same value as JSArray::kPreallocatedArrayElements and
+// if JSArray::kPreallocatedArrayElements is changed handling of loop unfolding
+// below should be reconsidered.
+static const int kLoopUnfoldLimit = 4;
+
+
+// Allocate an empty JSArray. The allocated array is put into the result
+// register. An elements backing store is allocated with size initial_capacity
+// and filled with the hole values.
+static void AllocateEmptyJSArray(MacroAssembler* masm,
+ Register array_function,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ int initial_capacity,
+ Label* gc_required) {
+ ASSERT(initial_capacity > 0);
+ // Load the initial map from the array function.
+ __ lw(scratch1, FieldMemOperand(array_function,
+ JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Allocate the JSArray object together with space for a fixed array with the
+ // requested elements.
+ int size = JSArray::kSize + FixedArray::SizeFor(initial_capacity);
+ __ AllocateInNewSpace(size,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+ // Allocated the JSArray. Now initialize the fields except for the elements
+ // array.
+ // result: JSObject
+ // scratch1: initial map
+ // scratch2: start of next object
+ __ sw(scratch1, FieldMemOperand(result, JSObject::kMapOffset));
+ __ LoadRoot(scratch1, Heap::kEmptyFixedArrayRootIndex);
+ __ sw(scratch1, FieldMemOperand(result, JSArray::kPropertiesOffset));
+ // Field JSArray::kElementsOffset is initialized later.
+ __ mov(scratch3, zero_reg);
+ __ sw(scratch3, FieldMemOperand(result, JSArray::kLengthOffset));
+
+ // Calculate the location of the elements array and set elements array member
+ // of the JSArray.
+ // result: JSObject
+ // scratch2: start of next object
+ __ Addu(scratch1, result, Operand(JSArray::kSize));
+ __ sw(scratch1, FieldMemOperand(result, JSArray::kElementsOffset));
+
+ // Clear the heap tag on the elements array.
+ __ And(scratch1, scratch1, Operand(~kHeapObjectTagMask));
+
+ // Initialize the FixedArray and fill it with holes. FixedArray length is
+ // stored as a smi.
+ // result: JSObject
+ // scratch1: elements array (untagged)
+ // scratch2: start of next object
+ __ LoadRoot(scratch3, Heap::kFixedArrayMapRootIndex);
+ ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset);
+ __ sw(scratch3, MemOperand(scratch1));
+ __ Addu(scratch1, scratch1, kPointerSize);
+ __ li(scratch3, Operand(Smi::FromInt(initial_capacity)));
+ ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset);
+ __ sw(scratch3, MemOperand(scratch1));
+ __ Addu(scratch1, scratch1, kPointerSize);
+
+ // Fill the FixedArray with the hole value.
+ ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize);
+ ASSERT(initial_capacity <= kLoopUnfoldLimit);
+ __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex);
+ for (int i = 0; i < initial_capacity; i++) {
+ __ sw(scratch3, MemOperand(scratch1));
+ __ Addu(scratch1, scratch1, kPointerSize);
+ }
+}
+
+
+// Allocate a JSArray with the number of elements stored in a register. The
+// register array_function holds the built-in Array function and the register
+// array_size holds the size of the array as a smi. The allocated array is put
+// into the result register and beginning and end of the FixedArray elements
+// storage is put into registers elements_array_storage and elements_array_end
+// (see below for when that is not the case). If the parameter fill_with_holes
+// is true the allocated elements backing store is filled with the hole values
+// otherwise it is left uninitialized. When the backing store is filled the
+// register elements_array_storage is scratched.
+static void AllocateJSArray(MacroAssembler* masm,
+ Register array_function, // Array function.
+ Register array_size, // As a smi.
+ Register result,
+ Register elements_array_storage,
+ Register elements_array_end,
+ Register scratch1,
+ Register scratch2,
+ bool fill_with_hole,
+ Label* gc_required) {
+ Label not_empty, allocated;
+
+ // Load the initial map from the array function.
+ __ lw(elements_array_storage,
+ FieldMemOperand(array_function,
+ JSFunction::kPrototypeOrInitialMapOffset));
+
+ // Check whether an empty sized array is requested.
+ __ Branch(&not_empty, ne, array_size, Operand(zero_reg));
+
+ // If an empty array is requested allocate a small elements array anyway. This
+ // keeps the code below free of special casing for the empty array.
+ int size = JSArray::kSize +
+ FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
+ __ AllocateInNewSpace(size,
+ result,
+ elements_array_end,
+ scratch1,
+ gc_required,
+ TAG_OBJECT);
+ __ Branch(&allocated);
+
+ // Allocate the JSArray object together with space for a FixedArray with the
+ // requested number of elements.
+ __ bind(&not_empty);
+ ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
+ __ li(elements_array_end,
+ (JSArray::kSize + FixedArray::kHeaderSize) / kPointerSize);
+ __ sra(scratch1, array_size, kSmiTagSize);
+ __ Addu(elements_array_end, elements_array_end, scratch1);
+ __ AllocateInNewSpace(
+ elements_array_end,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+
+ // Allocated the JSArray. Now initialize the fields except for the elements
+ // array.
+ // result: JSObject
+ // elements_array_storage: initial map
+ // array_size: size of array (smi)
+ __ bind(&allocated);
+ __ sw(elements_array_storage, FieldMemOperand(result, JSObject::kMapOffset));
+ __ LoadRoot(elements_array_storage, Heap::kEmptyFixedArrayRootIndex);
+ __ sw(elements_array_storage,
+ FieldMemOperand(result, JSArray::kPropertiesOffset));
+ // Field JSArray::kElementsOffset is initialized later.
+ __ sw(array_size, FieldMemOperand(result, JSArray::kLengthOffset));
+
+ // Calculate the location of the elements array and set elements array member
+ // of the JSArray.
+ // result: JSObject
+ // array_size: size of array (smi)
+ __ Addu(elements_array_storage, result, Operand(JSArray::kSize));
+ __ sw(elements_array_storage,
+ FieldMemOperand(result, JSArray::kElementsOffset));
+
+ // Clear the heap tag on the elements array.
+ __ And(elements_array_storage,
+ elements_array_storage,
+ Operand(~kHeapObjectTagMask));
+ // Initialize the fixed array and fill it with holes. FixedArray length is
+ // stored as a smi.
+ // result: JSObject
+ // elements_array_storage: elements array (untagged)
+ // array_size: size of array (smi)
+ __ LoadRoot(scratch1, Heap::kFixedArrayMapRootIndex);
+ ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset);
+ __ sw(scratch1, MemOperand(elements_array_storage));
+ __ Addu(elements_array_storage, elements_array_storage, kPointerSize);
+
+ // Length of the FixedArray is the number of pre-allocated elements if
+ // the actual JSArray has length 0 and the size of the JSArray for non-empty
+ // JSArrays. The length of a FixedArray is stored as a smi.
+ ASSERT(kSmiTag == 0);
+ __ li(at, Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements)));
+ __ movz(array_size, at, array_size);
+
+ ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset);
+ __ sw(array_size, MemOperand(elements_array_storage));
+ __ Addu(elements_array_storage, elements_array_storage, kPointerSize);
+
+ // Calculate elements array and elements array end.
+ // result: JSObject
+ // elements_array_storage: elements array element storage
+ // array_size: smi-tagged size of elements array
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ sll(elements_array_end, array_size, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(elements_array_end, elements_array_storage, elements_array_end);
+
+ // Fill the allocated FixedArray with the hole value if requested.
+ // result: JSObject
+ // elements_array_storage: elements array element storage
+ // elements_array_end: start of next object
+ if (fill_with_hole) {
+ Label loop, entry;
+ __ LoadRoot(scratch1, Heap::kTheHoleValueRootIndex);
+ __ Branch(&entry);
+ __ bind(&loop);
+ __ sw(scratch1, MemOperand(elements_array_storage));
+ __ Addu(elements_array_storage, elements_array_storage, kPointerSize);
+
+ __ bind(&entry);
+ __ Branch(&loop, lt, elements_array_storage, Operand(elements_array_end));
+ }
+}
+
+
+// Create a new array for the built-in Array function. This function allocates
+// the JSArray object and the FixedArray elements array and initializes these.
+// If the Array cannot be constructed in native code the runtime is called. This
+// function assumes the following state:
+// a0: argc
+// a1: constructor (built-in Array function)
+// ra: return address
+// sp[0]: last argument
+// This function is used for both construct and normal calls of Array. The only
+// difference between handling a construct call and a normal call is that for a
+// construct call the constructor function in a1 needs to be preserved for
+// entering the generic code. In both cases argc in a0 needs to be preserved.
+// Both registers are preserved by this code so no need to differentiate between
+// construct call and normal call.
+static void ArrayNativeCode(MacroAssembler* masm,
+ Label* call_generic_code) {
+ Counters* counters = masm->isolate()->counters();
+ Label argc_one_or_more, argc_two_or_more;
+
+ // Check for array construction with zero arguments or one.
+ __ Branch(&argc_one_or_more, ne, a0, Operand(zero_reg));
+ // Handle construction of an empty array.
+ AllocateEmptyJSArray(masm,
+ a1,
+ a2,
+ a3,
+ t0,
+ t1,
+ JSArray::kPreallocatedArrayElements,
+ call_generic_code);
+ __ IncrementCounter(counters->array_function_native(), 1, a3, t0);
+ // Setup return value, remove receiver from stack and return.
+ __ mov(v0, a2);
+ __ Addu(sp, sp, Operand(kPointerSize));
+ __ Ret();
+
+ // Check for one argument. Bail out if argument is not smi or if it is
+ // negative.
+ __ bind(&argc_one_or_more);
+ __ Branch(&argc_two_or_more, ne, a0, Operand(1));
+
+ ASSERT(kSmiTag == 0);
+ __ lw(a2, MemOperand(sp)); // Get the argument from the stack.
+ __ And(a3, a2, Operand(kIntptrSignBit | kSmiTagMask));
+ __ Branch(call_generic_code, eq, a3, Operand(zero_reg));
+
+ // Handle construction of an empty array of a certain size. Bail out if size
+ // is too large to actually allocate an elements array.
+ ASSERT(kSmiTag == 0);
+ __ Branch(call_generic_code, ge, a2,
+ Operand(JSObject::kInitialMaxFastElementArray << kSmiTagSize));
+
+ // a0: argc
+ // a1: constructor
+ // a2: array_size (smi)
+ // sp[0]: argument
+ AllocateJSArray(masm,
+ a1,
+ a2,
+ a3,
+ t0,
+ t1,
+ t2,
+ t3,
+ true,
+ call_generic_code);
+ __ IncrementCounter(counters->array_function_native(), 1, a2, t0);
+
+ // Setup return value, remove receiver and argument from stack and return.
+ __ mov(v0, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ // Handle construction of an array from a list of arguments.
+ __ bind(&argc_two_or_more);
+ __ sll(a2, a0, kSmiTagSize); // Convert argc to a smi.
+
+ // a0: argc
+ // a1: constructor
+ // a2: array_size (smi)
+ // sp[0]: last argument
+ AllocateJSArray(masm,
+ a1,
+ a2,
+ a3,
+ t0,
+ t1,
+ t2,
+ t3,
+ false,
+ call_generic_code);
+ __ IncrementCounter(counters->array_function_native(), 1, a2, t2);
+
+ // Fill arguments as array elements. Copy from the top of the stack (last
+ // element) to the array backing store filling it backwards. Note:
+ // elements_array_end points after the backing store.
+ // a0: argc
+ // a3: JSArray
+ // t0: elements_array storage start (untagged)
+ // t1: elements_array_end (untagged)
+ // sp[0]: last argument
+
+ Label loop, entry;
+ __ Branch(&entry);
+ __ bind(&loop);
+ __ pop(a2);
+ __ Addu(t1, t1, -kPointerSize);
+ __ sw(a2, MemOperand(t1));
+ __ bind(&entry);
+ __ Branch(&loop, lt, t0, Operand(t1));
+
+ // Remove caller arguments and receiver from the stack, setup return value and
+ // return.
+ // a0: argc
+ // a3: JSArray
+ // sp[0]: receiver
+ __ Addu(sp, sp, Operand(kPointerSize));
+ __ mov(v0, a3);
+ __ Ret();
}
void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+ Label generic_array_code;
+
+ // Get the Array function.
+ GenerateLoadArrayFunction(masm, a1);
+
+ if (FLAG_debug_code) {
+ // Initial map for the builtin Array functions should be maps.
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ And(t0, a2, Operand(kSmiTagMask));
+ __ Assert(ne, "Unexpected initial map for Array function (1)",
+ t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, t0);
+ __ Assert(eq, "Unexpected initial map for Array function (2)",
+ t0, Operand(MAP_TYPE));
+ }
+
+ // Run the native code for the Array function called as a normal function.
+ ArrayNativeCode(masm, &generic_array_code);
+
+ // Jump to the generic array code if the specialized code cannot handle
+ // the construction.
+ __ bind(&generic_array_code);
+
+ Handle<Code> array_code =
+ masm->isolate()->builtins()->ArrayCodeGeneric();
+ __ Jump(array_code, RelocInfo::CODE_TARGET);
}
void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+ Label generic_constructor;
+
+ if (FLAG_debug_code) {
+ // The array construct code is only set for the builtin and internal
+ // Array functions which always have a map.
+ // Initial map for the builtin Array function should be a map.
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ And(t0, a2, Operand(kSmiTagMask));
+ __ Assert(ne, "Unexpected initial map for Array function (3)",
+ t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, t0);
+ __ Assert(eq, "Unexpected initial map for Array function (4)",
+ t0, Operand(MAP_TYPE));
+ }
+
+ // Run the native code for the Array function called as a constructor.
+ ArrayNativeCode(masm, &generic_constructor);
+
+ // Jump to the generic construct code in case the specialized code cannot
+ // handle the construction.
+ __ bind(&generic_constructor);
+
+ Handle<Code> generic_construct_stub =
+ masm->isolate()->builtins()->JSConstructStubGeneric();
+ __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
}
void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero based)
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->string_ctor_calls(), 1, a2, a3);
+
+ Register function = a1;
+ if (FLAG_debug_code) {
+ __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, a2);
+ __ Assert(eq, "Unexpected String function", function, Operand(a2));
+ }
+
+ // Load the first arguments in a0 and get rid of the rest.
+ Label no_arguments;
+ __ Branch(&no_arguments, eq, a0, Operand(zero_reg));
+ // First args = sp[(argc - 1) * 4].
+ __ Subu(a0, a0, Operand(1));
+ __ sll(a0, a0, kPointerSizeLog2);
+ __ Addu(sp, a0, sp);
+ __ lw(a0, MemOperand(sp));
+ // sp now point to args[0], drop args[0] + receiver.
+ __ Drop(2);
+
+ Register argument = a2;
+ Label not_cached, argument_is_string;
+ NumberToStringStub::GenerateLookupNumberStringCache(
+ masm,
+ a0, // Input.
+ argument, // Result.
+ a3, // Scratch.
+ t0, // Scratch.
+ t1, // Scratch.
+ false, // Is it a Smi?
+ &not_cached);
+ __ IncrementCounter(counters->string_ctor_cached_number(), 1, a3, t0);
+ __ bind(&argument_is_string);
+
+ // ----------- S t a t e -------------
+ // -- a2 : argument converted to string
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -----------------------------------
+
+ Label gc_required;
+ __ AllocateInNewSpace(JSValue::kSize,
+ v0, // Result.
+ a3, // Scratch.
+ t0, // Scratch.
+ &gc_required,
+ TAG_OBJECT);
+
+ // Initialising the String Object.
+ Register map = a3;
+ __ LoadGlobalFunctionInitialMap(function, map, t0);
+ if (FLAG_debug_code) {
+ __ lbu(t0, FieldMemOperand(map, Map::kInstanceSizeOffset));
+ __ Assert(eq, "Unexpected string wrapper instance size",
+ t0, Operand(JSValue::kSize >> kPointerSizeLog2));
+ __ lbu(t0, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset));
+ __ Assert(eq, "Unexpected unused properties of string wrapper",
+ t0, Operand(zero_reg));
+ }
+ __ sw(map, FieldMemOperand(v0, HeapObject::kMapOffset));
+
+ __ LoadRoot(a3, Heap::kEmptyFixedArrayRootIndex);
+ __ sw(a3, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(a3, FieldMemOperand(v0, JSObject::kElementsOffset));
+
+ __ sw(argument, FieldMemOperand(v0, JSValue::kValueOffset));
+
+ // Ensure the object is fully initialized.
+ STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
+
+ __ Ret();
+
+ // The argument was not found in the number to string cache. Check
+ // if it's a string already before calling the conversion builtin.
+ Label convert_argument;
+ __ bind(&not_cached);
+ __ JumpIfSmi(a0, &convert_argument);
+
+ // Is it a String?
+ __ lw(a2, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ ASSERT(kNotStringTag != 0);
+ __ And(t0, a3, Operand(kIsNotStringMask));
+ __ Branch(&convert_argument, ne, t0, Operand(zero_reg));
+ __ mov(argument, a0);
+ __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0);
+ __ Branch(&argument_is_string);
+
+ // Invoke the conversion builtin and put the result into a2.
+ __ bind(&convert_argument);
+ __ push(function); // Preserve the function.
+ __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0);
+ __ EnterInternalFrame();
+ __ push(v0);
+ __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
+ __ LeaveInternalFrame();
+ __ pop(function);
+ __ mov(argument, v0);
+ __ Branch(&argument_is_string);
+
+ // Load the empty string into a2, remove the receiver from the
+ // stack, and jump back to the case where the argument is a string.
+ __ bind(&no_arguments);
+ __ LoadRoot(argument, Heap::kEmptyStringRootIndex);
+ __ Drop(1);
+ __ Branch(&argument_is_string);
+
+ // At this point the argument is already a string. Call runtime to
+ // create a string wrapper.
+ __ bind(&gc_required);
+ __ IncrementCounter(counters->string_ctor_gc_required(), 1, a3, t0);
+ __ EnterInternalFrame();
+ __ push(argument);
+ __ CallRuntime(Runtime::kNewStringWrapper, 1);
+ __ LeaveInternalFrame();
+ __ Ret();
}
void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+
+ Label non_function_call;
+ // Check that the function is not a smi.
+ __ And(t0, a1, Operand(kSmiTagMask));
+ __ Branch(&non_function_call, eq, t0, Operand(zero_reg));
+ // Check that the function is a JSFunction.
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&non_function_call, ne, a2, Operand(JS_FUNCTION_TYPE));
+
+ // Jump to the function-specific construct stub.
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a2, FieldMemOperand(a2, SharedFunctionInfo::kConstructStubOffset));
+ __ Addu(t9, a2, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ Jump(Operand(t9));
+
+ // a0: number of arguments
+ // a1: called object
+ __ bind(&non_function_call);
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc.
+ // Set expected number of arguments to zero (not changing a0).
+ __ mov(a2, zero_reg);
+ __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
+ __ SetCallKind(t1, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
+
+
+static void Generate_JSConstructStubHelper(MacroAssembler* masm,
+ bool is_api_function,
+ bool count_constructions) {
+ // Should never count constructions for api objects.
+ ASSERT(!is_api_function || !count_constructions);
+
+ Isolate* isolate = masm->isolate();
+
+ // ----------- S t a t e -------------
+ // -- a0 : number of arguments
+ // -- a1 : constructor function
+ // -- ra : return address
+ // -- sp[...]: constructor arguments
+ // -----------------------------------
+
+ // Enter a construct frame.
+ __ EnterConstructFrame();
+
+ // Preserve the two incoming parameters on the stack.
+ __ sll(a0, a0, kSmiTagSize); // Tag arguments count.
+ __ MultiPushReversed(a0.bit() | a1.bit());
+
+ // Use t7 to hold undefined, which is used in several places below.
+ __ LoadRoot(t7, Heap::kUndefinedValueRootIndex);
+
+ Label rt_call, allocated;
+ // Try to allocate the object without transitioning into C code. If any of the
+ // preconditions is not met, the code bails out to the runtime call.
+ if (FLAG_inline_new) {
+ Label undo_allocation;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ ExternalReference debug_step_in_fp =
+ ExternalReference::debug_step_in_fp_address(isolate);
+ __ li(a2, Operand(debug_step_in_fp));
+ __ lw(a2, MemOperand(a2));
+ __ Branch(&rt_call, ne, a2, Operand(zero_reg));
+#endif
+
+ // Load the initial map and verify that it is in fact a map.
+ // a1: constructor function
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ And(t0, a2, Operand(kSmiTagMask));
+ __ Branch(&rt_call, eq, t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, t4);
+ __ Branch(&rt_call, ne, t4, Operand(MAP_TYPE));
+
+ // Check that the constructor is not constructing a JSFunction (see comments
+ // in Runtime_NewObject in runtime.cc). In which case the initial map's
+ // instance type would be JS_FUNCTION_TYPE.
+ // a1: constructor function
+ // a2: initial map
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ __ Branch(&rt_call, eq, a3, Operand(JS_FUNCTION_TYPE));
+
+ if (count_constructions) {
+ Label allocate;
+ // Decrease generous allocation count.
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ MemOperand constructor_count =
+ FieldMemOperand(a3, SharedFunctionInfo::kConstructionCountOffset);
+ __ lbu(t0, constructor_count);
+ __ Subu(t0, t0, Operand(1));
+ __ sb(t0, constructor_count);
+ __ Branch(&allocate, ne, t0, Operand(zero_reg));
+
+ __ Push(a1, a2);
+
+ __ push(a1); // Constructor.
+ // The call will replace the stub, so the countdown is only done once.
+ __ CallRuntime(Runtime::kFinalizeInstanceSize, 1);
+
+ __ pop(a2);
+ __ pop(a1);
+
+ __ bind(&allocate);
+ }
+
+ // Now allocate the JSObject on the heap.
+ // a1: constructor function
+ // a2: initial map
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset));
+ __ AllocateInNewSpace(a3, t4, t5, t6, &rt_call, SIZE_IN_WORDS);
+
+ // Allocated the JSObject, now initialize the fields. Map is set to initial
+ // map and properties and elements are set to empty fixed array.
+ // a1: constructor function
+ // a2: initial map
+ // a3: object size
+ // t4: JSObject (not tagged)
+ __ LoadRoot(t6, Heap::kEmptyFixedArrayRootIndex);
+ __ mov(t5, t4);
+ __ sw(a2, MemOperand(t5, JSObject::kMapOffset));
+ __ sw(t6, MemOperand(t5, JSObject::kPropertiesOffset));
+ __ sw(t6, MemOperand(t5, JSObject::kElementsOffset));
+ __ Addu(t5, t5, Operand(3*kPointerSize));
+ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+ ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
+ ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
+
+ // Fill all the in-object properties with appropriate filler.
+ // a1: constructor function
+ // a2: initial map
+ // a3: object size (in words)
+ // t4: JSObject (not tagged)
+ // t5: First in-object property of JSObject (not tagged)
+ __ sll(t0, a3, kPointerSizeLog2);
+ __ addu(t6, t4, t0); // End of object.
+ ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize);
+ { Label loop, entry;
+ if (count_constructions) {
+ // To allow for truncation.
+ __ LoadRoot(t7, Heap::kOnePointerFillerMapRootIndex);
+ } else {
+ __ LoadRoot(t7, Heap::kUndefinedValueRootIndex);
+ }
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ sw(t7, MemOperand(t5, 0));
+ __ addiu(t5, t5, kPointerSize);
+ __ bind(&entry);
+ __ Branch(&loop, Uless, t5, Operand(t6));
+ }
+
+ // Add the object tag to make the JSObject real, so that we can continue and
+ // jump into the continuation code at any time from now on. Any failures
+ // need to undo the allocation, so that the heap is in a consistent state
+ // and verifiable.
+ __ Addu(t4, t4, Operand(kHeapObjectTag));
+
+ // Check if a non-empty properties array is needed. Continue with allocated
+ // object if not fall through to runtime call if it is.
+ // a1: constructor function
+ // t4: JSObject
+ // t5: start of next object (not tagged)
+ __ lbu(a3, FieldMemOperand(a2, Map::kUnusedPropertyFieldsOffset));
+ // The field instance sizes contains both pre-allocated property fields and
+ // in-object properties.
+ __ lw(a0, FieldMemOperand(a2, Map::kInstanceSizesOffset));
+ __ And(t6,
+ a0,
+ Operand(0x000000FF << Map::kPreAllocatedPropertyFieldsByte * 8));
+ __ srl(t0, t6, Map::kPreAllocatedPropertyFieldsByte * 8);
+ __ Addu(a3, a3, Operand(t0));
+ __ And(t6, a0, Operand(0x000000FF << Map::kInObjectPropertiesByte * 8));
+ __ srl(t0, t6, Map::kInObjectPropertiesByte * 8);
+ __ subu(a3, a3, t0);
+
+ // Done if no extra properties are to be allocated.
+ __ Branch(&allocated, eq, a3, Operand(zero_reg));
+ __ Assert(greater_equal, "Property allocation count failed.",
+ a3, Operand(zero_reg));
+
+ // Scale the number of elements by pointer size and add the header for
+ // FixedArrays to the start of the next object calculation from above.
+ // a1: constructor
+ // a3: number of elements in properties array
+ // t4: JSObject
+ // t5: start of next object
+ __ Addu(a0, a3, Operand(FixedArray::kHeaderSize / kPointerSize));
+ __ AllocateInNewSpace(
+ a0,
+ t5,
+ t6,
+ a2,
+ &undo_allocation,
+ static_cast<AllocationFlags>(RESULT_CONTAINS_TOP | SIZE_IN_WORDS));
+
+ // Initialize the FixedArray.
+ // a1: constructor
+ // a3: number of elements in properties array (un-tagged)
+ // t4: JSObject
+ // t5: start of next object
+ __ LoadRoot(t6, Heap::kFixedArrayMapRootIndex);
+ __ mov(a2, t5);
+ __ sw(t6, MemOperand(a2, JSObject::kMapOffset));
+ __ sll(a0, a3, kSmiTagSize);
+ __ sw(a0, MemOperand(a2, FixedArray::kLengthOffset));
+ __ Addu(a2, a2, Operand(2 * kPointerSize));
+
+ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+ ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset);
+
+ // Initialize the fields to undefined.
+ // a1: constructor
+ // a2: First element of FixedArray (not tagged)
+ // a3: number of elements in properties array
+ // t4: JSObject
+ // t5: FixedArray (not tagged)
+ __ sll(t3, a3, kPointerSizeLog2);
+ __ addu(t6, a2, t3); // End of object.
+ ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize);
+ { Label loop, entry;
+ if (count_constructions) {
+ __ LoadRoot(t7, Heap::kUndefinedValueRootIndex);
+ } else if (FLAG_debug_code) {
+ __ LoadRoot(t8, Heap::kUndefinedValueRootIndex);
+ __ Assert(eq, "Undefined value not loaded.", t7, Operand(t8));
+ }
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ sw(t7, MemOperand(a2));
+ __ addiu(a2, a2, kPointerSize);
+ __ bind(&entry);
+ __ Branch(&loop, less, a2, Operand(t6));
+ }
+
+ // Store the initialized FixedArray into the properties field of
+ // the JSObject.
+ // a1: constructor function
+ // t4: JSObject
+ // t5: FixedArray (not tagged)
+ __ Addu(t5, t5, Operand(kHeapObjectTag)); // Add the heap tag.
+ __ sw(t5, FieldMemOperand(t4, JSObject::kPropertiesOffset));
+
+ // Continue with JSObject being successfully allocated.
+ // a1: constructor function
+ // a4: JSObject
+ __ jmp(&allocated);
+
+ // Undo the setting of the new top so that the heap is verifiable. For
+ // example, the map's unused properties potentially do not match the
+ // allocated objects unused properties.
+ // t4: JSObject (previous new top)
+ __ bind(&undo_allocation);
+ __ UndoAllocationInNewSpace(t4, t5);
+ }
+
+ __ bind(&rt_call);
+ // Allocate the new receiver object using the runtime call.
+ // a1: constructor function
+ __ push(a1); // Argument for Runtime_NewObject.
+ __ CallRuntime(Runtime::kNewObject, 1);
+ __ mov(t4, v0);
+
+ // Receiver for constructor call allocated.
+ // t4: JSObject
+ __ bind(&allocated);
+ __ push(t4);
+
+ // Push the function and the allocated receiver from the stack.
+ // sp[0]: receiver (newly allocated object)
+ // sp[1]: constructor function
+ // sp[2]: number of arguments (smi-tagged)
+ __ lw(a1, MemOperand(sp, kPointerSize));
+ __ MultiPushReversed(a1.bit() | t4.bit());
+
+ // Reload the number of arguments from the stack.
+ // a1: constructor function
+ // sp[0]: receiver
+ // sp[1]: constructor function
+ // sp[2]: receiver
+ // sp[3]: constructor function
+ // sp[4]: number of arguments (smi-tagged)
+ __ lw(a3, MemOperand(sp, 4 * kPointerSize));
+
+ // Setup pointer to last argument.
+ __ Addu(a2, fp, Operand(StandardFrameConstants::kCallerSPOffset));
+
+ // Setup number of arguments for function call below.
+ __ srl(a0, a3, kSmiTagSize);
+
+ // Copy arguments and receiver to the expression stack.
+ // a0: number of arguments
+ // a1: constructor function
+ // a2: address of last argument (caller sp)
+ // a3: number of arguments (smi-tagged)
+ // sp[0]: receiver
+ // sp[1]: constructor function
+ // sp[2]: receiver
+ // sp[3]: constructor function
+ // sp[4]: number of arguments (smi-tagged)
+ Label loop, entry;
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(t0, a2, Operand(t0));
+ __ lw(t1, MemOperand(t0));
+ __ push(t1);
+ __ bind(&entry);
+ __ Addu(a3, a3, Operand(-2));
+ __ Branch(&loop, greater_equal, a3, Operand(zero_reg));
+
+ // Call the function.
+ // a0: number of arguments
+ // a1: constructor function
+ if (is_api_function) {
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+ Handle<Code> code =
+ masm->isolate()->builtins()->HandleApiCallConstruct();
+ ParameterCount expected(0);
+ __ InvokeCode(code, expected, expected,
+ RelocInfo::CODE_TARGET, CALL_FUNCTION);
+ } else {
+ ParameterCount actual(a0);
+ __ InvokeFunction(a1, actual, CALL_FUNCTION);
+ }
+
+ // Pop the function from the stack.
+ // v0: result
+ // sp[0]: constructor function
+ // sp[2]: receiver
+ // sp[3]: constructor function
+ // sp[4]: number of arguments (smi-tagged)
+ __ Pop();
+
+ // Restore context from the frame.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+
+ // If the result is an object (in the ECMA sense), we should get rid
+ // of the receiver and use the result; see ECMA-262 section 13.2.2-7
+ // on page 74.
+ Label use_receiver, exit;
+
+ // If the result is a smi, it is *not* an object in the ECMA sense.
+ // v0: result
+ // sp[0]: receiver (newly allocated object)
+ // sp[1]: constructor function
+ // sp[2]: number of arguments (smi-tagged)
+ __ And(t0, v0, Operand(kSmiTagMask));
+ __ Branch(&use_receiver, eq, t0, Operand(zero_reg));
+
+ // If the type of the result (stored in its map) is less than
+ // FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
+ __ GetObjectType(v0, a3, a3);
+ __ Branch(&exit, greater_equal, a3, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Throw away the result of the constructor invocation and use the
+ // on-stack receiver as the result.
+ __ bind(&use_receiver);
+ __ lw(v0, MemOperand(sp));
+
+ // Remove receiver from the stack, remove caller arguments, and
+ // return.
+ __ bind(&exit);
+ // v0: result
+ // sp[0]: receiver (newly allocated object)
+ // sp[1]: constructor function
+ // sp[2]: number of arguments (smi-tagged)
+ __ lw(a1, MemOperand(sp, 2 * kPointerSize));
+ __ LeaveConstructFrame();
+ __ sll(t0, a1, kPointerSizeLog2 - 1);
+ __ Addu(sp, sp, t0);
+ __ Addu(sp, sp, kPointerSize);
+ __ IncrementCounter(isolate->counters()->constructed_objects(), 1, a1, a2);
+ __ Ret();
}
void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Generate_JSConstructStubHelper(masm, false, true);
}
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Generate_JSConstructStubHelper(masm, false, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Generate_JSConstructStubHelper(masm, true, false);
+}
+
+
+static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
+ bool is_construct) {
+ // Called from JSEntryStub::GenerateBody
+
+ // ----------- S t a t e -------------
+ // -- a0: code entry
+ // -- a1: function
+ // -- a2: reveiver_pointer
+ // -- a3: argc
+ // -- s0: argv
+ // -----------------------------------
+
+ // Clear the context before we push it when entering the JS frame.
+ __ mov(cp, zero_reg);
+
+ // Enter an internal frame.
+ __ EnterInternalFrame();
+
+ // Set up the context from the function argument.
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Set up the roots register.
+ ExternalReference roots_address =
+ ExternalReference::roots_address(masm->isolate());
+ __ li(s6, Operand(roots_address));
+
+ // Push the function and the receiver onto the stack.
+ __ Push(a1, a2);
+
+ // Copy arguments to the stack in a loop.
+ // a3: argc
+ // s0: argv, ie points to first arg
+ Label loop, entry;
+ __ sll(t0, a3, kPointerSizeLog2);
+ __ addu(t2, s0, t0);
+ __ b(&entry);
+ __ nop(); // Branch delay slot nop.
+ // t2 points past last arg.
+ __ bind(&loop);
+ __ lw(t0, MemOperand(s0)); // Read next parameter.
+ __ addiu(s0, s0, kPointerSize);
+ __ lw(t0, MemOperand(t0)); // Dereference handle.
+ __ push(t0); // Push parameter.
+ __ bind(&entry);
+ __ Branch(&loop, ne, s0, Operand(t2));
+
+ // Initialize all JavaScript callee-saved registers, since they will be seen
+ // by the garbage collector as part of handlers.
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ mov(s1, t0);
+ __ mov(s2, t0);
+ __ mov(s3, t0);
+ __ mov(s4, t0);
+ __ mov(s5, t0);
+ // s6 holds the root address. Do not clobber.
+ // s7 is cp. Do not init.
+
+ // Invoke the code and pass argc as a0.
+ __ mov(a0, a3);
+ if (is_construct) {
+ __ Call(masm->isolate()->builtins()->JSConstructCall(),
+ RelocInfo::CODE_TARGET);
+ } else {
+ ParameterCount actual(a0);
+ __ InvokeFunction(a1, actual, CALL_FUNCTION);
+ }
+
+ __ LeaveInternalFrame();
+
+ __ Jump(ra);
}
void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Generate_JSEntryTrampolineHelper(masm, false);
}
void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Generate_JSEntryTrampolineHelper(masm, true);
}
void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Enter an internal frame.
+ __ EnterInternalFrame();
+
+ // Preserve the function.
+ __ push(a1);
+ // Push call kind information.
+ __ push(t1);
+
+ // Push the function on the stack as the argument to the runtime function.
+ __ push(a1);
+ // Call the runtime function.
+ __ CallRuntime(Runtime::kLazyCompile, 1);
+ // Calculate the entry point.
+ __ addiu(t9, v0, Code::kHeaderSize - kHeapObjectTag);
+
+ // Restore call kind information.
+ __ pop(t1);
+ // Restore saved function.
+ __ pop(a1);
+
+ // Tear down temporary frame.
+ __ LeaveInternalFrame();
+
+ // Do a tail-call of the compiled function.
+ __ Jump(t9);
}
void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Enter an internal frame.
+ __ EnterInternalFrame();
+
+ // Preserve the function.
+ __ push(a1);
+ // Push call kind information.
+ __ push(t1);
+
+ // Push the function on the stack as the argument to the runtime function.
+ __ push(a1);
+ __ CallRuntime(Runtime::kLazyRecompile, 1);
+ // Calculate the entry point.
+ __ Addu(t9, v0, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // Restore call kind information.
+ __ pop(t1);
+ // Restore saved function.
+ __ pop(a1);
+
+ // Tear down temporary frame.
+ __ LeaveInternalFrame();
+
+ // Do a tail-call of the compiled function.
+ __ Jump(t9);
}
+// These functions are called from C++ but cannot be used in live code.
void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ Abort("Call to unimplemented function in builtins-mips.cc");
}
void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ Abort("Call to unimplemented function in builtins-mips.cc");
}
void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ Abort("Call to unimplemented function in builtins-mips.cc");
}
void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ Abort("Call to unimplemented function in builtins-mips.cc");
}
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // 1. Make sure we have at least one argument.
+ // a0: actual number of arguments
+ { Label done;
+ __ Branch(&done, ne, a0, Operand(zero_reg));
+ __ LoadRoot(t2, Heap::kUndefinedValueRootIndex);
+ __ push(t2);
+ __ Addu(a0, a0, Operand(1));
+ __ bind(&done);
+ }
+
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ // a0: actual number of arguments
+ Label non_function;
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(at, sp, at);
+ __ lw(a1, MemOperand(at));
+ __ And(at, a1, Operand(kSmiTagMask));
+ __ Branch(&non_function, eq, at, Operand(zero_reg));
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&non_function, ne, a2, Operand(JS_FUNCTION_TYPE));
+
+ // 3a. Patch the first argument if necessary when calling a function.
+ // a0: actual number of arguments
+ // a1: function
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Do not transform the receiver for strict mode functions.
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a3, FieldMemOperand(a2, SharedFunctionInfo::kCompilerHintsOffset));
+ __ And(t0, a3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ kSmiTagSize)));
+ __ Branch(&shift_arguments, ne, t0, Operand(zero_reg));
+
+ // Do not transform the receiver for native (Compilerhints already in a3).
+ __ And(t0, a3, Operand(1 << (SharedFunctionInfo::kES5Native +
+ kSmiTagSize)));
+ __ Branch(&shift_arguments, ne, t0, Operand(zero_reg));
+
+ // Compute the receiver in non-strict mode.
+ // Load first argument in a2. a2 = -kPointerSize(sp + n_args << 2).
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a2, sp, at);
+ __ lw(a2, MemOperand(a2, -kPointerSize));
+ // a0: actual number of arguments
+ // a1: function
+ // a2: first argument
+ __ JumpIfSmi(a2, &convert_to_object, t2);
+
+ __ LoadRoot(a3, Heap::kUndefinedValueRootIndex);
+ __ Branch(&use_global_receiver, eq, a2, Operand(a3));
+ __ LoadRoot(a3, Heap::kNullValueRootIndex);
+ __ Branch(&use_global_receiver, eq, a2, Operand(a3));
+
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ __ GetObjectType(a2, a3, a3);
+ __ Branch(&shift_arguments, ge, a3, Operand(FIRST_JS_OBJECT_TYPE));
+
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
+ __ sll(a0, a0, kSmiTagSize); // Smi tagged.
+ __ push(a0);
+
+ __ push(a2);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ mov(a2, v0);
+
+ __ pop(a0);
+ __ sra(a0, a0, kSmiTagSize); // Un-tag.
+ __ LeaveInternalFrame();
+ // Restore the function to a1.
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(at, sp, at);
+ __ lw(a1, MemOperand(at));
+ __ Branch(&patch_receiver);
+
+ // Use the global receiver object from the called function as the
+ // receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalIndex =
+ Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
+ __ lw(a2, FieldMemOperand(cp, kGlobalIndex));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalContextOffset));
+ __ lw(a2, FieldMemOperand(a2, kGlobalIndex));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalReceiverOffset));
+
+ __ bind(&patch_receiver);
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a3, sp, at);
+ __ sw(a2, MemOperand(a3, -kPointerSize));
+
+ __ Branch(&shift_arguments);
+ }
+
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ // a0: actual number of arguments
+ // a1: function
+ __ bind(&non_function);
+ // Restore the function in case it has been modified.
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a2, sp, at);
+ __ sw(a1, MemOperand(a2, -kPointerSize));
+ // Clear a1 to indicate a non-function being called.
+ __ mov(a1, zero_reg);
+
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ // a0: actual number of arguments
+ // a1: function
+ __ bind(&shift_arguments);
+ { Label loop;
+ // Calculate the copy start address (destination). Copy end address is sp.
+ __ sll(at, a0, kPointerSizeLog2);
+ __ addu(a2, sp, at);
+
+ __ bind(&loop);
+ __ lw(at, MemOperand(a2, -kPointerSize));
+ __ sw(at, MemOperand(a2));
+ __ Subu(a2, a2, Operand(kPointerSize));
+ __ Branch(&loop, ne, a2, Operand(sp));
+ // Adjust the actual number of arguments and remove the top element
+ // (which is a copy of the last argument).
+ __ Subu(a0, a0, Operand(1));
+ __ Pop();
+ }
+
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ // a0: actual number of arguments
+ // a1: function
+ { Label function;
+ __ Branch(&function, ne, a1, Operand(zero_reg));
+ __ mov(a2, zero_reg); // expected arguments is 0 for CALL_NON_FUNCTION
+ __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION);
+ __ SetCallKind(t1, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ // a0: actual number of arguments
+ // a1: function
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a2,
+ FieldMemOperand(a3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ sra(a2, a2, kSmiTagSize);
+ __ lw(a3, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
+ __ SetCallKind(t1, CALL_AS_METHOD);
+ // Check formal and actual parameter counts.
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET, ne, a2, Operand(a0));
+
+ ParameterCount expected(0);
+ __ InvokeCode(a3, expected, expected, JUMP_FUNCTION);
}
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ const int kIndexOffset = -5 * kPointerSize;
+ const int kLimitOffset = -4 * kPointerSize;
+ const int kArgsOffset = 2 * kPointerSize;
+ const int kRecvOffset = 3 * kPointerSize;
+ const int kFunctionOffset = 4 * kPointerSize;
+
+ __ EnterInternalFrame();
+
+ __ lw(a0, MemOperand(fp, kFunctionOffset)); // Get the function.
+ __ push(a0);
+ __ lw(a0, MemOperand(fp, kArgsOffset)); // Get the args array.
+ __ push(a0);
+ // Returns (in v0) number of arguments to copy to stack as Smi.
+ __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
+
+ // Check the stack for overflow. We are not trying need to catch
+ // interruptions (e.g. debug break and preemption) here, so the "real stack
+ // limit" is checked.
+ Label okay;
+ __ LoadRoot(a2, Heap::kRealStackLimitRootIndex);
+ // Make a2 the space we have left. The stack might already be overflowed
+ // here which will cause a2 to become negative.
+ __ subu(a2, sp, a2);
+ // Check if the arguments will overflow the stack.
+ __ sll(t0, v0, kPointerSizeLog2 - kSmiTagSize);
+ __ Branch(&okay, gt, a2, Operand(t0)); // Signed comparison.
+
+ // Out of stack space.
+ __ lw(a1, MemOperand(fp, kFunctionOffset));
+ __ push(a1);
+ __ push(v0);
+ __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
+ // End of stack check.
+
+ // Push current limit and index.
+ __ bind(&okay);
+ __ push(v0); // Limit.
+ __ mov(a1, zero_reg); // Initial index.
+ __ push(a1);
+
+ // Change context eagerly to get the right global object if necessary.
+ __ lw(a0, MemOperand(fp, kFunctionOffset));
+ __ lw(cp, FieldMemOperand(a0, JSFunction::kContextOffset));
+ // Load the shared function info while the function is still in a0.
+ __ lw(a1, FieldMemOperand(a0, JSFunction::kSharedFunctionInfoOffset));
+
+ // Compute the receiver.
+ Label call_to_object, use_global_receiver, push_receiver;
+ __ lw(a0, MemOperand(fp, kRecvOffset));
+
+ // Do not transform the receiver for strict mode functions.
+ __ lw(a2, FieldMemOperand(a1, SharedFunctionInfo::kCompilerHintsOffset));
+ __ And(t0, a2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
+ kSmiTagSize)));
+ __ Branch(&push_receiver, ne, t0, Operand(zero_reg));
+
+ // Do not transform the receiver for native (Compilerhints already in a2).
+ __ And(t0, a2, Operand(1 << (SharedFunctionInfo::kES5Native +
+ kSmiTagSize)));
+ __ Branch(&push_receiver, ne, t0, Operand(zero_reg));
+
+ // Compute the receiver in non-strict mode.
+ __ And(t0, a0, Operand(kSmiTagMask));
+ __ Branch(&call_to_object, eq, t0, Operand(zero_reg));
+ __ LoadRoot(a1, Heap::kNullValueRootIndex);
+ __ Branch(&use_global_receiver, eq, a0, Operand(a1));
+ __ LoadRoot(a2, Heap::kUndefinedValueRootIndex);
+ __ Branch(&use_global_receiver, eq, a0, Operand(a2));
+
+ // Check if the receiver is already a JavaScript object.
+ // a0: receiver
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&push_receiver, ge, a1, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Convert the receiver to a regular object.
+ // a0: receiver
+ __ bind(&call_to_object);
+ __ push(a0);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ mov(a0, v0); // Put object in a0 to match other paths to push_receiver.
+ __ Branch(&push_receiver);
+
+ // Use the current global receiver object as the receiver.
+ __ bind(&use_global_receiver);
+ const int kGlobalOffset =
+ Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
+ __ lw(a0, FieldMemOperand(cp, kGlobalOffset));
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalContextOffset));
+ __ lw(a0, FieldMemOperand(a0, kGlobalOffset));
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset));
+
+ // Push the receiver.
+ // a0: receiver
+ __ bind(&push_receiver);
+ __ push(a0);
+
+ // Copy all arguments from the array to the stack.
+ Label entry, loop;
+ __ lw(a0, MemOperand(fp, kIndexOffset));
+ __ Branch(&entry);
+
+ // Load the current argument from the arguments array and push it to the
+ // stack.
+ // a0: current argument index
+ __ bind(&loop);
+ __ lw(a1, MemOperand(fp, kArgsOffset));
+ __ push(a1);
+ __ push(a0);
+
+ // Call the runtime to access the property in the arguments array.
+ __ CallRuntime(Runtime::kGetProperty, 2);
+ __ push(v0);
+
+ // Use inline caching to access the arguments.
+ __ lw(a0, MemOperand(fp, kIndexOffset));
+ __ Addu(a0, a0, Operand(1 << kSmiTagSize));
+ __ sw(a0, MemOperand(fp, kIndexOffset));
+
+ // Test if the copy loop has finished copying all the elements from the
+ // arguments object.
+ __ bind(&entry);
+ __ lw(a1, MemOperand(fp, kLimitOffset));
+ __ Branch(&loop, ne, a0, Operand(a1));
+ // Invoke the function.
+ ParameterCount actual(a0);
+ __ sra(a0, a0, kSmiTagSize);
+ __ lw(a1, MemOperand(fp, kFunctionOffset));
+ __ InvokeFunction(a1, actual, CALL_FUNCTION);
+
+ // Tear down the internal frame and remove function, receiver and args.
+ __ LeaveInternalFrame();
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+}
+
+
+static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
+ __ sll(a0, a0, kSmiTagSize);
+ __ li(t0, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ MultiPush(a0.bit() | a1.bit() | t0.bit() | fp.bit() | ra.bit());
+ __ Addu(fp, sp, Operand(3 * kPointerSize));
+}
+
+
+static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- v0 : result being passed through
+ // -----------------------------------
+ // Get the number of arguments passed (as a smi), tear down the frame and
+ // then tear down the parameters.
+ __ lw(a1, MemOperand(fp, -3 * kPointerSize));
+ __ mov(sp, fp);
+ __ MultiPop(fp.bit() | ra.bit());
+ __ sll(t0, a1, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(sp, sp, t0);
+ // Adjust for the receiver.
+ __ Addu(sp, sp, Operand(kPointerSize));
}
void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // State setup as expected by MacroAssembler::InvokePrologue.
+ // ----------- S t a t e -------------
+ // -- a0: actual arguments count
+ // -- a1: function (passed through to callee)
+ // -- a2: expected arguments count
+ // -- a3: callee code entry
+ // -- t1: call kind information
+ // -----------------------------------
+
+ Label invoke, dont_adapt_arguments;
+
+ Label enough, too_few;
+ __ Branch(&dont_adapt_arguments, eq,
+ a2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
+ // We use Uless as the number of argument should always be greater than 0.
+ __ Branch(&too_few, Uless, a0, Operand(a2));
+
+ { // Enough parameters: actual >= expected.
+ // a0: actual number of arguments as a smi
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ __ bind(&enough);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // Calculate copy start address into a0 and copy end address into a2.
+ __ sll(a0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a0, fp, a0);
+ // Adjust for return address and receiver.
+ __ Addu(a0, a0, Operand(2 * kPointerSize));
+ // Compute copy end address.
+ __ sll(a2, a2, kPointerSizeLog2);
+ __ subu(a2, a0, a2);
+
+ // Copy the arguments (including the receiver) to the new stack frame.
+ // a0: copy start address
+ // a1: function
+ // a2: copy end address
+ // a3: code entry to call
+
+ Label copy;
+ __ bind(&copy);
+ __ lw(t0, MemOperand(a0));
+ __ push(t0);
+ __ Branch(USE_DELAY_SLOT, &copy, ne, a0, Operand(a2));
+ __ addiu(a0, a0, -kPointerSize); // In delay slot.
+
+ __ jmp(&invoke);
+ }
+
+ { // Too few parameters: Actual < expected.
+ __ bind(&too_few);
+ EnterArgumentsAdaptorFrame(masm);
+
+ // TODO(MIPS): Optimize these loops.
+
+ // Calculate copy start address into a0 and copy end address is fp.
+ // a0: actual number of arguments as a smi
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ __ sll(a0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a0, fp, a0);
+ // Adjust for return address and receiver.
+ __ Addu(a0, a0, Operand(2 * kPointerSize));
+ // Compute copy end address. Also adjust for return address.
+ __ Addu(t1, fp, kPointerSize);
+
+ // Copy the arguments (including the receiver) to the new stack frame.
+ // a0: copy start address
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ // t1: copy end address
+ Label copy;
+ __ bind(&copy);
+ __ lw(t0, MemOperand(a0)); // Adjusted above for return addr and receiver.
+ __ push(t0);
+ __ Subu(a0, a0, kPointerSize);
+ __ Branch(&copy, ne, a0, Operand(t1));
+
+ // Fill the remaining expected arguments with undefined.
+ // a1: function
+ // a2: expected number of arguments
+ // a3: code entry to call
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ sll(t2, a2, kPointerSizeLog2);
+ __ Subu(a2, fp, Operand(t2));
+ __ Addu(a2, a2, Operand(-4 * kPointerSize)); // Adjust for frame.
+
+ Label fill;
+ __ bind(&fill);
+ __ push(t0);
+ __ Branch(&fill, ne, sp, Operand(a2));
+ }
+
+ // Call the entry point.
+ __ bind(&invoke);
+
+ __ Call(a3);
+
+ // Exit frame and return.
+ LeaveArgumentsAdaptorFrame(masm);
+ __ Ret();
+
+
+ // -------------------------------------------
+ // Don't adapt arguments.
+ // -------------------------------------------
+ __ bind(&dont_adapt_arguments);
+ __ Jump(a3);
}
diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc
index 6cc272c3..c9999949 100644
--- a/src/mips/code-stubs-mips.cc
+++ b/src/mips/code-stubs-mips.cc
@@ -31,7 +31,7 @@
#include "bootstrapper.h"
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "regexp-macro-assembler.h"
namespace v8 {
@@ -40,24 +40,233 @@ namespace internal {
#define __ ACCESS_MASM(masm)
+static void EmitIdenticalObjectComparison(MacroAssembler* masm,
+ Label* slow,
+ Condition cc,
+ bool never_nan_nan);
+static void EmitSmiNonsmiComparison(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* rhs_not_nan,
+ Label* slow,
+ bool strict);
+static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc);
+static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
+ Register lhs,
+ Register rhs);
+
+
+// Check if the operand is a heap number.
+static void EmitCheckForHeapNumber(MacroAssembler* masm, Register operand,
+ Register scratch1, Register scratch2,
+ Label* not_a_heap_number) {
+ __ lw(scratch1, FieldMemOperand(operand, HeapObject::kMapOffset));
+ __ LoadRoot(scratch2, Heap::kHeapNumberMapRootIndex);
+ __ Branch(not_a_heap_number, ne, scratch1, Operand(scratch2));
+}
+
void ToNumberStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // The ToNumber stub takes one argument in a0.
+ Label check_heap_number, call_builtin;
+ __ JumpIfNotSmi(a0, &check_heap_number);
+ __ mov(v0, a0);
+ __ Ret();
+
+ __ bind(&check_heap_number);
+ EmitCheckForHeapNumber(masm, a0, a1, t0, &call_builtin);
+ __ mov(v0, a0);
+ __ Ret();
+
+ __ bind(&call_builtin);
+ __ push(a0);
+ __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
}
void FastNewClosureStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Create a new closure from the given function info in new
+ // space. Set the context to the current context in cp.
+ Label gc;
+
+ // Pop the function info from the stack.
+ __ pop(a3);
+
+ // Attempt to allocate new JSFunction in new space.
+ __ AllocateInNewSpace(JSFunction::kSize,
+ v0,
+ a1,
+ a2,
+ &gc,
+ TAG_OBJECT);
+
+ int map_index = strict_mode_ == kStrictMode
+ ? Context::STRICT_MODE_FUNCTION_MAP_INDEX
+ : Context::FUNCTION_MAP_INDEX;
+
+ // Compute the function map in the current global context and set that
+ // as the map of the allocated object.
+ __ lw(a2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalContextOffset));
+ __ lw(a2, MemOperand(a2, Context::SlotOffset(map_index)));
+ __ sw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+
+ // Initialize the rest of the function. We don't have to update the
+ // write barrier because the allocated object is in new space.
+ __ LoadRoot(a1, Heap::kEmptyFixedArrayRootIndex);
+ __ LoadRoot(a2, Heap::kTheHoleValueRootIndex);
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ sw(a1, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(a1, FieldMemOperand(v0, JSObject::kElementsOffset));
+ __ sw(a2, FieldMemOperand(v0, JSFunction::kPrototypeOrInitialMapOffset));
+ __ sw(a3, FieldMemOperand(v0, JSFunction::kSharedFunctionInfoOffset));
+ __ sw(cp, FieldMemOperand(v0, JSFunction::kContextOffset));
+ __ sw(a1, FieldMemOperand(v0, JSFunction::kLiteralsOffset));
+ __ sw(t0, FieldMemOperand(v0, JSFunction::kNextFunctionLinkOffset));
+
+ // Initialize the code pointer in the function to be the one
+ // found in the shared function info object.
+ __ lw(a3, FieldMemOperand(a3, SharedFunctionInfo::kCodeOffset));
+ __ Addu(a3, a3, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ sw(a3, FieldMemOperand(v0, JSFunction::kCodeEntryOffset));
+
+ // Return result. The argument function info has been popped already.
+ __ Ret();
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ LoadRoot(t0, Heap::kFalseValueRootIndex);
+ __ Push(cp, a3, t0);
+ __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
}
void FastNewContextStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Try to allocate the context in new space.
+ Label gc;
+ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
+
+ // Attempt to allocate the context in new space.
+ __ AllocateInNewSpace(FixedArray::SizeFor(length),
+ v0,
+ a1,
+ a2,
+ &gc,
+ TAG_OBJECT);
+
+ // Load the function from the stack.
+ __ lw(a3, MemOperand(sp, 0));
+
+ // Setup the object header.
+ __ LoadRoot(a2, Heap::kContextMapRootIndex);
+ __ sw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ li(a2, Operand(Smi::FromInt(length)));
+ __ sw(a2, FieldMemOperand(v0, FixedArray::kLengthOffset));
+
+ // Setup the fixed slots.
+ __ li(a1, Operand(Smi::FromInt(0)));
+ __ sw(a3, MemOperand(v0, Context::SlotOffset(Context::CLOSURE_INDEX)));
+ __ sw(v0, MemOperand(v0, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ __ sw(a1, MemOperand(v0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
+ __ sw(a1, MemOperand(v0, Context::SlotOffset(Context::EXTENSION_INDEX)));
+
+ // Copy the global object from the surrounding context.
+ __ lw(a1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ sw(a1, MemOperand(v0, Context::SlotOffset(Context::GLOBAL_INDEX)));
+
+ // Initialize the rest of the slots to undefined.
+ __ LoadRoot(a1, Heap::kUndefinedValueRootIndex);
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
+ __ sw(a1, MemOperand(v0, Context::SlotOffset(i)));
+ }
+
+ // Remove the on-stack argument and return.
+ __ mov(cp, v0);
+ __ Pop();
+ __ Ret();
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(Runtime::kNewContext, 1, 1);
}
void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Stack layout on entry:
+ // [sp]: constant elements.
+ // [sp + kPointerSize]: literal index.
+ // [sp + (2 * kPointerSize)]: literals array.
+
+ // All sizes here are multiples of kPointerSize.
+ int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
+ int size = JSArray::kSize + elements_size;
+
+ // Load boilerplate object into r3 and check if we need to create a
+ // boilerplate.
+ Label slow_case;
+ __ lw(a3, MemOperand(sp, 2 * kPointerSize));
+ __ lw(a0, MemOperand(sp, 1 * kPointerSize));
+ __ Addu(a3, a3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(t0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(t0, a3, t0);
+ __ lw(a3, MemOperand(t0));
+ __ LoadRoot(t1, Heap::kUndefinedValueRootIndex);
+ __ Branch(&slow_case, eq, a3, Operand(t1));
+
+ if (FLAG_debug_code) {
+ const char* message;
+ Heap::RootListIndex expected_map_index;
+ if (mode_ == CLONE_ELEMENTS) {
+ message = "Expected (writable) fixed array";
+ expected_map_index = Heap::kFixedArrayMapRootIndex;
+ } else {
+ ASSERT(mode_ == COPY_ON_WRITE_ELEMENTS);
+ message = "Expected copy-on-write fixed array";
+ expected_map_index = Heap::kFixedCOWArrayMapRootIndex;
+ }
+ __ push(a3);
+ __ lw(a3, FieldMemOperand(a3, JSArray::kElementsOffset));
+ __ lw(a3, FieldMemOperand(a3, HeapObject::kMapOffset));
+ __ LoadRoot(at, expected_map_index);
+ __ Assert(eq, message, a3, Operand(at));
+ __ pop(a3);
+ }
+
+ // Allocate both the JS array and the elements array in one big
+ // allocation. This avoids multiple limit checks.
+ // Return new object in v0.
+ __ AllocateInNewSpace(size,
+ v0,
+ a1,
+ a2,
+ &slow_case,
+ TAG_OBJECT);
+
+ // Copy the JS array part.
+ for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
+ if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
+ __ lw(a1, FieldMemOperand(a3, i));
+ __ sw(a1, FieldMemOperand(v0, i));
+ }
+ }
+
+ if (length_ > 0) {
+ // Get hold of the elements array of the boilerplate and setup the
+ // elements pointer in the resulting object.
+ __ lw(a3, FieldMemOperand(a3, JSArray::kElementsOffset));
+ __ Addu(a2, v0, Operand(JSArray::kSize));
+ __ sw(a2, FieldMemOperand(v0, JSArray::kElementsOffset));
+
+ // Copy the elements array.
+ __ CopyFields(a2, a3, a1.bit(), elements_size / kPointerSize);
+ }
+
+ // Return and remove the on-stack parameters.
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&slow_case);
+ __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
}
@@ -107,72 +316,94 @@ class ConvertToDoubleStub : public CodeStub {
void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+#ifndef BIG_ENDIAN_FLOATING_POINT
+ Register exponent = result1_;
+ Register mantissa = result2_;
+#else
+ Register exponent = result2_;
+ Register mantissa = result1_;
+#endif
+ Label not_special;
+ // Convert from Smi to integer.
+ __ sra(source_, source_, kSmiTagSize);
+ // Move sign bit from source to destination. This works because the sign bit
+ // in the exponent word of the double has the same position and polarity as
+ // the 2's complement sign bit in a Smi.
+ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
+ __ And(exponent, source_, Operand(HeapNumber::kSignMask));
+ // Subtract from 0 if source was negative.
+ __ subu(at, zero_reg, source_);
+ __ movn(source_, at, exponent);
+
+ // We have -1, 0 or 1, which we treat specially. Register source_ contains
+ // absolute value: it is either equal to 1 (special case of -1 and 1),
+ // greater than 1 (not a special case) or less than 1 (special case of 0).
+ __ Branch(&not_special, gt, source_, Operand(1));
+
+ // For 1 or -1 we need to or in the 0 exponent (biased to 1023).
+ static const uint32_t exponent_word_for_1 =
+ HeapNumber::kExponentBias << HeapNumber::kExponentShift;
+ // Safe to use 'at' as dest reg here.
+ __ Or(at, exponent, Operand(exponent_word_for_1));
+ __ movn(exponent, at, source_); // Write exp when source not 0.
+ // 1, 0 and -1 all have 0 for the second word.
+ __ mov(mantissa, zero_reg);
+ __ Ret();
+
+ __ bind(&not_special);
+ // Count leading zeros.
+ // Gets the wrong answer for 0, but we already checked for that case above.
+ __ clz(zeros_, source_);
+ // Compute exponent and or it into the exponent register.
+ // We use mantissa as a scratch register here.
+ __ li(mantissa, Operand(31 + HeapNumber::kExponentBias));
+ __ subu(mantissa, mantissa, zeros_);
+ __ sll(mantissa, mantissa, HeapNumber::kExponentShift);
+ __ Or(exponent, exponent, mantissa);
+
+ // Shift up the source chopping the top bit off.
+ __ Addu(zeros_, zeros_, Operand(1));
+ // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0.
+ __ sllv(source_, source_, zeros_);
+ // Compute lower part of fraction (last 12 bits).
+ __ sll(mantissa, source_, HeapNumber::kMantissaBitsInTopWord);
+ // And the top (top 20 bits).
+ __ srl(source_, source_, 32 - HeapNumber::kMantissaBitsInTopWord);
+ __ or_(exponent, exponent, source_);
+
+ __ Ret();
}
-class FloatingPointHelper : public AllStatic {
- public:
-
- enum Destination {
- kFPURegisters,
- kCoreRegisters
- };
-
-
- // Loads smis from a0 and a1 (right and left in binary operations) into
- // floating point registers. Depending on the destination the values ends up
- // either f14 and f12 or in a2/a3 and a0/a1 respectively. If the destination
- // is floating point registers FPU must be supported. If core registers are
- // requested when FPU is supported f12 and f14 will be scratched.
- static void LoadSmis(MacroAssembler* masm,
- Destination destination,
- Register scratch1,
- Register scratch2);
-
- // Loads objects from a0 and a1 (right and left in binary operations) into
- // floating point registers. Depending on the destination the values ends up
- // either f14 and f12 or in a2/a3 and a0/a1 respectively. If the destination
- // is floating point registers FPU must be supported. If core registers are
- // requested when FPU is supported f12 and f14 will still be scratched. If
- // either a0 or a1 is not a number (not smi and not heap number object) the
- // not_number label is jumped to with a0 and a1 intact.
- static void LoadOperands(MacroAssembler* masm,
- FloatingPointHelper::Destination destination,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Label* not_number);
- // Loads the number from object into dst as a 32-bit integer if possible. If
- // the object is not a 32-bit integer control continues at the label
- // not_int32. If FPU is supported double_scratch is used but not scratch2.
- static void LoadNumberAsInteger(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- FPURegister double_scratch,
- Label* not_int32);
- private:
- static void LoadNumber(MacroAssembler* masm,
- FloatingPointHelper::Destination destination,
- Register object,
- FPURegister dst,
- Register dst1,
- Register dst2,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- Label* not_number);
-};
-
-
void FloatingPointHelper::LoadSmis(MacroAssembler* masm,
FloatingPointHelper::Destination destination,
Register scratch1,
Register scratch2) {
- UNIMPLEMENTED_MIPS();
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ sra(scratch1, a0, kSmiTagSize);
+ __ mtc1(scratch1, f14);
+ __ cvt_d_w(f14, f14);
+ __ sra(scratch1, a1, kSmiTagSize);
+ __ mtc1(scratch1, f12);
+ __ cvt_d_w(f12, f12);
+ if (destination == kCoreRegisters) {
+ __ Move(a2, a3, f14);
+ __ Move(a0, a1, f12);
+ }
+ } else {
+ ASSERT(destination == kCoreRegisters);
+ // Write Smi from a0 to a3 and a2 in double format.
+ __ mov(scratch1, a0);
+ ConvertToDoubleStub stub1(a3, a2, scratch1, scratch2);
+ __ push(ra);
+ __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
+ // Write Smi from a1 to a1 and a0 in double format.
+ __ mov(scratch1, a1);
+ ConvertToDoubleStub stub2(a1, a0, scratch1, scratch2);
+ __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
+ __ pop(ra);
+ }
}
@@ -183,7 +414,14 @@ void FloatingPointHelper::LoadOperands(
Register scratch1,
Register scratch2,
Label* slow) {
- UNIMPLEMENTED_MIPS();
+
+ // Load right operand (a0) to f12 or a2/a3.
+ LoadNumber(masm, destination,
+ a0, f14, a2, a3, heap_number_map, scratch1, scratch2, slow);
+
+ // Load left operand (a1) to f14 or a0/a1.
+ LoadNumber(masm, destination,
+ a1, f12, a0, a1, heap_number_map, scratch1, scratch2, slow);
}
@@ -197,30 +435,991 @@ void FloatingPointHelper::LoadNumber(MacroAssembler* masm,
Register scratch1,
Register scratch2,
Label* not_number) {
- UNIMPLEMENTED_MIPS();
+ if (FLAG_debug_code) {
+ __ AbortIfNotRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ "HeapNumberMap register clobbered.");
+ }
+
+ Label is_smi, done;
+
+ __ JumpIfSmi(object, &is_smi);
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_number);
+
+ // Handle loading a double from a heap number.
+ if (CpuFeatures::IsSupported(FPU) &&
+ destination == kFPURegisters) {
+ CpuFeatures::Scope scope(FPU);
+ // Load the double from tagged HeapNumber to double register.
+
+ // ARM uses a workaround here because of the unaligned HeapNumber
+ // kValueOffset. On MIPS this workaround is built into ldc1 so there's no
+ // point in generating even more instructions.
+ __ ldc1(dst, FieldMemOperand(object, HeapNumber::kValueOffset));
+ } else {
+ ASSERT(destination == kCoreRegisters);
+ // Load the double from heap number to dst1 and dst2 in double format.
+ __ lw(dst1, FieldMemOperand(object, HeapNumber::kValueOffset));
+ __ lw(dst2, FieldMemOperand(object,
+ HeapNumber::kValueOffset + kPointerSize));
+ }
+ __ Branch(&done);
+
+ // Handle loading a double from a smi.
+ __ bind(&is_smi);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Convert smi to double using FPU instructions.
+ __ SmiUntag(scratch1, object);
+ __ mtc1(scratch1, dst);
+ __ cvt_d_w(dst, dst);
+ if (destination == kCoreRegisters) {
+ // Load the converted smi to dst1 and dst2 in double format.
+ __ Move(dst1, dst2, dst);
+ }
+ } else {
+ ASSERT(destination == kCoreRegisters);
+ // Write smi to dst1 and dst2 double format.
+ __ mov(scratch1, object);
+ ConvertToDoubleStub stub(dst2, dst1, scratch1, scratch2);
+ __ push(ra);
+ __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
+ __ pop(ra);
+ }
+
+ __ bind(&done);
}
-void FloatingPointHelper::LoadNumberAsInteger(MacroAssembler* masm,
- Register object,
- Register dst,
- Register heap_number_map,
- Register scratch1,
- Register scratch2,
- FPURegister double_scratch,
- Label* not_int32) {
- UNIMPLEMENTED_MIPS();
+void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ FPURegister double_scratch,
+ Label* not_number) {
+ if (FLAG_debug_code) {
+ __ AbortIfNotRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ "HeapNumberMap register clobbered.");
+ }
+ Label is_smi;
+ Label done;
+ Label not_in_int32_range;
+
+ __ JumpIfSmi(object, &is_smi);
+ __ lw(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset));
+ __ Branch(not_number, ne, scratch1, Operand(heap_number_map));
+ __ ConvertToInt32(object,
+ dst,
+ scratch1,
+ scratch2,
+ double_scratch,
+ &not_in_int32_range);
+ __ jmp(&done);
+
+ __ bind(&not_in_int32_range);
+ __ lw(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ lw(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ __ EmitOutOfInt32RangeTruncate(dst,
+ scratch1,
+ scratch2,
+ scratch3);
+
+ __ jmp(&done);
+
+ __ bind(&is_smi);
+ __ SmiUntag(dst, object);
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm,
+ Register int_scratch,
+ Destination destination,
+ FPURegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register scratch2,
+ FPURegister single_scratch) {
+ ASSERT(!int_scratch.is(scratch2));
+ ASSERT(!int_scratch.is(dst1));
+ ASSERT(!int_scratch.is(dst2));
+
+ Label done;
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ mtc1(int_scratch, single_scratch);
+ __ cvt_d_w(double_dst, single_scratch);
+ if (destination == kCoreRegisters) {
+ __ Move(dst1, dst2, double_dst);
+ }
+ } else {
+ Label fewer_than_20_useful_bits;
+ // Expected output:
+ // | dst2 | dst1 |
+ // | s | exp | mantissa |
+
+ // Check for zero.
+ __ mov(dst2, int_scratch);
+ __ mov(dst1, int_scratch);
+ __ Branch(&done, eq, int_scratch, Operand(zero_reg));
+
+ // Preload the sign of the value.
+ __ And(dst2, int_scratch, Operand(HeapNumber::kSignMask));
+ // Get the absolute value of the object (as an unsigned integer).
+ Label skip_sub;
+ __ Branch(&skip_sub, ge, dst2, Operand(zero_reg));
+ __ Subu(int_scratch, zero_reg, int_scratch);
+ __ bind(&skip_sub);
+
+ // Get mantisssa[51:20].
+
+ // Get the position of the first set bit.
+ __ clz(dst1, int_scratch);
+ __ li(scratch2, 31);
+ __ Subu(dst1, scratch2, dst1);
+
+ // Set the exponent.
+ __ Addu(scratch2, dst1, Operand(HeapNumber::kExponentBias));
+ __ Ins(dst2, scratch2,
+ HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+
+ // Clear the first non null bit.
+ __ li(scratch2, Operand(1));
+ __ sllv(scratch2, scratch2, dst1);
+ __ li(at, -1);
+ __ Xor(scratch2, scratch2, at);
+ __ And(int_scratch, int_scratch, scratch2);
+
+ // Get the number of bits to set in the lower part of the mantissa.
+ __ Subu(scratch2, dst1, Operand(HeapNumber::kMantissaBitsInTopWord));
+ __ Branch(&fewer_than_20_useful_bits, lt, scratch2, Operand(zero_reg));
+ // Set the higher 20 bits of the mantissa.
+ __ srlv(at, int_scratch, scratch2);
+ __ or_(dst2, dst2, at);
+ __ li(at, 32);
+ __ subu(scratch2, at, scratch2);
+ __ sllv(dst1, int_scratch, scratch2);
+ __ Branch(&done);
+
+ __ bind(&fewer_than_20_useful_bits);
+ __ li(at, HeapNumber::kMantissaBitsInTopWord);
+ __ subu(scratch2, at, dst1);
+ __ sllv(scratch2, int_scratch, scratch2);
+ __ Or(dst2, dst2, scratch2);
+ // Set dst1 to 0.
+ __ mov(dst1, zero_reg);
+ }
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ FPURegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ FPURegister single_scratch,
+ Label* not_int32) {
+ ASSERT(!scratch1.is(object) && !scratch2.is(object));
+ ASSERT(!scratch1.is(scratch2));
+ ASSERT(!heap_number_map.is(object) &&
+ !heap_number_map.is(scratch1) &&
+ !heap_number_map.is(scratch2));
+
+ Label done, obj_is_not_smi;
+
+ __ JumpIfNotSmi(object, &obj_is_not_smi);
+ __ SmiUntag(scratch1, object);
+ ConvertIntToDouble(masm, scratch1, destination, double_dst, dst1, dst2,
+ scratch2, single_scratch);
+ __ Branch(&done);
+
+ __ bind(&obj_is_not_smi);
+ if (FLAG_debug_code) {
+ __ AbortIfNotRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ "HeapNumberMap register clobbered.");
+ }
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
+
+ // Load the number.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Load the double value.
+ __ ldc1(double_dst, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ // NOTE: ARM uses a MacroAssembler function here (EmitVFPTruncate).
+ // On MIPS a lot of things cannot be implemented the same way so right
+ // now it makes a lot more sense to just do things manually.
+
+ // Save FCSR.
+ __ cfc1(scratch1, FCSR);
+ // Disable FPU exceptions.
+ __ ctc1(zero_reg, FCSR);
+ __ trunc_w_d(single_scratch, double_dst);
+ // Retrieve FCSR.
+ __ cfc1(scratch2, FCSR);
+ // Restore FCSR.
+ __ ctc1(scratch1, FCSR);
+
+ // Check for inexact conversion.
+ __ srl(scratch2, scratch2, kFCSRFlagShift);
+ __ And(scratch2, scratch2, (kFCSRFlagMask | kFCSRInexactFlagBit));
+
+ // Jump to not_int32 if the operation did not succeed.
+ __ Branch(not_int32, ne, scratch2, Operand(zero_reg));
+
+ if (destination == kCoreRegisters) {
+ __ Move(dst1, dst2, double_dst);
+ }
+
+ } else {
+ ASSERT(!scratch1.is(object) && !scratch2.is(object));
+ // Load the double value in the destination registers.
+ __ lw(dst2, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ lw(dst1, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ // Check for 0 and -0.
+ __ And(scratch1, dst1, Operand(~HeapNumber::kSignMask));
+ __ Or(scratch1, scratch1, Operand(dst2));
+ __ Branch(&done, eq, scratch1, Operand(zero_reg));
+
+ // Check that the value can be exactly represented by a 32-bit integer.
+ // Jump to not_int32 if that's not the case.
+ DoubleIs32BitInteger(masm, dst1, dst2, scratch1, scratch2, not_int32);
+
+ // dst1 and dst2 were trashed. Reload the double value.
+ __ lw(dst2, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ lw(dst1, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+ }
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ FPURegister double_scratch,
+ Label* not_int32) {
+ ASSERT(!dst.is(object));
+ ASSERT(!scratch1.is(object) && !scratch2.is(object) && !scratch3.is(object));
+ ASSERT(!scratch1.is(scratch2) &&
+ !scratch1.is(scratch3) &&
+ !scratch2.is(scratch3));
+
+ Label done;
+
+ // Untag the object into the destination register.
+ __ SmiUntag(dst, object);
+ // Just return if the object is a smi.
+ __ JumpIfSmi(object, &done);
+
+ if (FLAG_debug_code) {
+ __ AbortIfNotRootValue(heap_number_map,
+ Heap::kHeapNumberMapRootIndex,
+ "HeapNumberMap register clobbered.");
+ }
+ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
+
+ // Object is a heap number.
+ // Convert the floating point value to a 32-bit integer.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Load the double value.
+ __ ldc1(double_scratch, FieldMemOperand(object, HeapNumber::kValueOffset));
+
+ // NOTE: ARM uses a MacroAssembler function here (EmitVFPTruncate).
+ // On MIPS a lot of things cannot be implemented the same way so right
+ // now it makes a lot more sense to just do things manually.
+
+ // Save FCSR.
+ __ cfc1(scratch1, FCSR);
+ // Disable FPU exceptions.
+ __ ctc1(zero_reg, FCSR);
+ __ trunc_w_d(double_scratch, double_scratch);
+ // Retrieve FCSR.
+ __ cfc1(scratch2, FCSR);
+ // Restore FCSR.
+ __ ctc1(scratch1, FCSR);
+
+ // Check for inexact conversion.
+ __ srl(scratch2, scratch2, kFCSRFlagShift);
+ __ And(scratch2, scratch2, (kFCSRFlagMask | kFCSRInexactFlagBit));
+
+ // Jump to not_int32 if the operation did not succeed.
+ __ Branch(not_int32, ne, scratch2, Operand(zero_reg));
+ // Get the result in the destination register.
+ __ mfc1(dst, double_scratch);
+
+ } else {
+ // Load the double value in the destination registers.
+ __ lw(scratch2, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ lw(scratch1, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+ // Check for 0 and -0.
+ __ And(dst, scratch1, Operand(~HeapNumber::kSignMask));
+ __ Or(dst, scratch2, Operand(dst));
+ __ Branch(&done, eq, dst, Operand(zero_reg));
+
+ DoubleIs32BitInteger(masm, scratch1, scratch2, dst, scratch3, not_int32);
+
+ // Registers state after DoubleIs32BitInteger.
+ // dst: mantissa[51:20].
+ // scratch2: 1
+
+ // Shift back the higher bits of the mantissa.
+ __ srlv(dst, dst, scratch3);
+ // Set the implicit first bit.
+ __ li(at, 32);
+ __ subu(scratch3, at, scratch3);
+ __ sllv(scratch2, scratch2, scratch3);
+ __ Or(dst, dst, scratch2);
+ // Set the sign.
+ __ lw(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ __ And(scratch1, scratch1, Operand(HeapNumber::kSignMask));
+ Label skip_sub;
+ __ Branch(&skip_sub, ge, scratch1, Operand(zero_reg));
+ __ Subu(dst, zero_reg, dst);
+ __ bind(&skip_sub);
+ }
+
+ __ bind(&done);
+}
+
+
+void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm,
+ Register src1,
+ Register src2,
+ Register dst,
+ Register scratch,
+ Label* not_int32) {
+ // Get exponent alone in scratch.
+ __ Ext(scratch,
+ src1,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // Substract the bias from the exponent.
+ __ Subu(scratch, scratch, Operand(HeapNumber::kExponentBias));
+
+ // src1: higher (exponent) part of the double value.
+ // src2: lower (mantissa) part of the double value.
+ // scratch: unbiased exponent.
+
+ // Fast cases. Check for obvious non 32-bit integer values.
+ // Negative exponent cannot yield 32-bit integers.
+ __ Branch(not_int32, lt, scratch, Operand(zero_reg));
+ // Exponent greater than 31 cannot yield 32-bit integers.
+ // Also, a positive value with an exponent equal to 31 is outside of the
+ // signed 32-bit integer range.
+ // Another way to put it is that if (exponent - signbit) > 30 then the
+ // number cannot be represented as an int32.
+ Register tmp = dst;
+ __ srl(at, src1, 31);
+ __ subu(tmp, scratch, at);
+ __ Branch(not_int32, gt, tmp, Operand(30));
+ // - Bits [21:0] in the mantissa are not null.
+ __ And(tmp, src2, 0x3fffff);
+ __ Branch(not_int32, ne, tmp, Operand(zero_reg));
+
+ // Otherwise the exponent needs to be big enough to shift left all the
+ // non zero bits left. So we need the (30 - exponent) last bits of the
+ // 31 higher bits of the mantissa to be null.
+ // Because bits [21:0] are null, we can check instead that the
+ // (32 - exponent) last bits of the 32 higher bits of the mantisssa are null.
+
+ // Get the 32 higher bits of the mantissa in dst.
+ __ Ext(dst,
+ src2,
+ HeapNumber::kMantissaBitsInTopWord,
+ 32 - HeapNumber::kMantissaBitsInTopWord);
+ __ sll(at, src1, HeapNumber::kNonMantissaBitsInTopWord);
+ __ or_(dst, dst, at);
+
+ // Create the mask and test the lower bits (of the higher bits).
+ __ li(at, 32);
+ __ subu(scratch, at, scratch);
+ __ li(src2, 1);
+ __ sllv(src1, src2, scratch);
+ __ Subu(src1, src1, Operand(1));
+ __ And(src1, dst, src1);
+ __ Branch(not_int32, ne, src1, Operand(zero_reg));
+}
+
+
+void FloatingPointHelper::CallCCodeForDoubleOperation(
+ MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch) {
+ // Using core registers:
+ // a0: Left value (least significant part of mantissa).
+ // a1: Left value (sign, exponent, top of mantissa).
+ // a2: Right value (least significant part of mantissa).
+ // a3: Right value (sign, exponent, top of mantissa).
+
+ // Assert that heap_number_result is saved.
+ // We currently always use s0 to pass it.
+ ASSERT(heap_number_result.is(s0));
+
+ // Push the current return address before the C call.
+ __ push(ra);
+ __ PrepareCallCFunction(4, scratch); // Two doubles are 4 arguments.
+ if (!IsMipsSoftFloatABI) {
+ CpuFeatures::Scope scope(FPU);
+ // We are not using MIPS FPU instructions, and parameters for the runtime
+ // function call are prepaired in a0-a3 registers, but function we are
+ // calling is compiled with hard-float flag and expecting hard float ABI
+ // (parameters in f12/f14 registers). We need to copy parameters from
+ // a0-a3 registers to f12/f14 register pairs.
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
+ }
+ // Call C routine that may not cause GC or other trouble.
+ __ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()),
+ 4);
+ // Store answer in the overwritable heap number.
+ if (!IsMipsSoftFloatABI) {
+ CpuFeatures::Scope scope(FPU);
+ // Double returned in register f0.
+ __ sdc1(f0, FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ } else {
+ // Double returned in registers v0 and v1.
+ __ sw(v1, FieldMemOperand(heap_number_result, HeapNumber::kExponentOffset));
+ __ sw(v0, FieldMemOperand(heap_number_result, HeapNumber::kMantissaOffset));
+ }
+ // Place heap_number_result in v0 and return to the pushed return address.
+ __ mov(v0, heap_number_result);
+ __ pop(ra);
+ __ Ret();
}
// See comment for class, this does NOT work for int32's that are in Smi range.
void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label max_negative_int;
+ // the_int_ has the answer which is a signed int32 but not a Smi.
+ // We test for the special value that has a different exponent.
+ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
+ // Test sign, and save for later conditionals.
+ __ And(sign_, the_int_, Operand(0x80000000u));
+ __ Branch(&max_negative_int, eq, the_int_, Operand(0x80000000u));
+
+ // Set up the correct exponent in scratch_. All non-Smi int32s have the same.
+ // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased).
+ uint32_t non_smi_exponent =
+ (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
+ __ li(scratch_, Operand(non_smi_exponent));
+ // Set the sign bit in scratch_ if the value was negative.
+ __ or_(scratch_, scratch_, sign_);
+ // Subtract from 0 if the value was negative.
+ __ subu(at, zero_reg, the_int_);
+ __ movn(the_int_, at, sign_);
+ // We should be masking the implict first digit of the mantissa away here,
+ // but it just ends up combining harmlessly with the last digit of the
+ // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get
+ // the most significant 1 to hit the last bit of the 12 bit sign and exponent.
+ ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0);
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2;
+ __ srl(at, the_int_, shift_distance);
+ __ or_(scratch_, scratch_, at);
+ __ sw(scratch_, FieldMemOperand(the_heap_number_,
+ HeapNumber::kExponentOffset));
+ __ sll(scratch_, the_int_, 32 - shift_distance);
+ __ sw(scratch_, FieldMemOperand(the_heap_number_,
+ HeapNumber::kMantissaOffset));
+ __ Ret();
+
+ __ bind(&max_negative_int);
+ // The max negative int32 is stored as a positive number in the mantissa of
+ // a double because it uses a sign bit instead of using two's complement.
+ // The actual mantissa bits stored are all 0 because the implicit most
+ // significant 1 bit is not stored.
+ non_smi_exponent += 1 << HeapNumber::kExponentShift;
+ __ li(scratch_, Operand(HeapNumber::kSignMask | non_smi_exponent));
+ __ sw(scratch_,
+ FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset));
+ __ mov(scratch_, zero_reg);
+ __ sw(scratch_,
+ FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset));
+ __ Ret();
+}
+
+
+// Handle the case where the lhs and rhs are the same object.
+// Equality is almost reflexive (everything but NaN), so this is a test
+// for "identity and not NaN".
+static void EmitIdenticalObjectComparison(MacroAssembler* masm,
+ Label* slow,
+ Condition cc,
+ bool never_nan_nan) {
+ Label not_identical;
+ Label heap_number, return_equal;
+ Register exp_mask_reg = t5;
+
+ __ Branch(&not_identical, ne, a0, Operand(a1));
+
+ // The two objects are identical. If we know that one of them isn't NaN then
+ // we now know they test equal.
+ if (cc != eq || !never_nan_nan) {
+ __ li(exp_mask_reg, Operand(HeapNumber::kExponentMask));
+
+ // Test for NaN. Sadly, we can't just compare to factory->nan_value(),
+ // so we do the second best thing - test it ourselves.
+ // They are both equal and they are not both Smis so both of them are not
+ // Smis. If it's not a heap number, then return equal.
+ if (cc == less || cc == greater) {
+ __ GetObjectType(a0, t4, t4);
+ __ Branch(slow, greater, t4, Operand(FIRST_JS_OBJECT_TYPE));
+ } else {
+ __ GetObjectType(a0, t4, t4);
+ __ Branch(&heap_number, eq, t4, Operand(HEAP_NUMBER_TYPE));
+ // Comparing JS objects with <=, >= is complicated.
+ if (cc != eq) {
+ __ Branch(slow, greater, t4, Operand(FIRST_JS_OBJECT_TYPE));
+ // Normally here we fall through to return_equal, but undefined is
+ // special: (undefined == undefined) == true, but
+ // (undefined <= undefined) == false! See ECMAScript 11.8.5.
+ if (cc == less_equal || cc == greater_equal) {
+ __ Branch(&return_equal, ne, t4, Operand(ODDBALL_TYPE));
+ __ LoadRoot(t2, Heap::kUndefinedValueRootIndex);
+ __ Branch(&return_equal, ne, a0, Operand(t2));
+ if (cc == le) {
+ // undefined <= undefined should fail.
+ __ li(v0, Operand(GREATER));
+ } else {
+ // undefined >= undefined should fail.
+ __ li(v0, Operand(LESS));
+ }
+ __ Ret();
+ }
+ }
+ }
+ }
+
+ __ bind(&return_equal);
+ if (cc == less) {
+ __ li(v0, Operand(GREATER)); // Things aren't less than themselves.
+ } else if (cc == greater) {
+ __ li(v0, Operand(LESS)); // Things aren't greater than themselves.
+ } else {
+ __ mov(v0, zero_reg); // Things are <=, >=, ==, === themselves.
+ }
+ __ Ret();
+
+ if (cc != eq || !never_nan_nan) {
+ // For less and greater we don't have to check for NaN since the result of
+ // x < x is false regardless. For the others here is some code to check
+ // for NaN.
+ if (cc != lt && cc != gt) {
+ __ bind(&heap_number);
+ // It is a heap number, so return non-equal if it's NaN and equal if it's
+ // not NaN.
+
+ // The representation of NaN values has all exponent bits (52..62) set,
+ // and not all mantissa bits (0..51) clear.
+ // Read top bits of double representation (second word of value).
+ __ lw(t2, FieldMemOperand(a0, HeapNumber::kExponentOffset));
+ // Test that exponent bits are all set.
+ __ And(t3, t2, Operand(exp_mask_reg));
+ // If all bits not set (ne cond), then not a NaN, objects are equal.
+ __ Branch(&return_equal, ne, t3, Operand(exp_mask_reg));
+
+ // Shift out flag and all exponent bits, retaining only mantissa.
+ __ sll(t2, t2, HeapNumber::kNonMantissaBitsInTopWord);
+ // Or with all low-bits of mantissa.
+ __ lw(t3, FieldMemOperand(a0, HeapNumber::kMantissaOffset));
+ __ Or(v0, t3, Operand(t2));
+ // For equal we already have the right value in v0: Return zero (equal)
+ // if all bits in mantissa are zero (it's an Infinity) and non-zero if
+ // not (it's a NaN). For <= and >= we need to load v0 with the failing
+ // value if it's a NaN.
+ if (cc != eq) {
+ // All-zero means Infinity means equal.
+ __ Ret(eq, v0, Operand(zero_reg));
+ if (cc == le) {
+ __ li(v0, Operand(GREATER)); // NaN <= NaN should fail.
+ } else {
+ __ li(v0, Operand(LESS)); // NaN >= NaN should fail.
+ }
+ }
+ __ Ret();
+ }
+ // No fall through here.
+ }
+
+ __ bind(&not_identical);
+}
+
+
+static void EmitSmiNonsmiComparison(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* both_loaded_as_doubles,
+ Label* slow,
+ bool strict) {
+ ASSERT((lhs.is(a0) && rhs.is(a1)) ||
+ (lhs.is(a1) && rhs.is(a0)));
+
+ Label lhs_is_smi;
+ __ And(t0, lhs, Operand(kSmiTagMask));
+ __ Branch(&lhs_is_smi, eq, t0, Operand(zero_reg));
+ // Rhs is a Smi.
+ // Check whether the non-smi is a heap number.
+ __ GetObjectType(lhs, t4, t4);
+ if (strict) {
+ // If lhs was not a number and rhs was a Smi then strict equality cannot
+ // succeed. Return non-equal (lhs is already not zero).
+ __ mov(v0, lhs);
+ __ Ret(ne, t4, Operand(HEAP_NUMBER_TYPE));
+ } else {
+ // Smi compared non-strictly with a non-Smi non-heap-number. Call
+ // the runtime.
+ __ Branch(slow, ne, t4, Operand(HEAP_NUMBER_TYPE));
+ }
+
+ // Rhs is a smi, lhs is a number.
+ // Convert smi rhs to double.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ sra(at, rhs, kSmiTagSize);
+ __ mtc1(at, f14);
+ __ cvt_d_w(f14, f14);
+ __ ldc1(f12, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+ } else {
+ // Load lhs to a double in a2, a3.
+ __ lw(a3, FieldMemOperand(lhs, HeapNumber::kValueOffset + 4));
+ __ lw(a2, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+
+ // Write Smi from rhs to a1 and a0 in double format. t5 is scratch.
+ __ mov(t6, rhs);
+ ConvertToDoubleStub stub1(a1, a0, t6, t5);
+ __ push(ra);
+ __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET);
+
+ __ pop(ra);
+ }
+
+ // We now have both loaded as doubles.
+ __ jmp(both_loaded_as_doubles);
+
+ __ bind(&lhs_is_smi);
+ // Lhs is a Smi. Check whether the non-smi is a heap number.
+ __ GetObjectType(rhs, t4, t4);
+ if (strict) {
+ // If lhs was not a number and rhs was a Smi then strict equality cannot
+ // succeed. Return non-equal.
+ __ li(v0, Operand(1));
+ __ Ret(ne, t4, Operand(HEAP_NUMBER_TYPE));
+ } else {
+ // Smi compared non-strictly with a non-Smi non-heap-number. Call
+ // the runtime.
+ __ Branch(slow, ne, t4, Operand(HEAP_NUMBER_TYPE));
+ }
+
+ // Lhs is a smi, rhs is a number.
+ // Convert smi lhs to double.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ sra(at, lhs, kSmiTagSize);
+ __ mtc1(at, f12);
+ __ cvt_d_w(f12, f12);
+ __ ldc1(f14, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ } else {
+ // Convert lhs to a double format. t5 is scratch.
+ __ mov(t6, lhs);
+ ConvertToDoubleStub stub2(a3, a2, t6, t5);
+ __ push(ra);
+ __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET);
+ __ pop(ra);
+ // Load rhs to a double in a1, a0.
+ if (rhs.is(a0)) {
+ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
+ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ } else {
+ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
+ }
+ }
+ // Fall through to both_loaded_as_doubles.
}
void EmitNanCheck(MacroAssembler* masm, Condition cc) {
- UNIMPLEMENTED_MIPS();
+ bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Lhs and rhs are already loaded to f12 and f14 register pairs.
+ __ Move(t0, t1, f14);
+ __ Move(t2, t3, f12);
+ } else {
+ // Lhs and rhs are already loaded to GP registers.
+ __ mov(t0, a0); // a0 has LS 32 bits of rhs.
+ __ mov(t1, a1); // a1 has MS 32 bits of rhs.
+ __ mov(t2, a2); // a2 has LS 32 bits of lhs.
+ __ mov(t3, a3); // a3 has MS 32 bits of lhs.
+ }
+ Register rhs_exponent = exp_first ? t0 : t1;
+ Register lhs_exponent = exp_first ? t2 : t3;
+ Register rhs_mantissa = exp_first ? t1 : t0;
+ Register lhs_mantissa = exp_first ? t3 : t2;
+ Label one_is_nan, neither_is_nan;
+ Label lhs_not_nan_exp_mask_is_loaded;
+
+ Register exp_mask_reg = t4;
+ __ li(exp_mask_reg, HeapNumber::kExponentMask);
+ __ and_(t5, lhs_exponent, exp_mask_reg);
+ __ Branch(&lhs_not_nan_exp_mask_is_loaded, ne, t5, Operand(exp_mask_reg));
+
+ __ sll(t5, lhs_exponent, HeapNumber::kNonMantissaBitsInTopWord);
+ __ Branch(&one_is_nan, ne, t5, Operand(zero_reg));
+
+ __ Branch(&one_is_nan, ne, lhs_mantissa, Operand(zero_reg));
+
+ __ li(exp_mask_reg, HeapNumber::kExponentMask);
+ __ bind(&lhs_not_nan_exp_mask_is_loaded);
+ __ and_(t5, rhs_exponent, exp_mask_reg);
+
+ __ Branch(&neither_is_nan, ne, t5, Operand(exp_mask_reg));
+
+ __ sll(t5, rhs_exponent, HeapNumber::kNonMantissaBitsInTopWord);
+ __ Branch(&one_is_nan, ne, t5, Operand(zero_reg));
+
+ __ Branch(&neither_is_nan, eq, rhs_mantissa, Operand(zero_reg));
+
+ __ bind(&one_is_nan);
+ // NaN comparisons always fail.
+ // Load whatever we need in v0 to make the comparison fail.
+ if (cc == lt || cc == le) {
+ __ li(v0, Operand(GREATER));
+ } else {
+ __ li(v0, Operand(LESS));
+ }
+ __ Ret(); // Return.
+
+ __ bind(&neither_is_nan);
+}
+
+
+static void EmitTwoNonNanDoubleComparison(MacroAssembler* masm, Condition cc) {
+ // f12 and f14 have the two doubles. Neither is a NaN.
+ // Call a native function to do a comparison between two non-NaNs.
+ // Call C routine that may not cause GC or other trouble.
+ // We use a call_was and return manually because we need arguments slots to
+ // be freed.
+
+ Label return_result_not_equal, return_result_equal;
+ if (cc == eq) {
+ // Doubles are not equal unless they have the same bit pattern.
+ // Exception: 0 and -0.
+ bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Lhs and rhs are already loaded to f12 and f14 register pairs.
+ __ Move(t0, t1, f14);
+ __ Move(t2, t3, f12);
+ } else {
+ // Lhs and rhs are already loaded to GP registers.
+ __ mov(t0, a0); // a0 has LS 32 bits of rhs.
+ __ mov(t1, a1); // a1 has MS 32 bits of rhs.
+ __ mov(t2, a2); // a2 has LS 32 bits of lhs.
+ __ mov(t3, a3); // a3 has MS 32 bits of lhs.
+ }
+ Register rhs_exponent = exp_first ? t0 : t1;
+ Register lhs_exponent = exp_first ? t2 : t3;
+ Register rhs_mantissa = exp_first ? t1 : t0;
+ Register lhs_mantissa = exp_first ? t3 : t2;
+
+ __ xor_(v0, rhs_mantissa, lhs_mantissa);
+ __ Branch(&return_result_not_equal, ne, v0, Operand(zero_reg));
+
+ __ subu(v0, rhs_exponent, lhs_exponent);
+ __ Branch(&return_result_equal, eq, v0, Operand(zero_reg));
+ // 0, -0 case.
+ __ sll(rhs_exponent, rhs_exponent, kSmiTagSize);
+ __ sll(lhs_exponent, lhs_exponent, kSmiTagSize);
+ __ or_(t4, rhs_exponent, lhs_exponent);
+ __ or_(t4, t4, rhs_mantissa);
+
+ __ Branch(&return_result_not_equal, ne, t4, Operand(zero_reg));
+
+ __ bind(&return_result_equal);
+ __ li(v0, Operand(EQUAL));
+ __ Ret();
+ }
+
+ __ bind(&return_result_not_equal);
+
+ if (!CpuFeatures::IsSupported(FPU)) {
+ __ push(ra);
+ __ PrepareCallCFunction(4, t4); // Two doubles count as 4 arguments.
+ if (!IsMipsSoftFloatABI) {
+ // We are not using MIPS FPU instructions, and parameters for the runtime
+ // function call are prepaired in a0-a3 registers, but function we are
+ // calling is compiled with hard-float flag and expecting hard float ABI
+ // (parameters in f12/f14 registers). We need to copy parameters from
+ // a0-a3 registers to f12/f14 register pairs.
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
+ }
+ __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()), 4);
+ __ pop(ra); // Because this function returns int, result is in v0.
+ __ Ret();
+ } else {
+ CpuFeatures::Scope scope(FPU);
+ Label equal, less_than;
+ __ c(EQ, D, f12, f14);
+ __ bc1t(&equal);
+ __ nop();
+
+ __ c(OLT, D, f12, f14);
+ __ bc1t(&less_than);
+ __ nop();
+
+ // Not equal, not less, not NaN, must be greater.
+ __ li(v0, Operand(GREATER));
+ __ Ret();
+
+ __ bind(&equal);
+ __ li(v0, Operand(EQUAL));
+ __ Ret();
+
+ __ bind(&less_than);
+ __ li(v0, Operand(LESS));
+ __ Ret();
+ }
+}
+
+
+static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
+ Register lhs,
+ Register rhs) {
+ // If either operand is a JSObject or an oddball value, then they are
+ // not equal since their pointers are different.
+ // There is no test for undetectability in strict equality.
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ Label first_non_object;
+ // Get the type of the first operand into a2 and compare it with
+ // FIRST_JS_OBJECT_TYPE.
+ __ GetObjectType(lhs, a2, a2);
+ __ Branch(&first_non_object, less, a2, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Return non-zero.
+ Label return_not_equal;
+ __ bind(&return_not_equal);
+ __ li(v0, Operand(1));
+ __ Ret();
+
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ Branch(&return_not_equal, eq, a2, Operand(ODDBALL_TYPE));
+
+ __ GetObjectType(rhs, a3, a3);
+ __ Branch(&return_not_equal, greater, a3, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Check for oddballs: true, false, null, undefined.
+ __ Branch(&return_not_equal, eq, a3, Operand(ODDBALL_TYPE));
+
+ // Now that we have the types we might as well check for symbol-symbol.
+ // Ensure that no non-strings have the symbol bit set.
+ STATIC_ASSERT(LAST_TYPE < kNotStringTag + kIsSymbolMask);
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ And(t2, a2, Operand(a3));
+ __ And(t0, t2, Operand(kIsSymbolMask));
+ __ Branch(&return_not_equal, ne, t0, Operand(zero_reg));
+}
+
+
+static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* both_loaded_as_doubles,
+ Label* not_heap_numbers,
+ Label* slow) {
+ __ GetObjectType(lhs, a3, a2);
+ __ Branch(not_heap_numbers, ne, a2, Operand(HEAP_NUMBER_TYPE));
+ __ lw(a2, FieldMemOperand(rhs, HeapObject::kMapOffset));
+ // If first was a heap number & second wasn't, go to slow case.
+ __ Branch(slow, ne, a3, Operand(a2));
+
+ // Both are heap numbers. Load them up then jump to the code we have
+ // for that.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ ldc1(f12, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+ __ ldc1(f14, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ } else {
+ __ lw(a2, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+ __ lw(a3, FieldMemOperand(lhs, HeapNumber::kValueOffset + 4));
+ if (rhs.is(a0)) {
+ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
+ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ } else {
+ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
+ }
+ }
+ __ jmp(both_loaded_as_doubles);
+}
+
+
+// Fast negative check for symbol-to-symbol equality.
+static void EmitCheckForSymbolsOrObjects(MacroAssembler* masm,
+ Register lhs,
+ Register rhs,
+ Label* possible_strings,
+ Label* not_both_strings) {
+ ASSERT((lhs.is(a0) && rhs.is(a1)) ||
+ (lhs.is(a1) && rhs.is(a0)));
+
+ // a2 is object type of lhs.
+ // Ensure that no non-strings have the symbol bit set.
+ Label object_test;
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ And(at, a2, Operand(kIsNotStringMask));
+ __ Branch(&object_test, ne, at, Operand(zero_reg));
+ __ And(at, a2, Operand(kIsSymbolMask));
+ __ Branch(possible_strings, eq, at, Operand(zero_reg));
+ __ GetObjectType(rhs, a3, a3);
+ __ Branch(not_both_strings, ge, a3, Operand(FIRST_NONSTRING_TYPE));
+ __ And(at, a3, Operand(kIsSymbolMask));
+ __ Branch(possible_strings, eq, at, Operand(zero_reg));
+
+ // Both are symbols. We already checked they weren't the same pointer
+ // so they are not equal.
+ __ li(v0, Operand(1)); // Non-zero indicates not equal.
+ __ Ret();
+
+ __ bind(&object_test);
+ __ Branch(not_both_strings, lt, a2, Operand(FIRST_JS_OBJECT_TYPE));
+ __ GetObjectType(rhs, a2, a3);
+ __ Branch(not_both_strings, lt, a3, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // If both objects are undetectable, they are equal. Otherwise, they
+ // are not equal, since they are different objects and an object is not
+ // equal to undefined.
+ __ lw(a3, FieldMemOperand(lhs, HeapObject::kMapOffset));
+ __ lbu(a2, FieldMemOperand(a2, Map::kBitFieldOffset));
+ __ lbu(a3, FieldMemOperand(a3, Map::kBitFieldOffset));
+ __ and_(a0, a2, a3);
+ __ And(a0, a0, Operand(1 << Map::kIsUndetectable));
+ __ Xor(v0, a0, Operand(1 << Map::kIsUndetectable));
+ __ Ret();
}
@@ -232,12 +1431,109 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
Register scratch3,
bool object_is_smi,
Label* not_found) {
- UNIMPLEMENTED_MIPS();
+ // Use of registers. Register result is used as a temporary.
+ Register number_string_cache = result;
+ Register mask = scratch3;
+
+ // Load the number string cache.
+ __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
+
+ // Make the hash mask from the length of the number string cache. It
+ // contains two elements (number and string) for each cache entry.
+ __ lw(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset));
+ // Divide length by two (length is a smi).
+ __ sra(mask, mask, kSmiTagSize + 1);
+ __ Addu(mask, mask, -1); // Make mask.
+
+ // Calculate the entry in the number string cache. The hash value in the
+ // number string cache for smis is just the smi value, and the hash for
+ // doubles is the xor of the upper and lower words. See
+ // Heap::GetNumberStringCache.
+ Isolate* isolate = masm->isolate();
+ Label is_smi;
+ Label load_result_from_cache;
+ if (!object_is_smi) {
+ __ JumpIfSmi(object, &is_smi);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ CheckMap(object,
+ scratch1,
+ Heap::kHeapNumberMapRootIndex,
+ not_found,
+ DONT_DO_SMI_CHECK);
+
+ STATIC_ASSERT(8 == kDoubleSize);
+ __ Addu(scratch1,
+ object,
+ Operand(HeapNumber::kValueOffset - kHeapObjectTag));
+ __ lw(scratch2, MemOperand(scratch1, kPointerSize));
+ __ lw(scratch1, MemOperand(scratch1, 0));
+ __ Xor(scratch1, scratch1, Operand(scratch2));
+ __ And(scratch1, scratch1, Operand(mask));
+
+ // Calculate address of entry in string cache: each entry consists
+ // of two pointer sized fields.
+ __ sll(scratch1, scratch1, kPointerSizeLog2 + 1);
+ __ Addu(scratch1, number_string_cache, scratch1);
+
+ Register probe = mask;
+ __ lw(probe,
+ FieldMemOperand(scratch1, FixedArray::kHeaderSize));
+ __ JumpIfSmi(probe, not_found);
+ __ ldc1(f12, FieldMemOperand(object, HeapNumber::kValueOffset));
+ __ ldc1(f14, FieldMemOperand(probe, HeapNumber::kValueOffset));
+ __ c(EQ, D, f12, f14);
+ __ bc1t(&load_result_from_cache);
+ __ nop(); // bc1t() requires explicit fill of branch delay slot.
+ __ Branch(not_found);
+ } else {
+ // Note that there is no cache check for non-FPU case, even though
+ // it seems there could be. May be a tiny opimization for non-FPU
+ // cores.
+ __ Branch(not_found);
+ }
+ }
+
+ __ bind(&is_smi);
+ Register scratch = scratch1;
+ __ sra(scratch, object, 1); // Shift away the tag.
+ __ And(scratch, mask, Operand(scratch));
+
+ // Calculate address of entry in string cache: each entry consists
+ // of two pointer sized fields.
+ __ sll(scratch, scratch, kPointerSizeLog2 + 1);
+ __ Addu(scratch, number_string_cache, scratch);
+
+ // Check if the entry is the smi we are looking for.
+ Register probe = mask;
+ __ lw(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
+ __ Branch(not_found, ne, object, Operand(probe));
+
+ // Get the result from the cache.
+ __ bind(&load_result_from_cache);
+ __ lw(result,
+ FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
+
+ __ IncrementCounter(isolate->counters()->number_to_string_native(),
+ 1,
+ scratch1,
+ scratch2);
}
void NumberToStringStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label runtime;
+
+ __ lw(a1, MemOperand(sp, 0));
+
+ // Generate code to lookup number in the number string cache.
+ GenerateLookupNumberStringCache(masm, a1, v0, a2, a3, t0, false, &runtime);
+ __ Addu(sp, sp, Operand(1 * kPointerSize));
+ __ Ret();
+
+ __ bind(&runtime);
+ // Handle number to string in the runtime system if not found in the cache.
+ __ TailCallRuntime(Runtime::kNumberToString, 1, 1);
}
@@ -245,105 +1541,1018 @@ void NumberToStringStub::Generate(MacroAssembler* masm) {
// On exit, v0 is 0, positive, or negative (smi) to indicate the result
// of the comparison.
void CompareStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label slow; // Call builtin.
+ Label not_smis, both_loaded_as_doubles;
+
+
+ if (include_smi_compare_) {
+ Label not_two_smis, smi_done;
+ __ Or(a2, a1, a0);
+ __ JumpIfNotSmi(a2, &not_two_smis);
+ __ sra(a1, a1, 1);
+ __ sra(a0, a0, 1);
+ __ Subu(v0, a1, a0);
+ __ Ret();
+ __ bind(&not_two_smis);
+ } else if (FLAG_debug_code) {
+ __ Or(a2, a1, a0);
+ __ And(a2, a2, kSmiTagMask);
+ __ Assert(ne, "CompareStub: unexpected smi operands.",
+ a2, Operand(zero_reg));
+ }
+
+
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
+
+ // Handle the case where the objects are identical. Either returns the answer
+ // or goes to slow. Only falls through if the objects were not identical.
+ EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
+
+ // If either is a Smi (we know that not both are), then they can only
+ // be strictly equal if the other is a HeapNumber.
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ And(t2, lhs_, Operand(rhs_));
+ __ JumpIfNotSmi(t2, &not_smis, t0);
+ // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
+ // 1) Return the answer.
+ // 2) Go to slow.
+ // 3) Fall through to both_loaded_as_doubles.
+ // 4) Jump to rhs_not_nan.
+ // In cases 3 and 4 we have found out we were dealing with a number-number
+ // comparison and the numbers have been loaded into f12 and f14 as doubles,
+ // or in GP registers (a0, a1, a2, a3) depending on the presence of the FPU.
+ EmitSmiNonsmiComparison(masm, lhs_, rhs_,
+ &both_loaded_as_doubles, &slow, strict_);
+
+ __ bind(&both_loaded_as_doubles);
+ // f12, f14 are the double representations of the left hand side
+ // and the right hand side if we have FPU. Otherwise a2, a3 represent
+ // left hand side and a0, a1 represent right hand side.
+
+ Isolate* isolate = masm->isolate();
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ Label nan;
+ __ li(t0, Operand(LESS));
+ __ li(t1, Operand(GREATER));
+ __ li(t2, Operand(EQUAL));
+
+ // Check if either rhs or lhs is NaN.
+ __ c(UN, D, f12, f14);
+ __ bc1t(&nan);
+ __ nop();
+
+ // Check if LESS condition is satisfied. If true, move conditionally
+ // result to v0.
+ __ c(OLT, D, f12, f14);
+ __ movt(v0, t0);
+ // Use previous check to store conditionally to v0 oposite condition
+ // (GREATER). If rhs is equal to lhs, this will be corrected in next
+ // check.
+ __ movf(v0, t1);
+ // Check if EQUAL condition is satisfied. If true, move conditionally
+ // result to v0.
+ __ c(EQ, D, f12, f14);
+ __ movt(v0, t2);
+
+ __ Ret();
+
+ __ bind(&nan);
+ // NaN comparisons always fail.
+ // Load whatever we need in v0 to make the comparison fail.
+ if (cc_ == lt || cc_ == le) {
+ __ li(v0, Operand(GREATER));
+ } else {
+ __ li(v0, Operand(LESS));
+ }
+ __ Ret();
+ } else {
+ // Checks for NaN in the doubles we have loaded. Can return the answer or
+ // fall through if neither is a NaN. Also binds rhs_not_nan.
+ EmitNanCheck(masm, cc_);
+
+ // Compares two doubles that are not NaNs. Returns the answer.
+ // Never falls through.
+ EmitTwoNonNanDoubleComparison(masm, cc_);
+ }
+
+ __ bind(&not_smis);
+ // At this point we know we are dealing with two different objects,
+ // and neither of them is a Smi. The objects are in lhs_ and rhs_.
+ if (strict_) {
+ // This returns non-equal for some object types, or falls through if it
+ // was not lucky.
+ EmitStrictTwoHeapObjectCompare(masm, lhs_, rhs_);
+ }
+
+ Label check_for_symbols;
+ Label flat_string_check;
+ // Check for heap-number-heap-number comparison. Can jump to slow case,
+ // or load both doubles and jump to the code that handles
+ // that case. If the inputs are not doubles then jumps to check_for_symbols.
+ // In this case a2 will contain the type of lhs_.
+ EmitCheckForTwoHeapNumbers(masm,
+ lhs_,
+ rhs_,
+ &both_loaded_as_doubles,
+ &check_for_symbols,
+ &flat_string_check);
+
+ __ bind(&check_for_symbols);
+ if (cc_ == eq && !strict_) {
+ // Returns an answer for two symbols or two detectable objects.
+ // Otherwise jumps to string case or not both strings case.
+ // Assumes that a2 is the type of lhs_ on entry.
+ EmitCheckForSymbolsOrObjects(masm, lhs_, rhs_, &flat_string_check, &slow);
+ }
+
+ // Check for both being sequential ASCII strings, and inline if that is the
+ // case.
+ __ bind(&flat_string_check);
+
+ __ JumpIfNonSmisNotBothSequentialAsciiStrings(lhs_, rhs_, a2, a3, &slow);
+
+ __ IncrementCounter(isolate->counters()->string_compare_native(), 1, a2, a3);
+ if (cc_ == eq) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
+ lhs_,
+ rhs_,
+ a2,
+ a3,
+ t0);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ lhs_,
+ rhs_,
+ a2,
+ a3,
+ t0,
+ t1);
+ }
+ // Never falls through to here.
+
+ __ bind(&slow);
+ // Prepare for call to builtin. Push object pointers, a0 (lhs) first,
+ // a1 (rhs) second.
+ __ Push(lhs_, rhs_);
+ // Figure out which native to call and setup the arguments.
+ Builtins::JavaScript native;
+ if (cc_ == eq) {
+ native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
+ } else {
+ native = Builtins::COMPARE;
+ int ncr; // NaN compare result.
+ if (cc_ == lt || cc_ == le) {
+ ncr = GREATER;
+ } else {
+ ASSERT(cc_ == gt || cc_ == ge); // Remaining cases.
+ ncr = LESS;
+ }
+ __ li(a0, Operand(Smi::FromInt(ncr)));
+ __ push(a0);
+ }
+
+ // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ InvokeBuiltin(native, JUMP_FUNCTION);
}
// This stub does not handle the inlined cases (Smis, Booleans, undefined).
// The stub returns zero for false, and a non-zero value for true.
void ToBooleanStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // This stub uses FPU instructions.
+ CpuFeatures::Scope scope(FPU);
+
+ Label false_result;
+ Label not_heap_number;
+ Register scratch0 = t5.is(tos_) ? t3 : t5;
+
+ // undefined -> false
+ __ LoadRoot(scratch0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&false_result, eq, tos_, Operand(scratch0));
+
+ // Boolean -> its value
+ __ LoadRoot(scratch0, Heap::kFalseValueRootIndex);
+ __ Branch(&false_result, eq, tos_, Operand(scratch0));
+ __ LoadRoot(scratch0, Heap::kTrueValueRootIndex);
+ // "tos_" is a register and contains a non-zero value. Hence we implicitly
+ // return true if the equal condition is satisfied.
+ __ Ret(eq, tos_, Operand(scratch0));
+
+ // Smis: 0 -> false, all other -> true
+ __ And(scratch0, tos_, tos_);
+ __ Branch(&false_result, eq, scratch0, Operand(zero_reg));
+ __ And(scratch0, tos_, Operand(kSmiTagMask));
+ // "tos_" is a register and contains a non-zero value. Hence we implicitly
+ // return true if the not equal condition is satisfied.
+ __ Ret(eq, scratch0, Operand(zero_reg));
+
+ // 'null' -> false
+ __ LoadRoot(scratch0, Heap::kNullValueRootIndex);
+ __ Branch(&false_result, eq, tos_, Operand(scratch0));
+
+ // HeapNumber => false if +0, -0, or NaN.
+ __ lw(scratch0, FieldMemOperand(tos_, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ __ Branch(&not_heap_number, ne, scratch0, Operand(at));
+
+ __ ldc1(f12, FieldMemOperand(tos_, HeapNumber::kValueOffset));
+ __ fcmp(f12, 0.0, UEQ);
+
+ // "tos_" is a register, and contains a non zero value by default.
+ // Hence we only need to overwrite "tos_" with zero to return false for
+ // FP_ZERO or FP_NAN cases. Otherwise, by default it returns true.
+ __ movt(tos_, zero_reg);
+ __ Ret();
+
+ __ bind(&not_heap_number);
+
+ // It can be an undetectable object.
+ // Undetectable => false.
+ __ lw(at, FieldMemOperand(tos_, HeapObject::kMapOffset));
+ __ lbu(scratch0, FieldMemOperand(at, Map::kBitFieldOffset));
+ __ And(scratch0, scratch0, Operand(1 << Map::kIsUndetectable));
+ __ Branch(&false_result, eq, scratch0, Operand(1 << Map::kIsUndetectable));
+
+ // JavaScript object => true.
+ __ lw(scratch0, FieldMemOperand(tos_, HeapObject::kMapOffset));
+ __ lbu(scratch0, FieldMemOperand(scratch0, Map::kInstanceTypeOffset));
+
+ // "tos_" is a register and contains a non-zero value.
+ // Hence we implicitly return true if the greater than
+ // condition is satisfied.
+ __ Ret(gt, scratch0, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Check for string.
+ __ lw(scratch0, FieldMemOperand(tos_, HeapObject::kMapOffset));
+ __ lbu(scratch0, FieldMemOperand(scratch0, Map::kInstanceTypeOffset));
+ // "tos_" is a register and contains a non-zero value.
+ // Hence we implicitly return true if the greater than
+ // condition is satisfied.
+ __ Ret(gt, scratch0, Operand(FIRST_NONSTRING_TYPE));
+
+ // String value => false iff empty, i.e., length is zero.
+ __ lw(tos_, FieldMemOperand(tos_, String::kLengthOffset));
+ // If length is zero, "tos_" contains zero ==> false.
+ // If length is not zero, "tos_" contains a non-zero value ==> true.
+ __ Ret();
+
+ // Return 0 in "tos_" for false.
+ __ bind(&false_result);
+ __ mov(tos_, zero_reg);
+ __ Ret();
}
-// We fall into this code if the operands were Smis, but the result was
-// not (eg. overflow). We branch into this code (to the not_smi label) if
-// the operands were not both Smi. The operands are in lhs and rhs.
-// To call the C-implemented binary fp operation routines we need to end up
-// with the double precision floating point operands in a0 and a1 (for the
-// value in a1) and a2 and a3 (for the value in a0).
-void GenericBinaryOpStub::HandleBinaryOpSlowCases(MacroAssembler* masm,
- Label* not_smi,
- Register lhs,
- Register rhs,
- const Builtins::JavaScript& builtin) {
- UNIMPLEMENTED_MIPS();
+Handle<Code> GetUnaryOpStub(int key, UnaryOpIC::TypeInfo type_info) {
+ UnaryOpStub stub(key, type_info);
+ return stub.GetCode();
}
-// For bitwise ops where the inputs are not both Smis we here try to determine
-// whether both inputs are either Smis or at least heap numbers that can be
-// represented by a 32 bit signed value. We truncate towards zero as required
-// by the ES spec. If this is the case we do the bitwise op and see if the
-// result is a Smi. If so, great, otherwise we try to find a heap number to
-// write the answer into (either by allocating or by overwriting).
-// On entry the operands are in lhs (x) and rhs (y). (Result = x op y).
-// On exit the result is in v0.
-void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
- Register lhs,
- Register rhs) {
- UNIMPLEMENTED_MIPS();
+const char* UnaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name = NULL; // Make g++ happy.
+ switch (mode_) {
+ case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "UnaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ UnaryOpIC::GetName(operand_type_));
+ return name_;
}
-void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operand_type_) {
+ case UnaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case UnaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case UnaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case UnaryOpIC::GENERIC:
+ GenerateGenericStub(masm);
+ break;
+ }
}
-void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ // Argument is in a0 and v0 at this point, so we can overwrite a0.
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ li(a2, Operand(Smi::FromInt(MinorKey())));
+ __ li(a1, Operand(Smi::FromInt(op_)));
+ __ li(a0, Operand(Smi::FromInt(operand_type_)));
+
+ __ Push(v0, a2, a1, a0);
+
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kUnaryOp_Patch),
+ masm->isolate()),
+ 4,
+ 1);
}
-Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
- GenericBinaryOpStub stub(key, type_info);
- return stub.GetCode();
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateSmiStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateSmiStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &slow);
+ __ bind(&non_smi);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
+ Label non_smi;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* slow) {
+ __ JumpIfNotSmi(a0, non_smi);
+
+ // The result of negating zero or the smallest negative smi is not a smi.
+ __ And(t0, a0, ~0x80000000);
+ __ Branch(slow, eq, t0, Operand(zero_reg));
+
+ // Return '0 - value'.
+ __ Subu(v0, zero_reg, a0);
+ __ Ret();
+}
+
+
+void UnaryOpStub::GenerateSmiCodeBitNot(MacroAssembler* masm,
+ Label* non_smi) {
+ __ JumpIfNotSmi(a0, non_smi);
+
+ // Flip bits and revert inverted smi-tag.
+ __ Neg(v0, a0);
+ __ And(v0, v0, ~kSmiTagMask);
+ __ Ret();
}
-Handle<Code> GetTypeRecordingBinaryOpStub(int key,
- TRBinaryOpIC::TypeInfo type_info,
- TRBinaryOpIC::TypeInfo result_type_info) {
- TypeRecordingBinaryOpStub stub(key, type_info, result_type_info);
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateHeapNumberStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateHeapNumberStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
+ Label non_smi, slow, call_builtin;
+ GenerateSmiCodeSub(masm, &non_smi, &call_builtin);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+ __ bind(&call_builtin);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
+ Label* slow) {
+ EmitCheckForHeapNumber(masm, a0, a1, t2, slow);
+ // a0 is a heap number. Get a new heap number in a1.
+ if (mode_ == UNARY_OVERWRITE) {
+ __ lw(a2, FieldMemOperand(a0, HeapNumber::kExponentOffset));
+ __ Xor(a2, a2, Operand(HeapNumber::kSignMask)); // Flip sign.
+ __ sw(a2, FieldMemOperand(a0, HeapNumber::kExponentOffset));
+ } else {
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(a1, a2, a3, t2, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(a0);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ mov(a1, v0);
+ __ pop(a0);
+ __ LeaveInternalFrame();
+
+ __ bind(&heapnumber_allocated);
+ __ lw(a3, FieldMemOperand(a0, HeapNumber::kMantissaOffset));
+ __ lw(a2, FieldMemOperand(a0, HeapNumber::kExponentOffset));
+ __ sw(a3, FieldMemOperand(a1, HeapNumber::kMantissaOffset));
+ __ Xor(a2, a2, Operand(HeapNumber::kSignMask)); // Flip sign.
+ __ sw(a2, FieldMemOperand(a1, HeapNumber::kExponentOffset));
+ __ mov(v0, a1);
+ }
+ __ Ret();
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeBitNot(
+ MacroAssembler* masm,
+ Label* slow) {
+ EmitCheckForHeapNumber(masm, a0, a1, t2, slow);
+ // Convert the heap number in a0 to an untagged integer in a1.
+ __ ConvertToInt32(a0, a1, a2, a3, f0, slow);
+
+ // Do the bitwise operation and check if the result fits in a smi.
+ Label try_float;
+ __ Neg(a1, a1);
+ __ Addu(a2, a1, Operand(0x40000000));
+ __ Branch(&try_float, lt, a2, Operand(zero_reg));
+
+ // Tag the result as a smi and we're done.
+ __ SmiTag(v0, a1);
+ __ Ret();
+
+ // Try to store the result in a heap number.
+ __ bind(&try_float);
+ if (mode_ == UNARY_NO_OVERWRITE) {
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(v0, a2, a3, t2, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(a1);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ pop(a1);
+ __ LeaveInternalFrame();
+
+ __ bind(&heapnumber_allocated);
+ }
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ // Convert the int32 in a1 to the heap number in v0. a2 is corrupted.
+ CpuFeatures::Scope scope(FPU);
+ __ mtc1(a1, f0);
+ __ cvt_d_w(f0, f0);
+ __ sdc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+ } else {
+ // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
+ // have to set up a frame.
+ WriteInt32ToHeapNumberStub stub(a1, v0, a2, a3);
+ __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
+ }
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateGenericStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateGenericStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &slow);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericCodeFallback(
+ MacroAssembler* masm) {
+ // Handle the slow case by jumping to the JavaScript builtin.
+ __ push(a0);
+ switch (op_) {
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
+ break;
+ case Token::BIT_NOT:
+ __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+Handle<Code> GetBinaryOpStub(int key,
+ BinaryOpIC::TypeInfo type_info,
+ BinaryOpIC::TypeInfo result_type_info) {
+ BinaryOpStub stub(key, type_info, result_type_info);
return stub.GetCode();
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ Label get_result;
+
+ __ Push(a1, a0);
+
+ __ li(a2, Operand(Smi::FromInt(MinorKey())));
+ __ li(a1, Operand(Smi::FromInt(op_)));
+ __ li(a0, Operand(Smi::FromInt(operands_type_)));
+ __ Push(a2, a1, a0);
+
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
+ masm->isolate()),
+ 5,
+ 1);
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransitionWithSavedArgs(
+void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(
MacroAssembler* masm) {
UNIMPLEMENTED();
}
-void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operands_type_) {
+ case BinaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case BinaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case BinaryOpIC::INT32:
+ GenerateInt32Stub(masm);
+ break;
+ case BinaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case BinaryOpIC::ODDBALL:
+ GenerateOddballStub(masm);
+ break;
+ case BinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
+ case BinaryOpIC::STRING:
+ GenerateStringStub(masm);
+ break;
+ case BinaryOpIC::GENERIC:
+ GenerateGeneric(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
}
-const char* TypeRecordingBinaryOpStub::GetName() {
- UNIMPLEMENTED_MIPS();
+const char* BinaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name;
+ switch (mode_) {
+ case NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
+ case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
+ default: overwrite_name = "UnknownOverwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "BinaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ BinaryOpIC::GetName(operands_type_));
return name_;
}
-void TypeRecordingBinaryOpStub::GenerateSmiSmiOperation(
- MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateSmiSmiOperation(MacroAssembler* masm) {
+ Register left = a1;
+ Register right = a0;
+
+ Register scratch1 = t0;
+ Register scratch2 = t1;
+
+ ASSERT(right.is(a0));
+ STATIC_ASSERT(kSmiTag == 0);
+
+ Label not_smi_result;
+ switch (op_) {
+ case Token::ADD:
+ __ AdduAndCheckForOverflow(v0, left, right, scratch1);
+ __ RetOnNoOverflow(scratch1);
+ // No need to revert anything - right and left are intact.
+ break;
+ case Token::SUB:
+ __ SubuAndCheckForOverflow(v0, left, right, scratch1);
+ __ RetOnNoOverflow(scratch1);
+ // No need to revert anything - right and left are intact.
+ break;
+ case Token::MUL: {
+ // Remove tag from one of the operands. This way the multiplication result
+ // will be a smi if it fits the smi range.
+ __ SmiUntag(scratch1, right);
+ // Do multiplication.
+ // lo = lower 32 bits of scratch1 * left.
+ // hi = higher 32 bits of scratch1 * left.
+ __ Mult(left, scratch1);
+ // Check for overflowing the smi range - no overflow if higher 33 bits of
+ // the result are identical.
+ __ mflo(scratch1);
+ __ mfhi(scratch2);
+ __ sra(scratch1, scratch1, 31);
+ __ Branch(&not_smi_result, ne, scratch1, Operand(scratch2));
+ // Go slow on zero result to handle -0.
+ __ mflo(v0);
+ __ Ret(ne, v0, Operand(zero_reg));
+ // We need -0 if we were multiplying a negative number with 0 to get 0.
+ // We know one of them was zero.
+ __ Addu(scratch2, right, left);
+ Label skip;
+ // ARM uses the 'pl' condition, which is 'ge'.
+ // Negating it results in 'lt'.
+ __ Branch(&skip, lt, scratch2, Operand(zero_reg));
+ ASSERT(Smi::FromInt(0) == 0);
+ __ mov(v0, zero_reg);
+ __ Ret(); // Return smi 0 if the non-zero one was positive.
+ __ bind(&skip);
+ // We fall through here if we multiplied a negative number with 0, because
+ // that would mean we should produce -0.
+ }
+ break;
+ case Token::DIV: {
+ Label done;
+ __ SmiUntag(scratch2, right);
+ __ SmiUntag(scratch1, left);
+ __ Div(scratch1, scratch2);
+ // A minor optimization: div may be calculated asynchronously, so we check
+ // for division by zero before getting the result.
+ __ Branch(&not_smi_result, eq, scratch2, Operand(zero_reg));
+ // If the result is 0, we need to make sure the dividsor (right) is
+ // positive, otherwise it is a -0 case.
+ // Quotient is in 'lo', remainder is in 'hi'.
+ // Check for no remainder first.
+ __ mfhi(scratch1);
+ __ Branch(&not_smi_result, ne, scratch1, Operand(zero_reg));
+ __ mflo(scratch1);
+ __ Branch(&done, ne, scratch1, Operand(zero_reg));
+ __ Branch(&not_smi_result, lt, scratch2, Operand(zero_reg));
+ __ bind(&done);
+ // Check that the signed result fits in a Smi.
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ __ Branch(&not_smi_result, lt, scratch2, Operand(zero_reg));
+ __ SmiTag(v0, scratch1);
+ __ Ret();
+ }
+ break;
+ case Token::MOD: {
+ Label done;
+ __ SmiUntag(scratch2, right);
+ __ SmiUntag(scratch1, left);
+ __ Div(scratch1, scratch2);
+ // A minor optimization: div may be calculated asynchronously, so we check
+ // for division by 0 before calling mfhi.
+ // Check for zero on the right hand side.
+ __ Branch(&not_smi_result, eq, scratch2, Operand(zero_reg));
+ // If the result is 0, we need to make sure the dividend (left) is
+ // positive (or 0), otherwise it is a -0 case.
+ // Remainder is in 'hi'.
+ __ mfhi(scratch2);
+ __ Branch(&done, ne, scratch2, Operand(zero_reg));
+ __ Branch(&not_smi_result, lt, scratch1, Operand(zero_reg));
+ __ bind(&done);
+ // Check that the signed result fits in a Smi.
+ __ Addu(scratch1, scratch2, Operand(0x40000000));
+ __ Branch(&not_smi_result, lt, scratch1, Operand(zero_reg));
+ __ SmiTag(v0, scratch2);
+ __ Ret();
+ }
+ break;
+ case Token::BIT_OR:
+ __ Or(v0, left, Operand(right));
+ __ Ret();
+ break;
+ case Token::BIT_AND:
+ __ And(v0, left, Operand(right));
+ __ Ret();
+ break;
+ case Token::BIT_XOR:
+ __ Xor(v0, left, Operand(right));
+ __ Ret();
+ break;
+ case Token::SAR:
+ // Remove tags from right operand.
+ __ GetLeastBitsFromSmi(scratch1, right, 5);
+ __ srav(scratch1, left, scratch1);
+ // Smi tag result.
+ __ And(v0, scratch1, Operand(~kSmiTagMask));
+ __ Ret();
+ break;
+ case Token::SHR:
+ // Remove tags from operands. We can't do this on a 31 bit number
+ // because then the 0s get shifted into bit 30 instead of bit 31.
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ srlv(v0, scratch1, scratch2);
+ // Unsigned shift is not allowed to produce a negative number, so
+ // check the sign bit and the sign bit after Smi tagging.
+ __ And(scratch1, v0, Operand(0xc0000000));
+ __ Branch(&not_smi_result, ne, scratch1, Operand(zero_reg));
+ // Smi tag result.
+ __ SmiTag(v0);
+ __ Ret();
+ break;
+ case Token::SHL:
+ // Remove tags from operands.
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ sllv(scratch1, scratch1, scratch2);
+ // Check that the signed result fits in a Smi.
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ __ Branch(&not_smi_result, lt, scratch2, Operand(zero_reg));
+ __ SmiTag(v0, scratch1);
+ __ Ret();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ __ bind(&not_smi_result);
}
-void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
- bool smi_operands,
- Label* not_numbers,
- Label* gc_required) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
+ bool smi_operands,
+ Label* not_numbers,
+ Label* gc_required) {
+ Register left = a1;
+ Register right = a0;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+ Register scratch3 = t0;
+
+ ASSERT(smi_operands || (not_numbers != NULL));
+ if (smi_operands && FLAG_debug_code) {
+ __ AbortIfNotSmi(left);
+ __ AbortIfNotSmi(right);
+ }
+
+ Register heap_number_map = t2;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // Load left and right operands into f12 and f14 or a0/a1 and a2/a3
+ // depending on whether FPU is available or not.
+ FloatingPointHelper::Destination destination =
+ CpuFeatures::IsSupported(FPU) &&
+ op_ != Token::MOD ?
+ FloatingPointHelper::kFPURegisters :
+ FloatingPointHelper::kCoreRegisters;
+
+ // Allocate new heap number for result.
+ Register result = s0;
+ GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required);
+
+ // Load the operands.
+ if (smi_operands) {
+ FloatingPointHelper::LoadSmis(masm, destination, scratch1, scratch2);
+ } else {
+ FloatingPointHelper::LoadOperands(masm,
+ destination,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ not_numbers);
+ }
+
+ // Calculate the result.
+ if (destination == FloatingPointHelper::kFPURegisters) {
+ // Using FPU registers:
+ // f12: Left value.
+ // f14: Right value.
+ CpuFeatures::Scope scope(FPU);
+ switch (op_) {
+ case Token::ADD:
+ __ add_d(f10, f12, f14);
+ break;
+ case Token::SUB:
+ __ sub_d(f10, f12, f14);
+ break;
+ case Token::MUL:
+ __ mul_d(f10, f12, f14);
+ break;
+ case Token::DIV:
+ __ div_d(f10, f12, f14);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // ARM uses a workaround here because of the unaligned HeapNumber
+ // kValueOffset. On MIPS this workaround is built into sdc1 so
+ // there's no point in generating even more instructions.
+ __ sdc1(f10, FieldMemOperand(result, HeapNumber::kValueOffset));
+ __ mov(v0, result);
+ __ Ret();
+ } else {
+ // Call the C function to handle the double operation.
+ FloatingPointHelper::CallCCodeForDoubleOperation(masm,
+ op_,
+ result,
+ scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+ }
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ if (smi_operands) {
+ __ SmiUntag(a3, left);
+ __ SmiUntag(a2, right);
+ } else {
+ // Convert operands to 32-bit integers. Right in a2 and left in a3.
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ left,
+ a3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ not_numbers);
+ FloatingPointHelper::ConvertNumberToInt32(masm,
+ right,
+ a2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ not_numbers);
+ }
+ Label result_not_a_smi;
+ switch (op_) {
+ case Token::BIT_OR:
+ __ Or(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_XOR:
+ __ Xor(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_AND:
+ __ And(a2, a3, Operand(a2));
+ break;
+ case Token::SAR:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(a2, a2, 5);
+ __ srav(a2, a3, a2);
+ break;
+ case Token::SHR:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(a2, a2, 5);
+ __ srlv(a2, a3, a2);
+ // SHR is special because it is required to produce a positive answer.
+ // The code below for writing into heap numbers isn't capable of
+ // writing the register as an unsigned int so we go to slow case if we
+ // hit this case.
+ if (CpuFeatures::IsSupported(FPU)) {
+ __ Branch(&result_not_a_smi, lt, a2, Operand(zero_reg));
+ } else {
+ __ Branch(not_numbers, lt, a2, Operand(zero_reg));
+ }
+ break;
+ case Token::SHL:
+ // Use only the 5 least significant bits of the shift count.
+ __ GetLeastBitsFromInt32(a2, a2, 5);
+ __ sllv(a2, a3, a2);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ // Check that the *signed* result fits in a smi.
+ __ Addu(a3, a2, Operand(0x40000000));
+ __ Branch(&result_not_a_smi, lt, a3, Operand(zero_reg));
+ __ SmiTag(v0, a2);
+ __ Ret();
+
+ // Allocate new heap number for result.
+ __ bind(&result_not_a_smi);
+ Register result = t1;
+ if (smi_operands) {
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ } else {
+ GenerateHeapResultAllocation(
+ masm, result, heap_number_map, scratch1, scratch2, gc_required);
+ }
+
+ // a2: Answer as signed int32.
+ // t1: Heap number to write answer into.
+
+ // Nothing can go wrong now, so move the heap number to v0, which is the
+ // result.
+ __ mov(v0, t1);
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ // Convert the int32 in a2 to the heap number in a0. As
+ // mentioned above SHR needs to always produce a positive result.
+ CpuFeatures::Scope scope(FPU);
+ __ mtc1(a2, f0);
+ if (op_ == Token::SHR) {
+ __ Cvt_d_uw(f0, f0);
+ } else {
+ __ cvt_d_w(f0, f0);
+ }
+ // ARM uses a workaround here because of the unaligned HeapNumber
+ // kValueOffset. On MIPS this workaround is built into sdc1 so
+ // there's no point in generating even more instructions.
+ __ sdc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+ } else {
+ // Tail call that writes the int32 in a2 to the heap number in v0, using
+ // a3 and a0 as scratch. v0 is preserved and returned.
+ WriteInt32ToHeapNumberStub stub(a2, v0, a3, a0);
+ __ TailCallStub(&stub);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
}
@@ -351,83 +2560,929 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
// generated. If the result is not a smi and heap number allocation is not
// requested the code falls through. If number allocation is requested but a
// heap number cannot be allocated the code jumps to the lable gc_required.
-void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+void BinaryOpStub::GenerateSmiCode(
+ MacroAssembler* masm,
+ Label* use_runtime,
Label* gc_required,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
- UNIMPLEMENTED_MIPS();
+ Label not_smis;
+
+ Register left = a1;
+ Register right = a0;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+
+ // Perform combined smi check on both operands.
+ __ Or(scratch1, left, Operand(right));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(scratch1, &not_smis);
+
+ // If the smi-smi operation results in a smi return is generated.
+ GenerateSmiSmiOperation(masm);
+
+ // If heap number results are possible generate the result in an allocated
+ // heap number.
+ if (allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS) {
+ GenerateFPOperation(masm, true, use_runtime, gc_required);
+ }
+ __ bind(&not_smis);
}
-void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ Label not_smis, call_runtime;
+
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
+ // Only allow smi results.
+ GenerateSmiCode(masm, &call_runtime, NULL, NO_HEAPNUMBER_RESULTS);
+ } else {
+ // Allow heap number result and don't make a transition if a heap number
+ // cannot be allocated.
+ GenerateSmiCode(masm,
+ &call_runtime,
+ &call_runtime,
+ ALLOW_HEAPNUMBER_RESULTS);
+ }
+
+ // Code falls through if the result is not returned as either a smi or heap
+ // number.
+ GenerateTypeTransition(masm);
+
+ __ bind(&call_runtime);
+ GenerateCallRuntime(masm);
}
-void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::STRING);
+ // Try to add arguments as strings, otherwise, transition to the generic
+ // BinaryOpIC type.
+ GenerateAddStrings(masm);
+ GenerateTypeTransition(masm);
}
-void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ GetObjectType(left, a2, a2);
+ __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ GetObjectType(right, a2, a2);
+ __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
}
-void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::INT32);
+
+ Register left = a1;
+ Register right = a0;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+ FPURegister double_scratch = f0;
+ FPURegister single_scratch = f6;
+
+ Register heap_number_result = no_reg;
+ Register heap_number_map = t2;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ Label call_runtime;
+ // Labels for type transition, used for wrong input or output types.
+ // Both label are currently actually bound to the same position. We use two
+ // different label to differentiate the cause leading to type transition.
+ Label transition;
+
+ // Smi-smi fast case.
+ Label skip;
+ __ Or(scratch1, left, right);
+ __ JumpIfNotSmi(scratch1, &skip);
+ GenerateSmiSmiOperation(masm);
+ // Fall through if the result is not a smi.
+ __ bind(&skip);
+
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD: {
+ // Load both operands and check that they are 32-bit integer.
+ // Jump to type transition if they are not. The registers a0 and a1 (right
+ // and left) are preserved for the runtime call.
+ FloatingPointHelper::Destination destination =
+ CpuFeatures::IsSupported(FPU) &&
+ op_ != Token::MOD ?
+ FloatingPointHelper::kFPURegisters :
+ FloatingPointHelper::kCoreRegisters;
+
+ FloatingPointHelper::LoadNumberAsInt32Double(masm,
+ right,
+ destination,
+ f14,
+ a2,
+ a3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ f2,
+ &transition);
+ FloatingPointHelper::LoadNumberAsInt32Double(masm,
+ left,
+ destination,
+ f12,
+ t0,
+ t1,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ f2,
+ &transition);
+
+ if (destination == FloatingPointHelper::kFPURegisters) {
+ CpuFeatures::Scope scope(FPU);
+ Label return_heap_number;
+ switch (op_) {
+ case Token::ADD:
+ __ add_d(f10, f12, f14);
+ break;
+ case Token::SUB:
+ __ sub_d(f10, f12, f14);
+ break;
+ case Token::MUL:
+ __ mul_d(f10, f12, f14);
+ break;
+ case Token::DIV:
+ __ div_d(f10, f12, f14);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (op_ != Token::DIV) {
+ // These operations produce an integer result.
+ // Try to return a smi if we can.
+ // Otherwise return a heap number if allowed, or jump to type
+ // transition.
+
+ // NOTE: ARM uses a MacroAssembler function here (EmitVFPTruncate).
+ // On MIPS a lot of things cannot be implemented the same way so right
+ // now it makes a lot more sense to just do things manually.
+
+ // Save FCSR.
+ __ cfc1(scratch1, FCSR);
+ // Disable FPU exceptions.
+ __ ctc1(zero_reg, FCSR);
+ __ trunc_w_d(single_scratch, f10);
+ // Retrieve FCSR.
+ __ cfc1(scratch2, FCSR);
+ // Restore FCSR.
+ __ ctc1(scratch1, FCSR);
+
+ // Check for inexact conversion.
+ __ srl(scratch2, scratch2, kFCSRFlagShift);
+ __ And(scratch2, scratch2, kFCSRFlagMask);
+
+ if (result_type_ <= BinaryOpIC::INT32) {
+ // If scratch2 != 0, result does not fit in a 32-bit integer.
+ __ Branch(&transition, ne, scratch2, Operand(zero_reg));
+ }
+
+ // Check if the result fits in a smi.
+ __ mfc1(scratch1, single_scratch);
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ // If not try to return a heap number.
+ __ Branch(&return_heap_number, lt, scratch2, Operand(zero_reg));
+ // Check for minus zero. Return heap number for minus zero.
+ Label not_zero;
+ __ Branch(&not_zero, ne, scratch1, Operand(zero_reg));
+ __ mfc1(scratch2, f11);
+ __ And(scratch2, scratch2, HeapNumber::kSignMask);
+ __ Branch(&return_heap_number, ne, scratch2, Operand(zero_reg));
+ __ bind(&not_zero);
+
+ // Tag the result and return.
+ __ SmiTag(v0, scratch1);
+ __ Ret();
+ } else {
+ // DIV just falls through to allocating a heap number.
+ }
+
+ if (result_type_ >= (op_ == Token::DIV) ? BinaryOpIC::HEAP_NUMBER
+ : BinaryOpIC::INT32) {
+ __ bind(&return_heap_number);
+ // We are using FPU registers so s0 is available.
+ heap_number_result = s0;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime);
+ __ mov(v0, heap_number_result);
+ __ sdc1(f10, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+ }
+
+ // A DIV operation expecting an integer result falls through
+ // to type transition.
+
+ } else {
+ // We preserved a0 and a1 to be able to call runtime.
+ // Save the left value on the stack.
+ __ Push(t1, t0);
+
+ Label pop_and_call_runtime;
+
+ // Allocate a heap number to store the result.
+ heap_number_result = s0;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &pop_and_call_runtime);
+
+ // Load the left value from the value saved on the stack.
+ __ Pop(a1, a0);
+
+ // Call the C function to handle the double operation.
+ FloatingPointHelper::CallCCodeForDoubleOperation(
+ masm, op_, heap_number_result, scratch1);
+ if (FLAG_debug_code) {
+ __ stop("Unreachable code.");
+ }
+
+ __ bind(&pop_and_call_runtime);
+ __ Drop(2);
+ __ Branch(&call_runtime);
+ }
+
+ break;
+ }
+
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::SHL: {
+ Label return_heap_number;
+ Register scratch3 = t1;
+ // Convert operands to 32-bit integers. Right in a2 and left in a3. The
+ // registers a0 and a1 (right and left) are preserved for the runtime
+ // call.
+ FloatingPointHelper::LoadNumberAsInt32(masm,
+ left,
+ a3,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ &transition);
+ FloatingPointHelper::LoadNumberAsInt32(masm,
+ right,
+ a2,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ scratch3,
+ f0,
+ &transition);
+
+ // The ECMA-262 standard specifies that, for shift operations, only the
+ // 5 least significant bits of the shift value should be used.
+ switch (op_) {
+ case Token::BIT_OR:
+ __ Or(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_XOR:
+ __ Xor(a2, a3, Operand(a2));
+ break;
+ case Token::BIT_AND:
+ __ And(a2, a3, Operand(a2));
+ break;
+ case Token::SAR:
+ __ And(a2, a2, Operand(0x1f));
+ __ srav(a2, a3, a2);
+ break;
+ case Token::SHR:
+ __ And(a2, a2, Operand(0x1f));
+ __ srlv(a2, a3, a2);
+ // SHR is special because it is required to produce a positive answer.
+ // We only get a negative result if the shift value (a2) is 0.
+ // This result cannot be respresented as a signed 32-bit integer, try
+ // to return a heap number if we can.
+ // The non FPU code does not support this special case, so jump to
+ // runtime if we don't support it.
+ if (CpuFeatures::IsSupported(FPU)) {
+ __ Branch((result_type_ <= BinaryOpIC::INT32)
+ ? &transition
+ : &return_heap_number,
+ lt,
+ a2,
+ Operand(zero_reg));
+ } else {
+ __ Branch((result_type_ <= BinaryOpIC::INT32)
+ ? &transition
+ : &call_runtime,
+ lt,
+ a2,
+ Operand(zero_reg));
+ }
+ break;
+ case Token::SHL:
+ __ And(a2, a2, Operand(0x1f));
+ __ sllv(a2, a3, a2);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ // Check if the result fits in a smi.
+ __ Addu(scratch1, a2, Operand(0x40000000));
+ // If not try to return a heap number. (We know the result is an int32.)
+ __ Branch(&return_heap_number, lt, scratch1, Operand(zero_reg));
+ // Tag the result and return.
+ __ SmiTag(v0, a2);
+ __ Ret();
+
+ __ bind(&return_heap_number);
+ heap_number_result = t1;
+ GenerateHeapResultAllocation(masm,
+ heap_number_result,
+ heap_number_map,
+ scratch1,
+ scratch2,
+ &call_runtime);
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+
+ if (op_ != Token::SHR) {
+ // Convert the result to a floating point value.
+ __ mtc1(a2, double_scratch);
+ __ cvt_d_w(double_scratch, double_scratch);
+ } else {
+ // The result must be interpreted as an unsigned 32-bit integer.
+ __ mtc1(a2, double_scratch);
+ __ Cvt_d_uw(double_scratch, double_scratch);
+ }
+
+ // Store the result.
+ __ mov(v0, heap_number_result);
+ __ sdc1(double_scratch, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+ } else {
+ // Tail call that writes the int32 in a2 to the heap number in v0, using
+ // a3 and a1 as scratch. v0 is preserved and returned.
+ __ mov(a0, t1);
+ WriteInt32ToHeapNumberStub stub(a2, v0, a3, a1);
+ __ TailCallStub(&stub);
+ }
+
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+
+ if (transition.is_linked()) {
+ __ bind(&transition);
+ GenerateTypeTransition(masm);
+ }
+
+ __ bind(&call_runtime);
+ GenerateCallRuntime(masm);
}
-void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (op_ == Token::ADD) {
+ // Handle string addition here, because it is the only operation
+ // that does not do a ToNumber conversion on the operands.
+ GenerateAddStrings(masm);
+ }
+
+ // Convert oddball arguments to numbers.
+ Label check, done;
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&check, ne, a1, Operand(t0));
+ if (Token::IsBitOp(op_)) {
+ __ li(a1, Operand(Smi::FromInt(0)));
+ } else {
+ __ LoadRoot(a1, Heap::kNanValueRootIndex);
+ }
+ __ jmp(&done);
+ __ bind(&check);
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&done, ne, a0, Operand(t0));
+ if (Token::IsBitOp(op_)) {
+ __ li(a0, Operand(Smi::FromInt(0)));
+ } else {
+ __ LoadRoot(a0, Heap::kNanValueRootIndex);
+ }
+ __ bind(&done);
+
+ GenerateHeapNumberStub(masm);
}
-void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ Label call_runtime;
+ GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
+
+ __ bind(&call_runtime);
+ GenerateCallRuntime(masm);
}
-void TypeRecordingBinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+ Label call_runtime, call_string_add_or_runtime;
+
+ GenerateSmiCode(masm, &call_runtime, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
+
+ GenerateFPOperation(masm, false, &call_string_add_or_runtime, &call_runtime);
+
+ __ bind(&call_string_add_or_runtime);
+ if (op_ == Token::ADD) {
+ GenerateAddStrings(masm);
+ }
+
+ __ bind(&call_runtime);
+ GenerateCallRuntime(masm);
+}
+
+
+void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
+ ASSERT(op_ == Token::ADD);
+ Label left_not_string, call_runtime;
+
+ Register left = a1;
+ Register right = a0;
+
+ // Check if left argument is a string.
+ __ JumpIfSmi(left, &left_not_string);
+ __ GetObjectType(left, a2, a2);
+ __ Branch(&left_not_string, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_left_stub);
+
+ // Left operand is not a string, test right.
+ __ bind(&left_not_string);
+ __ JumpIfSmi(right, &call_runtime);
+ __ GetObjectType(right, a2, a2);
+ __ Branch(&call_runtime, ge, a2, Operand(FIRST_NONSTRING_TYPE));
+
+ StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_right_stub);
+
+ // At least one argument is not a string.
+ __ bind(&call_runtime);
+}
+
+
+void BinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
+ GenerateRegisterArgsPush(masm);
+ switch (op_) {
+ case Token::ADD:
+ __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
+ break;
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
+ break;
+ case Token::MUL:
+ __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
+ break;
+ case Token::DIV:
+ __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
+ break;
+ case Token::MOD:
+ __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
+ break;
+ case Token::BIT_OR:
+ __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
+ break;
+ case Token::BIT_AND:
+ __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
+ break;
+ case Token::BIT_XOR:
+ __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
+ break;
+ case Token::SAR:
+ __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
+ break;
+ case Token::SHR:
+ __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
+ break;
+ case Token::SHL:
+ __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
}
-void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
+void BinaryOpStub::GenerateHeapResultAllocation(
MacroAssembler* masm,
Register result,
Register heap_number_map,
Register scratch1,
Register scratch2,
Label* gc_required) {
- UNIMPLEMENTED_MIPS();
+
+ // Code below will scratch result if allocation fails. To keep both arguments
+ // intact for the runtime call result cannot be one of these.
+ ASSERT(!result.is(a0) && !result.is(a1));
+
+ if (mode_ == OVERWRITE_LEFT || mode_ == OVERWRITE_RIGHT) {
+ Label skip_allocation, allocated;
+ Register overwritable_operand = mode_ == OVERWRITE_LEFT ? a1 : a0;
+ // If the overwritable operand is already an object, we skip the
+ // allocation of a heap number.
+ __ JumpIfNotSmi(overwritable_operand, &skip_allocation);
+ // Allocate a heap number for the result.
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ __ Branch(&allocated);
+ __ bind(&skip_allocation);
+ // Use object holding the overwritable operand for result.
+ __ mov(result, overwritable_operand);
+ __ bind(&allocated);
+ } else {
+ ASSERT(mode_ == NO_OVERWRITE);
+ __ AllocateHeapNumber(
+ result, scratch1, scratch2, heap_number_map, gc_required);
+ }
}
-void TypeRecordingBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ __ Push(a1, a0);
}
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Untagged case: double input in f4, double result goes
+ // into f4.
+ // Tagged case: tagged input on top of stack and in a0,
+ // tagged result (heap number) goes into v0.
+
+ Label input_not_smi;
+ Label loaded;
+ Label calculate;
+ Label invalid_cache;
+ const Register scratch0 = t5;
+ const Register scratch1 = t3;
+ const Register cache_entry = a0;
+ const bool tagged = (argument_type_ == TAGGED);
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+
+ if (tagged) {
+ // Argument is a number and is on stack and in a0.
+ // Load argument and check if it is a smi.
+ __ JumpIfNotSmi(a0, &input_not_smi);
+
+ // Input is a smi. Convert to double and load the low and high words
+ // of the double into a2, a3.
+ __ sra(t0, a0, kSmiTagSize);
+ __ mtc1(t0, f4);
+ __ cvt_d_w(f4, f4);
+ __ Move(a2, a3, f4);
+ __ Branch(&loaded);
+
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ CheckMap(a0,
+ a1,
+ Heap::kHeapNumberMapRootIndex,
+ &calculate,
+ DONT_DO_SMI_CHECK);
+ // Input is a HeapNumber. Store the
+ // low and high words into a2, a3.
+ __ lw(a2, FieldMemOperand(a0, HeapNumber::kValueOffset));
+ __ lw(a3, FieldMemOperand(a0, HeapNumber::kValueOffset + 4));
+ } else {
+ // Input is untagged double in f4. Output goes to f4.
+ __ Move(a2, a3, f4);
+ }
+ __ bind(&loaded);
+ // a2 = low 32 bits of double value.
+ // a3 = high 32 bits of double value.
+ // Compute hash (the shifts are arithmetic):
+ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
+ __ Xor(a1, a2, a3);
+ __ sra(t0, a1, 16);
+ __ Xor(a1, a1, t0);
+ __ sra(t0, a1, 8);
+ __ Xor(a1, a1, t0);
+ ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
+ __ And(a1, a1, Operand(TranscendentalCache::SubCache::kCacheSize - 1));
+
+ // a2 = low 32 bits of double value.
+ // a3 = high 32 bits of double value.
+ // a1 = TranscendentalCache::hash(double value).
+ __ li(cache_entry, Operand(
+ ExternalReference::transcendental_cache_array_address(
+ masm->isolate())));
+ // a0 points to cache array.
+ __ lw(cache_entry, MemOperand(cache_entry, type_ * sizeof(
+ Isolate::Current()->transcendental_cache()->caches_[0])));
+ // a0 points to the cache for the type type_.
+ // If NULL, the cache hasn't been initialized yet, so go through runtime.
+ __ Branch(&invalid_cache, eq, cache_entry, Operand(zero_reg));
+
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { TranscendentalCache::SubCache::Element test_elem[2];
+ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
+ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
+ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
+ CHECK_EQ(0, elem_in0 - elem_start);
+ CHECK_EQ(kIntSize, elem_in1 - elem_start);
+ CHECK_EQ(2 * kIntSize, elem_out - elem_start);
+ }
+#endif
+
+ // Find the address of the a1'st entry in the cache, i.e., &a0[a1*12].
+ __ sll(t0, a1, 1);
+ __ Addu(a1, a1, t0);
+ __ sll(t0, a1, 2);
+ __ Addu(cache_entry, cache_entry, t0);
+
+ // Check if cache matches: Double value is stored in uint32_t[2] array.
+ __ lw(t0, MemOperand(cache_entry, 0));
+ __ lw(t1, MemOperand(cache_entry, 4));
+ __ lw(t2, MemOperand(cache_entry, 8));
+ __ Addu(cache_entry, cache_entry, 12);
+ __ Branch(&calculate, ne, a2, Operand(t0));
+ __ Branch(&calculate, ne, a3, Operand(t1));
+ // Cache hit. Load result, cleanup and return.
+ if (tagged) {
+ // Pop input value from stack and load result into v0.
+ __ Drop(1);
+ __ mov(v0, t2);
+ } else {
+ // Load result into f4.
+ __ ldc1(f4, FieldMemOperand(t2, HeapNumber::kValueOffset));
+ }
+ __ Ret();
+ } // if (CpuFeatures::IsSupported(FPU))
+
+ __ bind(&calculate);
+ if (tagged) {
+ __ bind(&invalid_cache);
+ __ TailCallExternalReference(ExternalReference(RuntimeFunction(),
+ masm->isolate()),
+ 1,
+ 1);
+ } else {
+ if (!CpuFeatures::IsSupported(FPU)) UNREACHABLE();
+ CpuFeatures::Scope scope(FPU);
+
+ Label no_update;
+ Label skip_cache;
+ const Register heap_number_map = t2;
+
+ // Call C function to calculate the result and update the cache.
+ // Register a0 holds precalculated cache entry address; preserve
+ // it on the stack and pop it into register cache_entry after the
+ // call.
+ __ push(cache_entry);
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(f4);
+
+ // Try to update the cache. If we cannot allocate a
+ // heap number, we return the result without updating.
+ __ pop(cache_entry);
+ __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(t2, scratch0, scratch1, t1, &no_update);
+ __ sdc1(f4, FieldMemOperand(t2, HeapNumber::kValueOffset));
+
+ __ sw(a2, MemOperand(cache_entry, 0 * kPointerSize));
+ __ sw(a3, MemOperand(cache_entry, 1 * kPointerSize));
+ __ sw(t2, MemOperand(cache_entry, 2 * kPointerSize));
+
+ __ mov(v0, cache_entry);
+ __ Ret();
+
+ __ bind(&invalid_cache);
+ // The cache is invalid. Call runtime which will recreate the
+ // cache.
+ __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(a0, scratch0, scratch1, t1, &skip_cache);
+ __ sdc1(f4, FieldMemOperand(a0, HeapNumber::kValueOffset));
+ __ EnterInternalFrame();
+ __ push(a0);
+ __ CallRuntime(RuntimeFunction(), 1);
+ __ LeaveInternalFrame();
+ __ ldc1(f4, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+
+ __ bind(&skip_cache);
+ // Call C function to calculate the result and answer directly
+ // without updating the cache.
+ GenerateCallCFunction(masm, scratch0);
+ __ GetCFunctionDoubleResult(f4);
+ __ bind(&no_update);
+
+ // We return the value in f4 without adding it to the cache, but
+ // we cause a scavenging GC so that future allocations will succeed.
+ __ EnterInternalFrame();
+
+ // Allocate an aligned object larger than a HeapNumber.
+ ASSERT(4 * kPointerSize >= HeapNumber::kSize);
+ __ li(scratch0, Operand(4 * kPointerSize));
+ __ push(scratch0);
+ __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
+ __ LeaveInternalFrame();
+ __ Ret();
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm,
+ Register scratch) {
+ __ push(ra);
+ __ PrepareCallCFunction(2, scratch);
+ if (IsMipsSoftFloatABI) {
+ __ Move(v0, v1, f4);
+ } else {
+ __ mov_d(f12, f4);
+ }
+ switch (type_) {
+ case TranscendentalCache::SIN:
+ __ CallCFunction(
+ ExternalReference::math_sin_double_function(masm->isolate()), 2);
+ break;
+ case TranscendentalCache::COS:
+ __ CallCFunction(
+ ExternalReference::math_cos_double_function(masm->isolate()), 2);
+ break;
+ case TranscendentalCache::LOG:
+ __ CallCFunction(
+ ExternalReference::math_log_double_function(masm->isolate()), 2);
+ break;
+ default:
+ UNIMPLEMENTED();
+ break;
+ }
+ __ pop(ra);
}
Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
- UNIMPLEMENTED_MIPS();
- return Runtime::kAbort;
+ switch (type_) {
+ // Add more cases when necessary.
+ case TranscendentalCache::SIN: return Runtime::kMath_sin;
+ case TranscendentalCache::COS: return Runtime::kMath_cos;
+ case TranscendentalCache::LOG: return Runtime::kMath_log;
+ default:
+ UNIMPLEMENTED();
+ return Runtime::kAbort;
+ }
}
void StackCheckStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
}
-void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void MathPowStub::Generate(MacroAssembler* masm) {
+ Label call_runtime;
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+
+ Label base_not_smi;
+ Label exponent_not_smi;
+ Label convert_exponent;
+
+ const Register base = a0;
+ const Register exponent = a2;
+ const Register heapnumbermap = t1;
+ const Register heapnumber = s0; // Callee-saved register.
+ const Register scratch = t2;
+ const Register scratch2 = t3;
+
+ // Alocate FP values in the ABI-parameter-passing regs.
+ const DoubleRegister double_base = f12;
+ const DoubleRegister double_exponent = f14;
+ const DoubleRegister double_result = f0;
+ const DoubleRegister double_scratch = f2;
+
+ __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
+ __ lw(base, MemOperand(sp, 1 * kPointerSize));
+ __ lw(exponent, MemOperand(sp, 0 * kPointerSize));
+
+ // Convert base to double value and store it in f0.
+ __ JumpIfNotSmi(base, &base_not_smi);
+ // Base is a Smi. Untag and convert it.
+ __ SmiUntag(base);
+ __ mtc1(base, double_scratch);
+ __ cvt_d_w(double_base, double_scratch);
+ __ Branch(&convert_exponent);
+
+ __ bind(&base_not_smi);
+ __ lw(scratch, FieldMemOperand(base, JSObject::kMapOffset));
+ __ Branch(&call_runtime, ne, scratch, Operand(heapnumbermap));
+ // Base is a heapnumber. Load it into double register.
+ __ ldc1(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
+
+ __ bind(&convert_exponent);
+ __ JumpIfNotSmi(exponent, &exponent_not_smi);
+ __ SmiUntag(exponent);
+
+ // The base is in a double register and the exponent is
+ // an untagged smi. Allocate a heap number and call a
+ // C function for integer exponents. The register containing
+ // the heap number is callee-saved.
+ __ AllocateHeapNumber(heapnumber,
+ scratch,
+ scratch2,
+ heapnumbermap,
+ &call_runtime);
+ __ push(ra);
+ __ PrepareCallCFunction(3, scratch);
+ __ SetCallCDoubleArguments(double_base, exponent);
+ __ CallCFunction(
+ ExternalReference::power_double_int_function(masm->isolate()), 3);
+ __ pop(ra);
+ __ GetCFunctionDoubleResult(double_result);
+ __ sdc1(double_result,
+ FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
+ __ mov(v0, heapnumber);
+ __ DropAndRet(2 * kPointerSize);
+
+ __ bind(&exponent_not_smi);
+ __ lw(scratch, FieldMemOperand(exponent, JSObject::kMapOffset));
+ __ Branch(&call_runtime, ne, scratch, Operand(heapnumbermap));
+ // Exponent is a heapnumber. Load it into double register.
+ __ ldc1(double_exponent,
+ FieldMemOperand(exponent, HeapNumber::kValueOffset));
+
+ // The base and the exponent are in double registers.
+ // Allocate a heap number and call a C function for
+ // double exponents. The register containing
+ // the heap number is callee-saved.
+ __ AllocateHeapNumber(heapnumber,
+ scratch,
+ scratch2,
+ heapnumbermap,
+ &call_runtime);
+ __ push(ra);
+ __ PrepareCallCFunction(4, scratch);
+ // ABI (o32) for func(double a, double b): a in f12, b in f14.
+ ASSERT(double_base.is(f12));
+ ASSERT(double_exponent.is(f14));
+ __ SetCallCDoubleArguments(double_base, double_exponent);
+ __ CallCFunction(
+ ExternalReference::power_double_double_function(masm->isolate()), 4);
+ __ pop(ra);
+ __ GetCFunctionDoubleResult(double_result);
+ __ sdc1(double_result,
+ FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
+ __ mov(v0, heapnumber);
+ __ DropAndRet(2 * kPointerSize);
+ }
+
+ __ bind(&call_runtime);
+ __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
}
@@ -437,13 +3492,13 @@ bool CEntryStub::NeedsImmovableCode() {
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ Throw(v0);
}
void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm,
UncatchableExceptionType type) {
- UNIMPLEMENTED_MIPS();
+ __ ThrowUncatchable(type, v0);
}
@@ -453,78 +3508,1427 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate) {
- UNIMPLEMENTED_MIPS();
+ // v0: result parameter for PerformGC, if any
+ // s0: number of arguments including receiver (C callee-saved)
+ // s1: pointer to the first argument (C callee-saved)
+ // s2: pointer to builtin function (C callee-saved)
+
+ if (do_gc) {
+ // Move result passed in v0 into a0 to call PerformGC.
+ __ mov(a0, v0);
+ __ PrepareCallCFunction(1, a1);
+ __ CallCFunction(
+ ExternalReference::perform_gc_function(masm->isolate()), 1);
+ }
+
+ ExternalReference scope_depth =
+ ExternalReference::heap_always_allocate_scope_depth(masm->isolate());
+ if (always_allocate) {
+ __ li(a0, Operand(scope_depth));
+ __ lw(a1, MemOperand(a0));
+ __ Addu(a1, a1, Operand(1));
+ __ sw(a1, MemOperand(a0));
+ }
+
+ // Prepare arguments for C routine: a0 = argc, a1 = argv
+ __ mov(a0, s0);
+ __ mov(a1, s1);
+
+ // We are calling compiled C/C++ code. a0 and a1 hold our two arguments. We
+ // also need to reserve the 4 argument slots on the stack.
+
+ __ AssertStackIsAligned();
+
+ __ li(a2, Operand(ExternalReference::isolate_address()));
+
+ // From arm version of this function:
+ // TODO(1242173): To let the GC traverse the return address of the exit
+ // frames, we need to know where the return address is. Right now,
+ // we push it on the stack to be able to find it again, but we never
+ // restore from it in case of changes, which makes it impossible to
+ // support moving the C entry code stub. This should be fixed, but currently
+ // this is OK because the CEntryStub gets generated so early in the V8 boot
+ // sequence that it is not moving ever.
+
+ { Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+ // This branch-and-link sequence is needed to find the current PC on mips,
+ // saved to the ra register.
+ // Use masm-> here instead of the double-underscore macro since extra
+ // coverage code can interfere with the proper calculation of ra.
+ Label find_ra;
+ masm->bal(&find_ra); // bal exposes branch delay slot.
+ masm->nop(); // Branch delay slot nop.
+ masm->bind(&find_ra);
+
+ // Adjust the value in ra to point to the correct return location, 2nd
+ // instruction past the real call into C code (the jalr(t9)), and push it.
+ // This is the return address of the exit frame.
+ const int kNumInstructionsToJump = 6;
+ masm->Addu(ra, ra, kNumInstructionsToJump * kPointerSize);
+ masm->sw(ra, MemOperand(sp)); // This spot was reserved in EnterExitFrame.
+ masm->Subu(sp, sp, StandardFrameConstants::kCArgsSlotsSize);
+ // Stack is still aligned.
+
+ // Call the C routine.
+ masm->mov(t9, s2); // Function pointer to t9 to conform to ABI for PIC.
+ masm->jalr(t9);
+ masm->nop(); // Branch delay slot nop.
+ // Make sure the stored 'ra' points to this position.
+ ASSERT_EQ(kNumInstructionsToJump,
+ masm->InstructionsGeneratedSince(&find_ra));
+ }
+
+ // Restore stack (remove arg slots).
+ __ Addu(sp, sp, StandardFrameConstants::kCArgsSlotsSize);
+
+ if (always_allocate) {
+ // It's okay to clobber a2 and a3 here. v0 & v1 contain result.
+ __ li(a2, Operand(scope_depth));
+ __ lw(a3, MemOperand(a2));
+ __ Subu(a3, a3, Operand(1));
+ __ sw(a3, MemOperand(a2));
+ }
+
+ // Check for failure result.
+ Label failure_returned;
+ STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
+ __ addiu(a2, v0, 1);
+ __ andi(t0, a2, kFailureTagMask);
+ __ Branch(&failure_returned, eq, t0, Operand(zero_reg));
+
+ // Exit C frame and return.
+ // v0:v1: result
+ // sp: stack pointer
+ // fp: frame pointer
+ __ LeaveExitFrame(save_doubles_, s0);
+ __ Ret();
+
+ // Check if we should retry or throw exception.
+ Label retry;
+ __ bind(&failure_returned);
+ STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
+ __ andi(t0, v0, ((1 << kFailureTypeTagSize) - 1) << kFailureTagSize);
+ __ Branch(&retry, eq, t0, Operand(zero_reg));
+
+ // Special handling of out of memory exceptions.
+ Failure* out_of_memory = Failure::OutOfMemoryException();
+ __ Branch(throw_out_of_memory_exception, eq,
+ v0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+
+ // Retrieve the pending exception and clear the variable.
+ __ li(t0,
+ Operand(ExternalReference::the_hole_value_location(masm->isolate())));
+ __ lw(a3, MemOperand(t0));
+ __ li(t0, Operand(ExternalReference(Isolate::k_pending_exception_address,
+ masm->isolate())));
+ __ lw(v0, MemOperand(t0));
+ __ sw(a3, MemOperand(t0));
+
+ // Special handling of termination exceptions which are uncatchable
+ // by javascript code.
+ __ Branch(throw_termination_exception, eq,
+ v0, Operand(masm->isolate()->factory()->termination_exception()));
+
+ // Handle normal exception.
+ __ jmp(throw_normal_exception);
+
+ __ bind(&retry);
+ // Last failure (v0) will be moved to (a0) for parameter when retrying.
}
void CEntryStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Called from JavaScript; parameters are on stack as if calling JS function
+ // a0: number of arguments including receiver
+ // a1: pointer to builtin function
+ // fp: frame pointer (restored after C call)
+ // sp: stack pointer (restored as callee's sp after C call)
+ // cp: current context (C callee-saved)
+
+ // NOTE: Invocations of builtins may return failure objects
+ // instead of a proper result. The builtin entry handles
+ // this by performing a garbage collection and retrying the
+ // builtin once.
+
+ // Compute the argv pointer in a callee-saved register.
+ __ sll(s1, a0, kPointerSizeLog2);
+ __ Addu(s1, sp, s1);
+ __ Subu(s1, s1, Operand(kPointerSize));
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+ __ EnterExitFrame(save_doubles_);
+
+ // Setup argc and the builtin function in callee-saved registers.
+ __ mov(s0, a0);
+ __ mov(s2, a1);
+
+ // s0: number of arguments (C callee-saved)
+ // s1: pointer to first argument (C callee-saved)
+ // s2: pointer to builtin function (C callee-saved)
+
+ Label throw_normal_exception;
+ Label throw_termination_exception;
+ Label throw_out_of_memory_exception;
+
+ // Call into the runtime system.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ false,
+ false);
+
+ // Do space-specific GC and retry runtime call.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ false);
+
+ // Do full GC and retry runtime call one final time.
+ Failure* failure = Failure::InternalError();
+ __ li(v0, Operand(reinterpret_cast<int32_t>(failure)));
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_termination_exception,
+ &throw_out_of_memory_exception,
+ true,
+ true);
+
+ __ bind(&throw_out_of_memory_exception);
+ GenerateThrowUncatchable(masm, OUT_OF_MEMORY);
+
+ __ bind(&throw_termination_exception);
+ GenerateThrowUncatchable(masm, TERMINATION);
+
+ __ bind(&throw_normal_exception);
+ GenerateThrowTOS(masm);
}
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
- UNIMPLEMENTED_MIPS();
+ Label invoke, exit;
+
+ // Registers:
+ // a0: entry address
+ // a1: function
+ // a2: reveiver
+ // a3: argc
+ //
+ // Stack:
+ // 4 args slots
+ // args
+
+ // Save callee saved registers on the stack.
+ __ MultiPush((kCalleeSaved | ra.bit()) & ~sp.bit());
+
+ // Load argv in s0 register.
+ __ lw(s0, MemOperand(sp, kNumCalleeSaved * kPointerSize +
+ StandardFrameConstants::kCArgsSlotsSize));
+
+ // We build an EntryFrame.
+ __ li(t3, Operand(-1)); // Push a bad frame pointer to fail if it is used.
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ __ li(t2, Operand(Smi::FromInt(marker)));
+ __ li(t1, Operand(Smi::FromInt(marker)));
+ __ li(t0, Operand(ExternalReference(Isolate::k_c_entry_fp_address,
+ masm->isolate())));
+ __ lw(t0, MemOperand(t0));
+ __ Push(t3, t2, t1, t0);
+ // Setup frame pointer for the frame to be pushed.
+ __ addiu(fp, sp, -EntryFrameConstants::kCallerFPOffset);
+
+ // Registers:
+ // a0: entry_address
+ // a1: function
+ // a2: reveiver_pointer
+ // a3: argc
+ // s0: argv
+ //
+ // Stack:
+ // caller fp |
+ // function slot | entry frame
+ // context slot |
+ // bad fp (0xff...f) |
+ // callee saved registers + ra
+ // 4 args slots
+ // args
+
+ #ifdef ENABLE_LOGGING_AND_PROFILING
+ // If this is the outermost JS call, set js_entry_sp value.
+ Label non_outermost_js;
+ ExternalReference js_entry_sp(Isolate::k_js_entry_sp_address,
+ masm->isolate());
+ __ li(t1, Operand(ExternalReference(js_entry_sp)));
+ __ lw(t2, MemOperand(t1));
+ __ Branch(&non_outermost_js, ne, t2, Operand(zero_reg));
+ __ sw(fp, MemOperand(t1));
+ __ li(t0, Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ Label cont;
+ __ b(&cont);
+ __ nop(); // Branch delay slot nop.
+ __ bind(&non_outermost_js);
+ __ li(t0, Operand(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
+ __ bind(&cont);
+ __ push(t0);
+ #endif
+
+ // Call a faked try-block that does the invoke.
+ __ bal(&invoke); // bal exposes branch delay slot.
+ __ nop(); // Branch delay slot nop.
+
+ // Caught exception: Store result (exception) in the pending
+ // exception field in the JSEnv and return a failure sentinel.
+ // Coming in here the fp will be invalid because the PushTryHandler below
+ // sets it to 0 to signal the existence of the JSEntry frame.
+ __ li(t0, Operand(ExternalReference(Isolate::k_pending_exception_address,
+ masm->isolate())));
+ __ sw(v0, MemOperand(t0)); // We come back from 'invoke'. result is in v0.
+ __ li(v0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
+ __ b(&exit); // b exposes branch delay slot.
+ __ nop(); // Branch delay slot nop.
+
+ // Invoke: Link this frame into the handler chain.
+ __ bind(&invoke);
+ __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
+ // If an exception not caught by another handler occurs, this handler
+ // returns control to the code after the bal(&invoke) above, which
+ // restores all kCalleeSaved registers (including cp and fp) to their
+ // saved values before returning a failure to C.
+
+ // Clear any pending exceptions.
+ __ li(t0,
+ Operand(ExternalReference::the_hole_value_location(masm->isolate())));
+ __ lw(t1, MemOperand(t0));
+ __ li(t0, Operand(ExternalReference(Isolate::k_pending_exception_address,
+ masm->isolate())));
+ __ sw(t1, MemOperand(t0));
+
+ // Invoke the function by calling through JS entry trampoline builtin.
+ // Notice that we cannot store a reference to the trampoline code directly in
+ // this stub, because runtime stubs are not traversed when doing GC.
+
+ // Registers:
+ // a0: entry_address
+ // a1: function
+ // a2: reveiver_pointer
+ // a3: argc
+ // s0: argv
+ //
+ // Stack:
+ // handler frame
+ // entry frame
+ // callee saved registers + ra
+ // 4 args slots
+ // args
+
+ if (is_construct) {
+ ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
+ masm->isolate());
+ __ li(t0, Operand(construct_entry));
+ } else {
+ ExternalReference entry(Builtins::kJSEntryTrampoline, masm->isolate());
+ __ li(t0, Operand(entry));
+ }
+ __ lw(t9, MemOperand(t0)); // Deref address.
+
+ // Call JSEntryTrampoline.
+ __ addiu(t9, t9, Code::kHeaderSize - kHeapObjectTag);
+ __ Call(t9);
+
+ // Unlink this frame from the handler chain.
+ __ PopTryHandler();
+
+ __ bind(&exit); // v0 holds result
+ #ifdef ENABLE_LOGGING_AND_PROFILING
+ // Check if the current stack frame is marked as the outermost JS frame.
+ Label non_outermost_js_2;
+ __ pop(t1);
+ __ Branch(&non_outermost_js_2, ne, t1,
+ Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME)));
+ __ li(t1, Operand(ExternalReference(js_entry_sp)));
+ __ sw(zero_reg, MemOperand(t1));
+ __ bind(&non_outermost_js_2);
+ #endif
+
+ // Restore the top frame descriptors from the stack.
+ __ pop(t1);
+ __ li(t0, Operand(ExternalReference(Isolate::k_c_entry_fp_address,
+ masm->isolate())));
+ __ sw(t1, MemOperand(t0));
+
+ // Reset the stack to the callee saved registers.
+ __ addiu(sp, sp, -EntryFrameConstants::kCallerFPOffset);
+
+ // Restore callee saved registers from the stack.
+ __ MultiPop((kCalleeSaved | ra.bit()) & ~sp.bit());
+ // Return.
+ __ Jump(ra);
}
-// Uses registers a0 to t0. Expected input is
-// object in a0 (or at sp+1*kPointerSize) and function in
-// a1 (or at sp), depending on whether or not
-// args_in_registers() is true.
+// Uses registers a0 to t0.
+// Expected input (depending on whether args are in registers or on the stack):
+// * object: a0 or at sp + 1 * kPointerSize.
+// * function: a1 or at sp.
+//
+// Inlined call site patching is a crankshaft-specific feature that is not
+// implemented on MIPS.
void InstanceofStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // This is a crankshaft-specific feature that has not been implemented yet.
+ ASSERT(!HasCallSiteInlineCheck());
+ // Call site inlining and patching implies arguments in registers.
+ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
+ // ReturnTrueFalse is only implemented for inlined call sites.
+ ASSERT(!ReturnTrueFalseObject() || HasCallSiteInlineCheck());
+
+ // Fixed register usage throughout the stub:
+ const Register object = a0; // Object (lhs).
+ Register map = a3; // Map of the object.
+ const Register function = a1; // Function (rhs).
+ const Register prototype = t0; // Prototype of the function.
+ const Register inline_site = t5;
+ const Register scratch = a2;
+
+ Label slow, loop, is_instance, is_not_instance, not_js_object;
+
+ if (!HasArgsInRegisters()) {
+ __ lw(object, MemOperand(sp, 1 * kPointerSize));
+ __ lw(function, MemOperand(sp, 0));
+ }
+
+ // Check that the left hand is a JS object and load map.
+ __ JumpIfSmi(object, &not_js_object);
+ __ IsObjectJSObjectType(object, map, scratch, &not_js_object);
+
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ Label miss;
+ __ LoadRoot(t1, Heap::kInstanceofCacheFunctionRootIndex);
+ __ Branch(&miss, ne, function, Operand(t1));
+ __ LoadRoot(t1, Heap::kInstanceofCacheMapRootIndex);
+ __ Branch(&miss, ne, map, Operand(t1));
+ __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&miss);
+ }
+
+ // Get the prototype of the function.
+ __ TryGetFunctionPrototype(function, prototype, scratch, &slow);
+
+ // Check that the function prototype is a JS object.
+ __ JumpIfSmi(prototype, &slow);
+ __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
+
+ // Update the global instanceof or call site inlined cache with the current
+ // map and function. The cached answer will be set when it is known below.
+ if (!HasCallSiteInlineCheck()) {
+ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
+ } else {
+ UNIMPLEMENTED_MIPS();
+ }
+
+ // Register mapping: a3 is object map and t0 is function prototype.
+ // Get prototype of object into a2.
+ __ lw(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+
+ // We don't need map any more. Use it as a scratch register.
+ Register scratch2 = map;
+ map = no_reg;
+
+ // Loop through the prototype chain looking for the function prototype.
+ __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
+ __ bind(&loop);
+ __ Branch(&is_instance, eq, scratch, Operand(prototype));
+ __ Branch(&is_not_instance, eq, scratch, Operand(scratch2));
+ __ lw(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
+ __ lw(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
+ __ Branch(&loop);
+
+ __ bind(&is_instance);
+ ASSERT(Smi::FromInt(0) == 0);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(v0, zero_reg);
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ UNIMPLEMENTED_MIPS();
+ }
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&is_not_instance);
+ if (!HasCallSiteInlineCheck()) {
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ UNIMPLEMENTED_MIPS();
+ }
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ Label object_not_null, object_not_null_or_smi;
+ __ bind(&not_js_object);
+ // Before null, smi and string value checks, check that the rhs is a function
+ // as for a non-function rhs an exception needs to be thrown.
+ __ JumpIfSmi(function, &slow);
+ __ GetObjectType(function, scratch2, scratch);
+ __ Branch(&slow, ne, scratch, Operand(JS_FUNCTION_TYPE));
+
+ // Null is not instance of anything.
+ __ Branch(&object_not_null, ne, scratch,
+ Operand(masm->isolate()->factory()->null_value()));
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&object_not_null);
+ // Smi values are not instances of anything.
+ __ JumpIfNotSmi(object, &object_not_null_or_smi);
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&object_not_null_or_smi);
+ // String values are not instances of anything.
+ __ IsObjectJSStringType(object, scratch, &slow);
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ // Slow-case. Tail call builtin.
+ __ bind(&slow);
+ if (!ReturnTrueFalseObject()) {
+ if (HasArgsInRegisters()) {
+ __ Push(a0, a1);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+ } else {
+ __ EnterInternalFrame();
+ __ Push(a0, a1);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
+ __ LeaveInternalFrame();
+ __ mov(a0, v0);
+ __ LoadRoot(v0, Heap::kTrueValueRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2, eq, a0, Operand(zero_reg));
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+ }
}
+Register InstanceofStub::left() { return a0; }
+
+
+Register InstanceofStub::right() { return a1; }
+
+
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // The displacement is the offset of the last parameter (if any)
+ // relative to the frame pointer.
+ static const int kDisplacement =
+ StandardFrameConstants::kCallerSPOffset - kPointerSize;
+
+ // Check that the key is a smiGenerateReadElement.
+ Label slow;
+ __ JumpIfNotSmi(a1, &slow);
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor;
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&adaptor,
+ eq,
+ a3,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Check index (a1) against formal parameters count limit passed in
+ // through register a0. Use unsigned comparison to get negative
+ // check for free.
+ __ Branch(&slow, hs, a1, Operand(a0));
+
+ // Read the argument from the stack and return it.
+ __ subu(a3, a0, a1);
+ __ sll(t3, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a3, fp, Operand(t3));
+ __ lw(v0, MemOperand(a3, kDisplacement));
+ __ Ret();
+
+ // Arguments adaptor case: Check index (a1) against actual arguments
+ // limit found in the arguments adaptor frame. Use unsigned
+ // comparison to get negative check for free.
+ __ bind(&adaptor);
+ __ lw(a0, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ Branch(&slow, Ugreater_equal, a1, Operand(a0));
+
+ // Read the argument from the adaptor frame and return it.
+ __ subu(a3, a0, a1);
+ __ sll(t3, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a3, a2, Operand(t3));
+ __ lw(v0, MemOperand(a3, kDisplacement));
+ __ Ret();
+
+ // Slow-case: Handle non-smi or out-of-bounds access to arguments
+ // by calling the runtime system.
+ __ bind(&slow);
+ __ push(a1);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
}
void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // sp[0] : number of parameters
+ // sp[4] : receiver displacement
+ // sp[8] : function
+
+ // Check if the calling frame is an arguments adaptor frame.
+ Label adaptor_frame, try_allocate, runtime;
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&adaptor_frame,
+ eq,
+ a3,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Get the length from the frame.
+ __ lw(a1, MemOperand(sp, 0));
+ __ Branch(&try_allocate);
+
+ // Patch the arguments.length and the parameters pointer.
+ __ bind(&adaptor_frame);
+ __ lw(a1, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ sw(a1, MemOperand(sp, 0));
+ __ sll(at, a1, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a3, a2, Operand(at));
+
+ __ Addu(a3, a3, Operand(StandardFrameConstants::kCallerSPOffset));
+ __ sw(a3, MemOperand(sp, 1 * kPointerSize));
+
+ // Try the new space allocation. Start out with computing the size
+ // of the arguments object and the elements array in words.
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ Branch(&add_arguments_object, eq, a1, Operand(zero_reg));
+ __ srl(a1, a1, kSmiTagSize);
+
+ __ Addu(a1, a1, Operand(FixedArray::kHeaderSize / kPointerSize));
+ __ bind(&add_arguments_object);
+ __ Addu(a1, a1, Operand(GetArgumentsObjectSize() / kPointerSize));
+
+ // Do the allocation of both objects in one go.
+ __ AllocateInNewSpace(
+ a1,
+ v0,
+ a2,
+ a3,
+ &runtime,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+
+ // Get the arguments boilerplate from the current (global) context.
+ __ lw(t0, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ lw(t0, FieldMemOperand(t0, GlobalObject::kGlobalContextOffset));
+ __ lw(t0, MemOperand(t0,
+ Context::SlotOffset(GetArgumentsBoilerplateIndex())));
+
+ // Copy the JS object part.
+ __ CopyFields(v0, t0, a3.bit(), JSObject::kHeaderSize / kPointerSize);
+
+ if (type_ == NEW_NON_STRICT) {
+ // Setup the callee in-object property.
+ STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
+ __ lw(a3, MemOperand(sp, 2 * kPointerSize));
+ const int kCalleeOffset = JSObject::kHeaderSize +
+ Heap::kArgumentsCalleeIndex * kPointerSize;
+ __ sw(a3, FieldMemOperand(v0, kCalleeOffset));
+ }
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
+ __ lw(a1, MemOperand(sp, 0 * kPointerSize));
+ __ sw(a1, FieldMemOperand(v0, JSObject::kHeaderSize +
+ Heap::kArgumentsLengthIndex * kPointerSize));
+
+ Label done;
+ __ Branch(&done, eq, a1, Operand(zero_reg));
+
+ // Get the parameters pointer from the stack.
+ __ lw(a2, MemOperand(sp, 1 * kPointerSize));
+
+ // Setup the elements pointer in the allocated arguments object and
+ // initialize the header in the elements fixed array.
+ __ Addu(t0, v0, Operand(GetArgumentsObjectSize()));
+ __ sw(t0, FieldMemOperand(v0, JSObject::kElementsOffset));
+ __ LoadRoot(a3, Heap::kFixedArrayMapRootIndex);
+ __ sw(a3, FieldMemOperand(t0, FixedArray::kMapOffset));
+ __ sw(a1, FieldMemOperand(t0, FixedArray::kLengthOffset));
+ __ srl(a1, a1, kSmiTagSize); // Untag the length for the loop.
+
+ // Copy the fixed array slots.
+ Label loop;
+ // Setup t0 to point to the first array slot.
+ __ Addu(t0, t0, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ bind(&loop);
+ // Pre-decrement a2 with kPointerSize on each iteration.
+ // Pre-decrement in order to skip receiver.
+ __ Addu(a2, a2, Operand(-kPointerSize));
+ __ lw(a3, MemOperand(a2));
+ // Post-increment t0 with kPointerSize on each iteration.
+ __ sw(a3, MemOperand(t0));
+ __ Addu(t0, t0, Operand(kPointerSize));
+ __ Subu(a1, a1, Operand(1));
+ __ Branch(&loop, ne, a1, Operand(zero_reg));
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ // Do the runtime call to allocate the arguments object.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
}
void RegExpExecStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Just jump directly to runtime if native RegExp is not selected at compile
+ // time or if regexp entry in generated code is turned off runtime switch or
+ // at compilation.
+#ifdef V8_INTERPRETED_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#else // V8_INTERPRETED_REGEXP
+ if (!FLAG_regexp_entry_native) {
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+ return;
+ }
+
+ // Stack frame on entry.
+ // sp[0]: last_match_info (expected JSArray)
+ // sp[4]: previous index
+ // sp[8]: subject string
+ // sp[12]: JSRegExp object
+
+ static const int kLastMatchInfoOffset = 0 * kPointerSize;
+ static const int kPreviousIndexOffset = 1 * kPointerSize;
+ static const int kSubjectOffset = 2 * kPointerSize;
+ static const int kJSRegExpOffset = 3 * kPointerSize;
+
+ Label runtime, invoke_regexp;
+
+ // Allocation of registers for this function. These are in callee save
+ // registers and will be preserved by the call to the native RegExp code, as
+ // this code is called using the normal C calling convention. When calling
+ // directly from generated code the native RegExp code will not do a GC and
+ // therefore the content of these registers are safe to use after the call.
+ // MIPS - using s0..s2, since we are not using CEntry Stub.
+ Register subject = s0;
+ Register regexp_data = s1;
+ Register last_match_info_elements = s2;
+
+ // Ensure that a RegExp stack is allocated.
+ ExternalReference address_of_regexp_stack_memory_address =
+ ExternalReference::address_of_regexp_stack_memory_address(
+ masm->isolate());
+ ExternalReference address_of_regexp_stack_memory_size =
+ ExternalReference::address_of_regexp_stack_memory_size(masm->isolate());
+ __ li(a0, Operand(address_of_regexp_stack_memory_size));
+ __ lw(a0, MemOperand(a0, 0));
+ __ Branch(&runtime, eq, a0, Operand(zero_reg));
+
+ // Check that the first argument is a JSRegExp object.
+ __ lw(a0, MemOperand(sp, kJSRegExpOffset));
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a0, &runtime);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&runtime, ne, a1, Operand(JS_REGEXP_TYPE));
+
+ // Check that the RegExp has been compiled (data contains a fixed array).
+ __ lw(regexp_data, FieldMemOperand(a0, JSRegExp::kDataOffset));
+ if (FLAG_debug_code) {
+ __ And(t0, regexp_data, Operand(kSmiTagMask));
+ __ Check(nz,
+ "Unexpected type for RegExp data, FixedArray expected",
+ t0,
+ Operand(zero_reg));
+ __ GetObjectType(regexp_data, a0, a0);
+ __ Check(eq,
+ "Unexpected type for RegExp data, FixedArray expected",
+ a0,
+ Operand(FIXED_ARRAY_TYPE));
+ }
+
+ // regexp_data: RegExp data (FixedArray)
+ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
+ __ lw(a0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
+ __ Branch(&runtime, ne, a0, Operand(Smi::FromInt(JSRegExp::IRREGEXP)));
+
+ // regexp_data: RegExp data (FixedArray)
+ // Check that the number of captures fit in the static offsets vector buffer.
+ __ lw(a2,
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
+ // Calculate number of capture registers (number_of_captures + 1) * 2. This
+ // uses the asumption that smis are 2 * their untagged value.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ __ Addu(a2, a2, Operand(2)); // a2 was a smi.
+ // Check that the static offsets vector buffer is large enough.
+ __ Branch(&runtime, hi, a2, Operand(OffsetsVector::kStaticOffsetsVectorSize));
+
+ // a2: Number of capture registers
+ // regexp_data: RegExp data (FixedArray)
+ // Check that the second argument is a string.
+ __ lw(subject, MemOperand(sp, kSubjectOffset));
+ __ JumpIfSmi(subject, &runtime);
+ __ GetObjectType(subject, a0, a0);
+ __ And(a0, a0, Operand(kIsNotStringMask));
+ STATIC_ASSERT(kStringTag == 0);
+ __ Branch(&runtime, ne, a0, Operand(zero_reg));
+
+ // Get the length of the string to r3.
+ __ lw(a3, FieldMemOperand(subject, String::kLengthOffset));
+
+ // a2: Number of capture registers
+ // a3: Length of subject string as a smi
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // Check that the third argument is a positive smi less than the subject
+ // string length. A negative value will be greater (unsigned comparison).
+ __ lw(a0, MemOperand(sp, kPreviousIndexOffset));
+ __ And(at, a0, Operand(kSmiTagMask));
+ __ Branch(&runtime, ne, at, Operand(zero_reg));
+ __ Branch(&runtime, ls, a3, Operand(a0));
+
+ // a2: Number of capture registers
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // Check that the fourth object is a JSArray object.
+ __ lw(a0, MemOperand(sp, kLastMatchInfoOffset));
+ __ JumpIfSmi(a0, &runtime);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&runtime, ne, a1, Operand(JS_ARRAY_TYPE));
+ // Check that the JSArray is in fast case.
+ __ lw(last_match_info_elements,
+ FieldMemOperand(a0, JSArray::kElementsOffset));
+ __ lw(a0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset));
+ __ Branch(&runtime, ne, a0, Operand(
+ masm->isolate()->factory()->fixed_array_map()));
+ // Check that the last match info has space for the capture registers and the
+ // additional information.
+ __ lw(a0,
+ FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
+ __ Addu(a2, a2, Operand(RegExpImpl::kLastMatchOverhead));
+ __ sra(at, a0, kSmiTagSize); // Untag length for comparison.
+ __ Branch(&runtime, gt, a2, Operand(at));
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // Check the representation and encoding of the subject string.
+ Label seq_string;
+ __ lw(a0, FieldMemOperand(subject, HeapObject::kMapOffset));
+ __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset));
+ // First check for flat string.
+ __ And(at, a0, Operand(kIsNotStringMask | kStringRepresentationMask));
+ STATIC_ASSERT((kStringTag | kSeqStringTag) == 0);
+ __ Branch(&seq_string, eq, at, Operand(zero_reg));
+
+ // subject: Subject string
+ // a0: instance type if Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // Check for flat cons string.
+ // A flat cons string is a cons string where the second part is the empty
+ // string. In that case the subject string is just the first part of the cons
+ // string. Also in this case the first part of the cons string is known to be
+ // a sequential string or an external string.
+ STATIC_ASSERT(kExternalStringTag != 0);
+ STATIC_ASSERT((kConsStringTag & kExternalStringTag) == 0);
+ __ And(at, a0, Operand(kIsNotStringMask | kExternalStringTag));
+ __ Branch(&runtime, ne, at, Operand(zero_reg));
+ __ lw(a0, FieldMemOperand(subject, ConsString::kSecondOffset));
+ __ LoadRoot(a1, Heap::kEmptyStringRootIndex);
+ __ Branch(&runtime, ne, a0, Operand(a1));
+ __ lw(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
+ __ lw(a0, FieldMemOperand(subject, HeapObject::kMapOffset));
+ __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset));
+ // Is first part a flat string?
+ STATIC_ASSERT(kSeqStringTag == 0);
+ __ And(at, a0, Operand(kStringRepresentationMask));
+ __ Branch(&runtime, ne, at, Operand(zero_reg));
+
+ __ bind(&seq_string);
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // a0: Instance type of subject string
+ STATIC_ASSERT(kStringEncodingMask == 4);
+ STATIC_ASSERT(kAsciiStringTag == 4);
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ // Find the code object based on the assumptions above.
+ __ And(a0, a0, Operand(kStringEncodingMask)); // Non-zero for ascii.
+ __ lw(t9, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset));
+ __ sra(a3, a0, 2); // a3 is 1 for ascii, 0 for UC16 (usyed below).
+ __ lw(t0, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset));
+ __ movz(t9, t0, a0); // If UC16 (a0 is 0), replace t9 w/kDataUC16CodeOffset.
+
+ // Check that the irregexp code has been generated for the actual string
+ // encoding. If it has, the field contains a code object otherwise it
+ // contains the hole.
+ __ GetObjectType(t9, a0, a0);
+ __ Branch(&runtime, ne, a0, Operand(CODE_TYPE));
+
+ // a3: encoding of subject string (1 if ASCII, 0 if two_byte);
+ // t9: code
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // Load used arguments before starting to push arguments for call to native
+ // RegExp code to avoid handling changing stack height.
+ __ lw(a1, MemOperand(sp, kPreviousIndexOffset));
+ __ sra(a1, a1, kSmiTagSize); // Untag the Smi.
+
+ // a1: previous index
+ // a3: encoding of subject string (1 if ASCII, 0 if two_byte);
+ // t9: code
+ // subject: Subject string
+ // regexp_data: RegExp data (FixedArray)
+ // All checks done. Now push arguments for native regexp code.
+ __ IncrementCounter(masm->isolate()->counters()->regexp_entry_native(),
+ 1, a0, a2);
+
+ // Isolates: note we add an additional parameter here (isolate pointer).
+ static const int kRegExpExecuteArguments = 8;
+ static const int kParameterRegisters = 4;
+ __ EnterExitFrame(false, kRegExpExecuteArguments - kParameterRegisters);
+
+ // Stack pointer now points to cell where return address is to be written.
+ // Arguments are before that on the stack or in registers, meaning we
+ // treat the return address as argument 5. Thus every argument after that
+ // needs to be shifted back by 1. Since DirectCEntryStub will handle
+ // allocating space for the c argument slots, we don't need to calculate
+ // that into the argument positions on the stack. This is how the stack will
+ // look (sp meaning the value of sp at this moment):
+ // [sp + 4] - Argument 8
+ // [sp + 3] - Argument 7
+ // [sp + 2] - Argument 6
+ // [sp + 1] - Argument 5
+ // [sp + 0] - saved ra
+
+ // Argument 8: Pass current isolate address.
+ // CFunctionArgumentOperand handles MIPS stack argument slots.
+ __ li(a0, Operand(ExternalReference::isolate_address()));
+ __ sw(a0, MemOperand(sp, 4 * kPointerSize));
+
+ // Argument 7: Indicate that this is a direct call from JavaScript.
+ __ li(a0, Operand(1));
+ __ sw(a0, MemOperand(sp, 3 * kPointerSize));
+
+ // Argument 6: Start (high end) of backtracking stack memory area.
+ __ li(a0, Operand(address_of_regexp_stack_memory_address));
+ __ lw(a0, MemOperand(a0, 0));
+ __ li(a2, Operand(address_of_regexp_stack_memory_size));
+ __ lw(a2, MemOperand(a2, 0));
+ __ addu(a0, a0, a2);
+ __ sw(a0, MemOperand(sp, 2 * kPointerSize));
+
+ // Argument 5: static offsets vector buffer.
+ __ li(a0, Operand(
+ ExternalReference::address_of_static_offsets_vector(masm->isolate())));
+ __ sw(a0, MemOperand(sp, 1 * kPointerSize));
+
+ // For arguments 4 and 3 get string length, calculate start of string data
+ // and calculate the shift of the index (0 for ASCII and 1 for two byte).
+ __ lw(a0, FieldMemOperand(subject, String::kLengthOffset));
+ __ sra(a0, a0, kSmiTagSize);
+ STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
+ __ Addu(t0, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ __ Xor(a3, a3, Operand(1)); // 1 for 2-byte str, 0 for 1-byte.
+ // Argument 4 (a3): End of string data
+ // Argument 3 (a2): Start of string data
+ __ sllv(t1, a1, a3);
+ __ addu(a2, t0, t1);
+ __ sllv(t1, a0, a3);
+ __ addu(a3, t0, t1);
+
+ // Argument 2 (a1): Previous index.
+ // Already there
+
+ // Argument 1 (a0): Subject string.
+ __ mov(a0, subject);
+
+ // Locate the code entry and call it.
+ __ Addu(t9, t9, Operand(Code::kHeaderSize - kHeapObjectTag));
+ DirectCEntryStub stub;
+ stub.GenerateCall(masm, t9);
+
+ __ LeaveExitFrame(false, no_reg);
+
+ // v0: result
+ // subject: subject string (callee saved)
+ // regexp_data: RegExp data (callee saved)
+ // last_match_info_elements: Last match info elements (callee saved)
+
+ // Check the result.
+
+ Label success;
+ __ Branch(&success, eq, v0, Operand(NativeRegExpMacroAssembler::SUCCESS));
+ Label failure;
+ __ Branch(&failure, eq, v0, Operand(NativeRegExpMacroAssembler::FAILURE));
+ // If not exception it can only be retry. Handle that in the runtime system.
+ __ Branch(&runtime, ne, v0, Operand(NativeRegExpMacroAssembler::EXCEPTION));
+ // Result must now be exception. If there is no pending exception already a
+ // stack overflow (on the backtrack stack) was detected in RegExp code but
+ // haven't created the exception yet. Handle that in the runtime system.
+ // TODO(592): Rerunning the RegExp to get the stack overflow exception.
+ __ li(a1, Operand(
+ ExternalReference::the_hole_value_location(masm->isolate())));
+ __ lw(a1, MemOperand(a1, 0));
+ __ li(a2, Operand(ExternalReference(Isolate::k_pending_exception_address,
+ masm->isolate())));
+ __ lw(v0, MemOperand(a2, 0));
+ __ Branch(&runtime, eq, v0, Operand(a1));
+
+ __ sw(a1, MemOperand(a2, 0)); // Clear pending exception.
+
+ // Check if the exception is a termination. If so, throw as uncatchable.
+ __ LoadRoot(a0, Heap::kTerminationExceptionRootIndex);
+ Label termination_exception;
+ __ Branch(&termination_exception, eq, v0, Operand(a0));
+
+ __ Throw(a0); // Expects thrown value in v0.
+
+ __ bind(&termination_exception);
+ __ ThrowUncatchable(TERMINATION, v0); // Expects thrown value in v0.
+
+ __ bind(&failure);
+ // For failure and exception return null.
+ __ li(v0, Operand(masm->isolate()->factory()->null_value()));
+ __ Addu(sp, sp, Operand(4 * kPointerSize));
+ __ Ret();
+
+ // Process the result from the native regexp code.
+ __ bind(&success);
+ __ lw(a1,
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
+ // Calculate number of capture registers (number_of_captures + 1) * 2.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+ __ Addu(a1, a1, Operand(2)); // a1 was a smi.
+
+ // a1: number of capture registers
+ // subject: subject string
+ // Store the capture count.
+ __ sll(a2, a1, kSmiTagSize + kSmiShiftSize); // To smi.
+ __ sw(a2, FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastCaptureCountOffset));
+ // Store last subject and last input.
+ __ mov(a3, last_match_info_elements); // Moved up to reduce latency.
+ __ sw(subject,
+ FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastSubjectOffset));
+ __ RecordWrite(a3, Operand(RegExpImpl::kLastSubjectOffset), a2, t0);
+ __ sw(subject,
+ FieldMemOperand(last_match_info_elements,
+ RegExpImpl::kLastInputOffset));
+ __ mov(a3, last_match_info_elements);
+ __ RecordWrite(a3, Operand(RegExpImpl::kLastInputOffset), a2, t0);
+
+ // Get the static offsets vector filled by the native regexp code.
+ ExternalReference address_of_static_offsets_vector =
+ ExternalReference::address_of_static_offsets_vector(masm->isolate());
+ __ li(a2, Operand(address_of_static_offsets_vector));
+
+ // a1: number of capture registers
+ // a2: offsets vector
+ Label next_capture, done;
+ // Capture register counter starts from number of capture registers and
+ // counts down until wrapping after zero.
+ __ Addu(a0,
+ last_match_info_elements,
+ Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag));
+ __ bind(&next_capture);
+ __ Subu(a1, a1, Operand(1));
+ __ Branch(&done, lt, a1, Operand(zero_reg));
+ // Read the value from the static offsets vector buffer.
+ __ lw(a3, MemOperand(a2, 0));
+ __ addiu(a2, a2, kPointerSize);
+ // Store the smi value in the last match info.
+ __ sll(a3, a3, kSmiTagSize); // Convert to Smi.
+ __ sw(a3, MemOperand(a0, 0));
+ __ Branch(&next_capture, USE_DELAY_SLOT);
+ __ addiu(a0, a0, kPointerSize); // In branch delay slot.
+
+ __ bind(&done);
+
+ // Return last match info.
+ __ lw(v0, MemOperand(sp, kLastMatchInfoOffset));
+ __ Addu(sp, sp, Operand(4 * kPointerSize));
+ __ Ret();
+
+ // Do the runtime call to execute the regexp.
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#endif // V8_INTERPRETED_REGEXP
}
void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ const int kMaxInlineLength = 100;
+ Label slowcase;
+ Label done;
+ __ lw(a1, MemOperand(sp, kPointerSize * 2));
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ __ JumpIfNotSmi(a1, &slowcase);
+ __ Branch(&slowcase, hi, a1, Operand(Smi::FromInt(kMaxInlineLength)));
+ // Smi-tagging is equivalent to multiplying by 2.
+ // Allocate RegExpResult followed by FixedArray with size in ebx.
+ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
+ // Elements: [Map][Length][..elements..]
+ // Size of JSArray with two in-object properties and the header of a
+ // FixedArray.
+ int objects_size =
+ (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
+ __ srl(t1, a1, kSmiTagSize + kSmiShiftSize);
+ __ Addu(a2, t1, Operand(objects_size));
+ __ AllocateInNewSpace(
+ a2, // In: Size, in words.
+ v0, // Out: Start of allocation (tagged).
+ a3, // Scratch register.
+ t0, // Scratch register.
+ &slowcase,
+ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
+ // v0: Start of allocated area, object-tagged.
+ // a1: Number of elements in array, as smi.
+ // t1: Number of elements, untagged.
+
+ // Set JSArray map to global.regexp_result_map().
+ // Set empty properties FixedArray.
+ // Set elements to point to FixedArray allocated right after the JSArray.
+ // Interleave operations for better latency.
+ __ lw(a2, ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ Addu(a3, v0, Operand(JSRegExpResult::kSize));
+ __ li(t0, Operand(masm->isolate()->factory()->empty_fixed_array()));
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalContextOffset));
+ __ sw(a3, FieldMemOperand(v0, JSObject::kElementsOffset));
+ __ lw(a2, ContextOperand(a2, Context::REGEXP_RESULT_MAP_INDEX));
+ __ sw(t0, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ sw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+
+ // Set input, index and length fields from arguments.
+ __ lw(a1, MemOperand(sp, kPointerSize * 0));
+ __ sw(a1, FieldMemOperand(v0, JSRegExpResult::kInputOffset));
+ __ lw(a1, MemOperand(sp, kPointerSize * 1));
+ __ sw(a1, FieldMemOperand(v0, JSRegExpResult::kIndexOffset));
+ __ lw(a1, MemOperand(sp, kPointerSize * 2));
+ __ sw(a1, FieldMemOperand(v0, JSArray::kLengthOffset));
+
+ // Fill out the elements FixedArray.
+ // v0: JSArray, tagged.
+ // a3: FixedArray, tagged.
+ // t1: Number of elements in array, untagged.
+
+ // Set map.
+ __ li(a2, Operand(masm->isolate()->factory()->fixed_array_map()));
+ __ sw(a2, FieldMemOperand(a3, HeapObject::kMapOffset));
+ // Set FixedArray length.
+ __ sll(t2, t1, kSmiTagSize);
+ __ sw(t2, FieldMemOperand(a3, FixedArray::kLengthOffset));
+ // Fill contents of fixed-array with the-hole.
+ __ li(a2, Operand(masm->isolate()->factory()->the_hole_value()));
+ __ Addu(a3, a3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // Fill fixed array elements with hole.
+ // v0: JSArray, tagged.
+ // a2: the hole.
+ // a3: Start of elements in FixedArray.
+ // t1: Number of elements to fill.
+ Label loop;
+ __ sll(t1, t1, kPointerSizeLog2); // Convert num elements to num bytes.
+ __ addu(t1, t1, a3); // Point past last element to store.
+ __ bind(&loop);
+ __ Branch(&done, ge, a3, Operand(t1)); // Break when a3 past end of elem.
+ __ sw(a2, MemOperand(a3));
+ __ Branch(&loop, USE_DELAY_SLOT);
+ __ addiu(a3, a3, kPointerSize); // In branch delay slot.
+
+ __ bind(&done);
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&slowcase);
+ __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label slow;
+
+ // The receiver might implicitly be the global object. This is
+ // indicated by passing the hole as the receiver to the call
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
+ // Get the receiver from the stack.
+ // function, receiver [, arguments]
+ __ lw(t0, MemOperand(sp, argc_ * kPointerSize));
+ // Call as function is indicated with the hole.
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&call, ne, t0, Operand(at));
+ // Patch the receiver on the stack with the global receiver object.
+ __ lw(a1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a1, MemOperand(sp, argc_ * kPointerSize));
+ __ bind(&call);
+ }
+
+ // Get the function to call from the stack.
+ // function, receiver [, arguments]
+ __ lw(a1, MemOperand(sp, (argc_ + 1) * kPointerSize));
+
+ // Check that the function is really a JavaScript function.
+ // a1: pushed function (to be verified)
+ __ JumpIfSmi(a1, &slow);
+ // Get the map of the function object.
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&slow, ne, a2, Operand(JS_FUNCTION_TYPE));
+
+ // Fast-case: Invoke the function now.
+ // a1: pushed function
+ ParameterCount actual(argc_);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&call_as_function, eq, t0, Operand(at));
+ __ InvokeFunction(a1, actual, JUMP_FUNCTION);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(a1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
+
+ // Slow-case: Non-function called.
+ __ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ sw(a1, MemOperand(sp, argc_ * kPointerSize));
+ __ li(a0, Operand(argc_)); // Setup the number of arguments.
+ __ mov(a2, zero_reg);
+ __ GetBuiltinEntry(a3, Builtins::CALL_NON_FUNCTION);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
}
// Unfortunately you have to run without snapshots to see most of these
// names in the profile since most compare stubs end up in the snapshot.
const char* CompareStub::GetName() {
- UNIMPLEMENTED_MIPS();
+ ASSERT((lhs_.is(a0) && rhs_.is(a1)) ||
+ (lhs_.is(a1) && rhs_.is(a0)));
+
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+
+ const char* cc_name;
+ switch (cc_) {
+ case lt: cc_name = "LT"; break;
+ case gt: cc_name = "GT"; break;
+ case le: cc_name = "LE"; break;
+ case ge: cc_name = "GE"; break;
+ case eq: cc_name = "EQ"; break;
+ case ne: cc_name = "NE"; break;
+ default: cc_name = "UnknownCondition"; break;
+ }
+
+ const char* lhs_name = lhs_.is(a0) ? "_a0" : "_a1";
+ const char* rhs_name = rhs_.is(a0) ? "_a0" : "_a1";
+
+ const char* strict_name = "";
+ if (strict_ && (cc_ == eq || cc_ == ne)) {
+ strict_name = "_STRICT";
+ }
+
+ const char* never_nan_nan_name = "";
+ if (never_nan_nan_ && (cc_ == eq || cc_ == ne)) {
+ never_nan_nan_name = "_NO_NAN";
+ }
+
+ const char* include_number_compare_name = "";
+ if (!include_number_compare_) {
+ include_number_compare_name = "_NO_NUMBER";
+ }
+
+ const char* include_smi_compare_name = "";
+ if (!include_smi_compare_) {
+ include_smi_compare_name = "_NO_SMI";
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "CompareStub_%s%s%s%s%s%s",
+ cc_name,
+ lhs_name,
+ rhs_name,
+ strict_name,
+ never_nan_nan_name,
+ include_number_compare_name,
+ include_smi_compare_name);
return name_;
}
int CompareStub::MinorKey() {
- UNIMPLEMENTED_MIPS();
- return 0;
+ // Encode the two parameters in a unique 16 bit value.
+ ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
+ ASSERT((lhs_.is(a0) && rhs_.is(a1)) ||
+ (lhs_.is(a1) && rhs_.is(a0)));
+ return ConditionField::encode(static_cast<unsigned>(cc_))
+ | RegisterField::encode(lhs_.is(a0))
+ | StrictField::encode(strict_)
+ | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
+ | IncludeSmiCompareField::encode(include_smi_compare_);
}
-// StringCharCodeAtGenerator
-
+// StringCharCodeAtGenerator.
void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label flat_string;
+ Label ascii_string;
+ Label got_char_code;
+
+ ASSERT(!t0.is(scratch_));
+ ASSERT(!t0.is(index_));
+ ASSERT(!t0.is(result_));
+ ASSERT(!t0.is(object_));
+
+ // If the receiver is a smi trigger the non-string case.
+ __ JumpIfSmi(object_, receiver_not_string_);
+
+ // Fetch the instance type of the receiver into result register.
+ __ lw(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ lbu(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
+ // If the receiver is not a string trigger the non-string case.
+ __ And(t0, result_, Operand(kIsNotStringMask));
+ __ Branch(receiver_not_string_, ne, t0, Operand(zero_reg));
+
+ // If the index is non-smi trigger the non-smi case.
+ __ JumpIfNotSmi(index_, &index_not_smi_);
+
+ // Put smi-tagged index into scratch register.
+ __ mov(scratch_, index_);
+ __ bind(&got_smi_index_);
+
+ // Check for index out of range.
+ __ lw(t0, FieldMemOperand(object_, String::kLengthOffset));
+ __ Branch(index_out_of_range_, ls, t0, Operand(scratch_));
+
+ // We need special handling for non-flat strings.
+ STATIC_ASSERT(kSeqStringTag == 0);
+ __ And(t0, result_, Operand(kStringRepresentationMask));
+ __ Branch(&flat_string, eq, t0, Operand(zero_reg));
+
+ // Handle non-flat strings.
+ __ And(t0, result_, Operand(kIsConsStringMask));
+ __ Branch(&call_runtime_, eq, t0, Operand(zero_reg));
+
+ // ConsString.
+ // Check whether the right hand side is the empty string (i.e. if
+ // this is really a flat string in a cons string). If that is not
+ // the case we would rather go to the runtime system now to flatten
+ // the string.
+ __ lw(result_, FieldMemOperand(object_, ConsString::kSecondOffset));
+ __ LoadRoot(t0, Heap::kEmptyStringRootIndex);
+ __ Branch(&call_runtime_, ne, result_, Operand(t0));
+
+ // Get the first of the two strings and load its instance type.
+ __ lw(object_, FieldMemOperand(object_, ConsString::kFirstOffset));
+ __ lw(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ lbu(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
+ // If the first cons component is also non-flat, then go to runtime.
+ STATIC_ASSERT(kSeqStringTag == 0);
+
+ __ And(t0, result_, Operand(kStringRepresentationMask));
+ __ Branch(&call_runtime_, ne, t0, Operand(zero_reg));
+
+ // Check for 1-byte or 2-byte string.
+ __ bind(&flat_string);
+ STATIC_ASSERT(kAsciiStringTag != 0);
+ __ And(t0, result_, Operand(kStringEncodingMask));
+ __ Branch(&ascii_string, ne, t0, Operand(zero_reg));
+
+ // 2-byte string.
+ // Load the 2-byte character code into the result register. We can
+ // add without shifting since the smi tag size is the log2 of the
+ // number of bytes in a two-byte character.
+ STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1 && kSmiShiftSize == 0);
+ __ Addu(scratch_, object_, Operand(scratch_));
+ __ lhu(result_, FieldMemOperand(scratch_, SeqTwoByteString::kHeaderSize));
+ __ Branch(&got_char_code);
+
+ // ASCII string.
+ // Load the byte into the result register.
+ __ bind(&ascii_string);
+
+ __ srl(t0, scratch_, kSmiTagSize);
+ __ Addu(scratch_, object_, t0);
+
+ __ lbu(result_, FieldMemOperand(scratch_, SeqAsciiString::kHeaderSize));
+
+ __ bind(&got_char_code);
+ __ sll(result_, result_, kSmiTagSize);
+ __ bind(&exit_);
}
void StringCharCodeAtGenerator::GenerateSlow(
MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
- UNIMPLEMENTED_MIPS();
+ __ Abort("Unexpected fallthrough to CharCodeAt slow case");
+
+ // Index is not a smi.
+ __ bind(&index_not_smi_);
+ // If index is a heap number, try converting it to an integer.
+ __ CheckMap(index_,
+ scratch_,
+ Heap::kHeapNumberMapRootIndex,
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
+ call_helper.BeforeCall(masm);
+ // Consumed by runtime conversion function:
+ __ Push(object_, index_, index_);
+ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
+ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
+ } else {
+ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
+ // NumberToSmi discards numbers that are not exact integers.
+ __ CallRuntime(Runtime::kNumberToSmi, 1);
+ }
+
+ // Save the conversion result before the pop instructions below
+ // have a chance to overwrite it.
+
+ __ Move(scratch_, v0);
+
+ __ pop(index_);
+ __ pop(object_);
+ // Reload the instance type.
+ __ lw(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
+ __ lbu(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
+ call_helper.AfterCall(masm);
+ // If index is still not a smi, it must be out of range.
+ __ JumpIfNotSmi(scratch_, index_out_of_range_);
+ // Otherwise, return to the fast path.
+ __ Branch(&got_smi_index_);
+
+ // Call runtime. We get here when the receiver is a string and the
+ // index is a number, but the code of getting the actual character
+ // is too complex (e.g., when the string needs to be flattened).
+ __ bind(&call_runtime_);
+ call_helper.BeforeCall(masm);
+ __ Push(object_, index_);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+
+ __ Move(result_, v0);
+
+ call_helper.AfterCall(masm);
+ __ jmp(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharCodeAt slow case");
}
@@ -532,13 +4936,46 @@ void StringCharCodeAtGenerator::GenerateSlow(
// StringCharFromCodeGenerator
void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+
+ ASSERT(!t0.is(result_));
+ ASSERT(!t0.is(code_));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
+ __ And(t0,
+ code_,
+ Operand(kSmiTagMask |
+ ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+ __ Branch(&slow_case_, ne, t0, Operand(zero_reg));
+
+ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
+ // At this point code register contains smi tagged ASCII char code.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ sll(t0, code_, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(result_, result_, t0);
+ __ lw(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
+ __ LoadRoot(t0, Heap::kUndefinedValueRootIndex);
+ __ Branch(&slow_case_, eq, result_, Operand(t0));
+ __ bind(&exit_);
}
void StringCharFromCodeGenerator::GenerateSlow(
MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
- UNIMPLEMENTED_MIPS();
+ __ Abort("Unexpected fallthrough to CharFromCode slow case");
+
+ __ bind(&slow_case_);
+ call_helper.BeforeCall(masm);
+ __ push(code_);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ __ Move(result_, v0);
+
+ call_helper.AfterCall(masm);
+ __ Branch(&exit_);
+
+ __ Abort("Unexpected fallthrough from CharFromCode slow case");
}
@@ -546,13 +4983,15 @@ void StringCharFromCodeGenerator::GenerateSlow(
// StringCharAtGenerator
void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ char_code_at_generator_.GenerateFast(masm);
+ char_from_code_generator_.GenerateFast(masm);
}
void StringCharAtGenerator::GenerateSlow(
MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
- UNIMPLEMENTED_MIPS();
+ char_code_at_generator_.GenerateSlow(masm, call_helper);
+ char_from_code_generator_.GenerateSlow(masm, call_helper);
}
@@ -626,7 +5065,24 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
Register count,
Register scratch,
bool ascii) {
- UNIMPLEMENTED_MIPS();
+ Label loop;
+ Label done;
+ // This loop just copies one character at a time, as it is only used for
+ // very short strings.
+ if (!ascii) {
+ __ addu(count, count, count);
+ }
+ __ Branch(&done, eq, count, Operand(zero_reg));
+ __ addu(count, dest, count); // Count now points to the last dest byte.
+
+ __ bind(&loop);
+ __ lbu(scratch, MemOperand(src));
+ __ addiu(src, src, 1);
+ __ sb(scratch, MemOperand(dest));
+ __ addiu(dest, dest, 1);
+ __ Branch(&loop, lt, dest, Operand(count));
+
+ __ bind(&done);
}
@@ -646,7 +5102,105 @@ void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
Register scratch4,
Register scratch5,
int flags) {
- UNIMPLEMENTED_MIPS();
+ bool ascii = (flags & COPY_ASCII) != 0;
+ bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
+
+ if (dest_always_aligned && FLAG_debug_code) {
+ // Check that destination is actually word aligned if the flag says
+ // that it is.
+ __ And(scratch4, dest, Operand(kPointerAlignmentMask));
+ __ Check(eq,
+ "Destination of copy not aligned.",
+ scratch4,
+ Operand(zero_reg));
+ }
+
+ const int kReadAlignment = 4;
+ const int kReadAlignmentMask = kReadAlignment - 1;
+ // Ensure that reading an entire aligned word containing the last character
+ // of a string will not read outside the allocated area (because we pad up
+ // to kObjectAlignment).
+ STATIC_ASSERT(kObjectAlignment >= kReadAlignment);
+ // Assumes word reads and writes are little endian.
+ // Nothing to do for zero characters.
+ Label done;
+
+ if (!ascii) {
+ __ addu(count, count, count);
+ }
+ __ Branch(&done, eq, count, Operand(zero_reg));
+
+ Label byte_loop;
+ // Must copy at least eight bytes, otherwise just do it one byte at a time.
+ __ Subu(scratch1, count, Operand(8));
+ __ Addu(count, dest, Operand(count));
+ Register limit = count; // Read until src equals this.
+ __ Branch(&byte_loop, lt, scratch1, Operand(zero_reg));
+
+ if (!dest_always_aligned) {
+ // Align dest by byte copying. Copies between zero and three bytes.
+ __ And(scratch4, dest, Operand(kReadAlignmentMask));
+ Label dest_aligned;
+ __ Branch(&dest_aligned, eq, scratch4, Operand(zero_reg));
+ Label aligned_loop;
+ __ bind(&aligned_loop);
+ __ lbu(scratch1, MemOperand(src));
+ __ addiu(src, src, 1);
+ __ sb(scratch1, MemOperand(dest));
+ __ addiu(dest, dest, 1);
+ __ addiu(scratch4, scratch4, 1);
+ __ Branch(&aligned_loop, le, scratch4, Operand(kReadAlignmentMask));
+ __ bind(&dest_aligned);
+ }
+
+ Label simple_loop;
+
+ __ And(scratch4, src, Operand(kReadAlignmentMask));
+ __ Branch(&simple_loop, eq, scratch4, Operand(zero_reg));
+
+ // Loop for src/dst that are not aligned the same way.
+ // This loop uses lwl and lwr instructions. These instructions
+ // depend on the endianness, and the implementation assumes little-endian.
+ {
+ Label loop;
+ __ bind(&loop);
+ __ lwr(scratch1, MemOperand(src));
+ __ Addu(src, src, Operand(kReadAlignment));
+ __ lwl(scratch1, MemOperand(src, -1));
+ __ sw(scratch1, MemOperand(dest));
+ __ Addu(dest, dest, Operand(kReadAlignment));
+ __ Subu(scratch2, limit, dest);
+ __ Branch(&loop, ge, scratch2, Operand(kReadAlignment));
+ }
+
+ __ Branch(&byte_loop);
+
+ // Simple loop.
+ // Copy words from src to dest, until less than four bytes left.
+ // Both src and dest are word aligned.
+ __ bind(&simple_loop);
+ {
+ Label loop;
+ __ bind(&loop);
+ __ lw(scratch1, MemOperand(src));
+ __ Addu(src, src, Operand(kReadAlignment));
+ __ sw(scratch1, MemOperand(dest));
+ __ Addu(dest, dest, Operand(kReadAlignment));
+ __ Subu(scratch2, limit, dest);
+ __ Branch(&loop, ge, scratch2, Operand(kReadAlignment));
+ }
+
+ // Copy bytes from src to dest until dest hits limit.
+ __ bind(&byte_loop);
+ // Test if dest has already reached the limit.
+ __ Branch(&done, ge, dest, Operand(limit));
+ __ lbu(scratch1, MemOperand(src));
+ __ addiu(src, src, 1);
+ __ sb(scratch1, MemOperand(dest));
+ __ addiu(dest, dest, 1);
+ __ Branch(&byte_loop);
+
+ __ bind(&done);
}
@@ -659,88 +5213,1439 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
Register scratch4,
Register scratch5,
Label* not_found) {
- UNIMPLEMENTED_MIPS();
+ // Register scratch3 is the general scratch register in this function.
+ Register scratch = scratch3;
+
+ // Make sure that both characters are not digits as such strings has a
+ // different hash algorithm. Don't try to look for these in the symbol table.
+ Label not_array_index;
+ __ Subu(scratch, c1, Operand(static_cast<int>('0')));
+ __ Branch(&not_array_index,
+ Ugreater,
+ scratch,
+ Operand(static_cast<int>('9' - '0')));
+ __ Subu(scratch, c2, Operand(static_cast<int>('0')));
+
+ // If check failed combine both characters into single halfword.
+ // This is required by the contract of the method: code at the
+ // not_found branch expects this combination in c1 register.
+ Label tmp;
+ __ sll(scratch1, c2, kBitsPerByte);
+ __ Branch(&tmp, Ugreater, scratch, Operand(static_cast<int>('9' - '0')));
+ __ Or(c1, c1, scratch1);
+ __ bind(&tmp);
+ __ Branch(not_found,
+ Uless_equal,
+ scratch,
+ Operand(static_cast<int>('9' - '0')));
+
+ __ bind(&not_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ StringHelper::GenerateHashInit(masm, hash, c1);
+ StringHelper::GenerateHashAddCharacter(masm, hash, c2);
+ StringHelper::GenerateHashGetHash(masm, hash);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ sll(scratch, c2, kBitsPerByte);
+ __ Or(chars, chars, scratch);
+
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string.
+
+ // Load symbol table.
+ // Load address of first element of the symbol table.
+ Register symbol_table = c2;
+ __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
+
+ Register undefined = scratch4;
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ // Calculate capacity mask from the symbol table capacity.
+ Register mask = scratch2;
+ __ lw(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
+ __ sra(mask, mask, 1);
+ __ Addu(mask, mask, -1);
+
+ // Calculate untagged address of the first element of the symbol table.
+ Register first_symbol_table_element = symbol_table;
+ __ Addu(first_symbol_table_element, symbol_table,
+ Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
+
+ // Registers.
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string
+ // mask: capacity mask
+ // first_symbol_table_element: address of the first element of
+ // the symbol table
+ // undefined: the undefined object
+ // scratch: -
+
+ // Perform a number of probes in the symbol table.
+ static const int kProbes = 4;
+ Label found_in_symbol_table;
+ Label next_probe[kProbes];
+ Register candidate = scratch5; // Scratch register contains candidate.
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in symbol table.
+ if (i > 0) {
+ __ Addu(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
+ } else {
+ __ mov(candidate, hash);
+ }
+
+ __ And(candidate, candidate, Operand(mask));
+
+ // Load the entry from the symble table.
+ STATIC_ASSERT(SymbolTable::kEntrySize == 1);
+ __ sll(scratch, candidate, kPointerSizeLog2);
+ __ Addu(scratch, scratch, first_symbol_table_element);
+ __ lw(candidate, MemOperand(scratch));
+
+ // If entry is undefined no string with this hash can be found.
+ Label is_string;
+ __ GetObjectType(candidate, scratch, scratch);
+ __ Branch(&is_string, ne, scratch, Operand(ODDBALL_TYPE));
+
+ __ Branch(not_found, eq, undefined, Operand(candidate));
+ // Must be null (deleted entry).
+ if (FLAG_debug_code) {
+ __ LoadRoot(scratch, Heap::kNullValueRootIndex);
+ __ Assert(eq, "oddball in symbol table is not undefined or null",
+ scratch, Operand(candidate));
+ }
+ __ jmp(&next_probe[i]);
+
+ __ bind(&is_string);
+
+ // Check that the candidate is a non-external ASCII string. The instance
+ // type is still in the scratch register from the CompareObjectType
+ // operation.
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch, &next_probe[i]);
+
+ // If length is not 2 the string is not a candidate.
+ __ lw(scratch, FieldMemOperand(candidate, String::kLengthOffset));
+ __ Branch(&next_probe[i], ne, scratch, Operand(Smi::FromInt(2)));
+
+ // Check if the two characters match.
+ // Assumes that word load is little endian.
+ __ lhu(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
+ __ Branch(&found_in_symbol_table, eq, chars, Operand(scratch));
+ __ bind(&next_probe[i]);
+ }
+
+ // No matching 2 character string found by probing.
+ __ jmp(not_found);
+
+ // Scratch register contains result when we fall through to here.
+ Register result = candidate;
+ __ bind(&found_in_symbol_table);
+ __ mov(v0, result);
}
void StringHelper::GenerateHashInit(MacroAssembler* masm,
Register hash,
Register character) {
- UNIMPLEMENTED_MIPS();
+ // hash = character + (character << 10);
+ __ sll(hash, character, 10);
+ __ addu(hash, hash, character);
+ // hash ^= hash >> 6;
+ __ sra(at, hash, 6);
+ __ xor_(hash, hash, at);
}
void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
Register hash,
Register character) {
- UNIMPLEMENTED_MIPS();
+ // hash += character;
+ __ addu(hash, hash, character);
+ // hash += hash << 10;
+ __ sll(at, hash, 10);
+ __ addu(hash, hash, at);
+ // hash ^= hash >> 6;
+ __ sra(at, hash, 6);
+ __ xor_(hash, hash, at);
}
void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
Register hash) {
- UNIMPLEMENTED_MIPS();
+ // hash += hash << 3;
+ __ sll(at, hash, 3);
+ __ addu(hash, hash, at);
+ // hash ^= hash >> 11;
+ __ sra(at, hash, 11);
+ __ xor_(hash, hash, at);
+ // hash += hash << 15;
+ __ sll(at, hash, 15);
+ __ addu(hash, hash, at);
+
+ // if (hash == 0) hash = 27;
+ __ ori(at, zero_reg, 27);
+ __ movz(hash, at, hash);
}
void SubStringStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label sub_string_runtime;
+ // Stack frame on entry.
+ // ra: return address
+ // sp[0]: to
+ // sp[4]: from
+ // sp[8]: string
+
+ // This stub is called from the native-call %_SubString(...), so
+ // nothing can be assumed about the arguments. It is tested that:
+ // "string" is a sequential string,
+ // both "from" and "to" are smis, and
+ // 0 <= from <= to <= string.length.
+ // If any of these assumptions fail, we call the runtime system.
+
+ static const int kToOffset = 0 * kPointerSize;
+ static const int kFromOffset = 1 * kPointerSize;
+ static const int kStringOffset = 2 * kPointerSize;
+
+ Register to = t2;
+ Register from = t3;
+
+ // Check bounds and smi-ness.
+ __ lw(to, MemOperand(sp, kToOffset));
+ __ lw(from, MemOperand(sp, kFromOffset));
+ STATIC_ASSERT(kFromOffset == kToOffset + 4);
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
+
+ __ JumpIfNotSmi(from, &sub_string_runtime);
+ __ JumpIfNotSmi(to, &sub_string_runtime);
+
+ __ sra(a3, from, kSmiTagSize); // Remove smi tag.
+ __ sra(t5, to, kSmiTagSize); // Remove smi tag.
+
+ // a3: from index (untagged smi)
+ // t5: to index (untagged smi)
+
+ __ Branch(&sub_string_runtime, lt, a3, Operand(zero_reg)); // From < 0.
+
+ __ subu(a2, t5, a3);
+ __ Branch(&sub_string_runtime, gt, a3, Operand(t5)); // Fail if from > to.
+
+ // Special handling of sub-strings of length 1 and 2. One character strings
+ // are handled in the runtime system (looked up in the single character
+ // cache). Two character strings are looked for in the symbol cache.
+ __ Branch(&sub_string_runtime, lt, a2, Operand(2));
+
+ // Both to and from are smis.
+
+ // a2: result string length
+ // a3: from index (untagged smi)
+ // t2: (a.k.a. to): to (smi)
+ // t3: (a.k.a. from): from offset (smi)
+ // t5: to index (untagged smi)
+
+ // Make sure first argument is a sequential (or flat) string.
+ __ lw(t1, MemOperand(sp, kStringOffset));
+ __ Branch(&sub_string_runtime, eq, t1, Operand(kSmiTagMask));
+
+ __ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
+ __ And(t4, a1, Operand(kIsNotStringMask));
+
+ __ Branch(&sub_string_runtime, ne, t4, Operand(zero_reg));
+
+ // a1: instance type
+ // a2: result string length
+ // a3: from index (untagged smi)
+ // t1: string
+ // t2: (a.k.a. to): to (smi)
+ // t3: (a.k.a. from): from offset (smi)
+ // t5: to index (untagged smi)
+
+ Label seq_string;
+ __ And(t0, a1, Operand(kStringRepresentationMask));
+ STATIC_ASSERT(kSeqStringTag < kConsStringTag);
+ STATIC_ASSERT(kConsStringTag < kExternalStringTag);
+
+ // External strings go to runtime.
+ __ Branch(&sub_string_runtime, gt, t0, Operand(kConsStringTag));
+
+ // Sequential strings are handled directly.
+ __ Branch(&seq_string, lt, t0, Operand(kConsStringTag));
+
+ // Cons string. Try to recurse (once) on the first substring.
+ // (This adds a little more generality than necessary to handle flattened
+ // cons strings, but not much).
+ __ lw(t1, FieldMemOperand(t1, ConsString::kFirstOffset));
+ __ lw(t0, FieldMemOperand(t1, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSeqStringTag == 0);
+ // Cons and External strings go to runtime.
+ __ Branch(&sub_string_runtime, ne, a1, Operand(kStringRepresentationMask));
+
+ // Definitly a sequential string.
+ __ bind(&seq_string);
+
+ // a1: instance type
+ // a2: result string length
+ // a3: from index (untagged smi)
+ // t1: string
+ // t2: (a.k.a. to): to (smi)
+ // t3: (a.k.a. from): from offset (smi)
+ // t5: to index (untagged smi)
+
+ __ lw(t0, FieldMemOperand(t1, String::kLengthOffset));
+ __ Branch(&sub_string_runtime, lt, t0, Operand(to)); // Fail if to > length.
+ to = no_reg;
+
+ // a1: instance type
+ // a2: result string length
+ // a3: from index (untagged smi)
+ // t1: string
+ // t3: (a.k.a. from): from offset (smi)
+ // t5: to index (untagged smi)
+
+ // Check for flat ASCII string.
+ Label non_ascii_flat;
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+
+ __ And(t4, a1, Operand(kStringEncodingMask));
+ __ Branch(&non_ascii_flat, eq, t4, Operand(zero_reg));
+
+ Label result_longer_than_two;
+ __ Branch(&result_longer_than_two, gt, a2, Operand(2));
+
+ // Sub string of length 2 requested.
+ // Get the two characters forming the sub string.
+ __ Addu(t1, t1, Operand(a3));
+ __ lbu(a3, FieldMemOperand(t1, SeqAsciiString::kHeaderSize));
+ __ lbu(t0, FieldMemOperand(t1, SeqAsciiString::kHeaderSize + 1));
+
+ // Try to lookup two character string in symbol table.
+ Label make_two_character_string;
+ StringHelper::GenerateTwoCharacterSymbolTableProbe(
+ masm, a3, t0, a1, t1, t2, t3, t4, &make_two_character_string);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+
+ // a2: result string length.
+ // a3: two characters combined into halfword in little endian byte order.
+ __ bind(&make_two_character_string);
+ __ AllocateAsciiString(v0, a2, t0, t1, t4, &sub_string_runtime);
+ __ sh(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
+ __ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&result_longer_than_two);
+
+ // Allocate the result.
+ __ AllocateAsciiString(v0, a2, t4, t0, a1, &sub_string_runtime);
+
+ // v0: result string.
+ // a2: result string length.
+ // a3: from index (untagged smi)
+ // t1: string.
+ // t3: (a.k.a. from): from offset (smi)
+ // Locate first character of result.
+ __ Addu(a1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // Locate 'from' character of string.
+ __ Addu(t1, t1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ __ Addu(t1, t1, Operand(a3));
+
+ // v0: result string.
+ // a1: first character of result string.
+ // a2: result string length.
+ // t1: first character of sub string to copy.
+ STATIC_ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0);
+ StringHelper::GenerateCopyCharactersLong(
+ masm, a1, t1, a2, a3, t0, t2, t3, t4, COPY_ASCII | DEST_ALWAYS_ALIGNED);
+ __ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&non_ascii_flat);
+ // a2: result string length.
+ // t1: string.
+ // t3: (a.k.a. from): from offset (smi)
+ // Check for flat two byte string.
+
+ // Allocate the result.
+ __ AllocateTwoByteString(v0, a2, a1, a3, t0, &sub_string_runtime);
+
+ // v0: result string.
+ // a2: result string length.
+ // t1: string.
+ // Locate first character of result.
+ __ Addu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // Locate 'from' character of string.
+ __ Addu(t1, t1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // As "from" is a smi it is 2 times the value which matches the size of a two
+ // byte character.
+ __ Addu(t1, t1, Operand(from));
+ from = no_reg;
+
+ // v0: result string.
+ // a1: first character of result.
+ // a2: result length.
+ // t1: first character of string to copy.
+ STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ StringHelper::GenerateCopyCharactersLong(
+ masm, a1, t1, a2, a3, t0, t2, t3, t4, DEST_ALWAYS_ALIGNED);
+ __ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
+ __ Addu(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ // Just jump to runtime to create the sub string.
+ __ bind(&sub_string_runtime);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
+}
+
+
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label strings_not_equal, check_zero_length;
+ __ lw(length, FieldMemOperand(left, String::kLengthOffset));
+ __ lw(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ Branch(&check_zero_length, eq, length, Operand(scratch2));
+ __ bind(&strings_not_equal);
+ __ li(v0, Operand(Smi::FromInt(NOT_EQUAL)));
+ __ Ret();
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Branch(&compare_chars, ne, length, Operand(zero_reg));
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
+
+ // Compare characters.
+ __ bind(&compare_chars);
+
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, length, scratch2, scratch3, v0,
+ &strings_not_equal);
+
+ // Characters are equal.
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
}
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
- Register right,
Register left,
+ Register right,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4) {
- UNIMPLEMENTED_MIPS();
+ Label result_not_equal, compare_lengths;
+ // Find minimum length and length difference.
+ __ lw(scratch1, FieldMemOperand(left, String::kLengthOffset));
+ __ lw(scratch2, FieldMemOperand(right, String::kLengthOffset));
+ __ Subu(scratch3, scratch1, Operand(scratch2));
+ Register length_delta = scratch3;
+ __ slt(scratch4, scratch2, scratch1);
+ __ movn(scratch1, scratch2, scratch4);
+ Register min_length = scratch1;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Branch(&compare_lengths, eq, min_length, Operand(zero_reg));
+
+ // Compare loop.
+ GenerateAsciiCharsCompareLoop(masm,
+ left, right, min_length, scratch2, scratch4, v0,
+ &result_not_equal);
+
+ // Compare lengths - strings up to min-length are equal.
+ __ bind(&compare_lengths);
+ ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
+ // Use length_delta as result if it's zero.
+ __ mov(scratch2, length_delta);
+ __ mov(scratch4, zero_reg);
+ __ mov(v0, zero_reg);
+
+ __ bind(&result_not_equal);
+ // Conditionally update the result based either on length_delta or
+ // the last comparion performed in the loop above.
+ Label ret;
+ __ Branch(&ret, eq, scratch2, Operand(scratch4));
+ __ li(v0, Operand(Smi::FromInt(GREATER)));
+ __ Branch(&ret, gt, scratch2, Operand(scratch4));
+ __ li(v0, Operand(Smi::FromInt(LESS)));
+ __ bind(&ret);
+ __ Ret();
+}
+
+
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* chars_not_equal) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiUntag(length);
+ __ Addu(scratch1, length,
+ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ __ Addu(left, left, Operand(scratch1));
+ __ Addu(right, right, Operand(scratch1));
+ __ Subu(length, zero_reg, length);
+ Register index = length; // index = -length;
+
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ Addu(scratch3, left, index);
+ __ lbu(scratch1, MemOperand(scratch3));
+ __ Addu(scratch3, right, index);
+ __ lbu(scratch2, MemOperand(scratch3));
+ __ Branch(chars_not_equal, ne, scratch1, Operand(scratch2));
+ __ Addu(index, index, 1);
+ __ Branch(&loop, ne, index, Operand(zero_reg));
}
void StringCompareStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label runtime;
+
+ Counters* counters = masm->isolate()->counters();
+
+ // Stack frame on entry.
+ // sp[0]: right string
+ // sp[4]: left string
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize)); // Left.
+ __ lw(a0, MemOperand(sp, 0 * kPointerSize)); // Right.
+
+ Label not_same;
+ __ Branch(&not_same, ne, a0, Operand(a1));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+ __ IncrementCounter(counters->string_compare_native(), 1, a1, a2);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&not_same);
+
+ // Check that both objects are sequential ASCII strings.
+ __ JumpIfNotBothSequentialAsciiStrings(a1, a0, a2, a3, &runtime);
+
+ // Compare flat ASCII strings natively. Remove arguments from stack first.
+ __ IncrementCounter(counters->string_compare_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ GenerateCompareFlatAsciiStrings(masm, a1, a0, a2, a3, t0, t1);
+
+ __ bind(&runtime);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
}
void StringAddStub::Generate(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ Label string_add_runtime, call_builtin;
+ Builtins::JavaScript builtin_id = Builtins::ADD;
+
+ Counters* counters = masm->isolate()->counters();
+
+ // Stack on entry:
+ // sp[0]: second argument (right).
+ // sp[4]: first argument (left).
+
+ // Load the two arguments.
+ __ lw(a0, MemOperand(sp, 1 * kPointerSize)); // First argument.
+ __ lw(a1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
+
+ // Make sure that both arguments are strings if not known in advance.
+ if (flags_ == NO_STRING_ADD_FLAGS) {
+ __ JumpIfEitherSmi(a0, a1, &string_add_runtime);
+ // Load instance types.
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kStringTag == 0);
+ // If either is not a string, go to runtime.
+ __ Or(t4, t0, Operand(t1));
+ __ And(t4, t4, Operand(kIsNotStringMask));
+ __ Branch(&string_add_runtime, ne, t4, Operand(zero_reg));
+ } else {
+ // Here at least one of the arguments is definitely a string.
+ // We convert the one that is not known to be a string.
+ if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
+ ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
+ GenerateConvertArgument(
+ masm, 1 * kPointerSize, a0, a2, a3, t0, t1, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_RIGHT;
+ } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
+ ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
+ GenerateConvertArgument(
+ masm, 0 * kPointerSize, a1, a2, a3, t0, t1, &call_builtin);
+ builtin_id = Builtins::STRING_ADD_LEFT;
+ }
+ }
+
+ // Both arguments are strings.
+ // a0: first string
+ // a1: second string
+ // t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ {
+ Label strings_not_empty;
+ // Check if either of the strings are empty. In that case return the other.
+ // These tests use zero-length check on string-length whch is an Smi.
+ // Assert that Smi::FromInt(0) is really 0.
+ STATIC_ASSERT(kSmiTag == 0);
+ ASSERT(Smi::FromInt(0) == 0);
+ __ lw(a2, FieldMemOperand(a0, String::kLengthOffset));
+ __ lw(a3, FieldMemOperand(a1, String::kLengthOffset));
+ __ mov(v0, a0); // Assume we'll return first string (from a0).
+ __ movz(v0, a1, a2); // If first is empty, return second (from a1).
+ __ slt(t4, zero_reg, a2); // if (a2 > 0) t4 = 1.
+ __ slt(t5, zero_reg, a3); // if (a3 > 0) t5 = 1.
+ __ and_(t4, t4, t5); // Branch if both strings were non-empty.
+ __ Branch(&strings_not_empty, ne, t4, Operand(zero_reg));
+
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&strings_not_empty);
+ }
+
+ // Untag both string-lengths.
+ __ sra(a2, a2, kSmiTagSize);
+ __ sra(a3, a3, kSmiTagSize);
+
+ // Both strings are non-empty.
+ // a0: first string
+ // a1: second string
+ // a2: length of first string
+ // a3: length of second string
+ // t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // Look at the length of the result of adding the two strings.
+ Label string_add_flat_result, longer_than_two;
+ // Adding two lengths can't overflow.
+ STATIC_ASSERT(String::kMaxLength < String::kMaxLength * 2);
+ __ Addu(t2, a2, Operand(a3));
+ // Use the symbol table when adding two one character strings, as it
+ // helps later optimizations to return a symbol here.
+ __ Branch(&longer_than_two, ne, t2, Operand(2));
+
+ // Check that both strings are non-external ASCII strings.
+ if (flags_ != NO_STRING_ADD_FLAGS) {
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ }
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(t0, t1, t2, t3,
+ &string_add_runtime);
+
+ // Get the two characters forming the sub string.
+ __ lbu(a2, FieldMemOperand(a0, SeqAsciiString::kHeaderSize));
+ __ lbu(a3, FieldMemOperand(a1, SeqAsciiString::kHeaderSize));
+
+ // Try to lookup two character string in symbol table. If it is not found
+ // just allocate a new one.
+ Label make_two_character_string;
+ StringHelper::GenerateTwoCharacterSymbolTableProbe(
+ masm, a2, a3, t2, t3, t0, t1, t4, &make_two_character_string);
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&make_two_character_string);
+ // Resulting string has length 2 and first chars of two strings
+ // are combined into single halfword in a2 register.
+ // So we can fill resulting string without two loops by a single
+ // halfword store instruction (which assumes that processor is
+ // in a little endian mode).
+ __ li(t2, Operand(2));
+ __ AllocateAsciiString(v0, t2, t0, t1, t4, &string_add_runtime);
+ __ sh(a2, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&longer_than_two);
+ // Check if resulting string will be flat.
+ __ Branch(&string_add_flat_result, lt, t2,
+ Operand(String::kMinNonFlatLength));
+ // Handle exceptionally long strings in the runtime system.
+ STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
+ ASSERT(IsPowerOf2(String::kMaxLength + 1));
+ // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
+ __ Branch(&string_add_runtime, hs, t2, Operand(String::kMaxLength + 1));
+
+ // If result is not supposed to be flat, allocate a cons string object.
+ // If both strings are ASCII the result is an ASCII cons string.
+ if (flags_ != NO_STRING_ADD_FLAGS) {
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ }
+ Label non_ascii, allocated, ascii_data;
+ STATIC_ASSERT(kTwoByteStringTag == 0);
+ // Branch to non_ascii if either string-encoding field is zero (non-ascii).
+ __ And(t4, t0, Operand(t1));
+ __ And(t4, t4, Operand(kStringEncodingMask));
+ __ Branch(&non_ascii, eq, t4, Operand(zero_reg));
+
+ // Allocate an ASCII cons string.
+ __ bind(&ascii_data);
+ __ AllocateAsciiConsString(t3, t2, t0, t1, &string_add_runtime);
+ __ bind(&allocated);
+ // Fill the fields of the cons string.
+ __ sw(a0, FieldMemOperand(t3, ConsString::kFirstOffset));
+ __ sw(a1, FieldMemOperand(t3, ConsString::kSecondOffset));
+ __ mov(v0, t3);
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&non_ascii);
+ // At least one of the strings is two-byte. Check whether it happens
+ // to contain only ASCII characters.
+ // t0: first instance type.
+ // t1: second instance type.
+ // Branch to if _both_ instances have kAsciiDataHintMask set.
+ __ And(at, t0, Operand(kAsciiDataHintMask));
+ __ and_(at, at, t1);
+ __ Branch(&ascii_data, ne, at, Operand(zero_reg));
+
+ __ xor_(t0, t0, t1);
+ STATIC_ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
+ __ And(t0, t0, Operand(kAsciiStringTag | kAsciiDataHintTag));
+ __ Branch(&ascii_data, eq, t0, Operand(kAsciiStringTag | kAsciiDataHintTag));
+
+ // Allocate a two byte cons string.
+ __ AllocateTwoByteConsString(t3, t2, t0, t1, &string_add_runtime);
+ __ Branch(&allocated);
+
+ // Handle creating a flat result. First check that both strings are
+ // sequential and that they have the same encoding.
+ // a0: first string
+ // a1: second string
+ // a2: length of first string
+ // a3: length of second string
+ // t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
+ // t2: sum of lengths.
+ __ bind(&string_add_flat_result);
+ if (flags_ != NO_STRING_ADD_FLAGS) {
+ __ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
+ __ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
+ }
+ // Check that both strings are sequential, meaning that we
+ // branch to runtime if either string tag is non-zero.
+ STATIC_ASSERT(kSeqStringTag == 0);
+ __ Or(t4, t0, Operand(t1));
+ __ And(t4, t4, Operand(kStringRepresentationMask));
+ __ Branch(&string_add_runtime, ne, t4, Operand(zero_reg));
+
+ // Now check if both strings have the same encoding (ASCII/Two-byte).
+ // a0: first string
+ // a1: second string
+ // a2: length of first string
+ // a3: length of second string
+ // t0: first string instance type
+ // t1: second string instance type
+ // t2: sum of lengths.
+ Label non_ascii_string_add_flat_result;
+ ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
+ __ xor_(t3, t1, t0);
+ __ And(t3, t3, Operand(kStringEncodingMask));
+ __ Branch(&string_add_runtime, ne, t3, Operand(zero_reg));
+ // And see if it's ASCII (0) or two-byte (1).
+ __ And(t3, t0, Operand(kStringEncodingMask));
+ __ Branch(&non_ascii_string_add_flat_result, eq, t3, Operand(zero_reg));
+
+ // Both strings are sequential ASCII strings. We also know that they are
+ // short (since the sum of the lengths is less than kMinNonFlatLength).
+ // t2: length of resulting flat string
+ __ AllocateAsciiString(t3, t2, t0, t1, t4, &string_add_runtime);
+ // Locate first character of result.
+ __ Addu(t2, t3, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // Locate first character of first argument.
+ __ Addu(a0, a0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // a0: first character of first string.
+ // a1: second string.
+ // a2: length of first string.
+ // a3: length of second string.
+ // t2: first character of result.
+ // t3: result string.
+ StringHelper::GenerateCopyCharacters(masm, t2, a0, a2, t0, true);
+
+ // Load second argument and locate first character.
+ __ Addu(a1, a1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // a1: first character of second string.
+ // a3: length of second string.
+ // t2: next character of result.
+ // t3: result string.
+ StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, true);
+ __ mov(v0, t3);
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&non_ascii_string_add_flat_result);
+ // Both strings are sequential two byte strings.
+ // a0: first string.
+ // a1: second string.
+ // a2: length of first string.
+ // a3: length of second string.
+ // t2: sum of length of strings.
+ __ AllocateTwoByteString(t3, t2, t0, t1, t4, &string_add_runtime);
+ // a0: first string.
+ // a1: second string.
+ // a2: length of first string.
+ // a3: length of second string.
+ // t3: result string.
+
+ // Locate first character of result.
+ __ Addu(t2, t3, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // Locate first character of first argument.
+ __ Addu(a0, a0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+
+ // a0: first character of first string.
+ // a1: second string.
+ // a2: length of first string.
+ // a3: length of second string.
+ // t2: first character of result.
+ // t3: result string.
+ StringHelper::GenerateCopyCharacters(masm, t2, a0, a2, t0, false);
+
+ // Locate first character of second argument.
+ __ Addu(a1, a1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+
+ // a1: first character of second string.
+ // a3: length of second string.
+ // t2: next character of result (after copy of first string).
+ // t3: result string.
+ StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false);
+
+ __ mov(v0, t3);
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ Addu(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ // Just jump to runtime to add the two strings.
+ __ bind(&string_add_runtime);
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
+
+ if (call_builtin.is_linked()) {
+ __ bind(&call_builtin);
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
+ }
+}
+
+
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow) {
+ // First check if the argument is already a string.
+ Label not_string, done;
+ __ JumpIfSmi(arg, &not_string);
+ __ GetObjectType(arg, scratch1, scratch1);
+ __ Branch(&done, lt, scratch1, Operand(FIRST_NONSTRING_TYPE));
+
+ // Check the number to string cache.
+ Label not_cached;
+ __ bind(&not_string);
+ // Puts the cached result into scratch1.
+ NumberToStringStub::GenerateLookupNumberStringCache(masm,
+ arg,
+ scratch1,
+ scratch2,
+ scratch3,
+ scratch4,
+ false,
+ &not_cached);
+ __ mov(arg, scratch1);
+ __ sw(arg, MemOperand(sp, stack_offset));
+ __ jmp(&done);
+
+ // Check if the argument is a safe string wrapper.
+ __ bind(&not_cached);
+ __ JumpIfSmi(arg, slow);
+ __ GetObjectType(arg, scratch1, scratch2); // map -> scratch1.
+ __ Branch(slow, ne, scratch2, Operand(JS_VALUE_TYPE));
+ __ lbu(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
+ __ li(scratch4, 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ And(scratch2, scratch2, scratch4);
+ __ Branch(slow, ne, scratch2, Operand(scratch4));
+ __ lw(arg, FieldMemOperand(arg, JSValue::kValueOffset));
+ __ sw(arg, MemOperand(sp, stack_offset));
+
+ __ bind(&done);
}
void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(state_ == CompareIC::SMIS);
+ Label miss;
+ __ Or(a2, a1, a0);
+ __ JumpIfNotSmi(a2, &miss);
+
+ if (GetCondition() == eq) {
+ // For equality we do not care about the sign of the result.
+ __ Subu(v0, a0, a1);
+ } else {
+ // Untag before subtracting to avoid handling overflow.
+ __ SmiUntag(a1);
+ __ SmiUntag(a0);
+ __ Subu(v0, a1, a0);
+ }
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
}
void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(state_ == CompareIC::HEAP_NUMBERS);
+
+ Label generic_stub;
+ Label unordered;
+ Label miss;
+ __ And(a2, a1, Operand(a0));
+ __ JumpIfSmi(a2, &generic_stub);
+
+ __ GetObjectType(a0, a2, a2);
+ __ Branch(&miss, ne, a2, Operand(HEAP_NUMBER_TYPE));
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&miss, ne, a2, Operand(HEAP_NUMBER_TYPE));
+
+ // Inlining the double comparison and falling back to the general compare
+ // stub if NaN is involved or FPU is unsupported.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+
+ // Load left and right operand.
+ __ Subu(a2, a1, Operand(kHeapObjectTag));
+ __ ldc1(f0, MemOperand(a2, HeapNumber::kValueOffset));
+ __ Subu(a2, a0, Operand(kHeapObjectTag));
+ __ ldc1(f2, MemOperand(a2, HeapNumber::kValueOffset));
+
+ Label fpu_eq, fpu_lt, fpu_gt;
+ // Compare operands (test if unordered).
+ __ c(UN, D, f0, f2);
+ // Don't base result on status bits when a NaN is involved.
+ __ bc1t(&unordered);
+ __ nop();
+
+ // Test if equal.
+ __ c(EQ, D, f0, f2);
+ __ bc1t(&fpu_eq);
+ __ nop();
+
+ // Test if unordered or less (unordered case is already handled).
+ __ c(ULT, D, f0, f2);
+ __ bc1t(&fpu_lt);
+ __ nop();
+
+ // Otherwise it's greater.
+ __ bc1f(&fpu_gt);
+ __ nop();
+
+ // Return a result of -1, 0, or 1.
+ __ bind(&fpu_eq);
+ __ li(v0, Operand(EQUAL));
+ __ Ret();
+
+ __ bind(&fpu_lt);
+ __ li(v0, Operand(LESS));
+ __ Ret();
+
+ __ bind(&fpu_gt);
+ __ li(v0, Operand(GREATER));
+ __ Ret();
+
+ __ bind(&unordered);
+ }
+
+ CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS, a1, a0);
+ __ bind(&generic_stub);
+ __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SYMBOLS);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+ Register tmp1 = a2;
+ Register tmp2 = a3;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are symbols.
+ __ lw(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ lbu(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ And(tmp1, tmp1, Operand(tmp2));
+ __ And(tmp1, tmp1, kIsSymbolMask);
+ __ Branch(&miss, eq, tmp1, Operand(zero_reg));
+ // Make sure a0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(a0));
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(v0, right);
+ // Symbols are compared by identity.
+ __ Ret(ne, left, Operand(right));
+ __ li(v0, Operand(Smi::FromInt(EQUAL)));
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRINGS);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = a1;
+ Register right = a0;
+ Register tmp1 = a2;
+ Register tmp2 = a3;
+ Register tmp3 = t0;
+ Register tmp4 = t1;
+ Register tmp5 = t2;
+
+ // Check that both operands are heap objects.
+ __ JumpIfEitherSmi(left, right, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ lw(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ lbu(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ Or(tmp3, tmp1, tmp2);
+ __ And(tmp5, tmp3, Operand(kIsNotStringMask));
+ __ Branch(&miss, ne, tmp5, Operand(zero_reg));
+
+ // Fast check for identical strings.
+ Label left_ne_right;
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Branch(&left_ne_right, ne, left, Operand(right), USE_DELAY_SLOT);
+ __ mov(v0, zero_reg); // In the delay slot.
+ __ Ret();
+ __ bind(&left_ne_right);
+
+ // Handle not identical strings.
+
+ // Check that both strings are symbols. If they are, we're done
+ // because we already know they are not identical.
+ ASSERT(GetCondition() == eq);
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ And(tmp3, tmp1, Operand(tmp2));
+ __ And(tmp5, tmp3, Operand(kIsSymbolMask));
+ Label is_symbol;
+ __ Branch(&is_symbol, eq, tmp5, Operand(zero_reg), USE_DELAY_SLOT);
+ __ mov(v0, a0); // In the delay slot.
+ // Make sure a0 is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(a0));
+ __ Ret();
+ __ bind(&is_symbol);
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(tmp1, tmp2, tmp3, tmp4,
+ &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2, tmp3);
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ Push(left, right);
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
}
void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(state_ == CompareIC::OBJECTS);
+ Label miss;
+ __ And(a2, a1, Operand(a0));
+ __ JumpIfSmi(a2, &miss);
+
+ __ GetObjectType(a0, a2, a2);
+ __ Branch(&miss, ne, a2, Operand(JS_OBJECT_TYPE));
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&miss, ne, a2, Operand(JS_OBJECT_TYPE));
+
+ ASSERT(GetCondition() == eq);
+ __ Subu(v0, a0, Operand(a1));
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateMiss(masm);
}
void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ __ Push(a1, a0);
+ __ push(ra);
+
+ // Call the runtime system in a fresh internal frame.
+ ExternalReference miss = ExternalReference(IC_Utility(IC::kCompareIC_Miss),
+ masm->isolate());
+ __ EnterInternalFrame();
+ __ Push(a1, a0);
+ __ li(t0, Operand(Smi::FromInt(op_)));
+ __ push(t0);
+ __ CallExternalReference(miss, 3);
+ __ LeaveInternalFrame();
+ // Compute the entry point of the rewritten stub.
+ __ Addu(a2, v0, Operand(Code::kHeaderSize - kHeapObjectTag));
+ // Restore registers.
+ __ pop(ra);
+ __ pop(a0);
+ __ pop(a1);
+ __ Jump(a2);
}
+void DirectCEntryStub::Generate(MacroAssembler* masm) {
+ // No need to pop or drop anything, LeaveExitFrame will restore the old
+ // stack, thus dropping the allocated space for the return value.
+ // The saved ra is after the reserved stack space for the 4 args.
+ __ lw(t9, MemOperand(sp, kCArgsSlotsSize));
+
+ if (FLAG_debug_code && EnableSlowAsserts()) {
+ // In case of an error the return address may point to a memory area
+ // filled with kZapValue by the GC.
+ // Dereference the address and check for this.
+ __ lw(t0, MemOperand(t9));
+ __ Assert(ne, "Received invalid return address.", t0,
+ Operand(reinterpret_cast<uint32_t>(kZapValue)));
+ }
+ __ Jump(t9);
+}
-void GenerateFastPixelArrayLoad(MacroAssembler* masm,
- Register receiver,
- Register key,
- Register elements_map,
- Register elements,
- Register scratch1,
- Register scratch2,
- Register result,
- Label* not_pixel_array,
- Label* key_not_smi,
- Label* out_of_range) {
- UNIMPLEMENTED_MIPS();
+
+void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
+ ExternalReference function) {
+ __ li(t9, Operand(function));
+ this->GenerateCall(masm, t9);
+}
+
+void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
+ Register target) {
+ __ Move(t9, target);
+ __ AssertStackIsAligned();
+ // Allocate space for arg slots.
+ __ Subu(sp, sp, kCArgsSlotsSize);
+
+ // Block the trampoline pool through the whole function to make sure the
+ // number of generated instructions is constant.
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+
+ // We need to get the current 'pc' value, which is not available on MIPS.
+ Label find_ra;
+ masm->bal(&find_ra); // ra = pc + 8.
+ masm->nop(); // Branch delay slot nop.
+ masm->bind(&find_ra);
+
+ const int kNumInstructionsToJump = 6;
+ masm->addiu(ra, ra, kNumInstructionsToJump * kPointerSize);
+ // Push return address (accessible to GC through exit frame pc).
+ // This spot for ra was reserved in EnterExitFrame.
+ masm->sw(ra, MemOperand(sp, kCArgsSlotsSize));
+ masm->li(ra, Operand(reinterpret_cast<intptr_t>(GetCode().location()),
+ RelocInfo::CODE_TARGET), true);
+ // Call the function.
+ masm->Jump(t9);
+ // Make sure the stored 'ra' points to this position.
+ ASSERT_EQ(kNumInstructionsToJump, masm->InstructionsGeneratedSince(&find_ra));
+}
+
+
+MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ String* name,
+ Register scratch0) {
+// If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // scratch0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = scratch0;
+ // Capacity is smi 2^n.
+ __ lw(index, FieldMemOperand(properties, kCapacityOffset));
+ __ Subu(index, index, Operand(1));
+ __ And(index, index, Operand(
+ Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ // index *= 3.
+ __ mov(at, index);
+ __ sll(index, index, 1);
+ __ Addu(index, index, at);
+
+ Register entity_name = scratch0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ Register tmp = properties;
+
+ __ sll(scratch0, index, 1);
+ __ Addu(tmp, properties, scratch0);
+ __ lw(entity_name, FieldMemOperand(tmp, kElementsStartOffset));
+
+ ASSERT(!tmp.is(entity_name));
+ __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
+ __ Branch(done, eq, entity_name, Operand(tmp));
+
+ if (i != kInlinedProbes - 1) {
+ // Stop if found the property.
+ __ Branch(miss, eq, entity_name, Operand(Handle<String>(name)));
+
+ // Check if the entry name is not a symbol.
+ __ lw(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset));
+ __ lbu(entity_name,
+ FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
+ __ And(scratch0, entity_name, Operand(kIsSymbolMask));
+ __ Branch(miss, eq, scratch0, Operand(zero_reg));
+
+ // Restore the properties.
+ __ lw(properties,
+ FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ }
+ }
+
+ const int spill_mask =
+ (ra.bit() | t2.bit() | t1.bit() | t0.bit() | a3.bit() |
+ a2.bit() | a1.bit() | a0.bit());
+
+ __ MultiPush(spill_mask);
+ __ lw(a0, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ li(a1, Operand(Handle<String>(name)));
+ StringDictionaryLookupStub stub(NEGATIVE_LOOKUP);
+ MaybeObject* result = masm->TryCallStub(&stub);
+ if (result->IsFailure()) return result;
+ __ MultiPop(spill_mask);
+
+ __ Branch(done, eq, v0, Operand(zero_reg));
+ __ Branch(miss, ne, v0, Operand(zero_reg));
+ return result;
+}
+
+
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found. Jump to
+// the |miss| label otherwise.
+// If lookup was successful |scratch2| will be equal to elements + 4 * index.
+void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register scratch1,
+ Register scratch2) {
+ // Assert that name contains a string.
+ if (FLAG_debug_code) __ AbortIfNotString(name);
+
+ // Compute the capacity mask.
+ __ lw(scratch1, FieldMemOperand(elements, kCapacityOffset));
+ __ sra(scratch1, scratch1, kSmiTagSize); // convert smi to int
+ __ Subu(scratch1, scratch1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up. Measurements done on Gmail indicate that 2 probes
+ // cover ~93% of loads from dictionaries.
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ lw(scratch2, FieldMemOperand(name, String::kHashFieldOffset));
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(StringDictionary::GetProbeOffset(i) <
+ 1 << (32 - String::kHashFieldOffset));
+ __ Addu(scratch2, scratch2, Operand(
+ StringDictionary::GetProbeOffset(i) << String::kHashShift));
+ }
+ __ srl(scratch2, scratch2, String::kHashShift);
+ __ And(scratch2, scratch1, scratch2);
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ // scratch2 = scratch2 * 3.
+
+ __ mov(at, scratch2);
+ __ sll(scratch2, scratch2, 1);
+ __ Addu(scratch2, scratch2, at);
+
+ // Check if the key is identical to the name.
+ __ sll(at, scratch2, 2);
+ __ Addu(scratch2, elements, at);
+ __ lw(at, FieldMemOperand(scratch2, kElementsStartOffset));
+ __ Branch(done, eq, name, Operand(at));
+ }
+
+ const int spill_mask =
+ (ra.bit() | t2.bit() | t1.bit() | t0.bit() |
+ a3.bit() | a2.bit() | a1.bit() | a0.bit()) &
+ ~(scratch1.bit() | scratch2.bit());
+
+ __ MultiPush(spill_mask);
+ __ Move(a0, elements);
+ __ Move(a1, name);
+ StringDictionaryLookupStub stub(POSITIVE_LOOKUP);
+ __ CallStub(&stub);
+ __ mov(scratch2, a2);
+ __ MultiPop(spill_mask);
+
+ __ Branch(done, ne, v0, Operand(zero_reg));
+ __ Branch(miss, eq, v0, Operand(zero_reg));
+}
+
+
+void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
+ // Registers:
+ // result: StringDictionary to probe
+ // a1: key
+ // : StringDictionary to probe.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Register result = v0;
+ Register dictionary = a0;
+ Register key = a1;
+ Register index = a2;
+ Register mask = a3;
+ Register hash = t0;
+ Register undefined = t1;
+ Register entry_key = t2;
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ __ lw(mask, FieldMemOperand(dictionary, kCapacityOffset));
+ __ sra(mask, mask, kSmiTagSize);
+ __ Subu(mask, mask, Operand(1));
+
+ __ lw(hash, FieldMemOperand(key, String::kHashFieldOffset));
+
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ // Capacity is smi 2^n.
+ if (i > 0) {
+ // Add the probe offset (i + i * i) left shifted to avoid right shifting
+ // the hash in a separate instruction. The value hash + i + i * i is right
+ // shifted in the following and instruction.
+ ASSERT(StringDictionary::GetProbeOffset(i) <
+ 1 << (32 - String::kHashFieldOffset));
+ __ Addu(index, hash, Operand(
+ StringDictionary::GetProbeOffset(i) << String::kHashShift));
+ } else {
+ __ mov(index, hash);
+ }
+ __ srl(index, index, String::kHashShift);
+ __ And(index, mask, index);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ // index *= 3.
+ __ mov(at, index);
+ __ sll(index, index, 1);
+ __ Addu(index, index, at);
+
+
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ sll(index, index, 2);
+ __ Addu(index, index, dictionary);
+ __ lw(entry_key, FieldMemOperand(index, kElementsStartOffset));
+
+ // Having undefined at this place means the name is not contained.
+ __ Branch(&not_in_dictionary, eq, entry_key, Operand(undefined));
+
+ // Stop if found the property.
+ __ Branch(&in_dictionary, eq, entry_key, Operand(key));
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // Check if the entry name is not a symbol.
+ __ lw(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset));
+ __ lbu(entry_key,
+ FieldMemOperand(entry_key, Map::kInstanceTypeOffset));
+ __ And(result, entry_key, Operand(kIsSymbolMask));
+ __ Branch(&maybe_in_dictionary, eq, result, Operand(zero_reg));
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ mov(result, zero_reg);
+ __ Ret();
+ }
+
+ __ bind(&in_dictionary);
+ __ li(result, 1);
+ __ Ret();
+
+ __ bind(&not_in_dictionary);
+ __ mov(result, zero_reg);
+ __ Ret();
}
@@ -749,4 +6654,3 @@ void GenerateFastPixelArrayLoad(MacroAssembler* masm,
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_MIPS
-
diff --git a/src/mips/code-stubs-mips.h b/src/mips/code-stubs-mips.h
index 675730a5..356aa97f 100644
--- a/src/mips/code-stubs-mips.h
+++ b/src/mips/code-stubs-mips.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -39,13 +39,22 @@ namespace internal {
// TranscendentalCache runtime function.
class TranscendentalCacheStub: public CodeStub {
public:
- explicit TranscendentalCacheStub(TranscendentalCache::Type type)
- : type_(type) {}
+ enum ArgumentType {
+ TAGGED = 0 << TranscendentalCache::kTranscendentalTypeBits,
+ UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
+ };
+
+ TranscendentalCacheStub(TranscendentalCache::Type type,
+ ArgumentType argument_type)
+ : type_(type), argument_type_(argument_type) { }
void Generate(MacroAssembler* masm);
private:
TranscendentalCache::Type type_;
+ ArgumentType argument_type_;
+ void GenerateCallCFunction(MacroAssembler* masm, Register scratch);
+
Major MajorKey() { return TranscendentalCache; }
- int MinorKey() { return type_; }
+ int MinorKey() { return type_ | argument_type_; }
Runtime::FunctionId RuntimeFunction();
};
@@ -63,176 +72,108 @@ class ToBooleanStub: public CodeStub {
};
-class GenericBinaryOpStub : public CodeStub {
+class UnaryOpStub: public CodeStub {
public:
- static const int kUnknownIntValue = -1;
-
- GenericBinaryOpStub(Token::Value op,
- OverwriteMode mode,
- Register lhs,
- Register rhs,
- int constant_rhs = kUnknownIntValue)
+ UnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
- lhs_(lhs),
- rhs_(rhs),
- constant_rhs_(constant_rhs),
- specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)),
- runtime_operands_type_(BinaryOpIC::UNINIT_OR_SMI),
- name_(NULL) { }
+ operand_type_(UnaryOpIC::UNINITIALIZED),
+ name_(NULL) {
+ }
- GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info)
+ UnaryOpStub(
+ int key,
+ UnaryOpIC::TypeInfo operand_type)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
- lhs_(LhsRegister(RegisterBits::decode(key))),
- rhs_(RhsRegister(RegisterBits::decode(key))),
- constant_rhs_(KnownBitsForMinorKey(KnownIntBits::decode(key))),
- specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op_, constant_rhs_)),
- runtime_operands_type_(type_info),
- name_(NULL) { }
+ operand_type_(operand_type),
+ name_(NULL) {
+ }
private:
Token::Value op_;
- OverwriteMode mode_;
- Register lhs_;
- Register rhs_;
- int constant_rhs_;
- bool specialized_on_rhs_;
- BinaryOpIC::TypeInfo runtime_operands_type_;
- char* name_;
+ UnaryOverwriteMode mode_;
- static const int kMaxKnownRhs = 0x40000000;
- static const int kKnownRhsKeyBits = 6;
-
- // Minor key encoding in 16 bits.
- class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 6> {};
- class TypeInfoBits: public BitField<int, 8, 3> {};
- class RegisterBits: public BitField<bool, 11, 1> {};
- class KnownIntBits: public BitField<int, 12, kKnownRhsKeyBits> {};
+ // Operand type information determined at runtime.
+ UnaryOpIC::TypeInfo operand_type_;
- Major MajorKey() { return GenericBinaryOp; }
- int MinorKey() {
- ASSERT((lhs_.is(a0) && rhs_.is(a1)) ||
- (lhs_.is(a1) && rhs_.is(a0)));
- // Encode the parameters in a unique 16 bit value.
- return OpBits::encode(op_)
- | ModeBits::encode(mode_)
- | KnownIntBits::encode(MinorKeyForKnownInt())
- | TypeInfoBits::encode(runtime_operands_type_)
- | RegisterBits::encode(lhs_.is(a0));
- }
+ char* name_;
- void Generate(MacroAssembler* masm);
- void HandleNonSmiBitwiseOp(MacroAssembler* masm,
- Register lhs,
- Register rhs);
- void HandleBinaryOpSlowCases(MacroAssembler* masm,
- Label* not_smi,
- Register lhs,
- Register rhs,
- const Builtins::JavaScript& builtin);
- void GenerateTypeTransition(MacroAssembler* masm);
+ const char* GetName();
- static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int constant_rhs) {
- if (constant_rhs == kUnknownIntValue) return false;
- if (op == Token::DIV) return constant_rhs >= 2 && constant_rhs <= 3;
- if (op == Token::MOD) {
- if (constant_rhs <= 1) return false;
- if (constant_rhs <= 10) return true;
- if (constant_rhs <= kMaxKnownRhs && IsPowerOf2(constant_rhs)) return true;
- return false;
- }
- return false;
+#ifdef DEBUG
+ void Print() {
+ PrintF("UnaryOpStub %d (op %s), "
+ "(mode %d, runtime_type_info %s)\n",
+ MinorKey(),
+ Token::String(op_),
+ static_cast<int>(mode_),
+ UnaryOpIC::GetName(operand_type_));
}
+#endif
- int MinorKeyForKnownInt() {
- if (!specialized_on_rhs_) return 0;
- if (constant_rhs_ <= 10) return constant_rhs_ + 1;
- ASSERT(IsPowerOf2(constant_rhs_));
- int key = 12;
- int d = constant_rhs_;
- while ((d & 1) == 0) {
- key++;
- d >>= 1;
- }
- ASSERT(key >= 0 && key < (1 << kKnownRhsKeyBits));
- return key;
- }
+ class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
+ class OpBits: public BitField<Token::Value, 1, 7> {};
+ class OperandTypeInfoBits: public BitField<UnaryOpIC::TypeInfo, 8, 3> {};
- int KnownBitsForMinorKey(int key) {
- if (!key) return 0;
- if (key <= 11) return key - 1;
- int d = 1;
- while (key != 12) {
- key--;
- d <<= 1;
- }
- return d;
+ Major MajorKey() { return UnaryOp; }
+ int MinorKey() {
+ return ModeBits::encode(mode_)
+ | OpBits::encode(op_)
+ | OperandTypeInfoBits::encode(operand_type_);
}
- Register LhsRegister(bool lhs_is_a0) {
- return lhs_is_a0 ? a0 : a1;
- }
+ // Note: A lot of the helper functions below will vanish when we use virtual
+ // function instead of switch more often.
+ void Generate(MacroAssembler* masm);
- Register RhsRegister(bool lhs_is_a0) {
- return lhs_is_a0 ? a1 : a0;
- }
+ void GenerateTypeTransition(MacroAssembler* masm);
- bool HasSmiSmiFastPath() {
- return op_ != Token::DIV;
- }
+ void GenerateSmiStub(MacroAssembler* masm);
+ void GenerateSmiStubSub(MacroAssembler* masm);
+ void GenerateSmiStubBitNot(MacroAssembler* masm);
+ void GenerateSmiCodeSub(MacroAssembler* masm, Label* non_smi, Label* slow);
+ void GenerateSmiCodeBitNot(MacroAssembler* masm, Label* slow);
- bool ShouldGenerateSmiCode() {
- return ((op_ != Token::DIV && op_ != Token::MOD) || specialized_on_rhs_) &&
- runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
- runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
+ void GenerateHeapNumberStub(MacroAssembler* masm);
+ void GenerateHeapNumberStubSub(MacroAssembler* masm);
+ void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
+ void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
+ void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
- bool ShouldGenerateFPCode() {
- return runtime_operands_type_ != BinaryOpIC::STRINGS;
- }
+ void GenerateGenericStub(MacroAssembler* masm);
+ void GenerateGenericStubSub(MacroAssembler* masm);
+ void GenerateGenericStubBitNot(MacroAssembler* masm);
+ void GenerateGenericCodeFallback(MacroAssembler* masm);
- virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
+ virtual int GetCodeKind() { return Code::UNARY_OP_IC; }
virtual InlineCacheState GetICState() {
- return BinaryOpIC::ToState(runtime_operands_type_);
+ return UnaryOpIC::ToState(operand_type_);
}
- const char* GetName();
-
virtual void FinishCode(Code* code) {
- code->set_binary_op_type(runtime_operands_type_);
- }
-
-#ifdef DEBUG
- void Print() {
- if (!specialized_on_rhs_) {
- PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_));
- } else {
- PrintF("GenericBinaryOpStub (%s by %d)\n",
- Token::String(op_),
- constant_rhs_);
- }
+ code->set_unary_op_type(operand_type_);
}
-#endif
};
-class TypeRecordingBinaryOpStub: public CodeStub {
+
+class BinaryOpStub: public CodeStub {
public:
- TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
+ BinaryOpStub(Token::Value op, OverwriteMode mode)
: op_(op),
mode_(mode),
- operands_type_(TRBinaryOpIC::UNINITIALIZED),
- result_type_(TRBinaryOpIC::UNINITIALIZED),
+ operands_type_(BinaryOpIC::UNINITIALIZED),
+ result_type_(BinaryOpIC::UNINITIALIZED),
name_(NULL) {
- UNIMPLEMENTED_MIPS();
+ use_fpu_ = CpuFeatures::IsSupported(FPU);
+ ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
- TypeRecordingBinaryOpStub(
+ BinaryOpStub(
int key,
- TRBinaryOpIC::TypeInfo operands_type,
- TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED)
+ BinaryOpIC::TypeInfo operands_type,
+ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
use_fpu_(FPUBits::decode(key)),
@@ -251,8 +192,8 @@ class TypeRecordingBinaryOpStub: public CodeStub {
bool use_fpu_;
// Operand type information determined at runtime.
- TRBinaryOpIC::TypeInfo operands_type_;
- TRBinaryOpIC::TypeInfo result_type_;
+ BinaryOpIC::TypeInfo operands_type_;
+ BinaryOpIC::TypeInfo result_type_;
char* name_;
@@ -260,12 +201,12 @@ class TypeRecordingBinaryOpStub: public CodeStub {
#ifdef DEBUG
void Print() {
- PrintF("TypeRecordingBinaryOpStub %d (op %s), "
+ PrintF("BinaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
}
#endif
@@ -273,10 +214,10 @@ class TypeRecordingBinaryOpStub: public CodeStub {
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
class FPUBits: public BitField<bool, 9, 1> {};
- class OperandTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 10, 3> {};
- class ResultTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 13, 3> {};
+ class OperandTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 10, 3> {};
+ class ResultTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 13, 3> {};
- Major MajorKey() { return TypeRecordingBinaryOp; }
+ Major MajorKey() { return BinaryOp; }
int MinorKey() {
return OpBits::encode(op_)
| ModeBits::encode(mode_)
@@ -293,6 +234,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
Label* not_numbers,
Label* gc_required);
void GenerateSmiCode(MacroAssembler* masm,
+ Label* use_runtime,
Label* gc_required,
SmiCodeGenerateHeapNumberResults heapnumber_results);
void GenerateLoadArguments(MacroAssembler* masm);
@@ -301,7 +243,9 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateSmiStub(MacroAssembler* masm);
void GenerateInt32Stub(MacroAssembler* masm);
void GenerateHeapNumberStub(MacroAssembler* masm);
+ void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateAddStrings(MacroAssembler* masm);
void GenerateCallRuntime(MacroAssembler* masm);
@@ -316,15 +260,15 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
- virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; }
+ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
virtual InlineCacheState GetICState() {
- return TRBinaryOpIC::ToState(operands_type_);
+ return BinaryOpIC::ToState(operands_type_);
}
virtual void FinishCode(Code* code) {
- code->set_type_recording_binary_op_type(operands_type_);
- code->set_type_recording_binary_op_result_type(result_type_);
+ code->set_binary_op_type(operands_type_);
+ code->set_binary_op_result_type(result_type_);
}
friend class CodeGenerator;
@@ -334,24 +278,36 @@ class TypeRecordingBinaryOpStub: public CodeStub {
// Flag that indicates how to generate code for the stub StringAddStub.
enum StringAddFlags {
NO_STRING_ADD_FLAGS = 0,
- NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub.
+ // Omit left string check in stub (left is definitely a string).
+ NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0,
+ // Omit right string check in stub (right is definitely a string).
+ NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1,
+ // Omit both string checks in stub.
+ NO_STRING_CHECK_IN_STUB =
+ NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB
};
class StringAddStub: public CodeStub {
public:
- explicit StringAddStub(StringAddFlags flags) {
- string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
- }
+ explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
private:
Major MajorKey() { return StringAdd; }
- int MinorKey() { return string_check_ ? 0 : 1; }
+ int MinorKey() { return flags_; }
void Generate(MacroAssembler* masm);
- // Should the stub check whether arguments are strings?
- bool string_check_;
+ void GenerateConvertArgument(MacroAssembler* masm,
+ int stack_offset,
+ Register arg,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* slow);
+
+ const StringAddFlags flags_;
};
@@ -372,7 +328,6 @@ class StringCompareStub: public CodeStub {
StringCompareStub() { }
// Compare two flat ASCII strings and returns result in v0.
- // Does not use the stack.
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -381,11 +336,28 @@ class StringCompareStub: public CodeStub {
Register scratch3,
Register scratch4);
- private:
- Major MajorKey() { return StringCompare; }
- int MinorKey() { return 0; }
+ // Compares two flat ASCII strings for equality and returns result
+ // in v0.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3);
- void Generate(MacroAssembler* masm);
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* chars_not_equal);
};
@@ -484,26 +456,225 @@ class RegExpCEntryStub: public CodeStub {
const char* GetName() { return "RegExpCEntryStub"; }
};
+// Trampoline stub to call into native code. To call safely into native code
+// in the presence of compacting GC (which can move code objects) we need to
+// keep the code which called into native pinned in the memory. Currently the
+// simplest approach is to generate such stub early enough so it can never be
+// moved by GC
+class DirectCEntryStub: public CodeStub {
+ public:
+ DirectCEntryStub() {}
+ void Generate(MacroAssembler* masm);
+ void GenerateCall(MacroAssembler* masm,
+ ExternalReference function);
+ void GenerateCall(MacroAssembler* masm, Register target);
+
+ private:
+ Major MajorKey() { return DirectCEntry; }
+ int MinorKey() { return 0; }
+
+ bool NeedsImmovableCode() { return true; }
+
+ const char* GetName() { return "DirectCEntryStub"; }
+};
+
+class FloatingPointHelper : public AllStatic {
+ public:
+
+ enum Destination {
+ kFPURegisters,
+ kCoreRegisters
+ };
-// Generate code the to load an element from a pixel array. The receiver is
-// assumed to not be a smi and to have elements, the caller must guarantee this
-// precondition. If the receiver does not have elements that are pixel arrays,
-// the generated code jumps to not_pixel_array. If key is not a smi, then the
-// generated code branches to key_not_smi. Callers can specify NULL for
-// key_not_smi to signal that a smi check has already been performed on key so
-// that the smi check is not generated . If key is not a valid index within the
-// bounds of the pixel array, the generated code jumps to out_of_range.
-void GenerateFastPixelArrayLoad(MacroAssembler* masm,
- Register receiver,
- Register key,
- Register elements_map,
- Register elements,
+
+ // Loads smis from a0 and a1 (right and left in binary operations) into
+ // floating point registers. Depending on the destination the values ends up
+ // either f14 and f12 or in a2/a3 and a0/a1 respectively. If the destination
+ // is floating point registers FPU must be supported. If core registers are
+ // requested when FPU is supported f12 and f14 will be scratched.
+ static void LoadSmis(MacroAssembler* masm,
+ Destination destination,
+ Register scratch1,
+ Register scratch2);
+
+ // Loads objects from a0 and a1 (right and left in binary operations) into
+ // floating point registers. Depending on the destination the values ends up
+ // either f14 and f12 or in a2/a3 and a0/a1 respectively. If the destination
+ // is floating point registers FPU must be supported. If core registers are
+ // requested when FPU is supported f12 and f14 will still be scratched. If
+ // either a0 or a1 is not a number (not smi and not heap number object) the
+ // not_number label is jumped to with a0 and a1 intact.
+ static void LoadOperands(MacroAssembler* masm,
+ FloatingPointHelper::Destination destination,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* not_number);
+
+ // Convert the smi or heap number in object to an int32 using the rules
+ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+ // and brought into the range -2^31 .. +2^31 - 1.
+ static void ConvertNumberToInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ FPURegister double_scratch,
+ Label* not_int32);
+
+ // Converts the integer (untagged smi) in |int_scratch| to a double, storing
+ // the result either in |double_dst| or |dst2:dst1|, depending on
+ // |destination|.
+ // Warning: The value in |int_scratch| will be changed in the process!
+ static void ConvertIntToDouble(MacroAssembler* masm,
+ Register int_scratch,
+ Destination destination,
+ FPURegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register scratch2,
+ FPURegister single_scratch);
+
+ // Load the number from object into double_dst in the double format.
+ // Control will jump to not_int32 if the value cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be loaded.
+ static void LoadNumberAsInt32Double(MacroAssembler* masm,
+ Register object,
+ Destination destination,
+ FPURegister double_dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ FPURegister single_scratch,
+ Label* not_int32);
+
+ // Loads the number from object into dst as a 32-bit integer.
+ // Control will jump to not_int32 if the object cannot be exactly represented
+ // by a 32-bit integer.
+ // Floating point value in the 32-bit integer range that are not exact integer
+ // won't be converted.
+ // scratch3 is not used when FPU is supported.
+ static void LoadNumberAsInt32(MacroAssembler* masm,
+ Register object,
+ Register dst,
+ Register heap_number_map,
Register scratch1,
Register scratch2,
- Register result,
- Label* not_pixel_array,
- Label* key_not_smi,
- Label* out_of_range);
+ Register scratch3,
+ FPURegister double_scratch,
+ Label* not_int32);
+
+ // Generate non FPU code to check if a double can be exactly represented by a
+ // 32-bit integer. This does not check for 0 or -0, which need
+ // to be checked for separately.
+ // Control jumps to not_int32 if the value is not a 32-bit integer, and falls
+ // through otherwise.
+ // src1 and src2 will be cloberred.
+ //
+ // Expected input:
+ // - src1: higher (exponent) part of the double value.
+ // - src2: lower (mantissa) part of the double value.
+ // Output status:
+ // - dst: 32 higher bits of the mantissa. (mantissa[51:20])
+ // - src2: contains 1.
+ // - other registers are clobbered.
+ static void DoubleIs32BitInteger(MacroAssembler* masm,
+ Register src1,
+ Register src2,
+ Register dst,
+ Register scratch,
+ Label* not_int32);
+
+ // Generates code to call a C function to do a double operation using core
+ // registers. (Used when FPU is not supported.)
+ // This code never falls through, but returns with a heap number containing
+ // the result in v0.
+ // Register heapnumber_result must be a heap number in which the
+ // result of the operation will be stored.
+ // Requires the following layout on entry:
+ // a0: Left value (least significant part of mantissa).
+ // a1: Left value (sign, exponent, top of mantissa).
+ // a2: Right value (least significant part of mantissa).
+ // a3: Right value (sign, exponent, top of mantissa).
+ static void CallCCodeForDoubleOperation(MacroAssembler* masm,
+ Token::Value op,
+ Register heap_number_result,
+ Register scratch);
+
+ private:
+ static void LoadNumber(MacroAssembler* masm,
+ FloatingPointHelper::Destination destination,
+ Register object,
+ FPURegister dst,
+ Register dst1,
+ Register dst2,
+ Register heap_number_map,
+ Register scratch1,
+ Register scratch2,
+ Label* not_number);
+};
+
+
+class StringDictionaryLookupStub: public CodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ explicit StringDictionaryLookupStub(LookupMode mode) : mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register receiver,
+ Register properties,
+ String* name,
+ Register scratch0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("StringDictionaryLookupStub\n");
+ }
+#endif
+
+ Major MajorKey() { return StringDictionaryNegativeLookup; }
+
+ int MinorKey() {
+ return LookupModeBits::encode(mode_);
+ }
+
+ class LookupModeBits: public BitField<LookupMode, 0, 1> {};
+
+ LookupMode mode_;
+};
} } // namespace v8::internal
diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc
index c1149dfd..4400b643 100644
--- a/src/mips/codegen-mips.cc
+++ b/src/mips/codegen-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,61 +25,18 @@
// (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 "v8.h"
#if defined(V8_TARGET_ARCH_MIPS)
-#include "bootstrapper.h"
-#include "code-stubs.h"
-#include "codegen-inl.h"
-#include "compiler.h"
-#include "debug.h"
-#include "ic-inl.h"
-#include "jsregexp.h"
-#include "jump-target-inl.h"
-#include "parser.h"
-#include "regexp-macro-assembler.h"
-#include "regexp-stack.h"
-#include "register-allocator-inl.h"
-#include "runtime.h"
-#include "scopes.h"
-#include "stub-cache.h"
-#include "virtual-frame-inl.h"
-#include "virtual-frame-mips-inl.h"
+#include "codegen.h"
namespace v8 {
namespace internal {
-
-#define __ ACCESS_MASM(masm_)
-
-// -------------------------------------------------------------------------
-// Platform-specific DeferredCode functions.
-
-void DeferredCode::SaveRegisters() {
- // On MIPS you either have a completely spilled frame or you
- // handle it yourself, but at the moment there's no automation
- // of registers and deferred code.
-}
-
-
-void DeferredCode::RestoreRegisters() {
-}
-
-
// -------------------------------------------------------------------------
// Platform-specific RuntimeCallHelper functions.
-void VirtualFrameRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
- frame_state_->frame()->AssertIsSpilled();
-}
-
-
-void VirtualFrameRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
-}
-
-
void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
masm->EnterInternalFrame();
}
@@ -90,1124 +47,6 @@ void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
}
-// -----------------------------------------------------------------------------
-// CodeGenState implementation.
-
-CodeGenState::CodeGenState(CodeGenerator* owner)
- : owner_(owner),
- previous_(owner->state()) {
- owner->set_state(this);
-}
-
-
-ConditionCodeGenState::ConditionCodeGenState(CodeGenerator* owner,
- JumpTarget* true_target,
- JumpTarget* false_target)
- : CodeGenState(owner),
- true_target_(true_target),
- false_target_(false_target) {
- owner->set_state(this);
-}
-
-
-TypeInfoCodeGenState::TypeInfoCodeGenState(CodeGenerator* owner,
- Slot* slot,
- TypeInfo type_info)
- : CodeGenState(owner),
- slot_(slot) {
- owner->set_state(this);
- old_type_info_ = owner->set_type_info(slot, type_info);
-}
-
-
-CodeGenState::~CodeGenState() {
- ASSERT(owner_->state() == this);
- owner_->set_state(previous_);
-}
-
-
-TypeInfoCodeGenState::~TypeInfoCodeGenState() {
- owner()->set_type_info(slot_, old_type_info_);
-}
-
-
-// -----------------------------------------------------------------------------
-// CodeGenerator implementation.
-
-CodeGenerator::CodeGenerator(MacroAssembler* masm)
- : deferred_(8),
- masm_(masm),
- info_(NULL),
- frame_(NULL),
- allocator_(NULL),
- cc_reg_(cc_always),
- state_(NULL),
- loop_nesting_(0),
- type_info_(NULL),
- function_return_(JumpTarget::BIDIRECTIONAL),
- function_return_is_shadowed_(false) {
-}
-
-
-// Calling conventions:
-// fp: caller's frame pointer
-// sp: stack pointer
-// a1: called JS function
-// cp: callee's context
-
-void CodeGenerator::Generate(CompilationInfo* info) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-int CodeGenerator::NumberOfSlot(Slot* slot) {
- UNIMPLEMENTED_MIPS();
- return 0;
-}
-
-
-MemOperand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
-}
-
-
-MemOperand CodeGenerator::ContextSlotOperandCheckExtensions(
- Slot* slot,
- Register tmp,
- Register tmp2,
- JumpTarget* slow) {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
-}
-
-
-void CodeGenerator::LoadCondition(Expression* x,
- JumpTarget* true_target,
- JumpTarget* false_target,
- bool force_cc) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::Load(Expression* x) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadGlobal() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadGlobalReceiver(Register scratch) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-ArgumentsAllocationMode CodeGenerator::ArgumentsMode() {
- UNIMPLEMENTED_MIPS();
- return EAGER_ARGUMENTS_ALLOCATION;
-}
-
-
-void CodeGenerator::StoreArgumentsObject(bool initial) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadTypeofExpression(Expression* x) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-Reference::Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get)
- : cgen_(cgen),
- expression_(expression),
- type_(ILLEGAL),
- persist_after_get_(persist_after_get) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-Reference::~Reference() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadReference(Reference* ref) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::UnloadReference(Reference* ref) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-// ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given
-// register to a boolean in the condition code register. The code
-// may jump to 'false_target' in case the register converts to 'false'.
-void CodeGenerator::ToBoolean(JumpTarget* true_target,
- JumpTarget* false_target) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenericBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode,
- GenerateInlineSmi inline_smi,
- int constant_rhs) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredInlineSmiOperation: public DeferredCode {
- public:
- DeferredInlineSmiOperation(Token::Value op,
- int value,
- bool reversed,
- OverwriteMode overwrite_mode,
- Register tos)
- : op_(op),
- value_(value),
- reversed_(reversed),
- overwrite_mode_(overwrite_mode),
- tos_register_(tos) {
- set_comment("[ DeferredInlinedSmiOperation");
- }
-
- virtual void Generate();
- // This stub makes explicit calls to SaveRegisters(), RestoreRegisters() and
- // Exit(). Currently on MIPS SaveRegisters() and RestoreRegisters() are empty
- // methods, it is the responsibility of the deferred code to save and restore
- // registers.
- virtual bool AutoSaveAndRestore() { return false; }
-
- void JumpToNonSmiInput(Condition cond, Register cmp1, const Operand& cmp2);
- void JumpToAnswerOutOfRange(Condition cond,
- Register cmp1,
- const Operand& cmp2);
-
- private:
- void GenerateNonSmiInput();
- void GenerateAnswerOutOfRange();
- void WriteNonSmiAnswer(Register answer,
- Register heap_number,
- Register scratch);
-
- Token::Value op_;
- int value_;
- bool reversed_;
- OverwriteMode overwrite_mode_;
- Register tos_register_;
- Label non_smi_input_;
- Label answer_out_of_range_;
-};
-
-
-// For bit operations we try harder and handle the case where the input is not
-// a Smi but a 32bits integer without calling the generic stub.
-void DeferredInlineSmiOperation::JumpToNonSmiInput(Condition cond,
- Register cmp1,
- const Operand& cmp2) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-// For bit operations the result is always 32bits so we handle the case where
-// the result does not fit in a Smi without calling the generic stub.
-void DeferredInlineSmiOperation::JumpToAnswerOutOfRange(Condition cond,
- Register cmp1,
- const Operand& cmp2) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-// On entry the non-constant side of the binary operation is in tos_register_
-// and the constant smi side is nowhere. The tos_register_ is not used by the
-// virtual frame. On exit the answer is in the tos_register_ and the virtual
-// frame is unchanged.
-void DeferredInlineSmiOperation::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-// Convert and write the integer answer into heap_number.
-void DeferredInlineSmiOperation::WriteNonSmiAnswer(Register answer,
- Register heap_number,
- Register scratch) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void DeferredInlineSmiOperation::GenerateNonSmiInput() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void DeferredInlineSmiOperation::GenerateAnswerOutOfRange() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::SmiOperation(Token::Value op,
- Handle<Object> value,
- bool reversed,
- OverwriteMode mode) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-// On MIPS we load registers condReg1 and condReg2 with the values which should
-// be compared. With the CodeGenerator::cc_reg_ condition, functions will be
-// able to evaluate correctly the condition. (eg CodeGenerator::Branch)
-void CodeGenerator::Comparison(Condition cc,
- Expression* left,
- Expression* right,
- bool strict) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
- CallFunctionFlags flags,
- int position) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::Branch(bool if_true, JumpTarget* target) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::CheckStack() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitBlock(Block* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitDeclaration(Declaration* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitIfStatement(IfStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitContinueStatement(ContinueStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitBreakStatement(BreakStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateReturnSequence() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitWhileStatement(WhileStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitForStatement(ForStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitForInStatement(ForInStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::InstantiateFunction(
- Handle<SharedFunctionInfo> function_info,
- bool pretenure) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitSharedFunctionInfoLiteral(
- SharedFunctionInfoLiteral* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitConditional(Conditional* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
- TypeofState state) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow,
- JumpTarget* done) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitSlot(Slot* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitVariableProxy(VariableProxy* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitLiteral(Literal* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitSlotAssignment(Assignment* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitAssignment(Assignment* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitThrow(Throw* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitProperty(Property* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitCall(Call* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitCallNew(CallNew* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredStringCharCodeAt : public DeferredCode {
- public:
- DeferredStringCharCodeAt(Register object,
- Register index,
- Register scratch,
- Register result)
- : result_(result),
- char_code_at_generator_(object,
- index,
- scratch,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharCodeAtGenerator* fast_case_generator() {
- return &char_code_at_generator_;
- }
-
- virtual void Generate() {
- UNIMPLEMENTED_MIPS();
- }
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharCodeAtGenerator char_code_at_generator_;
-};
-
-
-void CodeGenerator::GenerateStringCharCodeAt(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredStringCharFromCode : public DeferredCode {
- public:
- DeferredStringCharFromCode(Register code,
- Register result)
- : char_from_code_generator_(code, result) {}
-
- StringCharFromCodeGenerator* fast_case_generator() {
- return &char_from_code_generator_;
- }
-
- virtual void Generate() {
- VirtualFrameRuntimeCallHelper call_helper(frame_state());
- char_from_code_generator_.GenerateSlow(masm(), call_helper);
- }
-
- private:
- StringCharFromCodeGenerator char_from_code_generator_;
-};
-
-
-void CodeGenerator::GenerateStringCharFromCode(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredStringCharAt : public DeferredCode {
- public:
- DeferredStringCharAt(Register object,
- Register index,
- Register scratch1,
- Register scratch2,
- Register result)
- : result_(result),
- char_at_generator_(object,
- index,
- scratch1,
- scratch2,
- result,
- &need_conversion_,
- &need_conversion_,
- &index_out_of_range_,
- STRING_INDEX_IS_NUMBER) {}
-
- StringCharAtGenerator* fast_case_generator() {
- return &char_at_generator_;
- }
-
- virtual void Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
- private:
- Register result_;
-
- Label need_conversion_;
- Label index_out_of_range_;
-
- StringCharAtGenerator char_at_generator_;
-};
-
-
-void CodeGenerator::GenerateStringCharAt(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsSpecObject(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode {
- public:
- DeferredIsStringWrapperSafeForDefaultValueOf(Register object,
- Register map_result,
- Register scratch1,
- Register scratch2)
- : object_(object),
- map_result_(map_result),
- scratch1_(scratch1),
- scratch2_(scratch2) { }
-
- virtual void Generate() {
- UNIMPLEMENTED_MIPS();
- }
-
- private:
- Register object_;
- Register map_result_;
- Register scratch1_;
- Register scratch2_;
-};
-
-
-void CodeGenerator::GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsUndetectableObject(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateRandomHeapNumber(
- ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredSearchCache: public DeferredCode {
- public:
- DeferredSearchCache(Register dst, Register cache, Register key)
- : dst_(dst), cache_(cache), key_(key) {
- set_comment("[ DeferredSearchCache");
- }
-
- virtual void Generate();
-
- private:
- Register dst_, cache_, key_;
-};
-
-
-void DeferredSearchCache::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredSwapElements: public DeferredCode {
- public:
- DeferredSwapElements(Register object, Register index1, Register index2)
- : object_(object), index1_(index1), index2_(index2) {
- set_comment("[ DeferredSwapElements");
- }
-
- virtual void Generate();
-
- private:
- Register object_, index1_, index2_;
-};
-
-
-void DeferredSwapElements::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateMathLog(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredCountOperation: public DeferredCode {
- public:
- DeferredCountOperation(Register value,
- bool is_increment,
- bool is_postfix,
- int target_size)
- : value_(value),
- is_increment_(is_increment),
- is_postfix_(is_postfix),
- target_size_(target_size) {}
-
- virtual void Generate() {
- UNIMPLEMENTED_MIPS();
- }
-
- private:
- Register value_;
- bool is_increment_;
- bool is_postfix_;
- int target_size_;
-};
-
-
-void CodeGenerator::VisitCountOperation(CountOperation* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitThisFunction(ThisFunction* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::VisitCompareToNull(CompareToNull* node) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredReferenceGetNamedValue: public DeferredCode {
- public:
- explicit DeferredReferenceGetNamedValue(Register receiver,
- Handle<String> name,
- bool is_contextual)
- : receiver_(receiver),
- name_(name),
- is_contextual_(is_contextual),
- is_dont_delete_(false) {
- set_comment(is_contextual
- ? "[ DeferredReferenceGetNamedValue (contextual)"
- : "[ DeferredReferenceGetNamedValue");
- }
-
- virtual void Generate();
-
- void set_is_dont_delete(bool value) {
- ASSERT(is_contextual_);
- is_dont_delete_ = value;
- }
-
- private:
- Register receiver_;
- Handle<String> name_;
- bool is_contextual_;
- bool is_dont_delete_;
-};
-
-
-
-void DeferredReferenceGetNamedValue::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredReferenceGetKeyedValue: public DeferredCode {
- public:
- DeferredReferenceGetKeyedValue(Register key, Register receiver)
- : key_(key), receiver_(receiver) {
- set_comment("[ DeferredReferenceGetKeyedValue");
- }
-
- virtual void Generate();
-
- private:
- Register key_;
- Register receiver_;
-};
-
-
-void DeferredReferenceGetKeyedValue::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredReferenceSetKeyedValue: public DeferredCode {
- public:
- DeferredReferenceSetKeyedValue(Register value,
- Register key,
- Register receiver)
- : value_(value), key_(key), receiver_(receiver) {
- set_comment("[ DeferredReferenceSetKeyedValue");
- }
-
- virtual void Generate();
-
- private:
- Register value_;
- Register key_;
- Register receiver_;
-};
-
-
-void DeferredReferenceSetKeyedValue::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-class DeferredReferenceSetNamedValue: public DeferredCode {
- public:
- DeferredReferenceSetNamedValue(Register value,
- Register receiver,
- Handle<String> name)
- : value_(value), receiver_(receiver), name_(name) {
- set_comment("[ DeferredReferenceSetNamedValue");
- }
-
- virtual void Generate();
-
- private:
- Register value_;
- Register receiver_;
- Handle<String> name_;
-};
-
-
-void DeferredReferenceSetNamedValue::Generate() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitKeyedLoad() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void CodeGenerator::EmitKeyedStore(StaticType* key_type,
- WriteBarrierCharacter wb_info) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-#ifdef DEBUG
-bool CodeGenerator::HasValidEntryRegisters() {
- UNIMPLEMENTED_MIPS();
- return false;
-}
-#endif
-
-
-#undef __
-#define __ ACCESS_MASM(masm)
-
-// -----------------------------------------------------------------------------
-// Reference support.
-
-
-Handle<String> Reference::GetName() {
- UNIMPLEMENTED_MIPS();
- return Handle<String>();
-}
-
-
-void Reference::DupIfPersist() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void Reference::GetValue() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void Reference::SetValue(InitState init_state, WriteBarrierCharacter wb_info) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-const char* GenericBinaryOpStub::GetName() {
- UNIMPLEMENTED_MIPS();
- return name_;
-}
-
-
-#undef __
-
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_MIPS
diff --git a/src/mips/codegen-mips.h b/src/mips/codegen-mips.h
index 0a2cd458..fecd321f 100644
--- a/src/mips/codegen-mips.h
+++ b/src/mips/codegen-mips.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -37,204 +37,16 @@
namespace v8 {
namespace internal {
-#if(defined(__mips_hard_float) && __mips_hard_float != 0)
-// Use floating-point coprocessor instructions. This flag is raised when
-// -mhard-float is passed to the compiler.
-static const bool IsMipsSoftFloatABI = false;
-#elif(defined(__mips_soft_float) && __mips_soft_float != 0)
-// Not using floating-point coprocessor instructions. This flag is raised when
-// -msoft-float is passed to the compiler.
-static const bool IsMipsSoftFloatABI = true;
-#else
-static const bool IsMipsSoftFloatABI = true;
-#endif
-
// Forward declarations
class CompilationInfo;
-class DeferredCode;
-class JumpTarget;
-class RegisterAllocator;
-class RegisterFile;
-enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
-enum GenerateInlineSmi { DONT_GENERATE_INLINE_SMI, GENERATE_INLINE_SMI };
-enum WriteBarrierCharacter { UNLIKELY_SMI, LIKELY_SMI, NEVER_NEWSPACE };
-
-
-// -----------------------------------------------------------------------------
-// Reference support
-
-// A reference is a C++ stack-allocated object that keeps an ECMA
-// reference on the execution stack while in scope. For variables
-// the reference is empty, indicating that it isn't necessary to
-// store state on the stack for keeping track of references to those.
-// For properties, we keep either one (named) or two (indexed) values
-// on the execution stack to represent the reference.
-class Reference BASE_EMBEDDED {
- public:
- // The values of the types is important, see size().
- enum Type { UNLOADED = -2, ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 };
- Reference(CodeGenerator* cgen,
- Expression* expression,
- bool persist_after_get = false);
- ~Reference();
-
- Expression* expression() const { return expression_; }
- Type type() const { return type_; }
- void set_type(Type value) {
- ASSERT_EQ(ILLEGAL, type_);
- type_ = value;
- }
-
- void set_unloaded() {
- ASSERT_NE(ILLEGAL, type_);
- ASSERT_NE(UNLOADED, type_);
- type_ = UNLOADED;
- }
- // The size the reference takes up on the stack.
- int size() const {
- return (type_ < SLOT) ? 0 : type_;
- }
-
- bool is_illegal() const { return type_ == ILLEGAL; }
- bool is_slot() const { return type_ == SLOT; }
- bool is_property() const { return type_ == NAMED || type_ == KEYED; }
- bool is_unloaded() const { return type_ == UNLOADED; }
-
- // Return the name. Only valid for named property references.
- Handle<String> GetName();
-
- // Generate code to push the value of the reference on top of the
- // expression stack. The reference is expected to be already on top of
- // the expression stack, and it is consumed by the call unless the
- // reference is for a compound assignment.
- // If the reference is not consumed, it is left in place under its value.
- void GetValue();
-
- // Generate code to pop a reference, push the value of the reference,
- // and then spill the stack frame.
- inline void GetValueAndSpill();
-
- // Generate code to store the value on top of the expression stack in the
- // reference. The reference is expected to be immediately below the value
- // on the expression stack. The value is stored in the location specified
- // by the reference, and is left on top of the stack, after the reference
- // is popped from beneath it (unloaded).
- void SetValue(InitState init_state, WriteBarrierCharacter wb);
-
- // This is in preparation for something that uses the reference on the stack.
- // If we need this reference afterwards get then dup it now. Otherwise mark
- // it as used.
- inline void DupIfPersist();
-
- private:
- CodeGenerator* cgen_;
- Expression* expression_;
- Type type_;
- // Keep the reference on the stack after get, so it can be used by set later.
- bool persist_after_get_;
-};
-
-
-// -----------------------------------------------------------------------------
-// Code generation state
-
-// The state is passed down the AST by the code generator (and back up, in
-// the form of the state of the label pair). It is threaded through the
-// call stack. Constructing a state implicitly pushes it on the owning code
-// generator's stack of states, and destroying one implicitly pops it.
-
-class CodeGenState BASE_EMBEDDED {
- public:
- // Create an initial code generator state. Destroying the initial state
- // leaves the code generator with a NULL state.
- explicit CodeGenState(CodeGenerator* owner);
-
-
-
- // Destroy a code generator state and restore the owning code generator's
- // previous state.
- virtual ~CodeGenState();
-
- virtual JumpTarget* true_target() const { return NULL; }
- virtual JumpTarget* false_target() const { return NULL; }
-
- protected:
- inline CodeGenerator* owner() { return owner_; }
- inline CodeGenState* previous() const { return previous_; }
-
- private:
- // The owning code generator.
- CodeGenerator* owner_;
-
-
-
- // The previous state of the owning code generator, restored when
- // this state is destroyed.
- CodeGenState* previous_;
-};
-
-
-class ConditionCodeGenState : public CodeGenState {
- public:
- // Create a code generator state based on a code generator's current
- // state. The new state has its own pair of branch labels.
- ConditionCodeGenState(CodeGenerator* owner,
- JumpTarget* true_target,
- JumpTarget* false_target);
-
- virtual JumpTarget* true_target() const { return true_target_; }
- virtual JumpTarget* false_target() const { return false_target_; }
-
- private:
- JumpTarget* true_target_;
- JumpTarget* false_target_;
-};
-
-
-class TypeInfoCodeGenState : public CodeGenState {
- public:
- TypeInfoCodeGenState(CodeGenerator* owner,
- Slot* slot_number,
- TypeInfo info);
- virtual ~TypeInfoCodeGenState();
-
- virtual JumpTarget* true_target() const { return previous()->true_target(); }
- virtual JumpTarget* false_target() const {
- return previous()->false_target();
- }
-
- private:
- Slot* slot_;
- TypeInfo old_type_info_;
-};
-
// -------------------------------------------------------------------------
-// Arguments allocation mode
-
-enum ArgumentsAllocationMode {
- NO_ARGUMENTS_ALLOCATION,
- EAGER_ARGUMENTS_ALLOCATION,
- LAZY_ARGUMENTS_ALLOCATION
-};
-
-
-// -----------------------------------------------------------------------------
// CodeGenerator
class CodeGenerator: public AstVisitor {
public:
- // Compilation mode. Either the compiler is used as the primary
- // compiler and needs to setup everything or the compiler is used as
- // the secondary compiler for split compilation and has to handle
- // bailouts.
- enum Mode {
- PRIMARY,
- SECONDARY
- };
-
static bool MakeCode(CompilationInfo* info);
// Printing of AST, etc. as requested by flags.
@@ -261,50 +73,14 @@ class CodeGenerator: public AstVisitor {
int pos,
bool right_here = false);
- // Accessors
- MacroAssembler* masm() { return masm_; }
- VirtualFrame* frame() const { return frame_; }
- inline Handle<Script> script();
-
- bool has_valid_frame() const { return frame_ != NULL; }
-
- // Set the virtual frame to be new_frame, with non-frame register
- // reference counts given by non_frame_registers. The non-frame
- // register reference counts of the old frame are returned in
- // non_frame_registers.
- void SetFrame(VirtualFrame* new_frame, RegisterFile* non_frame_registers);
-
- void DeleteFrame();
-
- RegisterAllocator* allocator() const { return allocator_; }
-
- CodeGenState* state() { return state_; }
- void set_state(CodeGenState* state) { state_ = state; }
-
- TypeInfo type_info(Slot* slot) {
- int index = NumberOfSlot(slot);
- if (index == kInvalidSlotNumber) return TypeInfo::Unknown();
- return (*type_info_)[index];
- }
-
- TypeInfo set_type_info(Slot* slot, TypeInfo info) {
- int index = NumberOfSlot(slot);
- ASSERT(index >= kInvalidSlotNumber);
- if (index != kInvalidSlotNumber) {
- TypeInfo previous_value = (*type_info_)[index];
- (*type_info_)[index] = info;
- return previous_value;
- }
- return TypeInfo::Unknown();
- }
- void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
-
// Constants related to patching of inlined load/store.
static int GetInlinedKeyedLoadInstructionsAfterPatch() {
// This is in correlation with the padding in MacroAssembler::Abort.
return FLAG_debug_code ? 45 : 20;
}
- static const int kInlinedKeyedStoreInstructionsAfterPatch = 9;
+
+ static const int kInlinedKeyedStoreInstructionsAfterPatch = 13;
+
static int GetInlinedNamedStoreInstructionsAfterPatch() {
ASSERT(Isolate::Current()->inlined_write_barrier_size() != -1);
// Magic number 5: instruction count after patched map load:
@@ -313,317 +89,6 @@ class CodeGenerator: public AstVisitor {
}
private:
- // Type of a member function that generates inline code for a native function.
- typedef void (CodeGenerator::*InlineFunctionGenerator)
- (ZoneList<Expression*>*);
-
- static const InlineFunctionGenerator kInlineFunctionGenerators[];
-
-
- // Construction/Destruction.
- explicit CodeGenerator(MacroAssembler* masm);
-
- // Accessors.
- inline bool is_eval();
- inline Scope* scope();
- inline bool is_strict_mode();
- inline StrictModeFlag strict_mode_flag();
-
- // Generating deferred code.
- void ProcessDeferred();
-
- static const int kInvalidSlotNumber = -1;
-
- int NumberOfSlot(Slot* slot);
- // State
- bool has_cc() const { return cc_reg_ != cc_always; }
-
- JumpTarget* true_target() const { return state_->true_target(); }
- JumpTarget* false_target() const { return state_->false_target(); }
-
- // Track loop nesting level.
- int loop_nesting() const { return loop_nesting_; }
- void IncrementLoopNesting() { loop_nesting_++; }
- void DecrementLoopNesting() { loop_nesting_--; }
-
- // Node visitors.
- void VisitStatements(ZoneList<Statement*>* statements);
-
- virtual void VisitSlot(Slot* node);
-#define DEF_VISIT(type) \
- virtual void Visit##type(type* node);
- AST_NODE_LIST(DEF_VISIT)
-#undef DEF_VISIT
-
- // Main code generation function
- void Generate(CompilationInfo* info);
-
- // Generate the return sequence code. Should be called no more than
- // once per compiled function, immediately after binding the return
- // target (which can not be done more than once). The return value should
- // be in v0.
- void GenerateReturnSequence();
-
- // Returns the arguments allocation mode.
- ArgumentsAllocationMode ArgumentsMode();
-
- // Store the arguments object and allocate it if necessary.
- void StoreArgumentsObject(bool initial);
-
- // The following are used by class Reference.
- void LoadReference(Reference* ref);
- void UnloadReference(Reference* ref);
-
- MemOperand SlotOperand(Slot* slot, Register tmp);
-
- MemOperand ContextSlotOperandCheckExtensions(Slot* slot,
- Register tmp,
- Register tmp2,
- JumpTarget* slow);
-
- void LoadCondition(Expression* x,
- JumpTarget* true_target,
- JumpTarget* false_target,
- bool force_cc);
- void Load(Expression* x);
- void LoadGlobal();
- void LoadGlobalReceiver(Register scratch);
-
-
- // Special code for typeof expressions: Unfortunately, we must
- // be careful when loading the expression in 'typeof'
- // expressions. We are not allowed to throw reference errors for
- // non-existing properties of the global object, so we must make it
- // look like an explicit property access, instead of an access
- // through the context chain.
- void LoadTypeofExpression(Expression* x);
-
- // Store a keyed property. Key and receiver are on the stack and the value is
- // in a0. Result is returned in r0.
- void EmitKeyedStore(StaticType* key_type, WriteBarrierCharacter wb_info);
-
- // Read a value from a slot and leave it on top of the expression stack.
- void LoadFromSlot(Slot* slot, TypeofState typeof_state);
- void LoadFromGlobalSlotCheckExtensions(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow);
- void LoadFromSlotCheckForArguments(Slot* slot, TypeofState state);
-
- // Support for loading from local/global variables and arguments
- // whose location is known unless they are shadowed by
- // eval-introduced bindings. Generates no code for unsupported slot
- // types and therefore expects to fall through to the slow jump target.
- void EmitDynamicLoadFromSlotFastCase(Slot* slot,
- TypeofState typeof_state,
- JumpTarget* slow,
- JumpTarget* done);
-
- // Store the value on top of the stack to a slot.
- void StoreToSlot(Slot* slot, InitState init_state);
-
- // Support for compiling assignment expressions.
- void EmitSlotAssignment(Assignment* node);
- void EmitNamedPropertyAssignment(Assignment* node);
- void EmitKeyedPropertyAssignment(Assignment* node);
-
- // Load a named property, returning it in v0. The receiver is passed on the
- // stack, and remains there.
- void EmitNamedLoad(Handle<String> name, bool is_contextual);
-
- // Store to a named property. If the store is contextual, value is passed on
- // the frame and consumed. Otherwise, receiver and value are passed on the
- // frame and consumed. The result is returned in v0.
- void EmitNamedStore(Handle<String> name, bool is_contextual);
-
- // Load a keyed property, leaving it in v0. The receiver and key are
- // passed on the stack, and remain there.
- void EmitKeyedLoad();
-
- void ToBoolean(JumpTarget* true_target, JumpTarget* false_target);
-
- // Generate code that computes a shortcutting logical operation.
- void GenerateLogicalBooleanOperation(BinaryOperation* node);
-
- void GenericBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode,
- GenerateInlineSmi inline_smi,
- int known_rhs =
- GenericBinaryOpStub::kUnknownIntValue);
-
- void VirtualFrameBinaryOperation(Token::Value op,
- OverwriteMode overwrite_mode,
- int known_rhs =
- GenericBinaryOpStub::kUnknownIntValue);
-
- void SmiOperation(Token::Value op,
- Handle<Object> value,
- bool reversed,
- OverwriteMode mode);
-
- void Comparison(Condition cc,
- Expression* left,
- Expression* right,
- bool strict = false);
-
- void CallWithArguments(ZoneList<Expression*>* arguments,
- CallFunctionFlags flags,
- int position);
-
- // An optimized implementation of expressions of the form
- // x.apply(y, arguments). We call x the applicand and y the receiver.
- // The optimization avoids allocating an arguments object if possible.
- void CallApplyLazy(Expression* applicand,
- Expression* receiver,
- VariableProxy* arguments,
- int position);
-
- // Control flow
- void Branch(bool if_true, JumpTarget* target);
- void CheckStack();
-
- bool CheckForInlineRuntimeCall(CallRuntime* node);
-
- static Handle<Code> ComputeLazyCompile(int argc);
- void ProcessDeclarations(ZoneList<Declaration*>* declarations);
-
- // Declare global variables and functions in the given array of
- // name/value pairs.
- void DeclareGlobals(Handle<FixedArray> pairs);
-
- // Instantiate the function based on the shared function info.
- void InstantiateFunction(Handle<SharedFunctionInfo> function_info,
- bool pretenure);
-
- // Support for type checks.
- void GenerateIsSmi(ZoneList<Expression*>* args);
- void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
- void GenerateIsArray(ZoneList<Expression*>* args);
- void GenerateIsRegExp(ZoneList<Expression*>* args);
-
- // Support for construct call checks.
- void GenerateIsConstructCall(ZoneList<Expression*>* args);
-
- // Support for arguments.length and arguments[?].
- void GenerateArgumentsLength(ZoneList<Expression*>* args);
- void GenerateArguments(ZoneList<Expression*>* args);
-
- // Support for accessing the class and value fields of an object.
- void GenerateClassOf(ZoneList<Expression*>* args);
- void GenerateValueOf(ZoneList<Expression*>* args);
- void GenerateSetValueOf(ZoneList<Expression*>* args);
-
- // Fast support for charCodeAt(n).
- void GenerateStringCharCodeAt(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharFromCode(ZoneList<Expression*>* args);
-
- // Fast support for string.charAt(n) and string[n].
- void GenerateStringCharAt(ZoneList<Expression*>* args);
-
- // Fast support for object equality testing.
- void GenerateObjectEquals(ZoneList<Expression*>* args);
-
- void GenerateLog(ZoneList<Expression*>* args);
-
- // Fast support for Math.random().
- void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
-
- void GenerateIsObject(ZoneList<Expression*>* args);
- void GenerateIsSpecObject(ZoneList<Expression*>* args);
- void GenerateIsFunction(ZoneList<Expression*>* args);
- void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
- void GenerateStringAdd(ZoneList<Expression*>* args);
- void GenerateSubString(ZoneList<Expression*>* args);
- void GenerateStringCompare(ZoneList<Expression*>* args);
- void GenerateIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* args);
-
- // Support for direct calls from JavaScript to native RegExp code.
- void GenerateRegExpExec(ZoneList<Expression*>* args);
-
- void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
-
- // Support for fast native caches.
- void GenerateGetFromCache(ZoneList<Expression*>* args);
-
- // Fast support for number to string.
- void GenerateNumberToString(ZoneList<Expression*>* args);
-
- // Fast swapping of elements.
- void GenerateSwapElements(ZoneList<Expression*>* args);
-
- // Fast call for custom callbacks.
- void GenerateCallFunction(ZoneList<Expression*>* args);
-
- // Fast call to math functions.
- void GenerateMathPow(ZoneList<Expression*>* args);
- void GenerateMathSin(ZoneList<Expression*>* args);
- void GenerateMathCos(ZoneList<Expression*>* args);
- void GenerateMathSqrt(ZoneList<Expression*>* args);
- void GenerateMathLog(ZoneList<Expression*>* args);
-
- void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
-
- void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
- void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args);
-
- // Simple condition analysis.
- enum ConditionAnalysis {
- ALWAYS_TRUE,
- ALWAYS_FALSE,
- DONT_KNOW
- };
- ConditionAnalysis AnalyzeCondition(Expression* cond);
-
- // Methods used to indicate which source code is generated for. Source
- // positions are collected by the assembler and emitted with the relocation
- // information.
- void CodeForFunctionPosition(FunctionLiteral* fun);
- void CodeForReturnPosition(FunctionLiteral* fun);
- void CodeForStatementPosition(Statement* node);
- void CodeForDoWhileConditionPosition(DoWhileStatement* stmt);
- void CodeForSourcePosition(int pos);
-
-#ifdef DEBUG
- // True if the registers are valid for entry to a block.
- bool HasValidEntryRegisters();
-#endif
-
- List<DeferredCode*> deferred_;
-
- // Assembler
- MacroAssembler* masm_; // to generate code
-
- CompilationInfo* info_;
-
- // Code generation state
- VirtualFrame* frame_;
- RegisterAllocator* allocator_;
- Condition cc_reg_;
- CodeGenState* state_;
- int loop_nesting_;
-
- Vector<TypeInfo>* type_info_;
- // Jump targets
- BreakTarget function_return_;
-
- // True if the function return is shadowed (ie, jumping to the target
- // function_return_ does not jump to the true function return, but rather
- // to some unlinking code).
- bool function_return_is_shadowed_;
-
- friend class VirtualFrame;
- friend class Isolate;
- friend class JumpTarget;
- friend class Reference;
- friend class FastCodeGenerator;
- friend class FullCodeGenerator;
- friend class FullCodeGenSyntaxChecker;
- friend class InlineRuntimeFunctionsTable;
- friend class LCodeGen;
-
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/src/mips/constants-mips.cc b/src/mips/constants-mips.cc
index 16e49c9c..96a23338 100644
--- a/src/mips/constants-mips.cc
+++ b/src/mips/constants-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -36,7 +36,7 @@ namespace internal {
// -----------------------------------------------------------------------------
-// Registers
+// Registers.
// These register names are defined in a way to match the native disassembler
@@ -145,7 +145,7 @@ int FPURegisters::Number(const char* name) {
// -----------------------------------------------------------------------------
-// Instruction
+// Instructions.
bool Instruction::IsForbiddenInBranchDelay() const {
const int op = OpcodeFieldRaw();
@@ -293,15 +293,15 @@ Instruction::Type Instruction::InstructionType() const {
UNREACHABLE();
};
break;
- case COP1: // Coprocessor instructions
+ case COP1: // Coprocessor instructions.
switch (RsFieldRawNoAssert()) {
- case BC1: // branch on coprocessor condition
+ case BC1: // Branch on coprocessor condition.
return kImmediateType;
default:
return kRegisterType;
};
break;
- // 16 bits Immediate type instructions. eg: addi dest, src, imm16
+ // 16 bits Immediate type instructions. eg: addi dest, src, imm16.
case REGIMM:
case BEQ:
case BNE:
@@ -336,7 +336,7 @@ Instruction::Type Instruction::InstructionType() const {
case SWC1:
case SDC1:
return kImmediateType;
- // 26 bits immediate type instructions. eg: j imm26
+ // 26 bits immediate type instructions. eg: j imm26.
case J:
case JAL:
return kJumpType;
diff --git a/src/mips/constants-mips.h b/src/mips/constants-mips.h
index b20e9a28..25673301 100644
--- a/src/mips/constants-mips.h
+++ b/src/mips/constants-mips.h
@@ -47,6 +47,19 @@
#endif
+#if(defined(__mips_hard_float) && __mips_hard_float != 0)
+// Use floating-point coprocessor instructions. This flag is raised when
+// -mhard-float is passed to the compiler.
+static const bool IsMipsSoftFloatABI = false;
+#elif(defined(__mips_soft_float) && __mips_soft_float != 0)
+// Not using floating-point coprocessor instructions. This flag is raised when
+// -msoft-float is passed to the compiler.
+static const bool IsMipsSoftFloatABI = true;
+#else
+static const bool IsMipsSoftFloatABI = true;
+#endif
+
+
// Defines constants and accessor classes to assemble, disassemble and
// simulate MIPS32 instructions.
//
@@ -58,7 +71,7 @@ namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
-// Registers and FPURegister.
+// Registers and FPURegisters.
// Number of general purpose registers.
static const int kNumRegisters = 32;
@@ -82,6 +95,11 @@ static const uint32_t kFPUInvalidResult = (uint32_t) (1 << 31) - 1;
// FCSR constants.
static const uint32_t kFCSRFlagMask = (1 << 6) - 1;
static const uint32_t kFCSRFlagShift = 2;
+static const uint32_t kFCSRInexactFlagBit = 1 << 0;
+static const uint32_t kFCSRUnderflowFlagBit = 1 << 1;
+static const uint32_t kFCSROverflowFlagBit = 1 << 2;
+static const uint32_t kFCSRDivideByZeroFlagBit = 1 << 3;
+static const uint32_t kFCSRInvalidOpFlagBit = 1 << 4;
// Helper functions for converting between register numbers and names.
class Registers {
@@ -133,8 +151,6 @@ class FPURegisters {
// On MIPS all instructions are 32 bits.
typedef int32_t Instr;
-typedef unsigned char byte_;
-
// Special Software Interrupt codes when used in the presence of the MIPS
// simulator.
enum SoftwareInterruptCodes {
@@ -175,7 +191,7 @@ static const int kFBccBits = 3;
static const int kFBtrueShift = 16;
static const int kFBtrueBits = 1;
-// ----- Miscellianous useful masks.
+// ----- Miscellaneous useful masks.
// Instruction bit masks.
static const int kOpcodeMask = ((1 << kOpcodeBits) - 1) << kOpcodeShift;
static const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift;
@@ -215,7 +231,7 @@ enum Opcode {
XORI = ((1 << 3) + 6) << kOpcodeShift,
LUI = ((1 << 3) + 7) << kOpcodeShift,
- COP1 = ((2 << 3) + 1) << kOpcodeShift, // Coprocessor 1 class
+ COP1 = ((2 << 3) + 1) << kOpcodeShift, // Coprocessor 1 class.
BEQL = ((2 << 3) + 4) << kOpcodeShift,
BNEL = ((2 << 3) + 5) << kOpcodeShift,
BLEZL = ((2 << 3) + 6) << kOpcodeShift,
@@ -393,7 +409,7 @@ enum Condition {
cc_always = 16,
- // aliases
+ // Aliases.
carry = Uless,
not_carry = Ugreater_equal,
zero = equal,
@@ -455,14 +471,14 @@ inline Condition ReverseCondition(Condition cc) {
// ----- Coprocessor conditions.
enum FPUCondition {
- F, // False
- UN, // Unordered
- EQ, // Equal
- UEQ, // Unordered or Equal
- OLT, // Ordered or Less Than
- ULT, // Unordered or Less Than
- OLE, // Ordered or Less Than or Equal
- ULE // Unordered or Less Than or Equal
+ F, // False.
+ UN, // Unordered.
+ EQ, // Equal.
+ UEQ, // Unordered or Equal.
+ OLT, // Ordered or Less Than.
+ ULT, // Unordered or Less Than.
+ OLE, // Ordered or Less Than or Equal.
+ ULE // Unordered or Less Than or Equal.
};
@@ -494,7 +510,7 @@ extern const Instr kPopInstruction;
extern const Instr kPushInstruction;
// sw(r, MemOperand(sp, 0))
extern const Instr kPushRegPattern;
-// lw(r, MemOperand(sp, 0))
+// lw(r, MemOperand(sp, 0))
extern const Instr kPopRegPattern;
extern const Instr kLwRegFpOffsetPattern;
extern const Instr kSwRegFpOffsetPattern;
@@ -687,7 +703,7 @@ class Instruction {
// reference to an instruction is to convert a pointer. There is no way
// to allocate or create instances of class Instruction.
// Use the At(pc) function to create references to Instruction.
- static Instruction* At(byte_* pc) {
+ static Instruction* At(byte* pc) {
return reinterpret_cast<Instruction*>(pc);
}
diff --git a/src/mips/cpu-mips.cc b/src/mips/cpu-mips.cc
index 36f577bd..26e95fb2 100644
--- a/src/mips/cpu-mips.cc
+++ b/src/mips/cpu-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -48,19 +48,25 @@ namespace internal {
void CPU::Setup() {
- CpuFeatures* cpu_features = Isolate::Current()->cpu_features();
- cpu_features->Probe(true);
- if (!cpu_features->IsSupported(FPU) || Serializer::enabled()) {
- V8::DisableCrankshaft();
- }
+ CpuFeatures::Probe();
+}
+
+
+bool CPU::SupportsCrankshaft() {
+ return CpuFeatures::IsSupported(FPU);
}
void CPU::FlushICache(void* start, size_t size) {
+ // Nothing to do, flushing no instructions.
+ if (size == 0) {
+ return;
+ }
+
#if !defined (USE_SIMULATOR)
int res;
- // See http://www.linux-mips.org/wiki/Cacheflush_Syscall
+ // See http://www.linux-mips.org/wiki/Cacheflush_Syscall.
res = syscall(__NR_cacheflush, start, size, ICACHE);
if (res) {
diff --git a/src/mips/debug-mips.cc b/src/mips/debug-mips.cc
index 35df69b8..e323c505 100644
--- a/src/mips/debug-mips.cc
+++ b/src/mips/debug-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -31,7 +31,7 @@
#if defined(V8_TARGET_ARCH_MIPS)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
namespace v8 {
@@ -40,106 +40,259 @@ namespace internal {
#ifdef ENABLE_DEBUGGER_SUPPORT
bool BreakLocationIterator::IsDebugBreakAtReturn() {
- UNIMPLEMENTED_MIPS();
- return false;
+ return Debug::IsDebugBreakAtReturn(rinfo());
}
void BreakLocationIterator::SetDebugBreakAtReturn() {
- UNIMPLEMENTED_MIPS();
+ // Mips return sequence:
+ // mov sp, fp
+ // lw fp, sp(0)
+ // lw ra, sp(4)
+ // addiu sp, sp, 8
+ // addiu sp, sp, N
+ // jr ra
+ // nop (in branch delay slot)
+
+ // Make sure this constant matches the number if instrucntions we emit.
+ ASSERT(Assembler::kJSReturnSequenceInstructions == 7);
+ CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
+ // li and Call pseudo-instructions emit two instructions each.
+ patcher.masm()->li(v8::internal::t9,
+ Operand(reinterpret_cast<int32_t>(
+ Isolate::Current()->debug()->debug_break_return()->entry())));
+ patcher.masm()->Call(v8::internal::t9);
+ patcher.masm()->nop();
+ patcher.masm()->nop();
+ patcher.masm()->nop();
+
+ // TODO(mips): Open issue about using breakpoint instruction instead of nops.
+ // patcher.masm()->bkpt(0);
}
// Restore the JS frame exit code.
void BreakLocationIterator::ClearDebugBreakAtReturn() {
- UNIMPLEMENTED_MIPS();
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kJSReturnSequenceInstructions);
}
// A debug break in the exit code is identified by the JS frame exit code
-// having been patched with li/call psuedo-instrunction (liu/ori/jalr)
+// having been patched with li/call psuedo-instrunction (liu/ori/jalr).
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
- UNIMPLEMENTED_MIPS();
- return false;
+ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
+ return rinfo->IsPatchedReturnSequence();
}
bool BreakLocationIterator::IsDebugBreakAtSlot() {
- UNIMPLEMENTED_MIPS();
- return false;
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return rinfo()->IsPatchedDebugBreakSlotSequence();
}
void BreakLocationIterator::SetDebugBreakAtSlot() {
- UNIMPLEMENTED_MIPS();
+ ASSERT(IsDebugBreakSlot());
+ // Patch the code changing the debug break slot code from:
+ // nop(DEBUG_BREAK_NOP) - nop(1) is sll(zero_reg, zero_reg, 1)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // nop(DEBUG_BREAK_NOP)
+ // to a call to the debug break slot code.
+ // li t9, address (lui t9 / ori t9 instruction pair)
+ // call t9 (jalr t9 / nop instruction pair)
+ CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
+ patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
+ Isolate::Current()->debug()->debug_break_slot()->entry())));
+ patcher.masm()->Call(v8::internal::t9);
}
void BreakLocationIterator::ClearDebugBreakAtSlot() {
- UNIMPLEMENTED_MIPS();
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kDebugBreakSlotInstructions);
}
#define __ ACCESS_MASM(masm)
+
+static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
+ RegList object_regs,
+ RegList non_object_regs) {
+ __ EnterInternalFrame();
+
+ // Store the registers containing live values on the expression stack to
+ // make sure that these are correctly updated during GC. Non object values
+ // are stored as a smi causing it to be untouched by GC.
+ ASSERT((object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((object_regs & non_object_regs) == 0);
+ if ((object_regs | non_object_regs) != 0) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if ((non_object_regs & (1 << r)) != 0) {
+ if (FLAG_debug_code) {
+ __ And(at, reg, 0xc0000000);
+ __ Assert(eq, "Unable to encode value as smi", at, Operand(zero_reg));
+ }
+ __ sll(reg, reg, kSmiTagSize);
+ }
+ }
+ __ MultiPush(object_regs | non_object_regs);
+ }
+
+#ifdef DEBUG
+ __ RecordComment("// Calling from debug break to runtime - come in - over");
+#endif
+ __ mov(a0, zero_reg); // No arguments.
+ __ li(a1, Operand(ExternalReference::debug_break(masm->isolate())));
+
+ CEntryStub ceb(1);
+ __ CallStub(&ceb);
+
+ // Restore the register values from the expression stack.
+ if ((object_regs | non_object_regs) != 0) {
+ __ MultiPop(object_regs | non_object_regs);
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if ((non_object_regs & (1 << r)) != 0) {
+ __ srl(reg, reg, kSmiTagSize);
+ }
+ if (FLAG_debug_code &&
+ (((object_regs |non_object_regs) & (1 << r)) == 0)) {
+ __ li(reg, kDebugZapValue);
+ }
+ }
+ }
+
+ __ LeaveInternalFrame();
+
+ // Now that the break point has been handled, resume normal execution by
+ // jumping to the target address intended by the caller and that was
+ // overwritten by the address of DebugBreakXXX.
+ __ li(t9, Operand(
+ ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate())));
+ __ lw(t9, MemOperand(t9));
+ __ Jump(t9);
+}
+
+
void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Calling convention for IC load (from ic-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- [sp] : receiver
+ // -----------------------------------
+ // Registers a0 and a2 contain objects that need to be pushed on the
+ // expression stack of the fake JS frame.
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a2.bit(), 0);
}
void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Calling convention for IC store (from ic-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ // Registers a0, a1, and a2 contain objects that need to be pushed on the
+ // expression stack of the fake JS frame.
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0);
}
void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit(), 0);
}
void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ Generate_DebugBreakCallHelper(masm, a0.bit() | a1.bit() | a2.bit(), 0);
}
void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Calling convention for IC call (from ic-mips.cc).
+ // ----------- S t a t e -------------
+ // -- a2: name
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, a2.bit(), 0);
}
void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Calling convention for construct call (from builtins-mips.cc).
+ // -- a0 : number of arguments (not smi)
+ // -- a1 : constructor function
+ Generate_DebugBreakCallHelper(masm, a1.bit(), a0.bit());
}
void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // In places other than IC call sites it is expected that v0 is TOS which
+ // is an object - this is not generally the case so this should be used with
+ // care.
+ Generate_DebugBreakCallHelper(masm, v0.bit(), 0);
}
void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // No registers used on entry.
+ // -----------------------------------
+ Generate_DebugBreakCallHelper(masm, 0, 0);
}
void Debug::GenerateSlot(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the trampoline pool in the debug break slot code.
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
+ }
+ ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_codesize));
}
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // In the places where a debug break slot is inserted no registers can contain
+ // object pointers.
+ Generate_DebugBreakCallHelper(masm, 0, 0);
}
void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ masm->Abort("LiveEdit frame dropping is not supported on mips");
}
void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ masm->Abort("LiveEdit frame dropping is not supported on mips");
}
diff --git a/src/mips/disasm-mips.cc b/src/mips/disasm-mips.cc
index b7ceb2b1..7df5c417 100644
--- a/src/mips/disasm-mips.cc
+++ b/src/mips/disasm-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -33,7 +33,7 @@
//
// NameConverter converter;
// Disassembler d(converter);
-// for (byte_* pc = begin; pc < end;) {
+// for (byte* pc = begin; pc < end;) {
// v8::internal::EmbeddedVector<char, 256> buffer;
// byte* prev_pc = pc;
// pc += d.InstructionDecode(buffer, pc);
@@ -85,7 +85,7 @@ class Decoder {
// Writes one disassembled instruction into 'buffer' (0-terminated).
// Returns the length of the disassembled machine instruction in bytes.
- int InstructionDecode(byte_* instruction);
+ int InstructionDecode(byte* instruction);
private:
// Bottleneck functions to print into the out_buffer.
@@ -103,6 +103,8 @@ class Decoder {
void PrintFd(Instruction* instr);
void PrintSa(Instruction* instr);
void PrintSd(Instruction* instr);
+ void PrintSs1(Instruction* instr);
+ void PrintSs2(Instruction* instr);
void PrintBc(Instruction* instr);
void PrintCc(Instruction* instr);
void PrintFunction(Instruction* instr);
@@ -212,13 +214,29 @@ void Decoder::PrintSa(Instruction* instr) {
}
-// Print the integer value of the rd field, (when it is not used as reg).
+// Print the integer value of the rd field, when it is not used as reg.
void Decoder::PrintSd(Instruction* instr) {
int sd = instr->RdValue();
out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", sd);
}
+// Print the integer value of the rd field, when used as 'ext' size.
+void Decoder::PrintSs1(Instruction* instr) {
+ int ss = instr->RdValue();
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", ss + 1);
+}
+
+
+// Print the integer value of the rd field, when used as 'ins' size.
+void Decoder::PrintSs2(Instruction* instr) {
+ int ss = instr->RdValue();
+ int pos = instr->SaValue();
+ out_buffer_pos_ +=
+ OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", ss - pos + 1);
+}
+
+
// Print the integer value of the cc field for the bc1t/f instructions.
void Decoder::PrintBc(Instruction* instr) {
int cc = instr->FBccValue();
@@ -242,7 +260,7 @@ void Decoder::PrintUImm16(Instruction* instr) {
// Print 16-bit signed immediate value.
void Decoder::PrintSImm16(Instruction* instr) {
- int32_t imm = ((instr->Imm16Value())<<16)>>16;
+ int32_t imm = ((instr->Imm16Value()) << 16) >> 16;
out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}
@@ -298,15 +316,15 @@ void Decoder::PrintInstructionName(Instruction* instr) {
// complexity of FormatOption.
int Decoder::FormatRegister(Instruction* instr, const char* format) {
ASSERT(format[0] == 'r');
- if (format[1] == 's') { // 'rs: Rs register
+ if (format[1] == 's') { // 'rs: Rs register.
int reg = instr->RsValue();
PrintRegister(reg);
return 2;
- } else if (format[1] == 't') { // 'rt: rt register
+ } else if (format[1] == 't') { // 'rt: rt register.
int reg = instr->RtValue();
PrintRegister(reg);
return 2;
- } else if (format[1] == 'd') { // 'rd: rd register
+ } else if (format[1] == 'd') { // 'rd: rd register.
int reg = instr->RdValue();
PrintRegister(reg);
return 2;
@@ -320,15 +338,15 @@ int Decoder::FormatRegister(Instruction* instr, const char* format) {
// complexity of FormatOption.
int Decoder::FormatFPURegister(Instruction* instr, const char* format) {
ASSERT(format[0] == 'f');
- if (format[1] == 's') { // 'fs: fs register
+ if (format[1] == 's') { // 'fs: fs register.
int reg = instr->FsValue();
PrintFPURegister(reg);
return 2;
- } else if (format[1] == 't') { // 'ft: ft register
+ } else if (format[1] == 't') { // 'ft: ft register.
int reg = instr->FtValue();
PrintFPURegister(reg);
return 2;
- } else if (format[1] == 'd') { // 'fd: fd register
+ } else if (format[1] == 'd') { // 'fd: fd register.
int reg = instr->FdValue();
PrintFPURegister(reg);
return 2;
@@ -345,12 +363,12 @@ int Decoder::FormatFPURegister(Instruction* instr, const char* format) {
// characters that were consumed from the formatting string.
int Decoder::FormatOption(Instruction* instr, const char* format) {
switch (format[0]) {
- case 'c': { // 'code for break or trap instructions
+ case 'c': { // 'code for break or trap instructions.
ASSERT(STRING_STARTS_WITH(format, "code"));
PrintCode(instr);
return 4;
}
- case 'i': { // 'imm16u or 'imm26
+ case 'i': { // 'imm16u or 'imm26.
if (format[3] == '1') {
ASSERT(STRING_STARTS_WITH(format, "imm16"));
if (format[5] == 's') {
@@ -370,13 +388,13 @@ int Decoder::FormatOption(Instruction* instr, const char* format) {
return 5;
}
}
- case 'r': { // 'r: registers
+ case 'r': { // 'r: registers.
return FormatRegister(instr, format);
}
- case 'f': { // 'f: FPUregisters
+ case 'f': { // 'f: FPUregisters.
return FormatFPURegister(instr, format);
}
- case 's': { // 'sa
+ case 's': { // 'sa.
switch (format[1]) {
case 'a': {
ASSERT(STRING_STARTS_WITH(format, "sa"));
@@ -388,6 +406,17 @@ int Decoder::FormatOption(Instruction* instr, const char* format) {
PrintSd(instr);
return 2;
}
+ case 's': {
+ if (format[2] == '1') {
+ ASSERT(STRING_STARTS_WITH(format, "ss1")); /* ext size */
+ PrintSs1(instr);
+ return 3;
+ } else {
+ ASSERT(STRING_STARTS_WITH(format, "ss2")); /* ins size */
+ PrintSs2(instr);
+ return 3;
+ }
+ }
}
}
case 'b': { // 'bc - Special for bc1 cc field.
@@ -432,29 +461,29 @@ void Decoder::Unknown(Instruction* instr) {
void Decoder::DecodeTypeRegister(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
- case COP1: // Coprocessor instructions
+ case COP1: // Coprocessor instructions.
switch (instr->RsFieldRaw()) {
case BC1: // bc1 handled in DecodeTypeImmediate.
UNREACHABLE();
break;
case MFC1:
- Format(instr, "mfc1 'rt, 'fs");
+ Format(instr, "mfc1 'rt, 'fs");
break;
case MFHC1:
- Format(instr, "mfhc1 'rt, 'fs");
+ Format(instr, "mfhc1 'rt, 'fs");
break;
case MTC1:
- Format(instr, "mtc1 'rt, 'fs");
+ Format(instr, "mtc1 'rt, 'fs");
break;
// These are called "fs" too, although they are not FPU registers.
case CTC1:
- Format(instr, "ctc1 'rt, 'fs");
+ Format(instr, "ctc1 'rt, 'fs");
break;
case CFC1:
- Format(instr, "cfc1 'rt, 'fs");
+ Format(instr, "cfc1 'rt, 'fs");
break;
case MTHC1:
- Format(instr, "mthc1 'rt, 'fs");
+ Format(instr, "mthc1 'rt, 'fs");
break;
case D:
switch (instr->FunctionFieldRaw()) {
@@ -480,7 +509,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
Format(instr, "neg.d 'fd, 'fs");
break;
case SQRT_D:
- Format(instr, "sqrt.d 'fd, 'fs");
+ Format(instr, "sqrt.d 'fd, 'fs");
break;
case CVT_W_D:
Format(instr, "cvt.w.d 'fd, 'fs");
@@ -592,134 +621,134 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
case SPECIAL:
switch (instr->FunctionFieldRaw()) {
case JR:
- Format(instr, "jr 'rs");
+ Format(instr, "jr 'rs");
break;
case JALR:
- Format(instr, "jalr 'rs");
+ Format(instr, "jalr 'rs");
break;
case SLL:
if ( 0x0 == static_cast<int>(instr->InstructionBits()))
Format(instr, "nop");
else
- Format(instr, "sll 'rd, 'rt, 'sa");
+ Format(instr, "sll 'rd, 'rt, 'sa");
break;
case SRL:
if (instr->RsValue() == 0) {
- Format(instr, "srl 'rd, 'rt, 'sa");
+ Format(instr, "srl 'rd, 'rt, 'sa");
} else {
if (mips32r2) {
- Format(instr, "rotr 'rd, 'rt, 'sa");
+ Format(instr, "rotr 'rd, 'rt, 'sa");
} else {
Unknown(instr);
}
}
break;
case SRA:
- Format(instr, "sra 'rd, 'rt, 'sa");
+ Format(instr, "sra 'rd, 'rt, 'sa");
break;
case SLLV:
- Format(instr, "sllv 'rd, 'rt, 'rs");
+ Format(instr, "sllv 'rd, 'rt, 'rs");
break;
case SRLV:
if (instr->SaValue() == 0) {
- Format(instr, "srlv 'rd, 'rt, 'rs");
+ Format(instr, "srlv 'rd, 'rt, 'rs");
} else {
if (mips32r2) {
- Format(instr, "rotrv 'rd, 'rt, 'rs");
+ Format(instr, "rotrv 'rd, 'rt, 'rs");
} else {
Unknown(instr);
}
}
break;
case SRAV:
- Format(instr, "srav 'rd, 'rt, 'rs");
+ Format(instr, "srav 'rd, 'rt, 'rs");
break;
case MFHI:
- Format(instr, "mfhi 'rd");
+ Format(instr, "mfhi 'rd");
break;
case MFLO:
- Format(instr, "mflo 'rd");
+ Format(instr, "mflo 'rd");
break;
case MULT:
- Format(instr, "mult 'rs, 'rt");
+ Format(instr, "mult 'rs, 'rt");
break;
case MULTU:
- Format(instr, "multu 'rs, 'rt");
+ Format(instr, "multu 'rs, 'rt");
break;
case DIV:
- Format(instr, "div 'rs, 'rt");
+ Format(instr, "div 'rs, 'rt");
break;
case DIVU:
- Format(instr, "divu 'rs, 'rt");
+ Format(instr, "divu 'rs, 'rt");
break;
case ADD:
- Format(instr, "add 'rd, 'rs, 'rt");
+ Format(instr, "add 'rd, 'rs, 'rt");
break;
case ADDU:
- Format(instr, "addu 'rd, 'rs, 'rt");
+ Format(instr, "addu 'rd, 'rs, 'rt");
break;
case SUB:
- Format(instr, "sub 'rd, 'rs, 'rt");
+ Format(instr, "sub 'rd, 'rs, 'rt");
break;
case SUBU:
- Format(instr, "sub 'rd, 'rs, 'rt");
+ Format(instr, "subu 'rd, 'rs, 'rt");
break;
case AND:
- Format(instr, "and 'rd, 'rs, 'rt");
+ Format(instr, "and 'rd, 'rs, 'rt");
break;
case OR:
if (0 == instr->RsValue()) {
- Format(instr, "mov 'rd, 'rt");
+ Format(instr, "mov 'rd, 'rt");
} else if (0 == instr->RtValue()) {
- Format(instr, "mov 'rd, 'rs");
+ Format(instr, "mov 'rd, 'rs");
} else {
- Format(instr, "or 'rd, 'rs, 'rt");
+ Format(instr, "or 'rd, 'rs, 'rt");
}
break;
case XOR:
- Format(instr, "xor 'rd, 'rs, 'rt");
+ Format(instr, "xor 'rd, 'rs, 'rt");
break;
case NOR:
- Format(instr, "nor 'rd, 'rs, 'rt");
+ Format(instr, "nor 'rd, 'rs, 'rt");
break;
case SLT:
- Format(instr, "slt 'rd, 'rs, 'rt");
+ Format(instr, "slt 'rd, 'rs, 'rt");
break;
case SLTU:
- Format(instr, "sltu 'rd, 'rs, 'rt");
+ Format(instr, "sltu 'rd, 'rs, 'rt");
break;
case BREAK:
Format(instr, "break, code: 'code");
break;
case TGE:
- Format(instr, "tge 'rs, 'rt, code: 'code");
+ Format(instr, "tge 'rs, 'rt, code: 'code");
break;
case TGEU:
- Format(instr, "tgeu 'rs, 'rt, code: 'code");
+ Format(instr, "tgeu 'rs, 'rt, code: 'code");
break;
case TLT:
- Format(instr, "tlt 'rs, 'rt, code: 'code");
+ Format(instr, "tlt 'rs, 'rt, code: 'code");
break;
case TLTU:
- Format(instr, "tltu 'rs, 'rt, code: 'code");
+ Format(instr, "tltu 'rs, 'rt, code: 'code");
break;
case TEQ:
- Format(instr, "teq 'rs, 'rt, code: 'code");
+ Format(instr, "teq 'rs, 'rt, code: 'code");
break;
case TNE:
- Format(instr, "tne 'rs, 'rt, code: 'code");
+ Format(instr, "tne 'rs, 'rt, code: 'code");
break;
case MOVZ:
- Format(instr, "movz 'rd, 'rs, 'rt");
+ Format(instr, "movz 'rd, 'rs, 'rt");
break;
case MOVN:
- Format(instr, "movn 'rd, 'rs, 'rt");
+ Format(instr, "movn 'rd, 'rs, 'rt");
break;
case MOVCI:
if (instr->Bit(16)) {
- Format(instr, "movt 'rd, 'rs, 'Cc");
+ Format(instr, "movt 'rd, 'rs, 'bc");
} else {
- Format(instr, "movf 'rd, 'rs, 'Cc");
+ Format(instr, "movf 'rd, 'rs, 'bc");
}
break;
default:
@@ -729,10 +758,10 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
case SPECIAL2:
switch (instr->FunctionFieldRaw()) {
case MUL:
- Format(instr, "mul 'rd, 'rs, 'rt");
+ Format(instr, "mul 'rd, 'rs, 'rt");
break;
case CLZ:
- Format(instr, "clz 'rd, 'rs");
+ Format(instr, "clz 'rd, 'rs");
break;
default:
UNREACHABLE();
@@ -742,7 +771,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
switch (instr->FunctionFieldRaw()) {
case INS: {
if (mips32r2) {
- Format(instr, "ins 'rt, 'rs, 'sd, 'sa");
+ Format(instr, "ins 'rt, 'rs, 'sa, 'ss2");
} else {
Unknown(instr);
}
@@ -750,7 +779,7 @@ void Decoder::DecodeTypeRegister(Instruction* instr) {
}
case EXT: {
if (mips32r2) {
- Format(instr, "ext 'rt, 'rs, 'sd, 'sa");
+ Format(instr, "ext 'rt, 'rs, 'sa, 'ss1");
} else {
Unknown(instr);
}
@@ -785,16 +814,16 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
case REGIMM:
switch (instr->RtFieldRaw()) {
case BLTZ:
- Format(instr, "bltz 'rs, 'imm16u");
+ Format(instr, "bltz 'rs, 'imm16u");
break;
case BLTZAL:
- Format(instr, "bltzal 'rs, 'imm16u");
+ Format(instr, "bltzal 'rs, 'imm16u");
break;
case BGEZ:
- Format(instr, "bgez 'rs, 'imm16u");
+ Format(instr, "bgez 'rs, 'imm16u");
break;
case BGEZAL:
- Format(instr, "bgezal 'rs, 'imm16u");
+ Format(instr, "bgezal 'rs, 'imm16u");
break;
default:
UNREACHABLE();
@@ -802,90 +831,90 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
break; // Case REGIMM.
// ------------- Branch instructions.
case BEQ:
- Format(instr, "beq 'rs, 'rt, 'imm16u");
+ Format(instr, "beq 'rs, 'rt, 'imm16u");
break;
case BNE:
- Format(instr, "bne 'rs, 'rt, 'imm16u");
+ Format(instr, "bne 'rs, 'rt, 'imm16u");
break;
case BLEZ:
- Format(instr, "blez 'rs, 'imm16u");
+ Format(instr, "blez 'rs, 'imm16u");
break;
case BGTZ:
- Format(instr, "bgtz 'rs, 'imm16u");
+ Format(instr, "bgtz 'rs, 'imm16u");
break;
// ------------- Arithmetic instructions.
case ADDI:
- Format(instr, "addi 'rt, 'rs, 'imm16s");
+ Format(instr, "addi 'rt, 'rs, 'imm16s");
break;
case ADDIU:
- Format(instr, "addiu 'rt, 'rs, 'imm16s");
+ Format(instr, "addiu 'rt, 'rs, 'imm16s");
break;
case SLTI:
- Format(instr, "slti 'rt, 'rs, 'imm16s");
+ Format(instr, "slti 'rt, 'rs, 'imm16s");
break;
case SLTIU:
- Format(instr, "sltiu 'rt, 'rs, 'imm16u");
+ Format(instr, "sltiu 'rt, 'rs, 'imm16u");
break;
case ANDI:
- Format(instr, "andi 'rt, 'rs, 'imm16x");
+ Format(instr, "andi 'rt, 'rs, 'imm16x");
break;
case ORI:
- Format(instr, "ori 'rt, 'rs, 'imm16x");
+ Format(instr, "ori 'rt, 'rs, 'imm16x");
break;
case XORI:
- Format(instr, "xori 'rt, 'rs, 'imm16x");
+ Format(instr, "xori 'rt, 'rs, 'imm16x");
break;
case LUI:
- Format(instr, "lui 'rt, 'imm16x");
+ Format(instr, "lui 'rt, 'imm16x");
break;
// ------------- Memory instructions.
case LB:
- Format(instr, "lb 'rt, 'imm16s('rs)");
+ Format(instr, "lb 'rt, 'imm16s('rs)");
break;
case LH:
- Format(instr, "lh 'rt, 'imm16s('rs)");
+ Format(instr, "lh 'rt, 'imm16s('rs)");
break;
case LWL:
- Format(instr, "lwl 'rt, 'imm16s('rs)");
+ Format(instr, "lwl 'rt, 'imm16s('rs)");
break;
case LW:
- Format(instr, "lw 'rt, 'imm16s('rs)");
+ Format(instr, "lw 'rt, 'imm16s('rs)");
break;
case LBU:
- Format(instr, "lbu 'rt, 'imm16s('rs)");
+ Format(instr, "lbu 'rt, 'imm16s('rs)");
break;
case LHU:
- Format(instr, "lhu 'rt, 'imm16s('rs)");
+ Format(instr, "lhu 'rt, 'imm16s('rs)");
break;
case LWR:
- Format(instr, "lwr 'rt, 'imm16s('rs)");
+ Format(instr, "lwr 'rt, 'imm16s('rs)");
break;
case SB:
- Format(instr, "sb 'rt, 'imm16s('rs)");
+ Format(instr, "sb 'rt, 'imm16s('rs)");
break;
case SH:
- Format(instr, "sh 'rt, 'imm16s('rs)");
+ Format(instr, "sh 'rt, 'imm16s('rs)");
break;
case SWL:
- Format(instr, "swl 'rt, 'imm16s('rs)");
+ Format(instr, "swl 'rt, 'imm16s('rs)");
break;
case SW:
- Format(instr, "sw 'rt, 'imm16s('rs)");
+ Format(instr, "sw 'rt, 'imm16s('rs)");
break;
case SWR:
- Format(instr, "swr 'rt, 'imm16s('rs)");
+ Format(instr, "swr 'rt, 'imm16s('rs)");
break;
case LWC1:
- Format(instr, "lwc1 'ft, 'imm16s('rs)");
+ Format(instr, "lwc1 'ft, 'imm16s('rs)");
break;
case LDC1:
- Format(instr, "ldc1 'ft, 'imm16s('rs)");
+ Format(instr, "ldc1 'ft, 'imm16s('rs)");
break;
case SWC1:
- Format(instr, "swc1 'ft, 'imm16s('rs)");
+ Format(instr, "swc1 'ft, 'imm16s('rs)");
break;
case SDC1:
- Format(instr, "sdc1 'ft, 'imm16s('rs)");
+ Format(instr, "sdc1 'ft, 'imm16s('rs)");
break;
default:
UNREACHABLE();
@@ -897,10 +926,10 @@ void Decoder::DecodeTypeImmediate(Instruction* instr) {
void Decoder::DecodeTypeJump(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case J:
- Format(instr, "j 'imm26");
+ Format(instr, "j 'imm26");
break;
case JAL:
- Format(instr, "jal 'imm26");
+ Format(instr, "jal 'imm26");
break;
default:
UNREACHABLE();
@@ -909,7 +938,7 @@ void Decoder::DecodeTypeJump(Instruction* instr) {
// Disassemble the instruction at *instr_ptr into the output buffer.
-int Decoder::InstructionDecode(byte_* instr_ptr) {
+int Decoder::InstructionDecode(byte* instr_ptr) {
Instruction* instr = Instruction::At(instr_ptr);
// Print raw instruction bytes.
out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
@@ -944,15 +973,13 @@ int Decoder::InstructionDecode(byte_* instr_ptr) {
namespace disasm {
-using v8::internal::byte_;
-
-const char* NameConverter::NameOfAddress(byte_* addr) const {
+const char* NameConverter::NameOfAddress(byte* addr) const {
v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
return tmp_buffer_.start();
}
-const char* NameConverter::NameOfConstant(byte_* addr) const {
+const char* NameConverter::NameOfConstant(byte* addr) const {
return NameOfAddress(addr);
}
@@ -968,12 +995,12 @@ const char* NameConverter::NameOfXMMRegister(int reg) const {
const char* NameConverter::NameOfByteCPURegister(int reg) const {
- UNREACHABLE(); // MIPS does not have the concept of a byte register
+ UNREACHABLE(); // MIPS does not have the concept of a byte register.
return "nobytereg";
}
-const char* NameConverter::NameInCode(byte_* addr) const {
+const char* NameConverter::NameInCode(byte* addr) const {
// The default name converter is called for unknown code. So we will not try
// to access any memory.
return "";
@@ -990,25 +1017,25 @@ Disassembler::~Disassembler() {}
int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
- byte_* instruction) {
+ byte* instruction) {
v8::internal::Decoder d(converter_, buffer);
return d.InstructionDecode(instruction);
}
// The MIPS assembler does not currently use constant pools.
-int Disassembler::ConstantPoolSizeAt(byte_* instruction) {
+int Disassembler::ConstantPoolSizeAt(byte* instruction) {
return -1;
}
-void Disassembler::Disassemble(FILE* f, byte_* begin, byte_* end) {
+void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
NameConverter converter;
Disassembler d(converter);
- for (byte_* pc = begin; pc < end;) {
+ for (byte* pc = begin; pc < end;) {
v8::internal::EmbeddedVector<char, 128> buffer;
buffer[0] = '\0';
- byte_* prev_pc = pc;
+ byte* prev_pc = pc;
pc += d.InstructionDecode(buffer, pc);
fprintf(f, "%p %08x %s\n",
prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer.start());
diff --git a/src/mips/frames-mips.cc b/src/mips/frames-mips.cc
index e2e0c919..faaa0e0f 100644
--- a/src/mips/frames-mips.cc
+++ b/src/mips/frames-mips.cc
@@ -38,8 +38,7 @@ namespace internal {
Address ExitFrame::ComputeStackPointer(Address fp) {
- UNIMPLEMENTED_MIPS();
- return fp;
+ return Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
}
diff --git a/src/mips/frames-mips.h b/src/mips/frames-mips.h
index f507590b..2e720fb1 100644
--- a/src/mips/frames-mips.h
+++ b/src/mips/frames-mips.h
@@ -59,7 +59,7 @@ static const RegList kCalleeSaved =
// Saved temporaries.
1 << 16 | 1 << 17 | 1 << 18 | 1 << 19 |
1 << 20 | 1 << 21 | 1 << 22 | 1 << 23 |
- // gp, sp, fp
+ // gp, sp, fp.
1 << 28 | 1 << 29 | 1 << 30;
static const int kNumCalleeSaved = 11;
@@ -79,6 +79,43 @@ static const int kNumSafepointSavedRegisters =
typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
+static const int kUndefIndex = -1;
+// Map with indexes on stack that corresponds to codes of saved registers.
+static const int kSafepointRegisterStackIndexMap[kNumRegs] = {
+ kUndefIndex,
+ kUndefIndex,
+ 0, // v0
+ kUndefIndex,
+ 1, // a0
+ 2, // a1
+ 3, // a2
+ 4, // a3
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ 5, // Saved temporaries.
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ 13, // gp
+ 14, // sp
+ 15, // fp
+ kUndefIndex
+};
+
// ----------------------------------------------------
@@ -101,22 +138,24 @@ class EntryFrameConstants : public AllStatic {
class ExitFrameConstants : public AllStatic {
public:
- static const int kDebugMarkOffset = -1 * kPointerSize;
- // Must be the same as kDebugMarkOffset. Alias introduced when upgrading.
- static const int kCodeOffset = -1 * kPointerSize;
- static const int kSPOffset = -1 * kPointerSize;
+ // See some explanation in MacroAssembler::EnterExitFrame.
+ // This marks the top of the extra allocated stack space.
+ static const int kStackSpaceOffset = -3 * kPointerSize;
- // TODO(mips): Use a patched sp value on the stack instead.
- // A marker of 0 indicates that double registers are saved.
- static const int kMarkerOffset = -2 * kPointerSize;
+ static const int kCodeOffset = -2 * kPointerSize;
+
+ static const int kSPOffset = -1 * kPointerSize;
// The caller fields are below the frame pointer on the stack.
static const int kCallerFPOffset = +0 * kPointerSize;
// The calling JS function is between FP and PC.
static const int kCallerPCOffset = +1 * kPointerSize;
+ // MIPS-specific: a pointer to the old sp to avoid unnecessary calculations.
+ static const int kCallerSPOffset = +2 * kPointerSize;
+
// FP-relative displacement of the caller's SP.
- static const int kCallerSPDisplacement = +3 * kPointerSize;
+ static const int kCallerSPDisplacement = +2 * kPointerSize;
};
@@ -135,7 +174,8 @@ class StandardFrameConstants : public AllStatic {
static const int kRegularArgsSlotsSize = kRArgsSlotsSize;
// C/C++ argument slots size.
- static const int kCArgsSlotsSize = 4 * kPointerSize;
+ static const int kCArgSlotCount = 4;
+ static const int kCArgsSlotsSize = kCArgSlotCount * kPointerSize;
// JS argument slots size.
static const int kJSArgsSlotsSize = 0 * kPointerSize;
// Assembly builtins argument slots size.
diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc
index 87507ff1..9c93c633 100644
--- a/src/mips/full-codegen-mips.cc
+++ b/src/mips/full-codegen-mips.cc
@@ -38,7 +38,7 @@
// next call: mov(a0, v0). This is not needed on the other architectures.
#include "code-stubs.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "compiler.h"
#include "debug.h"
#include "full-codegen.h"
@@ -53,6 +53,73 @@ namespace internal {
#define __ ACCESS_MASM(masm_)
+
+static unsigned GetPropertyId(Property* property) {
+ if (property->is_synthetic()) return AstNode::kNoNumber;
+ return property->id();
+}
+
+
+// A patch site is a location in the code which it is possible to patch. This
+// class has a number of methods to emit the code which is patchable and the
+// method EmitPatchInfo to record a marker back to the patchable code. This
+// marker is a andi at, rx, #yyy instruction, and x * 0x0000ffff + yyy (raw 16
+// bit immediate value is used) is the delta from the pc to the first
+// instruction of the patchable code.
+class JumpPatchSite BASE_EMBEDDED {
+ public:
+ explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) {
+#ifdef DEBUG
+ info_emitted_ = false;
+#endif
+ }
+
+ ~JumpPatchSite() {
+ ASSERT(patch_site_.is_bound() == info_emitted_);
+ }
+
+ // When initially emitting this ensure that a jump is always generated to skip
+ // the inlined smi code.
+ void EmitJumpIfNotSmi(Register reg, Label* target) {
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ __ bind(&patch_site_);
+ __ andi(at, reg, 0);
+ // Always taken before patched.
+ __ Branch(target, eq, at, Operand(zero_reg));
+ }
+
+ // When initially emitting this ensure that a jump is never generated to skip
+ // the inlined smi code.
+ void EmitJumpIfSmi(Register reg, Label* target) {
+ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ ASSERT(!patch_site_.is_bound() && !info_emitted_);
+ __ bind(&patch_site_);
+ __ andi(at, reg, 0);
+ // Never taken before patched.
+ __ Branch(target, ne, at, Operand(zero_reg));
+ }
+
+ void EmitPatchInfo() {
+ int delta_to_patch_site = masm_->InstructionsGeneratedSince(&patch_site_);
+ Register reg = Register::from_code(delta_to_patch_site / kImm16Mask);
+ __ andi(at, reg, delta_to_patch_site % kImm16Mask);
+#ifdef DEBUG
+ info_emitted_ = true;
+#endif
+ }
+
+ bool is_bound() const { return patch_site_.is_bound(); }
+
+ private:
+ MacroAssembler* masm_;
+ Label patch_site_;
+#ifdef DEBUG
+ bool info_emitted_;
+#endif
+};
+
+
// Generate code for a JS function. On entry to the function the receiver
// and arguments have been pushed on the stack left to right. The actual
// argument count matches the formal parameter count expected by the
@@ -68,189 +135,510 @@ namespace internal {
// The function builds a JS frame. Please see JavaScriptFrameConstants in
// frames-mips.h for its layout.
void FullCodeGenerator::Generate(CompilationInfo* info) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(info_ == NULL);
+ info_ = info;
+ SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
+ __ stop("stop-at");
+ }
+#endif
+
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). t1 is zero for method calls and non-zero for function
+ // calls.
+ if (info->is_strict_mode()) {
+ Label ok;
+ __ Branch(&ok, eq, t1, Operand(zero_reg));
+ int receiver_offset = scope()->num_parameters() * kPointerSize;
+ __ LoadRoot(a2, Heap::kUndefinedValueRootIndex);
+ __ sw(a2, MemOperand(sp, receiver_offset));
+ __ bind(&ok);
+ }
+
+ int locals_count = scope()->num_stack_slots();
+
+ __ Push(ra, fp, cp, a1);
+ if (locals_count > 0) {
+ // Load undefined value here, so the value is ready for the loop
+ // below.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ }
+ // Adjust fp to point to caller's fp.
+ __ Addu(fp, sp, Operand(2 * kPointerSize));
+
+ { Comment cmnt(masm_, "[ Allocate locals");
+ for (int i = 0; i < locals_count; i++) {
+ __ push(at);
+ }
+ }
+
+ bool function_in_register = true;
+
+ // Possibly allocate a local context.
+ int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ if (heap_slots > 0) {
+ Comment cmnt(masm_, "[ Allocate local context");
+ // Argument to NewContext is the function, which is in a1.
+ __ push(a1);
+ if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ __ CallStub(&stub);
+ } else {
+ __ CallRuntime(Runtime::kNewContext, 1);
+ }
+ function_in_register = false;
+ // Context is returned in both v0 and cp. It replaces the context
+ // passed to us. It's saved in the stack and kept live in cp.
+ __ sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ // Copy any necessary parameters into the context.
+ int num_parameters = scope()->num_parameters();
+ for (int i = 0; i < num_parameters; i++) {
+ Slot* slot = scope()->parameter(i)->AsSlot();
+ if (slot != NULL && slot->type() == Slot::CONTEXT) {
+ int parameter_offset = StandardFrameConstants::kCallerSPOffset +
+ (num_parameters - 1 - i) * kPointerSize;
+ // Load parameter from stack.
+ __ lw(a0, MemOperand(fp, parameter_offset));
+ // Store it in the context.
+ __ li(a1, Operand(Context::SlotOffset(slot->index())));
+ __ addu(a2, cp, a1);
+ __ sw(a0, MemOperand(a2, 0));
+ // Update the write barrier. This clobbers all involved
+ // registers, so we have to use two more registers to avoid
+ // clobbering cp.
+ __ mov(a2, cp);
+ __ RecordWrite(a2, a1, a3);
+ }
+ }
+ }
+
+ Variable* arguments = scope()->arguments();
+ if (arguments != NULL) {
+ // Function uses arguments object.
+ Comment cmnt(masm_, "[ Allocate arguments object");
+ if (!function_in_register) {
+ // Load this again, if it's used by the local context below.
+ __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ } else {
+ __ mov(a3, a1);
+ }
+ // Receiver is just before the parameters on the caller's stack.
+ int offset = scope()->num_parameters() * kPointerSize;
+ __ Addu(a2, fp,
+ Operand(StandardFrameConstants::kCallerSPOffset + offset));
+ __ li(a1, Operand(Smi::FromInt(scope()->num_parameters())));
+ __ Push(a3, a2, a1);
+
+ // Arguments to ArgumentsAccessStub:
+ // function, receiver address, parameter count.
+ // The stub will rewrite receiever and parameter count if the previous
+ // stack frame was an arguments adapter frame.
+ ArgumentsAccessStub stub(
+ is_strict_mode() ? ArgumentsAccessStub::NEW_STRICT
+ : ArgumentsAccessStub::NEW_NON_STRICT);
+ __ CallStub(&stub);
+
+ Variable* arguments_shadow = scope()->arguments_shadow();
+ if (arguments_shadow != NULL) {
+ // Duplicate the value; move-to-slot operation might clobber registers.
+ __ mov(a3, v0);
+ Move(arguments_shadow->AsSlot(), a3, a1, a2);
+ }
+ Move(arguments->AsSlot(), v0, a1, a2);
+ }
+
+ if (FLAG_trace) {
+ __ CallRuntime(Runtime::kTraceEnter, 0);
+ }
+
+ // Visit the declarations and body unless there is an illegal
+ // redeclaration.
+ if (scope()->HasIllegalRedeclaration()) {
+ Comment cmnt(masm_, "[ Declarations");
+ scope()->VisitIllegalRedeclaration(this);
+
+ } else {
+ { Comment cmnt(masm_, "[ Declarations");
+ // For named function expressions, declare the function name as a
+ // constant.
+ if (scope()->is_function_scope() && scope()->function() != NULL) {
+ EmitDeclaration(scope()->function(), Variable::CONST, NULL);
+ }
+ VisitDeclarations(scope()->declarations());
+ }
+
+ { Comment cmnt(masm_, "[ Stack check");
+ PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS);
+ Label ok;
+ __ LoadRoot(t0, Heap::kStackLimitRootIndex);
+ __ Branch(&ok, hs, sp, Operand(t0));
+ StackCheckStub stub;
+ __ CallStub(&stub);
+ __ bind(&ok);
+ }
+
+ { Comment cmnt(masm_, "[ Body");
+ ASSERT(loop_depth() == 0);
+ VisitStatements(function()->body());
+ ASSERT(loop_depth() == 0);
+ }
+ }
+
+ // Always emit a 'return undefined' in case control fell off the end of
+ // the body.
+ { Comment cmnt(masm_, "[ return <undefined>;");
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ }
+ EmitReturnSequence();
}
void FullCodeGenerator::ClearAccumulator() {
- UNIMPLEMENTED_MIPS();
+ ASSERT(Smi::FromInt(0) == 0);
+ __ mov(v0, zero_reg);
}
void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ Stack check");
+ Label ok;
+ __ LoadRoot(t0, Heap::kStackLimitRootIndex);
+ __ Branch(&ok, hs, sp, Operand(t0));
+ StackCheckStub stub;
+ // Record a mapping of this PC offset to the OSR id. This is used to find
+ // the AST id from the unoptimized code in order to use it as a key into
+ // the deoptimization input data found in the optimized code.
+ RecordStackCheck(stmt->OsrEntryId());
+
+ __ CallStub(&stub);
+ __ bind(&ok);
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+ // Record a mapping of the OSR id to this PC. This is used if the OSR
+ // entry becomes the target of a bailout. We don't expect it to be, but
+ // we want it to work if it is.
+ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
}
void FullCodeGenerator::EmitReturnSequence() {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ Return sequence");
+ if (return_label_.is_bound()) {
+ __ Branch(&return_label_);
+ } else {
+ __ bind(&return_label_);
+ if (FLAG_trace) {
+ // Push the return value on the stack as the parameter.
+ // Runtime::TraceExit returns its parameter in v0.
+ __ push(v0);
+ __ CallRuntime(Runtime::kTraceExit, 1);
+ }
+
+#ifdef DEBUG
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+#endif
+ // Make sure that the constant pool is not emitted inside of the return
+ // sequence.
+ { Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
+ // Here we use masm_-> instead of the __ macro to avoid the code coverage
+ // tool from instrumenting as we rely on the code size here.
+ int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
+ CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
+ __ RecordJSReturn();
+ masm_->mov(sp, fp);
+ masm_->MultiPop(static_cast<RegList>(fp.bit() | ra.bit()));
+ masm_->Addu(sp, sp, Operand(sp_delta));
+ masm_->Jump(ra);
+ }
+
+#ifdef DEBUG
+ // Check that the size of the code used for returning is large enough
+ // for the debugger's requirements.
+ ASSERT(Assembler::kJSReturnSequenceInstructions <=
+ masm_->InstructionsGeneratedSince(&check_exit_codesize));
+#endif
+ }
}
void FullCodeGenerator::EffectContext::Plug(Slot* slot) const {
- UNIMPLEMENTED_MIPS();
}
void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const {
- UNIMPLEMENTED_MIPS();
+ codegen()->Move(result_register(), slot);
}
void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const {
- UNIMPLEMENTED_MIPS();
+ codegen()->Move(result_register(), slot);
+ __ push(result_register());
}
void FullCodeGenerator::TestContext::Plug(Slot* slot) const {
- UNIMPLEMENTED_MIPS();
+ // For simplicity we always test the accumulator register.
+ codegen()->Move(result_register(), slot);
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
+ codegen()->DoTest(true_label_, false_label_, fall_through_);
}
void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const {
- UNIMPLEMENTED_MIPS();
}
void FullCodeGenerator::AccumulatorValueContext::Plug(
Heap::RootListIndex index) const {
- UNIMPLEMENTED_MIPS();
+ __ LoadRoot(result_register(), index);
}
void FullCodeGenerator::StackValueContext::Plug(
Heap::RootListIndex index) const {
- UNIMPLEMENTED_MIPS();
+ __ LoadRoot(result_register(), index);
+ __ push(result_register());
}
void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const {
- UNIMPLEMENTED_MIPS();
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG,
+ true,
+ true_label_,
+ false_label_);
+ if (index == Heap::kUndefinedValueRootIndex ||
+ index == Heap::kNullValueRootIndex ||
+ index == Heap::kFalseValueRootIndex) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else if (index == Heap::kTrueValueRootIndex) {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ } else {
+ __ LoadRoot(result_register(), index);
+ codegen()->DoTest(true_label_, false_label_, fall_through_);
+ }
}
void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
- UNIMPLEMENTED_MIPS();
}
void FullCodeGenerator::AccumulatorValueContext::Plug(
Handle<Object> lit) const {
- UNIMPLEMENTED_MIPS();
+ __ li(result_register(), Operand(lit));
}
void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
- UNIMPLEMENTED_MIPS();
+ // Immediates cannot be pushed directly.
+ __ li(result_register(), Operand(lit));
+ __ push(result_register());
}
void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const {
- UNIMPLEMENTED_MIPS();
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG,
+ true,
+ true_label_,
+ false_label_);
+ ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals.
+ if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else if (lit->IsTrue() || lit->IsJSObject()) {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ } else if (lit->IsString()) {
+ if (String::cast(*lit)->length() == 0) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ }
+ } else if (lit->IsSmi()) {
+ if (Smi::cast(*lit)->value() == 0) {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ } else {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ }
+ } else {
+ // For simplicity we always test the accumulator register.
+ __ li(result_register(), Operand(lit));
+ codegen()->DoTest(true_label_, false_label_, fall_through_);
+ }
}
void FullCodeGenerator::EffectContext::DropAndPlug(int count,
Register reg) const {
- UNIMPLEMENTED_MIPS();
+ ASSERT(count > 0);
+ __ Drop(count);
}
void FullCodeGenerator::AccumulatorValueContext::DropAndPlug(
int count,
Register reg) const {
- UNIMPLEMENTED_MIPS();
+ ASSERT(count > 0);
+ __ Drop(count);
+ __ Move(result_register(), reg);
}
void FullCodeGenerator::StackValueContext::DropAndPlug(int count,
Register reg) const {
- UNIMPLEMENTED_MIPS();
+ ASSERT(count > 0);
+ if (count > 1) __ Drop(count - 1);
+ __ sw(reg, MemOperand(sp, 0));
}
void FullCodeGenerator::TestContext::DropAndPlug(int count,
Register reg) const {
- UNIMPLEMENTED_MIPS();
+ ASSERT(count > 0);
+ // For simplicity we always test the accumulator register.
+ __ Drop(count);
+ __ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
+ codegen()->DoTest(true_label_, false_label_, fall_through_);
}
void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
Label* materialize_false) const {
- UNIMPLEMENTED_MIPS();
+ ASSERT(materialize_true == materialize_false);
+ __ bind(materialize_true);
}
void FullCodeGenerator::AccumulatorValueContext::Plug(
Label* materialize_true,
Label* materialize_false) const {
- UNIMPLEMENTED_MIPS();
+ Label done;
+ __ bind(materialize_true);
+ __ LoadRoot(result_register(), Heap::kTrueValueRootIndex);
+ __ Branch(&done);
+ __ bind(materialize_false);
+ __ LoadRoot(result_register(), Heap::kFalseValueRootIndex);
+ __ bind(&done);
}
void FullCodeGenerator::StackValueContext::Plug(
Label* materialize_true,
Label* materialize_false) const {
- UNIMPLEMENTED_MIPS();
+ Label done;
+ __ bind(materialize_true);
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ __ push(at);
+ __ Branch(&done);
+ __ bind(materialize_false);
+ __ LoadRoot(at, Heap::kFalseValueRootIndex);
+ __ push(at);
+ __ bind(&done);
}
void FullCodeGenerator::TestContext::Plug(Label* materialize_true,
Label* materialize_false) const {
- UNIMPLEMENTED_MIPS();
+ ASSERT(materialize_true == true_label_);
+ ASSERT(materialize_false == false_label_);
}
void FullCodeGenerator::EffectContext::Plug(bool flag) const {
- UNIMPLEMENTED_MIPS();
}
void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const {
- UNIMPLEMENTED_MIPS();
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(result_register(), value_root_index);
}
void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
- UNIMPLEMENTED_MIPS();
+ Heap::RootListIndex value_root_index =
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
+ __ LoadRoot(at, value_root_index);
+ __ push(at);
}
void FullCodeGenerator::TestContext::Plug(bool flag) const {
- UNIMPLEMENTED_MIPS();
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG,
+ true,
+ true_label_,
+ false_label_);
+ if (flag) {
+ if (true_label_ != fall_through_) __ Branch(true_label_);
+ } else {
+ if (false_label_ != fall_through_) __ Branch(false_label_);
+ }
}
void FullCodeGenerator::DoTest(Label* if_true,
Label* if_false,
Label* fall_through) {
- UNIMPLEMENTED_MIPS();
+ if (CpuFeatures::IsSupported(FPU)) {
+ ToBooleanStub stub(result_register());
+ __ CallStub(&stub);
+ __ mov(at, zero_reg);
+ } else {
+ // Call the runtime to find the boolean value of the source and then
+ // translate it into control flow to the pair of labels.
+ __ push(result_register());
+ __ CallRuntime(Runtime::kToBool, 1);
+ __ LoadRoot(at, Heap::kFalseValueRootIndex);
+ }
+ Split(ne, v0, Operand(at), if_true, if_false, fall_through);
}
-// Original prototype for mips, needs arch-indep change. Leave out for now.
-// void FullCodeGenerator::Split(Condition cc,
-// Register lhs,
-// const Operand& rhs,
-// Label* if_true,
-// Label* if_false,
-// Label* fall_through) {
void FullCodeGenerator::Split(Condition cc,
+ Register lhs,
+ const Operand& rhs,
Label* if_true,
Label* if_false,
Label* fall_through) {
- UNIMPLEMENTED_MIPS();
+ if (if_false == fall_through) {
+ __ Branch(if_true, cc, lhs, rhs);
+ } else if (if_true == fall_through) {
+ __ Branch(if_false, NegateCondition(cc), lhs, rhs);
+ } else {
+ __ Branch(if_true, cc, lhs, rhs);
+ __ Branch(if_false);
+ }
}
MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ return MemOperand(fp, SlotOffset(slot));
+ case Slot::CONTEXT: {
+ int context_chain_length =
+ scope()->ContextChainLength(slot->var()->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return ContextOperand(scratch, slot->index());
+ }
+ case Slot::LOOKUP:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+ return MemOperand(v0, 0);
}
void FullCodeGenerator::Move(Register destination, Slot* source) {
- UNIMPLEMENTED_MIPS();
+ // Use destination as scratch.
+ MemOperand slot_operand = EmitSlotSearch(source, destination);
+ __ lw(destination, slot_operand);
}
@@ -258,7 +646,25 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state,
bool should_normalize,
Label* if_true,
Label* if_false) {
- UNIMPLEMENTED_MIPS();
+ // Only prepare for bailouts before splits if we're in a test
+ // context. Otherwise, we let the Visit function deal with the
+ // preparation to avoid preparing with the same AST id twice.
+ if (!context()->IsTest() || !info_->IsOptimizable()) return;
+
+ Label skip;
+ if (should_normalize) __ Branch(&skip);
+
+ ForwardBailoutStack* current = forward_bailout_stack_;
+ while (current != NULL) {
+ PrepareForBailout(current->expr(), state);
+ current = current->parent();
+ }
+
+ if (should_normalize) {
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ Split(eq, a0, Operand(t0), if_true, if_false, NULL);
+ __ bind(&skip);
+ }
}
@@ -266,391 +672,3375 @@ void FullCodeGenerator::Move(Slot* dst,
Register src,
Register scratch1,
Register scratch2) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented.
+ ASSERT(!scratch1.is(src) && !scratch2.is(src));
+ MemOperand location = EmitSlotSearch(dst, scratch1);
+ __ sw(src, location);
+ // Emit the write barrier code if the location is in the heap.
+ if (dst->type() == Slot::CONTEXT) {
+ __ RecordWrite(scratch1,
+ Operand(Context::SlotOffset(dst->index())),
+ scratch2,
+ src);
+ }
}
void FullCodeGenerator::EmitDeclaration(Variable* variable,
Variable::Mode mode,
FunctionLiteral* function) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ Declaration");
+ ASSERT(variable != NULL); // Must have been resolved.
+ Slot* slot = variable->AsSlot();
+ Property* prop = variable->AsProperty();
+
+ if (slot != NULL) {
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ if (mode == Variable::CONST) {
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ sw(t0, MemOperand(fp, SlotOffset(slot)));
+ } else if (function != NULL) {
+ VisitForAccumulatorValue(function);
+ __ sw(result_register(), MemOperand(fp, SlotOffset(slot)));
+ }
+ break;
+
+ case Slot::CONTEXT:
+ // We bypass the general EmitSlotSearch because we know more about
+ // this specific context.
+
+ // The variable in the decl always resides in the current function
+ // context.
+ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
+ if (FLAG_debug_code) {
+ // Check that we're not inside a 'with'.
+ __ lw(a1, ContextOperand(cp, Context::FCONTEXT_INDEX));
+ __ Check(eq, "Unexpected declaration in current context.",
+ a1, Operand(cp));
+ }
+ if (mode == Variable::CONST) {
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ sw(at, ContextOperand(cp, slot->index()));
+ // No write barrier since the_hole_value is in old space.
+ } else if (function != NULL) {
+ VisitForAccumulatorValue(function);
+ __ sw(result_register(), ContextOperand(cp, slot->index()));
+ int offset = Context::SlotOffset(slot->index());
+ // We know that we have written a function, which is not a smi.
+ __ mov(a1, cp);
+ __ RecordWrite(a1, Operand(offset), a2, result_register());
+ }
+ break;
+
+ case Slot::LOOKUP: {
+ __ li(a2, Operand(variable->name()));
+ // Declaration nodes are always introduced in one of two modes.
+ ASSERT(mode == Variable::VAR ||
+ mode == Variable::CONST);
+ PropertyAttributes attr =
+ (mode == Variable::VAR) ? NONE : READ_ONLY;
+ __ li(a1, Operand(Smi::FromInt(attr)));
+ // Push initial value, if any.
+ // Note: For variables we must not push an initial value (such as
+ // 'undefined') because we may have a (legal) redeclaration and we
+ // must not destroy the current value.
+ if (mode == Variable::CONST) {
+ __ LoadRoot(a0, Heap::kTheHoleValueRootIndex);
+ __ Push(cp, a2, a1, a0);
+ } else if (function != NULL) {
+ __ Push(cp, a2, a1);
+ // Push initial value for function declaration.
+ VisitForStackValue(function);
+ } else {
+ ASSERT(Smi::FromInt(0) == 0);
+ // No initial value!
+ __ mov(a0, zero_reg); // Operand(Smi::FromInt(0)));
+ __ Push(cp, a2, a1, a0);
+ }
+ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
+ break;
+ }
+ }
+
+ } else if (prop != NULL) {
+ // A const declaration aliasing a parameter is an illegal redeclaration.
+ ASSERT(mode != Variable::CONST);
+ if (function != NULL) {
+ // We are declaring a function that rewrites to a property.
+ // Use (keyed) IC to set the initial value. We cannot visit the
+ // rewrite because it's shared and we risk recording duplicate AST
+ // IDs for bailouts from optimized code.
+ ASSERT(prop->obj()->AsVariableProxy() != NULL);
+ { AccumulatorValueContext for_object(this);
+ EmitVariableLoad(prop->obj()->AsVariableProxy()->var());
+ }
+
+ __ push(result_register());
+ VisitForAccumulatorValue(function);
+ __ mov(a0, result_register());
+ __ pop(a2);
+
+ ASSERT(prop->key()->AsLiteral() != NULL &&
+ prop->key()->AsLiteral()->handle()->IsSmi());
+ __ li(a1, Operand(prop->key()->AsLiteral()->handle()));
+
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
+ : isolate()->builtins()->KeyedStoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
+ // Value in v0 is ignored (declarations are statements).
+ }
+ }
}
void FullCodeGenerator::VisitDeclaration(Declaration* decl) {
- UNIMPLEMENTED_MIPS();
+ EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun());
}
void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
- UNIMPLEMENTED_MIPS();
+ // Call the runtime to declare the globals.
+ // The context is the first argument.
+ __ li(a2, Operand(pairs));
+ __ li(a1, Operand(Smi::FromInt(is_eval() ? 1 : 0)));
+ __ li(a0, Operand(Smi::FromInt(strict_mode_flag())));
+ __ Push(cp, a2, a1, a0);
+ __ CallRuntime(Runtime::kDeclareGlobals, 4);
+ // Return value is ignored.
}
void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ SwitchStatement");
+ Breakable nested_statement(this, stmt);
+ SetStatementPosition(stmt);
+
+ // Keep the switch value on the stack until a case matches.
+ VisitForStackValue(stmt->tag());
+ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
+
+ ZoneList<CaseClause*>* clauses = stmt->cases();
+ CaseClause* default_clause = NULL; // Can occur anywhere in the list.
+
+ Label next_test; // Recycled for each test.
+ // Compile all the tests with branches to their bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ CaseClause* clause = clauses->at(i);
+ clause->body_target()->Unuse();
+
+ // The default is not a test, but remember it as final fall through.
+ if (clause->is_default()) {
+ default_clause = clause;
+ continue;
+ }
+
+ Comment cmnt(masm_, "[ Case comparison");
+ __ bind(&next_test);
+ next_test.Unuse();
+
+ // Compile the label expression.
+ VisitForAccumulatorValue(clause->label());
+ __ mov(a0, result_register()); // CompareStub requires args in a0, a1.
+
+ // Perform the comparison as if via '==='.
+ __ lw(a1, MemOperand(sp, 0)); // Switch value.
+ bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ or_(a2, a1, a0);
+ patch_site.EmitJumpIfNotSmi(a2, &slow_case);
+
+ __ Branch(&next_test, ne, a1, Operand(a0));
+ __ Drop(1); // Switch value is no longer needed.
+ __ Branch(clause->body_target());
+
+ __ bind(&slow_case);
+ }
+
+ // Record position before stub call for type feedback.
+ SetSourcePosition(clause->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT);
+ EmitCallIC(ic, &patch_site, clause->CompareId());
+
+ __ Branch(&next_test, ne, v0, Operand(zero_reg));
+ __ Drop(1); // Switch value is no longer needed.
+ __ Branch(clause->body_target());
+ }
+
+ // Discard the test value and jump to the default if present, otherwise to
+ // the end of the statement.
+ __ bind(&next_test);
+ __ Drop(1); // Switch value is no longer needed.
+ if (default_clause == NULL) {
+ __ Branch(nested_statement.break_target());
+ } else {
+ __ Branch(default_clause->body_target());
+ }
+
+ // Compile all the case bodies.
+ for (int i = 0; i < clauses->length(); i++) {
+ Comment cmnt(masm_, "[ Case body");
+ CaseClause* clause = clauses->at(i);
+ __ bind(clause->body_target());
+ PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
+ VisitStatements(clause->statements());
+ }
+
+ __ bind(nested_statement.break_target());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
}
void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ ForInStatement");
+ SetStatementPosition(stmt);
+
+ Label loop, exit;
+ ForIn loop_statement(this, stmt);
+ increment_loop_depth();
+
+ // Get the object to enumerate over. Both SpiderMonkey and JSC
+ // ignore null and undefined in contrast to the specification; see
+ // ECMA-262 section 12.6.4.
+ VisitForAccumulatorValue(stmt->enumerable());
+ __ mov(a0, result_register()); // Result as param to InvokeBuiltin below.
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&exit, eq, a0, Operand(at));
+ Register null_value = t1;
+ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
+ __ Branch(&exit, eq, a0, Operand(null_value));
+
+ // Convert the object to a JS object.
+ Label convert, done_convert;
+ __ JumpIfSmi(a0, &convert);
+ __ GetObjectType(a0, a1, a1);
+ __ Branch(&done_convert, hs, a1, Operand(FIRST_JS_OBJECT_TYPE));
+ __ bind(&convert);
+ __ push(a0);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ mov(a0, v0);
+ __ bind(&done_convert);
+ __ push(a0);
+
+ // Check cache validity in generated code. This is a fast case for
+ // the JSObject::IsSimpleEnum cache validity checks. If we cannot
+ // guarantee cache validity, call the runtime system to check cache
+ // validity or get the property names in a fixed array.
+ Label next, call_runtime;
+ // Preload a couple of values used in the loop.
+ Register empty_fixed_array_value = t2;
+ __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
+ Register empty_descriptor_array_value = t3;
+ __ LoadRoot(empty_descriptor_array_value,
+ Heap::kEmptyDescriptorArrayRootIndex);
+ __ mov(a1, a0);
+ __ bind(&next);
+
+ // Check that there are no elements. Register a1 contains the
+ // current JS object we've reached through the prototype chain.
+ __ lw(a2, FieldMemOperand(a1, JSObject::kElementsOffset));
+ __ Branch(&call_runtime, ne, a2, Operand(empty_fixed_array_value));
+
+ // Check that instance descriptors are not empty so that we can
+ // check for an enum cache. Leave the map in a2 for the subsequent
+ // prototype load.
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lw(a3, FieldMemOperand(a2, Map::kInstanceDescriptorsOrBitField3Offset));
+ __ JumpIfSmi(a3, &call_runtime);
+
+ // Check that there is an enum cache in the non-empty instance
+ // descriptors (a3). This is the case if the next enumeration
+ // index field does not contain a smi.
+ __ lw(a3, FieldMemOperand(a3, DescriptorArray::kEnumerationIndexOffset));
+ __ JumpIfSmi(a3, &call_runtime);
+
+ // For all objects but the receiver, check that the cache is empty.
+ Label check_prototype;
+ __ Branch(&check_prototype, eq, a1, Operand(a0));
+ __ lw(a3, FieldMemOperand(a3, DescriptorArray::kEnumCacheBridgeCacheOffset));
+ __ Branch(&call_runtime, ne, a3, Operand(empty_fixed_array_value));
+
+ // Load the prototype from the map and loop if non-null.
+ __ bind(&check_prototype);
+ __ lw(a1, FieldMemOperand(a2, Map::kPrototypeOffset));
+ __ Branch(&next, ne, a1, Operand(null_value));
+
+ // The enum cache is valid. Load the map of the object being
+ // iterated over and use the cache for the iteration.
+ Label use_cache;
+ __ lw(v0, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ Branch(&use_cache);
+
+ // Get the set of properties to enumerate.
+ __ bind(&call_runtime);
+ __ push(a0); // Duplicate the enumerable object on the stack.
+ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
+
+ // If we got a map from the runtime call, we can do a fast
+ // modification check. Otherwise, we got a fixed array, and we have
+ // to do a slow check.
+ Label fixed_array;
+ __ mov(a2, v0);
+ __ lw(a1, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kMetaMapRootIndex);
+ __ Branch(&fixed_array, ne, a1, Operand(at));
+
+ // We got a map in register v0. Get the enumeration cache from it.
+ __ bind(&use_cache);
+ __ LoadInstanceDescriptors(v0, a1);
+ __ lw(a1, FieldMemOperand(a1, DescriptorArray::kEnumerationIndexOffset));
+ __ lw(a2, FieldMemOperand(a1, DescriptorArray::kEnumCacheBridgeCacheOffset));
+
+ // Setup the four remaining stack slots.
+ __ push(v0); // Map.
+ __ lw(a1, FieldMemOperand(a2, FixedArray::kLengthOffset));
+ __ li(a0, Operand(Smi::FromInt(0)));
+ // Push enumeration cache, enumeration cache length (as smi) and zero.
+ __ Push(a2, a1, a0);
+ __ jmp(&loop);
+
+ // We got a fixed array in register v0. Iterate through that.
+ __ bind(&fixed_array);
+ __ li(a1, Operand(Smi::FromInt(0))); // Map (0) - force slow check.
+ __ Push(a1, v0);
+ __ lw(a1, FieldMemOperand(v0, FixedArray::kLengthOffset));
+ __ li(a0, Operand(Smi::FromInt(0)));
+ __ Push(a1, a0); // Fixed array length (as smi) and initial index.
+
+ // Generate code for doing the condition check.
+ __ bind(&loop);
+ // Load the current count to a0, load the length to a1.
+ __ lw(a0, MemOperand(sp, 0 * kPointerSize));
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+ __ Branch(loop_statement.break_target(), hs, a0, Operand(a1));
+
+ // Get the current entry of the array into register a3.
+ __ lw(a2, MemOperand(sp, 2 * kPointerSize));
+ __ Addu(a2, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(t0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(t0, a2, t0); // Array base + scaled (smi) index.
+ __ lw(a3, MemOperand(t0)); // Current entry.
+
+ // Get the expected map from the stack or a zero map in the
+ // permanent slow case into register a2.
+ __ lw(a2, MemOperand(sp, 3 * kPointerSize));
+
+ // Check if the expected map still matches that of the enumerable.
+ // If not, we have to filter the key.
+ Label update_each;
+ __ lw(a1, MemOperand(sp, 4 * kPointerSize));
+ __ lw(t0, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ Branch(&update_each, eq, t0, Operand(a2));
+
+ // Convert the entry to a string or (smi) 0 if it isn't a property
+ // any more. If the property has been removed while iterating, we
+ // just skip it.
+ __ push(a1); // Enumerable.
+ __ push(a3); // Current entry.
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
+ __ mov(a3, result_register());
+ __ Branch(loop_statement.continue_target(), eq, a3, Operand(zero_reg));
+
+ // Update the 'each' property or variable from the possibly filtered
+ // entry in register a3.
+ __ bind(&update_each);
+ __ mov(result_register(), a3);
+ // Perform the assignment as if via '='.
+ { EffectContext context(this);
+ EmitAssignment(stmt->each(), stmt->AssignmentId());
+ }
+
+ // Generate code for the body of the loop.
+ Visit(stmt->body());
+
+ // Generate code for the going to the next element by incrementing
+ // the index (smi) stored on top of the stack.
+ __ bind(loop_statement.continue_target());
+ __ pop(a0);
+ __ Addu(a0, a0, Operand(Smi::FromInt(1)));
+ __ push(a0);
+
+ EmitStackCheck(stmt);
+ __ Branch(&loop);
+
+ // Remove the pointers stored on the stack.
+ __ bind(loop_statement.break_target());
+ __ Drop(5);
+
+ // Exit and decrement the loop depth.
+ __ bind(&exit);
+ decrement_loop_depth();
}
void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
bool pretenure) {
- UNIMPLEMENTED_MIPS();
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning. If
+ // we're running with the --always-opt or the --prepare-always-opt
+ // flag, we need to use the runtime function so that the new function
+ // we are creating here gets a chance to have its code optimized and
+ // doesn't just get a copy of the existing unoptimized code.
+ if (!FLAG_always_opt &&
+ !FLAG_prepare_always_opt &&
+ !pretenure &&
+ scope()->is_function_scope() &&
+ info->num_literals() == 0) {
+ FastNewClosureStub stub(info->strict_mode() ? kStrictMode : kNonStrictMode);
+ __ li(a0, Operand(info));
+ __ push(a0);
+ __ CallStub(&stub);
+ } else {
+ __ li(a0, Operand(info));
+ __ LoadRoot(a1, pretenure ? Heap::kTrueValueRootIndex
+ : Heap::kFalseValueRootIndex);
+ __ Push(cp, a0, a1);
+ __ CallRuntime(Runtime::kNewClosure, 3);
+ }
+ context()->Plug(v0);
}
void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ VariableProxy");
+ EmitVariableLoad(expr->var());
}
-MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
Slot* slot,
+ TypeofState typeof_state,
Label* slow) {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
+ Register current = cp;
+ Register next = a1;
+ Register temp = a2;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ lw(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+ __ Branch(slow, ne, temp, Operand(zero_reg));
+ }
+ // Load next context in chain.
+ __ lw(next, ContextOperand(current, Context::CLOSURE_INDEX));
+ __ lw(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ // Walk the rest of the chain without clobbering cp.
+ current = next;
+ }
+ // If no outer scope calls eval, we do not need to check more
+ // context extensions.
+ if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s->is_eval_scope()) {
+ Label loop, fast;
+ if (!current.is(next)) {
+ __ Move(next, current);
+ }
+ __ bind(&loop);
+ // Terminate at global context.
+ __ lw(temp, FieldMemOperand(next, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kGlobalContextMapRootIndex);
+ __ Branch(&fast, eq, temp, Operand(t0));
+ // Check that extension is NULL.
+ __ lw(temp, ContextOperand(next, Context::EXTENSION_INDEX));
+ __ Branch(slow, ne, temp, Operand(zero_reg));
+ // Load next context in chain.
+ __ lw(next, ContextOperand(next, Context::CLOSURE_INDEX));
+ __ lw(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ __ Branch(&loop);
+ __ bind(&fast);
+ }
+
+ __ lw(a0, GlobalObjectOperand());
+ __ li(a2, Operand(slot->var()->name()));
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ EmitCallIC(ic, mode, AstNode::kNoNumber);
}
-void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
Slot* slot,
- TypeofState typeof_state,
- Label* slow,
- Label* done) {
- UNIMPLEMENTED_MIPS();
+ Label* slow) {
+ ASSERT(slot->type() == Slot::CONTEXT);
+ Register context = cp;
+ Register next = a3;
+ Register temp = t0;
+
+ for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ lw(temp, ContextOperand(context, Context::EXTENSION_INDEX));
+ __ Branch(slow, ne, temp, Operand(zero_reg));
+ }
+ __ lw(next, ContextOperand(context, Context::CLOSURE_INDEX));
+ __ lw(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ // Walk the rest of the chain without clobbering cp.
+ context = next;
+ }
+ }
+ // Check that last extension is NULL.
+ __ lw(temp, ContextOperand(context, Context::EXTENSION_INDEX));
+ __ Branch(slow, ne, temp, Operand(zero_reg));
+
+ // This function is used only for loads, not stores, so it's safe to
+ // return an cp-based operand (the write barrier cannot be allowed to
+ // destroy the cp register).
+ return ContextOperand(context, slot->index());
}
-void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
+void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
Slot* slot,
TypeofState typeof_state,
- Label* slow) {
- UNIMPLEMENTED_MIPS();
+ Label* slow,
+ Label* done) {
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // introducing variables. In those cases, we do not want to
+ // perform a runtime call for all variables in the scope
+ // containing the eval.
+ if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
+ EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow);
+ __ Branch(done);
+ } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
+ Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot();
+ Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
+ if (potential_slot != NULL) {
+ // Generate fast case for locals that rewrite to slots.
+ __ lw(v0, ContextSlotOperandCheckExtensions(potential_slot, slow));
+ if (potential_slot->var()->mode() == Variable::CONST) {
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ subu(at, v0, at); // Sub as compare: at == 0 on eq.
+ __ LoadRoot(a0, Heap::kUndefinedValueRootIndex);
+ __ movz(v0, a0, at); // Conditional move.
+ }
+ __ Branch(done);
+ } else if (rewrite != NULL) {
+ // Generate fast case for calls of an argument function.
+ Property* property = rewrite->AsProperty();
+ if (property != NULL) {
+ VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+ Literal* key_literal = property->key()->AsLiteral();
+ if (obj_proxy != NULL &&
+ key_literal != NULL &&
+ obj_proxy->IsArguments() &&
+ key_literal->handle()->IsSmi()) {
+ // Load arguments object if there are no eval-introduced
+ // variables. Then load the argument from the arguments
+ // object using keyed load.
+ __ lw(a1,
+ ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(),
+ slow));
+ __ li(a0, Operand(key_literal->handle()));
+ Handle<Code> ic =
+ isolate()->builtins()->KeyedLoadIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
+ __ Branch(done);
+ }
+ }
+ }
+ }
}
void FullCodeGenerator::EmitVariableLoad(Variable* var) {
- UNIMPLEMENTED_MIPS();
+ // Four cases: non-this global variables, lookup slots, all other
+ // types of slots, and parameters that rewrite to explicit property
+ // accesses on the arguments object.
+ Slot* slot = var->AsSlot();
+ Property* property = var->AsProperty();
+
+ if (var->is_global() && !var->is_this()) {
+ Comment cmnt(masm_, "Global variable");
+ // Use inline caching. Variable name is passed in a2 and the global
+ // object (receiver) in a0.
+ __ lw(a0, GlobalObjectOperand());
+ __ li(a2, Operand(var->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
+ context()->Plug(v0);
+
+ } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
+ Comment cmnt(masm_, "Lookup slot");
+ __ li(a1, Operand(var->name()));
+ __ Push(cp, a1); // Context and name.
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ bind(&done);
+
+ context()->Plug(v0);
+
+ } else if (slot != NULL) {
+ Comment cmnt(masm_, (slot->type() == Slot::CONTEXT)
+ ? "Context slot"
+ : "Stack slot");
+ if (var->mode() == Variable::CONST) {
+ // Constants may be the hole value if they have not been initialized.
+ // Unhole them.
+ MemOperand slot_operand = EmitSlotSearch(slot, a0);
+ __ lw(v0, slot_operand);
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ subu(at, v0, at); // Sub as compare: at == 0 on eq.
+ __ LoadRoot(a0, Heap::kUndefinedValueRootIndex);
+ __ movz(v0, a0, at); // Conditional move.
+ context()->Plug(v0);
+ } else {
+ context()->Plug(slot);
+ }
+ } else {
+ Comment cmnt(masm_, "Rewritten parameter");
+ ASSERT_NOT_NULL(property);
+ // Rewritten parameter accesses are of the form "slot[literal]".
+ // Assert that the object is in a slot.
+ Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
+ ASSERT_NOT_NULL(object_var);
+ Slot* object_slot = object_var->AsSlot();
+ ASSERT_NOT_NULL(object_slot);
+
+ // Load the object.
+ Move(a1, object_slot);
+
+ // Assert that the key is a smi.
+ Literal* key_literal = property->key()->AsLiteral();
+ ASSERT_NOT_NULL(key_literal);
+ ASSERT(key_literal->handle()->IsSmi());
+
+ // Load the key.
+ __ li(a0, Operand(key_literal->handle()));
+
+ // Call keyed load IC. It has arguments key and receiver in a0 and a1.
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
+ context()->Plug(v0);
+ }
}
void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ RegExpLiteral");
+ Label materialized;
+ // Registers will be used as follows:
+ // t1 = materialized value (RegExp literal)
+ // t0 = JS function, literals array
+ // a3 = literal index
+ // a2 = RegExp pattern
+ // a1 = RegExp flags
+ // a0 = RegExp literal clone
+ __ lw(a0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ lw(t0, FieldMemOperand(a0, JSFunction::kLiteralsOffset));
+ int literal_offset =
+ FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
+ __ lw(t1, FieldMemOperand(t0, literal_offset));
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(&materialized, ne, t1, Operand(at));
+
+ // Create regexp literal using runtime function.
+ // Result will be in v0.
+ __ li(a3, Operand(Smi::FromInt(expr->literal_index())));
+ __ li(a2, Operand(expr->pattern()));
+ __ li(a1, Operand(expr->flags()));
+ __ Push(t0, a3, a2, a1);
+ __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
+ __ mov(t1, v0);
+
+ __ bind(&materialized);
+ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
+ Label allocated, runtime_allocate;
+ __ AllocateInNewSpace(size, v0, a2, a3, &runtime_allocate, TAG_OBJECT);
+ __ jmp(&allocated);
+
+ __ bind(&runtime_allocate);
+ __ push(t1);
+ __ li(a0, Operand(Smi::FromInt(size)));
+ __ push(a0);
+ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
+ __ pop(t1);
+
+ __ bind(&allocated);
+
+ // After this, registers are used as follows:
+ // v0: Newly allocated regexp.
+ // t1: Materialized regexp.
+ // a2: temp.
+ __ CopyFields(v0, t1, a2.bit(), size / kPointerSize);
+ context()->Plug(v0);
}
void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ ObjectLiteral");
+ __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ lw(a3, FieldMemOperand(a3, JSFunction::kLiteralsOffset));
+ __ li(a2, Operand(Smi::FromInt(expr->literal_index())));
+ __ li(a1, Operand(expr->constant_properties()));
+ int flags = expr->fast_elements()
+ ? ObjectLiteral::kFastElements
+ : ObjectLiteral::kNoFlags;
+ flags |= expr->has_function()
+ ? ObjectLiteral::kHasFunction
+ : ObjectLiteral::kNoFlags;
+ __ li(a0, Operand(Smi::FromInt(flags)));
+ __ Push(a3, a2, a1, a0);
+ if (expr->depth() > 1) {
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
+ } else {
+ __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
+ }
+
+ // If result_saved is true the result is on top of the stack. If
+ // result_saved is false the result is in v0.
+ bool result_saved = false;
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore();
+
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ ObjectLiteral::Property* property = expr->properties()->at(i);
+ if (property->IsCompileTimeValue()) continue;
+
+ Literal* key = property->key();
+ Expression* value = property->value();
+ if (!result_saved) {
+ __ push(v0); // Save result on stack.
+ result_saved = true;
+ }
+ switch (property->kind()) {
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
+ ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value()));
+ // Fall through.
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->handle()->IsSymbol()) {
+ if (property->emit_store()) {
+ VisitForAccumulatorValue(value);
+ __ mov(a0, result_register());
+ __ li(a2, Operand(key->handle()));
+ __ lw(a1, MemOperand(sp));
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, key->id());
+ PrepareForBailoutForId(key->id(), NO_REGISTERS);
+ } else {
+ VisitForEffect(value);
+ }
+ break;
+ }
+ // Fall through.
+ case ObjectLiteral::Property::PROTOTYPE:
+ // Duplicate receiver on stack.
+ __ lw(a0, MemOperand(sp));
+ __ push(a0);
+ VisitForStackValue(key);
+ VisitForStackValue(value);
+ if (property->emit_store()) {
+ __ li(a0, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
+ __ push(a0);
+ __ CallRuntime(Runtime::kSetProperty, 4);
+ } else {
+ __ Drop(3);
+ }
+ break;
+ case ObjectLiteral::Property::GETTER:
+ case ObjectLiteral::Property::SETTER:
+ // Duplicate receiver on stack.
+ __ lw(a0, MemOperand(sp));
+ __ push(a0);
+ VisitForStackValue(key);
+ __ li(a1, Operand(property->kind() == ObjectLiteral::Property::SETTER ?
+ Smi::FromInt(1) :
+ Smi::FromInt(0)));
+ __ push(a1);
+ VisitForStackValue(value);
+ __ CallRuntime(Runtime::kDefineAccessor, 4);
+ break;
+ }
+ }
+
+ if (expr->has_function()) {
+ ASSERT(result_saved);
+ __ lw(a0, MemOperand(sp));
+ __ push(a0);
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ }
+
+ if (result_saved) {
+ context()->PlugTOS();
+ } else {
+ context()->Plug(v0);
+ }
}
void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ ArrayLiteral");
+
+ ZoneList<Expression*>* subexprs = expr->values();
+ int length = subexprs->length();
+ __ mov(a0, result_register());
+ __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ lw(a3, FieldMemOperand(a3, JSFunction::kLiteralsOffset));
+ __ li(a2, Operand(Smi::FromInt(expr->literal_index())));
+ __ li(a1, Operand(expr->constant_elements()));
+ __ Push(a3, a2, a1);
+ if (expr->constant_elements()->map() ==
+ isolate()->heap()->fixed_cow_array_map()) {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ __ CallStub(&stub);
+ __ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(),
+ 1, a1, a2);
+ } else if (expr->depth() > 1) {
+ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
+ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ FastCloneShallowArrayStub stub(
+ FastCloneShallowArrayStub::CLONE_ELEMENTS, length);
+ __ CallStub(&stub);
+ }
+
+ bool result_saved = false; // Is the result saved to the stack?
+
+ // Emit code to evaluate all the non-constant subexpressions and to store
+ // them into the newly cloned array.
+ for (int i = 0; i < length; i++) {
+ Expression* subexpr = subexprs->at(i);
+ // If the subexpression is a literal or a simple materialized literal it
+ // is already set in the cloned array.
+ if (subexpr->AsLiteral() != NULL ||
+ CompileTimeValue::IsCompileTimeValue(subexpr)) {
+ continue;
+ }
+
+ if (!result_saved) {
+ __ push(v0);
+ result_saved = true;
+ }
+ VisitForAccumulatorValue(subexpr);
+
+ // Store the subexpression value in the array's elements.
+ __ lw(a1, MemOperand(sp)); // Copy of array literal.
+ __ lw(a1, FieldMemOperand(a1, JSObject::kElementsOffset));
+ int offset = FixedArray::kHeaderSize + (i * kPointerSize);
+ __ sw(result_register(), FieldMemOperand(a1, offset));
+
+ // Update the write barrier for the array store with v0 as the scratch
+ // register.
+ __ li(a2, Operand(offset));
+ // TODO(PJ): double check this RecordWrite call.
+ __ RecordWrite(a1, a2, result_register());
+
+ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
+ }
+
+ if (result_saved) {
+ context()->PlugTOS();
+ } else {
+ context()->Plug(v0);
+ }
}
void FullCodeGenerator::VisitAssignment(Assignment* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ Assignment");
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // on the left-hand side.
+ if (!expr->target()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->target());
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* property = expr->target()->AsProperty();
+ if (property != NULL) {
+ assign_type = (property->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForAccumulatorValue(property->obj());
+ __ push(result_register());
+ } else {
+ VisitForStackValue(property->obj());
+ }
+ break;
+ case KEYED_PROPERTY:
+ // We need the key and receiver on both the stack and in v0 and a1.
+ if (expr->is_compound()) {
+ if (property->is_arguments_access()) {
+ VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+ __ lw(v0, EmitSlotSearch(obj_proxy->var()->AsSlot(), v0));
+ __ push(v0);
+ __ li(v0, Operand(property->key()->AsLiteral()->handle()));
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForAccumulatorValue(property->key());
+ }
+ __ lw(a1, MemOperand(sp, 0));
+ __ push(v0);
+ } else {
+ if (property->is_arguments_access()) {
+ VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+ __ lw(a1, EmitSlotSearch(obj_proxy->var()->AsSlot(), v0));
+ __ li(v0, Operand(property->key()->AsLiteral()->handle()));
+ __ Push(a1, v0);
+ } else {
+ VisitForStackValue(property->obj());
+ VisitForStackValue(property->key());
+ }
+ }
+ break;
+ }
+
+ // For compound assignments we need another deoptimization point after the
+ // variable/property load.
+ if (expr->is_compound()) {
+ { AccumulatorValueContext context(this);
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy()->var());
+ PrepareForBailout(expr->target(), TOS_REG);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(property);
+ PrepareForBailoutForId(expr->CompoundLoadId(), TOS_REG);
+ break;
+ }
+ }
+
+ Token::Value op = expr->binary_op();
+ __ push(v0); // Left operand goes on the stack.
+ VisitForAccumulatorValue(expr->value());
+
+ OverwriteMode mode = expr->value()->ResultOverwriteAllowed()
+ ? OVERWRITE_RIGHT
+ : NO_OVERWRITE;
+ SetSourcePosition(expr->position() + 1);
+ AccumulatorValueContext context(this);
+ if (ShouldInlineSmiCase(op)) {
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
+ op,
+ mode,
+ expr->target(),
+ expr->value());
+ } else {
+ EmitBinaryOp(expr->binary_operation(), op, mode);
+ }
+
+ // Deoptimization point in case the binary operation may have side effects.
+ PrepareForBailout(expr->binary_operation(), TOS_REG);
+ } else {
+ VisitForAccumulatorValue(expr->value());
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ expr->op());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
}
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
- UNIMPLEMENTED_MIPS();
+ SetSourcePosition(prop->position());
+ Literal* key = prop->key()->AsLiteral();
+ __ mov(a0, result_register());
+ __ li(a2, Operand(key->handle()));
+ // Call load IC. It has arguments receiver and property name a0 and a2.
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
- UNIMPLEMENTED_MIPS();
+ SetSourcePosition(prop->position());
+ __ mov(a0, result_register());
+ // Call keyed load IC. It has arguments key and receiver in a0 and a1.
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
-void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
- Expression* left,
- Expression* right) {
- UNIMPLEMENTED_MIPS();
+ Expression* left_expr,
+ Expression* right_expr) {
+ Label done, smi_case, stub_call;
+
+ Register scratch1 = a2;
+ Register scratch2 = a3;
+
+ // Get the arguments.
+ Register left = a1;
+ Register right = a0;
+ __ pop(left);
+ __ mov(a0, result_register());
+
+ // Perform combined smi check on both operands.
+ __ Or(scratch1, left, Operand(right));
+ STATIC_ASSERT(kSmiTag == 0);
+ JumpPatchSite patch_site(masm_);
+ patch_site.EmitJumpIfSmi(scratch1, &smi_case);
+
+ __ bind(&stub_call);
+ BinaryOpStub stub(op, mode);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->id());
+ __ jmp(&done);
+
+ __ bind(&smi_case);
+ // Smi case. This code works the same way as the smi-smi case in the type
+ // recording binary operation stub, see
+ // BinaryOpStub::GenerateSmiSmiOperation for comments.
+ switch (op) {
+ case Token::SAR:
+ __ Branch(&stub_call);
+ __ GetLeastBitsFromSmi(scratch1, right, 5);
+ __ srav(right, left, scratch1);
+ __ And(v0, right, Operand(~kSmiTagMask));
+ break;
+ case Token::SHL: {
+ __ Branch(&stub_call);
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ sllv(scratch1, scratch1, scratch2);
+ __ Addu(scratch2, scratch1, Operand(0x40000000));
+ __ Branch(&stub_call, lt, scratch2, Operand(zero_reg));
+ __ SmiTag(v0, scratch1);
+ break;
+ }
+ case Token::SHR: {
+ __ Branch(&stub_call);
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ srlv(scratch1, scratch1, scratch2);
+ __ And(scratch2, scratch1, 0xc0000000);
+ __ Branch(&stub_call, ne, scratch2, Operand(zero_reg));
+ __ SmiTag(v0, scratch1);
+ break;
+ }
+ case Token::ADD:
+ __ AdduAndCheckForOverflow(v0, left, right, scratch1);
+ __ BranchOnOverflow(&stub_call, scratch1);
+ break;
+ case Token::SUB:
+ __ SubuAndCheckForOverflow(v0, left, right, scratch1);
+ __ BranchOnOverflow(&stub_call, scratch1);
+ break;
+ case Token::MUL: {
+ __ SmiUntag(scratch1, right);
+ __ Mult(left, scratch1);
+ __ mflo(scratch1);
+ __ mfhi(scratch2);
+ __ sra(scratch1, scratch1, 31);
+ __ Branch(&stub_call, ne, scratch1, Operand(scratch2));
+ __ mflo(v0);
+ __ Branch(&done, ne, v0, Operand(zero_reg));
+ __ Addu(scratch2, right, left);
+ __ Branch(&stub_call, lt, scratch2, Operand(zero_reg));
+ ASSERT(Smi::FromInt(0) == 0);
+ __ mov(v0, zero_reg);
+ break;
+ }
+ case Token::BIT_OR:
+ __ Or(v0, left, Operand(right));
+ break;
+ case Token::BIT_AND:
+ __ And(v0, left, Operand(right));
+ break;
+ case Token::BIT_XOR:
+ __ Xor(v0, left, Operand(right));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ __ bind(&done);
+ context()->Plug(v0);
}
-void FullCodeGenerator::EmitBinaryOp(Token::Value op,
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
OverwriteMode mode) {
- UNIMPLEMENTED_MIPS();
+ __ mov(a0, result_register());
+ __ pop(a1);
+ BinaryOpStub stub(op, mode);
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
- UNIMPLEMENTED_MIPS();
+ // Invalid left-hand sides are rewritten to have a 'throw
+ // ReferenceError' on the left-hand side.
+ if (!expr->IsValidLeftHandSide()) {
+ VisitForEffect(expr);
+ return;
+ }
+
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->AsProperty();
+ if (prop != NULL) {
+ assign_type = (prop->key()->IsPropertyName())
+ ? NAMED_PROPERTY
+ : KEYED_PROPERTY;
+ }
+
+ switch (assign_type) {
+ case VARIABLE: {
+ Variable* var = expr->AsVariableProxy()->var();
+ EffectContext context(this);
+ EmitVariableAssignment(var, Token::ASSIGN);
+ break;
+ }
+ case NAMED_PROPERTY: {
+ __ push(result_register()); // Preserve value.
+ VisitForAccumulatorValue(prop->obj());
+ __ mov(a1, result_register());
+ __ pop(a0); // Restore value.
+ __ li(a2, Operand(prop->key()->AsLiteral()->handle()));
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ push(result_register()); // Preserve value.
+ if (prop->is_synthetic()) {
+ ASSERT(prop->obj()->AsVariableProxy() != NULL);
+ ASSERT(prop->key()->AsLiteral() != NULL);
+ { AccumulatorValueContext for_object(this);
+ EmitVariableLoad(prop->obj()->AsVariableProxy()->var());
+ }
+ __ mov(a2, result_register());
+ __ li(a1, Operand(prop->key()->AsLiteral()->handle()));
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ __ mov(a1, result_register());
+ __ pop(a2);
+ }
+ __ pop(a0); // Restore value.
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
+ : isolate()->builtins()->KeyedStoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
+ break;
+ }
+ }
+ PrepareForBailoutForId(bailout_ast_id, TOS_REG);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Token::Value op) {
- UNIMPLEMENTED_MIPS();
+ // Left-hand sides that rewrite to explicit property accesses do not reach
+ // here.
+ ASSERT(var != NULL);
+ ASSERT(var->is_global() || var->AsSlot() != NULL);
+
+ if (var->is_global()) {
+ ASSERT(!var->is_this());
+ // Assignment to a global variable. Use inline caching for the
+ // assignment. Right-hand-side value is passed in a0, variable name in
+ // a2, and the global object in a1.
+ __ mov(a0, result_register());
+ __ li(a2, Operand(var->name()));
+ __ lw(a1, GlobalObjectOperand());
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
+
+ } else if (op == Token::INIT_CONST) {
+ // Like var declarations, const declarations are hoisted to function
+ // scope. However, unlike var initializers, const initializers are able
+ // to drill a hole to that function context, even from inside a 'with'
+ // context. We thus bypass the normal static scope lookup.
+ Slot* slot = var->AsSlot();
+ Label skip;
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ // No const parameters.
+ UNREACHABLE();
+ break;
+ case Slot::LOCAL:
+ // Detect const reinitialization by checking for the hole value.
+ __ lw(a1, MemOperand(fp, SlotOffset(slot)));
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ Branch(&skip, ne, a1, Operand(t0));
+ __ sw(result_register(), MemOperand(fp, SlotOffset(slot)));
+ break;
+ case Slot::CONTEXT: {
+ __ lw(a1, ContextOperand(cp, Context::FCONTEXT_INDEX));
+ __ lw(a2, ContextOperand(a1, slot->index()));
+ __ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ __ Branch(&skip, ne, a2, Operand(t0));
+ __ sw(result_register(), ContextOperand(a1, slot->index()));
+ int offset = Context::SlotOffset(slot->index());
+ __ mov(a3, result_register()); // Preserve the stored value in v0.
+ __ RecordWrite(a1, Operand(offset), a3, a2);
+ break;
+ }
+ case Slot::LOOKUP:
+ __ push(result_register());
+ __ li(a0, Operand(slot->var()->name()));
+ __ Push(cp, a0); // Context and name.
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
+ break;
+ }
+ __ bind(&skip);
+
+ } else if (var->mode() != Variable::CONST) {
+ // Perform the assignment for non-const variables. Const assignments
+ // are simply skipped.
+ Slot* slot = var->AsSlot();
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ // Perform the assignment.
+ __ sw(result_register(), MemOperand(fp, SlotOffset(slot)));
+ break;
+
+ case Slot::CONTEXT: {
+ MemOperand target = EmitSlotSearch(slot, a1);
+ // Perform the assignment and issue the write barrier.
+ __ sw(result_register(), target);
+ // RecordWrite may destroy all its register arguments.
+ __ mov(a3, result_register());
+ int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+ __ RecordWrite(a1, Operand(offset), a2, a3);
+ break;
+ }
+
+ case Slot::LOOKUP:
+ // Call the runtime for the assignment.
+ __ push(v0); // Value.
+ __ li(a1, Operand(slot->var()->name()));
+ __ li(a0, Operand(Smi::FromInt(strict_mode_flag())));
+ __ Push(cp, a1, a0); // Context, name, strict mode.
+ __ CallRuntime(Runtime::kStoreContextSlot, 4);
+ break;
+ }
+ }
}
void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
- UNIMPLEMENTED_MIPS();
+ // Assignment to a property, using a named store IC.
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ ASSERT(prop->key()->AsLiteral() != NULL);
+
+ // If the assignment starts a block of assignments to the same object,
+ // change to slow case to avoid the quadratic behavior of repeatedly
+ // adding fast properties.
+ if (expr->starts_initialization_block()) {
+ __ push(result_register());
+ __ lw(t0, MemOperand(sp, kPointerSize)); // Receiver is now under value.
+ __ push(t0);
+ __ CallRuntime(Runtime::kToSlowProperties, 1);
+ __ pop(result_register());
+ }
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ __ mov(a0, result_register()); // Load the value.
+ __ li(a2, Operand(prop->key()->AsLiteral()->handle()));
+ // Load receiver to a1. Leave a copy in the stack if needed for turning the
+ // receiver into fast case.
+ if (expr->ends_initialization_block()) {
+ __ lw(a1, MemOperand(sp));
+ } else {
+ __ pop(a1);
+ }
+
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
+
+ // If the assignment ends an initialization block, revert to fast case.
+ if (expr->ends_initialization_block()) {
+ __ push(v0); // Result of assignment, saved even if not needed.
+ // Receiver is under the result value.
+ __ lw(t0, MemOperand(sp, kPointerSize));
+ __ push(t0);
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ __ pop(v0);
+ __ Drop(1);
+ }
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
- UNIMPLEMENTED_MIPS();
+ // Assignment to a property, using a keyed store IC.
+
+ // If the assignment starts a block of assignments to the same object,
+ // change to slow case to avoid the quadratic behavior of repeatedly
+ // adding fast properties.
+ if (expr->starts_initialization_block()) {
+ __ push(result_register());
+ // Receiver is now under the key and value.
+ __ lw(t0, MemOperand(sp, 2 * kPointerSize));
+ __ push(t0);
+ __ CallRuntime(Runtime::kToSlowProperties, 1);
+ __ pop(result_register());
+ }
+
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
+ // Call keyed store IC.
+ // The arguments are:
+ // - a0 is the value,
+ // - a1 is the key,
+ // - a2 is the receiver.
+ __ mov(a0, result_register());
+ __ pop(a1); // Key.
+ // Load receiver to a2. Leave a copy in the stack if needed for turning the
+ // receiver into fast case.
+ if (expr->ends_initialization_block()) {
+ __ lw(a2, MemOperand(sp));
+ } else {
+ __ pop(a2);
+ }
+
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
+ : isolate()->builtins()->KeyedStoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
+
+ // If the assignment ends an initialization block, revert to fast case.
+ if (expr->ends_initialization_block()) {
+ __ push(v0); // Result of assignment, saved even if not needed.
+ // Receiver is under the result value.
+ __ lw(t0, MemOperand(sp, kPointerSize));
+ __ push(t0);
+ __ CallRuntime(Runtime::kToFastProperties, 1);
+ __ pop(v0);
+ __ Drop(1);
+ }
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
}
void FullCodeGenerator::VisitProperty(Property* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ Property");
+ Expression* key = expr->key();
+
+ if (key->IsPropertyName()) {
+ VisitForAccumulatorValue(expr->obj());
+ EmitNamedPropertyLoad(expr);
+ context()->Plug(v0);
+ } else {
+ VisitForStackValue(expr->obj());
+ VisitForAccumulatorValue(expr->key());
+ __ pop(a1);
+ EmitKeyedPropertyLoad(expr);
+ context()->Plug(v0);
+ }
}
void FullCodeGenerator::EmitCallWithIC(Call* expr,
Handle<Object> name,
RelocInfo::Mode mode) {
- UNIMPLEMENTED_MIPS();
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ li(a2, Operand(name));
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ EmitCallIC(ic, mode, expr->id());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
- Expression* key,
- RelocInfo::Mode mode) {
- UNIMPLEMENTED_MIPS();
+ Expression* key) {
+ // Load the key.
+ VisitForAccumulatorValue(key);
+
+ // Swap the name of the function and the receiver on the stack to follow
+ // the calling convention for call ICs.
+ __ pop(a1);
+ __ push(v0);
+ __ push(a1);
+
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
+ __ lw(a2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, v0); // Drop the key still on the stack.
+}
+
+
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
+ // Code common for calls using the call stub.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ CallFunctionStub stub(arg_count, in_loop, flags);
+ __ CallStub(&stub);
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, v0);
}
-void FullCodeGenerator::EmitCallWithStub(Call* expr) {
- UNIMPLEMENTED_MIPS();
+void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag,
+ int arg_count) {
+ // Push copy of the first argument or undefined if it doesn't exist.
+ if (arg_count > 0) {
+ __ lw(a1, MemOperand(sp, arg_count * kPointerSize));
+ } else {
+ __ LoadRoot(a1, Heap::kUndefinedValueRootIndex);
+ }
+ __ push(a1);
+
+ // Push the receiver of the enclosing function and do runtime call.
+ __ lw(a1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize));
+ __ push(a1);
+ // Push the strict mode flag.
+ __ li(a1, Operand(Smi::FromInt(strict_mode_flag())));
+ __ push(a1);
+
+ __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP
+ ? Runtime::kResolvePossiblyDirectEvalNoLookup
+ : Runtime::kResolvePossiblyDirectEval, 4);
}
void FullCodeGenerator::VisitCall(Call* expr) {
- UNIMPLEMENTED_MIPS();
+#ifdef DEBUG
+ // We want to verify that RecordJSReturnSite gets called on all paths
+ // through this function. Avoid early returns.
+ expr->return_is_recorded_ = false;
+#endif
+
+ Comment cmnt(masm_, "[ Call");
+ Expression* fun = expr->expression();
+ Variable* var = fun->AsVariableProxy()->AsVariable();
+
+ if (var != NULL && var->is_possibly_eval()) {
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to
+ // resolve the function we need to call and the receiver of the
+ // call. Then we call the resolved function using the given
+ // arguments.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+
+ { PreservePositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ __ LoadRoot(a2, Heap::kUndefinedValueRootIndex);
+ __ push(a2); // Reserved receiver slot.
+
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ // If we know that eval can only be shadowed by eval-introduced
+ // variables we attempt to load the global eval function directly
+ // in generated code. If we succeed, there is no need to perform a
+ // context lookup in the runtime system.
+ Label done;
+ if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) {
+ Label slow;
+ EmitLoadGlobalSlotCheckExtensions(var->AsSlot(),
+ NOT_INSIDE_TYPEOF,
+ &slow);
+ // Push the function and resolve eval.
+ __ push(v0);
+ EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count);
+ __ jmp(&done);
+ __ bind(&slow);
+ }
+
+ // Push copy of the function (found below the arguments) and
+ // resolve eval.
+ __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ push(a1);
+ EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count);
+ if (done.is_linked()) {
+ __ bind(&done);
+ }
+
+ // The runtime call returns a pair of values in v0 (function) and
+ // v1 (receiver). Touch up the stack with the right values.
+ __ sw(v0, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ sw(v1, MemOperand(sp, arg_count * kPointerSize));
+ }
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
+ __ CallStub(&stub);
+ RecordJSReturnSite(expr);
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->DropAndPlug(1, v0);
+ } else if (var != NULL && !var->is_this() && var->is_global()) {
+ // Push global object as receiver for the call IC.
+ __ lw(a0, GlobalObjectOperand());
+ __ push(a0);
+ EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
+ } else if (var != NULL && var->AsSlot() != NULL &&
+ var->AsSlot()->type() == Slot::LOOKUP) {
+ // Call to a lookup slot (dynamically introduced variable).
+ Label slow, done;
+
+ { PreservePositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
+ NOT_INSIDE_TYPEOF,
+ &slow,
+ &done);
+ }
+
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in v0)
+ // and the object holding it (returned in v1).
+ __ push(context_register());
+ __ li(a2, Operand(var->name()));
+ __ push(a2);
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ Push(v0, v1); // Function, receiver.
+
+ // If fast case code has been generated, emit code to push the
+ // function and receiver and have the slow path jump around this
+ // code.
+ if (done.is_linked()) {
+ Label call;
+ __ Branch(&call);
+ __ bind(&done);
+ // Push function.
+ __ push(v0);
+ // Push global receiver.
+ __ lw(a1, GlobalObjectOperand());
+ __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ push(a1);
+ __ bind(&call);
+ }
+
+ // The receiver is either the global receiver or an object found
+ // by LoadContextSlot. That object could be the hole if the
+ // receiver is implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
+ } else if (fun->AsProperty() != NULL) {
+ // Call to an object property.
+ Property* prop = fun->AsProperty();
+ Literal* key = prop->key()->AsLiteral();
+ if (key != NULL && key->handle()->IsSymbol()) {
+ // Call to a named property, use call IC.
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
+ EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
+ } else {
+ // Call to a keyed property.
+ // For a synthetic property use keyed load IC followed by function call,
+ // for a regular property use keyed EmitCallIC.
+ if (prop->is_synthetic()) {
+ // Do not visit the object and key subexpressions (they are shared
+ // by all occurrences of the same rewritten parameter).
+ ASSERT(prop->obj()->AsVariableProxy() != NULL);
+ ASSERT(prop->obj()->AsVariableProxy()->var()->AsSlot() != NULL);
+ Slot* slot = prop->obj()->AsVariableProxy()->var()->AsSlot();
+ MemOperand operand = EmitSlotSearch(slot, a1);
+ __ lw(a1, operand);
+
+ ASSERT(prop->key()->AsLiteral() != NULL);
+ ASSERT(prop->key()->AsLiteral()->handle()->IsSmi());
+ __ li(a0, Operand(prop->key()->AsLiteral()->handle()));
+
+ // Record source code position for IC call.
+ SetSourcePosition(prop->position());
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
+ __ lw(a1, GlobalObjectOperand());
+ __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ Push(v0, a1); // Function, receiver.
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ } else {
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
+ EmitKeyedCallWithIC(expr, prop->key());
+ }
+ }
+ } else {
+ { PreservePositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ }
+ // Load global receiver object.
+ __ lw(a1, GlobalObjectOperand());
+ __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ push(a1);
+ // Emit function call.
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
+ }
+
+#ifdef DEBUG
+ // RecordJSReturnSite should have been called.
+ ASSERT(expr->return_is_recorded_);
+#endif
}
void FullCodeGenerator::VisitCallNew(CallNew* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ CallNew");
+ // According to ECMA-262, section 11.2.2, page 44, the function
+ // expression in new calls must be evaluated before the
+ // arguments.
+
+ // Push constructor on the stack. If it's not a function it's used as
+ // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
+ // ignored.
+ VisitForStackValue(expr->expression());
+
+ // Push the arguments ("left-to-right") on the stack.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Call the construct call builtin that handles allocation and
+ // constructor invocation.
+ SetSourcePosition(expr->position());
+
+ // Load function and argument count into a1 and a0.
+ __ li(a0, Operand(arg_count));
+ __ lw(a1, MemOperand(sp, arg_count * kPointerSize));
+
+ Handle<Code> construct_builtin =
+ isolate()->builtins()->JSConstructCall();
+ __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ __ And(t0, v0, Operand(kSmiTagMask));
+ Split(eq, t0, Operand(zero_reg), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ __ And(at, v0, Operand(kSmiTagMask | 0x80000000));
+ Split(eq, at, Operand(zero_reg), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ JumpIfSmi(v0, if_false);
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ __ lw(a2, FieldMemOperand(v0, HeapObject::kMapOffset));
+ // Undetectable objects behave like undefined when tested with typeof.
+ __ lbu(a1, FieldMemOperand(a2, Map::kBitFieldOffset));
+ __ And(at, a1, Operand(1 << Map::kIsUndetectable));
+ __ Branch(if_false, ne, at, Operand(zero_reg));
+ __ lbu(a1, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ __ Branch(if_false, lt, a1, Operand(FIRST_JS_OBJECT_TYPE));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(le, a1, Operand(LAST_JS_OBJECT_TYPE), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a1);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(ge, a1, Operand(FIRST_JS_OBJECT_TYPE),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ JumpIfSmi(v0, if_false);
+ __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset));
+ __ And(at, a1, Operand(1 << Map::kIsUndetectable));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(ne, at, Operand(zero_reg), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ if (FLAG_debug_code) __ AbortIfSmi(v0);
+
+ __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(t0, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ And(t0, t0, 1 << Map::kStringWrapperSafeForDefaultValueOf);
+ __ Branch(if_true, ne, t0, Operand(zero_reg));
+
+ // Check for fast case object. Generate false result for slow case object.
+ __ lw(a2, FieldMemOperand(v0, JSObject::kPropertiesOffset));
+ __ lw(a2, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kHashTableMapRootIndex);
+ __ Branch(if_false, eq, a2, Operand(t0));
+
+ // Look for valueOf symbol in the descriptor array, and indicate false if
+ // found. The type is not checked, so if it is a transition it is a false
+ // negative.
+ __ LoadInstanceDescriptors(a1, t0);
+ __ lw(a3, FieldMemOperand(t0, FixedArray::kLengthOffset));
+ // t0: descriptor array
+ // a3: length of descriptor array
+ // Calculate the end of the descriptor array.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kPointerSize == 4);
+ __ Addu(a2, t0, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(t1, a3, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(a2, a2, t1);
+
+ // Calculate location of the first key name.
+ __ Addu(t0,
+ t0,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag +
+ DescriptorArray::kFirstIndex * kPointerSize));
+ // Loop through all the keys in the descriptor array. If one of these is the
+ // symbol valueOf the result is false.
+ Label entry, loop;
+ // The use of t2 to store the valueOf symbol asumes that it is not otherwise
+ // used in the loop below.
+ __ li(t2, Operand(FACTORY->value_of_symbol()));
+ __ jmp(&entry);
+ __ bind(&loop);
+ __ lw(a3, MemOperand(t0, 0));
+ __ Branch(if_false, eq, a3, Operand(t2));
+ __ Addu(t0, t0, Operand(kPointerSize));
+ __ bind(&entry);
+ __ Branch(&loop, ne, t0, Operand(a2));
+
+ // If a valueOf property is not found on the object check that it's
+ // prototype is the un-modified String prototype. If not result is false.
+ __ lw(a2, FieldMemOperand(a1, Map::kPrototypeOffset));
+ __ JumpIfSmi(a2, if_false);
+ __ lw(a2, FieldMemOperand(a2, HeapObject::kMapOffset));
+ __ lw(a3, ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ lw(a3, FieldMemOperand(a3, GlobalObject::kGlobalContextOffset));
+ __ lw(a3, ContextOperand(a3, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
+ __ Branch(if_false, ne, a2, Operand(a3));
+
+ // Set the bit in the map to indicate that it has been checked safe for
+ // default valueOf and set true result.
+ __ lbu(a2, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ Or(a2, a2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
+ __ sb(a2, FieldMemOperand(a1, Map::kBitField2Offset));
+ __ jmp(if_true);
+
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a2);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ __ Branch(if_true, eq, a2, Operand(JS_FUNCTION_TYPE));
+ __ Branch(if_false);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a1);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(eq, a1, Operand(JS_ARRAY_TYPE),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, a1);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(eq, a1, Operand(JS_REGEXP_TYPE), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 0);
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ // Get the frame pointer for the calling frame.
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+
+ // Skip the arguments adaptor frame if it exists.
+ Label check_frame_marker;
+ __ lw(a1, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&check_frame_marker, ne,
+ a1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ lw(a2, MemOperand(a2, StandardFrameConstants::kCallerFPOffset));
+
+ // Check the marker in the calling frame.
+ __ bind(&check_frame_marker);
+ __ lw(a1, MemOperand(a2, StandardFrameConstants::kMarkerOffset));
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(eq, a1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)),
+ if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 2);
+
+ // Load the two objects into registers and perform the comparison.
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ pop(a1);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(eq, v0, Operand(a1), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ // ArgumentsAccessStub expects the key in a1 and the formal
+ // parameter count in a0.
+ VisitForAccumulatorValue(args->at(0));
+ __ mov(a1, v0);
+ __ li(a0, Operand(Smi::FromInt(scope()->num_parameters())));
+ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 0);
+
+ Label exit;
+ // Get the number of formal parameters.
+ __ li(v0, Operand(Smi::FromInt(scope()->num_parameters())));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ __ lw(a2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ lw(a3, MemOperand(a2, StandardFrameConstants::kContextOffset));
+ __ Branch(&exit, ne, a3,
+ Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+
+ // Arguments adaptor case: Read the arguments length from the
+ // adaptor frame.
+ __ lw(v0, MemOperand(a2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ __ bind(&exit);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+ Label done, null, function, non_function_constructor;
+
+ VisitForAccumulatorValue(args->at(0));
+
+ // If the object is a smi, we return null.
+ __ JumpIfSmi(v0, &null);
+
+ // Check that the object is a JS object but take special care of JS
+ // functions to make sure they have 'Function' as their class.
+ __ GetObjectType(v0, v0, a1); // Map is now in v0.
+ __ Branch(&null, lt, a1, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // As long as JS_FUNCTION_TYPE is the last instance type and it is
+ // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
+ // LAST_JS_OBJECT_TYPE.
+ ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
+ __ Branch(&function, eq, a1, Operand(JS_FUNCTION_TYPE));
+
+ // Check if the constructor in the map is a function.
+ __ lw(v0, FieldMemOperand(v0, Map::kConstructorOffset));
+ __ GetObjectType(v0, a1, a1);
+ __ Branch(&non_function_constructor, ne, a1, Operand(JS_FUNCTION_TYPE));
+
+ // v0 now contains the constructor function. Grab the
+ // instance class name from there.
+ __ lw(v0, FieldMemOperand(v0, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(v0, FieldMemOperand(v0, SharedFunctionInfo::kInstanceClassNameOffset));
+ __ Branch(&done);
+
+ // Functions have class 'Function'.
+ __ bind(&function);
+ __ LoadRoot(v0, Heap::kfunction_class_symbolRootIndex);
+ __ jmp(&done);
+
+ // Objects with a non-function constructor have class 'Object'.
+ __ bind(&non_function_constructor);
+ __ LoadRoot(v0, Heap::kfunction_class_symbolRootIndex);
+ __ jmp(&done);
+
+ // Non-JS objects have class null.
+ __ bind(&null);
+ __ LoadRoot(v0, Heap::kNullValueRootIndex);
+
+ // All done.
+ __ bind(&done);
+
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ // Conditionally generate a log call.
+ // Args:
+ // 0 (literal string): The type of logging (corresponds to the flags).
+ // This is used to determine whether or not to generate the log call.
+ // 1 (string): Format string. Access the string at argument index 2
+ // with '%2s' (see Logger::LogRuntime for all the formats).
+ // 2 (array): Arguments to the format string.
+ ASSERT_EQ(args->length(), 3);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ if (CodeGenerator::ShouldGenerateLog(args->at(0))) {
+ VisitForStackValue(args->at(1));
+ VisitForStackValue(args->at(2));
+ __ CallRuntime(Runtime::kLog, 2);
+ }
+#endif
+ // Finally, we're expected to leave a value on the top of the stack.
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 0);
+
+ Label slow_allocate_heapnumber;
+ Label heapnumber_allocated;
+
+ // Save the new heap number in callee-saved register s0, since
+ // we call out to external C code below.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(s0, a1, a2, t6, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+
+ // Allocate a heap number.
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ mov(s0, v0); // Save result in s0, so it is saved thru CFunc call.
+
+ __ bind(&heapnumber_allocated);
+
+ // Convert 32 random bits in v0 to 0.(32 random bits) in a double
+ // by computing:
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
+ if (CpuFeatures::IsSupported(FPU)) {
+ __ PrepareCallCFunction(1, a0);
+ __ li(a0, Operand(ExternalReference::isolate_address()));
+ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
+
+
+ CpuFeatures::Scope scope(FPU);
+ // 0x41300000 is the top half of 1.0 x 2^20 as a double.
+ __ li(a1, Operand(0x41300000));
+ // Move 0x41300000xxxxxxxx (x = random bits in v0) to FPU.
+ __ Move(f12, v0, a1);
+ // Move 0x4130000000000000 to FPU.
+ __ Move(f14, zero_reg, a1);
+ // Subtract and store the result in the heap number.
+ __ sub_d(f0, f12, f14);
+ __ sdc1(f0, MemOperand(s0, HeapNumber::kValueOffset - kHeapObjectTag));
+ __ mov(v0, s0);
+ } else {
+ __ PrepareCallCFunction(2, a0);
+ __ mov(a0, s0);
+ __ li(a1, Operand(ExternalReference::isolate_address()));
+ __ CallCFunction(
+ ExternalReference::fill_heap_number_with_random_function(isolate()), 2);
+ }
+
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ // Load the arguments on the stack and call the stub.
+ SubStringStub stub;
+ ASSERT(args->length() == 3);
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+ VisitForStackValue(args->at(2));
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ // Load the arguments on the stack and call the stub.
+ RegExpExecStub stub;
+ ASSERT(args->length() == 4);
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+ VisitForStackValue(args->at(2));
+ VisitForStackValue(args->at(3));
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0)); // Load the object.
+
+ Label done;
+ // If the object is a smi return the object.
+ __ JumpIfSmi(v0, &done);
+ // If the object is not a value type, return the object.
+ __ GetObjectType(v0, a1, a1);
+ __ Branch(&done, ne, a1, Operand(JS_VALUE_TYPE));
+
+ __ lw(v0, FieldMemOperand(v0, JSValue::kValueOffset));
+
+ __ bind(&done);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ // Load the arguments on the stack and call the runtime function.
+ ASSERT(args->length() == 2);
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+ MathPowStub stub;
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0)); // Load the object.
+ VisitForAccumulatorValue(args->at(1)); // Load the value.
+ __ pop(a1); // v0 = value. a1 = object.
+
+ Label done;
+ // If the object is a smi, return the value.
+ __ JumpIfSmi(a1, &done);
+
+ // If the object is not a value type, return the value.
+ __ GetObjectType(a1, a2, a2);
+ __ Branch(&done, ne, a2, Operand(JS_VALUE_TYPE));
+
+ // Store the value.
+ __ sw(v0, FieldMemOperand(a1, JSValue::kValueOffset));
+ // Update the write barrier. Save the value as it will be
+ // overwritten by the write barrier code and is needed afterward.
+ __ RecordWrite(a1, Operand(JSValue::kValueOffset - kHeapObjectTag), a2, a3);
+
+ __ bind(&done);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT_EQ(args->length(), 1);
+
+ // Load the argument on the stack and call the stub.
+ VisitForStackValue(args->at(0));
+
+ NumberToStringStub stub;
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+
+ VisitForAccumulatorValue(args->at(0));
+
+ Label done;
+ StringCharFromCodeGenerator generator(v0, a1);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(a1);
}
void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ mov(a0, result_register());
+
+ Register object = a1;
+ Register index = a0;
+ Register scratch = a2;
+ Register result = v0;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharCodeAtGenerator generator(object,
+ index,
+ scratch,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // NaN.
+ __ LoadRoot(result, Heap::kNanValueRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Load the undefined value into the result register, which will
+ // trigger conversion.
+ __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
}
void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 2);
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1));
+ __ mov(a0, result_register());
+
+ Register object = a1;
+ Register index = a0;
+ Register scratch1 = a2;
+ Register scratch2 = a3;
+ Register result = v0;
+
+ __ pop(object);
+
+ Label need_conversion;
+ Label index_out_of_range;
+ Label done;
+ StringCharAtGenerator generator(object,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &need_conversion,
+ &need_conversion,
+ &index_out_of_range,
+ STRING_INDEX_IS_NUMBER);
+ generator.GenerateFast(masm_);
+ __ jmp(&done);
+
+ __ bind(&index_out_of_range);
+ // When the index is out of range, the spec requires us to return
+ // the empty string.
+ __ LoadRoot(result, Heap::kEmptyStringRootIndex);
+ __ jmp(&done);
+
+ __ bind(&need_conversion);
+ // Move smi zero into the result register, which will trigger
+ // conversion.
+ __ li(result, Operand(Smi::FromInt(0)));
+ __ jmp(&done);
+
+ NopRuntimeCallHelper call_helper;
+ generator.GenerateSlow(masm_, call_helper);
+
+ __ bind(&done);
+ context()->Plug(result);
}
void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringAddStub stub(NO_STRING_ADD_FLAGS);
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT_EQ(2, args->length());
+
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+
+ StringCompareStub stub;
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ // Load the argument on the stack and call the stub.
+ TranscendentalCacheStub stub(TranscendentalCache::SIN,
+ TranscendentalCacheStub::TAGGED);
+ ASSERT(args->length() == 1);
+ VisitForStackValue(args->at(0));
+ __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ // Load the argument on the stack and call the stub.
+ TranscendentalCacheStub stub(TranscendentalCache::COS,
+ TranscendentalCacheStub::TAGGED);
+ ASSERT(args->length() == 1);
+ VisitForStackValue(args->at(0));
+ __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
-void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) {
+ // Load the argument on the stack and call the stub.
+ TranscendentalCacheStub stub(TranscendentalCache::LOG,
+ TranscendentalCacheStub::TAGGED);
+ ASSERT(args->length() == 1);
+ VisitForStackValue(args->at(0));
+ __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos.
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
-void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
+ // Load the argument on the stack and call the runtime function.
+ ASSERT(args->length() == 1);
+ VisitForStackValue(args->at(0));
+ __ CallRuntime(Runtime::kMath_sqrt, 1);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() >= 2);
+
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(args->last()); // Function.
+
+ // InvokeFunction requires the function in a1. Move it in there.
+ __ mov(a1, result_register());
+ ParameterCount count(arg_count);
+ __ InvokeFunction(a1, count, CALL_FUNCTION);
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ RegExpConstructResultStub stub;
+ ASSERT(args->length() == 3);
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+ VisitForStackValue(args->at(2));
+ __ CallStub(&stub);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 3);
+ VisitForStackValue(args->at(0));
+ VisitForStackValue(args->at(1));
+ VisitForStackValue(args->at(2));
+ Label done;
+ Label slow_case;
+ Register object = a0;
+ Register index1 = a1;
+ Register index2 = a2;
+ Register elements = a3;
+ Register scratch1 = t0;
+ Register scratch2 = t1;
+
+ __ lw(object, MemOperand(sp, 2 * kPointerSize));
+ // Fetch the map and check if array is in fast case.
+ // Check that object doesn't require security checks and
+ // has no indexed interceptor.
+ __ GetObjectType(object, scratch1, scratch2);
+ __ Branch(&slow_case, ne, scratch2, Operand(JS_ARRAY_TYPE));
+ // Map is now in scratch1.
+
+ __ lbu(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset));
+ __ And(scratch2, scratch2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
+ __ Branch(&slow_case, ne, scratch2, Operand(zero_reg));
+
+ // Check the object's elements are in fast case and writable.
+ __ lw(elements, FieldMemOperand(object, JSObject::kElementsOffset));
+ __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(scratch2, Heap::kFixedArrayMapRootIndex);
+ __ Branch(&slow_case, ne, scratch1, Operand(scratch2));
+
+ // Check that both indices are smis.
+ __ lw(index1, MemOperand(sp, 1 * kPointerSize));
+ __ lw(index2, MemOperand(sp, 0));
+ __ JumpIfNotBothSmi(index1, index2, &slow_case);
+
+ // Check that both indices are valid.
+ Label not_hi;
+ __ lw(scratch1, FieldMemOperand(object, JSArray::kLengthOffset));
+ __ Branch(&slow_case, ls, scratch1, Operand(index1));
+ __ Branch(&not_hi, NegateCondition(hi), scratch1, Operand(index1));
+ __ Branch(&slow_case, ls, scratch1, Operand(index2));
+ __ bind(&not_hi);
+
+ // Bring the address of the elements into index1 and index2.
+ __ Addu(scratch1, elements,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(index1, index1, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(index1, scratch1, index1);
+ __ sll(index2, index2, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(index2, scratch1, index2);
+
+ // Swap elements.
+ __ lw(scratch1, MemOperand(index1, 0));
+ __ lw(scratch2, MemOperand(index2, 0));
+ __ sw(scratch1, MemOperand(index2, 0));
+ __ sw(scratch2, MemOperand(index1, 0));
+
+ Label new_space;
+ __ InNewSpace(elements, scratch1, eq, &new_space);
+ // Possible optimization: do a check that both values are Smis
+ // (or them and test against Smi mask).
+
+ __ mov(scratch1, elements);
+ __ RecordWriteHelper(elements, index1, scratch2);
+ __ RecordWriteHelper(scratch1, index2, scratch2); // scratch1 holds elements.
+
+ __ bind(&new_space);
+ // We are done. Drop elements from the stack, and return undefined.
+ __ Drop(3);
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ jmp(&done);
+
+ __ bind(&slow_case);
+ __ CallRuntime(Runtime::kSwapElements, 3);
+
+ __ bind(&done);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT_EQ(2, args->length());
+
+ ASSERT_NE(NULL, args->at(0)->AsLiteral());
+ int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
+
+ Handle<FixedArray> jsfunction_result_caches(
+ isolate()->global_context()->jsfunction_result_caches());
+ if (jsfunction_result_caches->length() <= cache_id) {
+ __ Abort("Attempt to use undefined cache.");
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ context()->Plug(v0);
+ return;
+ }
+
+ VisitForAccumulatorValue(args->at(1));
+
+ Register key = v0;
+ Register cache = a1;
+ __ lw(cache, ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ lw(cache, FieldMemOperand(cache, GlobalObject::kGlobalContextOffset));
+ __ lw(cache,
+ ContextOperand(
+ cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ lw(cache,
+ FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
+
+
+ Label done, not_found;
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ lw(a2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
+ // a2 now holds finger offset as a smi.
+ __ Addu(a3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // a3 now points to the start of fixed array elements.
+ __ sll(at, a2, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(a3, a3, at);
+ // a3 now points to key of indexed element of cache.
+ __ lw(a2, MemOperand(a3));
+ __ Branch(&not_found, ne, key, Operand(a2));
+
+ __ lw(v0, MemOperand(a3, kPointerSize));
+ __ Branch(&done);
+
+ __ bind(&not_found);
+ // Call runtime to perform the lookup.
+ __ Push(cache, key);
+ __ CallRuntime(Runtime::kGetFromCache, 2);
+
+ __ bind(&done);
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT_EQ(2, args->length());
+
+ Register right = v0;
+ Register left = a1;
+ Register tmp = a2;
+ Register tmp2 = a3;
+
+ VisitForStackValue(args->at(0));
+ VisitForAccumulatorValue(args->at(1)); // Result (right) in v0.
+ __ pop(left);
+
+ Label done, fail, ok;
+ __ Branch(&ok, eq, left, Operand(right));
+ // Fail if either is a non-HeapObject.
+ __ And(tmp, left, Operand(right));
+ __ And(at, tmp, Operand(kSmiTagMask));
+ __ Branch(&fail, eq, at, Operand(zero_reg));
+ __ lw(tmp, FieldMemOperand(left, HeapObject::kMapOffset));
+ __ lbu(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset));
+ __ Branch(&fail, ne, tmp2, Operand(JS_REGEXP_TYPE));
+ __ lw(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
+ __ Branch(&fail, ne, tmp, Operand(tmp2));
+ __ lw(tmp, FieldMemOperand(left, JSRegExp::kDataOffset));
+ __ lw(tmp2, FieldMemOperand(right, JSRegExp::kDataOffset));
+ __ Branch(&ok, eq, tmp, Operand(tmp2));
+ __ bind(&fail);
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ jmp(&done);
+ __ bind(&ok);
+ __ LoadRoot(v0, Heap::kTrueValueRootIndex);
+ __ bind(&done);
+
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ VisitForAccumulatorValue(args->at(0));
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ lw(a0, FieldMemOperand(v0, String::kHashFieldOffset));
+ __ And(a0, a0, Operand(String::kContainsCachedArrayIndexMask));
+
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(eq, a0, Operand(zero_reg), if_true, if_false, fall_through);
+
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(args->length() == 1);
+ VisitForAccumulatorValue(args->at(0));
+
+ if (FLAG_debug_code) {
+ __ AbortIfNotString(v0);
+ }
+
+ __ lw(v0, FieldMemOperand(v0, String::kHashFieldOffset));
+ __ IndexFromHash(v0, v0);
+
+ context()->Plug(v0);
}
void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) {
- UNIMPLEMENTED_MIPS();
+ Label bailout, done, one_char_separator, long_separator,
+ non_trivial_array, not_size_one_array, loop,
+ empty_separator_loop, one_char_separator_loop,
+ one_char_separator_loop_entry, long_separator_loop;
+
+ ASSERT(args->length() == 2);
+ VisitForStackValue(args->at(1));
+ VisitForAccumulatorValue(args->at(0));
+
+ // All aliases of the same register have disjoint lifetimes.
+ Register array = v0;
+ Register elements = no_reg; // Will be v0.
+ Register result = no_reg; // Will be v0.
+ Register separator = a1;
+ Register array_length = a2;
+ Register result_pos = no_reg; // Will be a2.
+ Register string_length = a3;
+ Register string = t0;
+ Register element = t1;
+ Register elements_end = t2;
+ Register scratch1 = t3;
+ Register scratch2 = t5;
+ Register scratch3 = t4;
+ Register scratch4 = v1;
+
+ // Separator operand is on the stack.
+ __ pop(separator);
+
+ // Check that the array is a JSArray.
+ __ JumpIfSmi(array, &bailout);
+ __ GetObjectType(array, scratch1, scratch2);
+ __ Branch(&bailout, ne, scratch2, Operand(JS_ARRAY_TYPE));
+
+ // Check that the array has fast elements.
+ __ lbu(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
+ __ And(scratch3, scratch2, Operand(1 << Map::kHasFastElements));
+ __ Branch(&bailout, eq, scratch3, Operand(zero_reg));
+
+ // If the array has length zero, return the empty string.
+ __ lw(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
+ __ SmiUntag(array_length);
+ __ Branch(&non_trivial_array, ne, array_length, Operand(zero_reg));
+ __ LoadRoot(v0, Heap::kEmptyStringRootIndex);
+ __ Branch(&done);
+
+ __ bind(&non_trivial_array);
+
+ // Get the FixedArray containing array's elements.
+ elements = array;
+ __ lw(elements, FieldMemOperand(array, JSArray::kElementsOffset));
+ array = no_reg; // End of array's live range.
+
+ // Check that all array elements are sequential ASCII strings, and
+ // accumulate the sum of their lengths, as a smi-encoded value.
+ __ mov(string_length, zero_reg);
+ __ Addu(element,
+ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(elements_end, array_length, kPointerSizeLog2);
+ __ Addu(elements_end, element, elements_end);
+ // Loop condition: while (element < elements_end).
+ // Live values in registers:
+ // elements: Fixed array of strings.
+ // array_length: Length of the fixed array of strings (not smi)
+ // separator: Separator string
+ // string_length: Accumulated sum of string lengths (smi).
+ // element: Current array element.
+ // elements_end: Array end.
+ if (FLAG_debug_code) {
+ __ Assert(gt, "No empty arrays here in EmitFastAsciiArrayJoin",
+ array_length, Operand(zero_reg));
+ }
+ __ bind(&loop);
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ JumpIfSmi(string, &bailout);
+ __ lw(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
+ __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
+ __ lw(scratch1, FieldMemOperand(string, SeqAsciiString::kLengthOffset));
+ __ AdduAndCheckForOverflow(string_length, string_length, scratch1, scratch3);
+ __ BranchOnOverflow(&bailout, scratch3);
+ __ Branch(&loop, lt, element, Operand(elements_end));
+
+ // If array_length is 1, return elements[0], a string.
+ __ Branch(&not_size_one_array, ne, array_length, Operand(1));
+ __ lw(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
+ __ Branch(&done);
+
+ __ bind(&not_size_one_array);
+
+ // Live values in registers:
+ // separator: Separator string
+ // array_length: Length of the array.
+ // string_length: Sum of string lengths (smi).
+ // elements: FixedArray of strings.
+
+ // Check that the separator is a flat ASCII string.
+ __ JumpIfSmi(separator, &bailout);
+ __ lw(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
+ __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
+
+ // Add (separator length times array_length) - separator length to the
+ // string_length to get the length of the result string. array_length is not
+ // smi but the other values are, so the result is a smi.
+ __ lw(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset));
+ __ Subu(string_length, string_length, Operand(scratch1));
+ __ Mult(array_length, scratch1);
+ // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
+ // zero.
+ __ mfhi(scratch2);
+ __ Branch(&bailout, ne, scratch2, Operand(zero_reg));
+ __ mflo(scratch2);
+ __ And(scratch3, scratch2, Operand(0x80000000));
+ __ Branch(&bailout, ne, scratch3, Operand(zero_reg));
+ __ AdduAndCheckForOverflow(string_length, string_length, scratch2, scratch3);
+ __ BranchOnOverflow(&bailout, scratch3);
+ __ SmiUntag(string_length);
+
+ // Get first element in the array to free up the elements register to be used
+ // for the result.
+ __ Addu(element,
+ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ result = elements; // End of live range for elements.
+ elements = no_reg;
+ // Live values in registers:
+ // element: First array element
+ // separator: Separator string
+ // string_length: Length of result string (not smi)
+ // array_length: Length of the array.
+ __ AllocateAsciiString(result,
+ string_length,
+ scratch1,
+ scratch2,
+ elements_end,
+ &bailout);
+ // Prepare for looping. Set up elements_end to end of the array. Set
+ // result_pos to the position of the result where to write the first
+ // character.
+ __ sll(elements_end, array_length, kPointerSizeLog2);
+ __ Addu(elements_end, element, elements_end);
+ result_pos = array_length; // End of live range for array_length.
+ array_length = no_reg;
+ __ Addu(result_pos,
+ result,
+ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+
+ // Check the length of the separator.
+ __ lw(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset));
+ __ li(at, Operand(Smi::FromInt(1)));
+ __ Branch(&one_char_separator, eq, scratch1, Operand(at));
+ __ Branch(&long_separator, gt, scratch1, Operand(at));
+
+ // Empty separator case.
+ __ bind(&empty_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+
+ // Copy next array element to the result.
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string, string, SeqAsciiString::kHeaderSize - kHeapObjectTag);
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ // End while (element < elements_end).
+ __ Branch(&empty_separator_loop, lt, element, Operand(elements_end));
+ ASSERT(result.is(v0));
+ __ Branch(&done);
+
+ // One-character separator case.
+ __ bind(&one_char_separator);
+ // Replace separator with its ascii character value.
+ __ lbu(separator, FieldMemOperand(separator, SeqAsciiString::kHeaderSize));
+ // Jump into the loop after the code that copies the separator, so the first
+ // element is not preceded by a separator.
+ __ jmp(&one_char_separator_loop_entry);
+
+ __ bind(&one_char_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+ // separator: Single separator ascii char (in lower byte).
+
+ // Copy the separator character to the result.
+ __ sb(separator, MemOperand(result_pos));
+ __ Addu(result_pos, result_pos, 1);
+
+ // Copy next array element to the result.
+ __ bind(&one_char_separator_loop_entry);
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string, string, SeqAsciiString::kHeaderSize - kHeapObjectTag);
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ // End while (element < elements_end).
+ __ Branch(&one_char_separator_loop, lt, element, Operand(elements_end));
+ ASSERT(result.is(v0));
+ __ Branch(&done);
+
+ // Long separator case (separator is more than one character). Entry is at the
+ // label long_separator below.
+ __ bind(&long_separator_loop);
+ // Live values in registers:
+ // result_pos: the position to which we are currently copying characters.
+ // element: Current array element.
+ // elements_end: Array end.
+ // separator: Separator string.
+
+ // Copy the separator to the result.
+ __ lw(string_length, FieldMemOperand(separator, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string,
+ separator,
+ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+
+ __ bind(&long_separator);
+ __ lw(string, MemOperand(element));
+ __ Addu(element, element, kPointerSize);
+ __ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
+ __ SmiUntag(string_length);
+ __ Addu(string, string, SeqAsciiString::kHeaderSize - kHeapObjectTag);
+ __ CopyBytes(string, result_pos, string_length, scratch1);
+ // End while (element < elements_end).
+ __ Branch(&long_separator_loop, lt, element, Operand(elements_end));
+ ASSERT(result.is(v0));
+ __ Branch(&done);
+
+ __ bind(&bailout);
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ bind(&done);
+ context()->Plug(v0);
}
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
- UNIMPLEMENTED_MIPS();
+ Handle<String> name = expr->name();
+ if (name->length() > 0 && name->Get(0) == '_') {
+ Comment cmnt(masm_, "[ InlineRuntimeCall");
+ EmitInlineRuntimeCall(expr);
+ return;
+ }
+
+ Comment cmnt(masm_, "[ CallRuntime");
+ ZoneList<Expression*>* args = expr->arguments();
+
+ if (expr->is_jsruntime()) {
+ // Prepare for calling JS runtime function.
+ __ lw(a0, GlobalObjectOperand());
+ __ lw(a0, FieldMemOperand(a0, GlobalObject::kBuiltinsOffset));
+ __ push(a0);
+ }
+
+ // Push the arguments ("left-to-right").
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ if (expr->is_jsruntime()) {
+ // Call the JS runtime function.
+ __ li(a2, Operand(expr->name()));
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count,
+ NOT_IN_LOOP,
+ mode);
+ EmitCallIC(ic, mode, expr->id());
+ // Restore context register.
+ __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ } else {
+ // Call the C runtime function.
+ __ CallRuntime(expr->function(), arg_count);
+ }
+ context()->Plug(v0);
}
void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
- UNIMPLEMENTED_MIPS();
+ switch (expr->op()) {
+ case Token::DELETE: {
+ Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
+ Property* prop = expr->expression()->AsProperty();
+ Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
+
+ if (prop != NULL) {
+ if (prop->is_synthetic()) {
+ // Result of deleting parameters is false, even when they rewrite
+ // to accesses on the arguments object.
+ context()->Plug(false);
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForStackValue(prop->key());
+ __ li(a1, Operand(Smi::FromInt(strict_mode_flag())));
+ __ push(a1);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ context()->Plug(v0);
+ }
+ } else if (var != NULL) {
+ // Delete of an unqualified identifier is disallowed in strict mode
+ // but "delete this" is.
+ ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this());
+ if (var->is_global()) {
+ __ lw(a2, GlobalObjectOperand());
+ __ li(a1, Operand(var->name()));
+ __ li(a0, Operand(Smi::FromInt(kNonStrictMode)));
+ __ Push(a2, a1, a0);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
+ context()->Plug(v0);
+ } else if (var->AsSlot() != NULL &&
+ var->AsSlot()->type() != Slot::LOOKUP) {
+ // Result of deleting non-global, non-dynamic variables is false.
+ // The subexpression does not have side effects.
+ context()->Plug(false);
+ } else {
+ // Non-global variable. Call the runtime to try to delete from the
+ // context where the variable was introduced.
+ __ push(context_register());
+ __ li(a2, Operand(var->name()));
+ __ push(a2);
+ __ CallRuntime(Runtime::kDeleteContextSlot, 2);
+ context()->Plug(v0);
+ }
+ } else {
+ // Result of deleting non-property, non-variable reference is true.
+ // The subexpression may have side effects.
+ VisitForEffect(expr->expression());
+ context()->Plug(true);
+ }
+ break;
+ }
+
+ case Token::VOID: {
+ Comment cmnt(masm_, "[ UnaryOperation (VOID)");
+ VisitForEffect(expr->expression());
+ context()->Plug(Heap::kUndefinedValueRootIndex);
+ break;
+ }
+
+ case Token::NOT: {
+ Comment cmnt(masm_, "[ UnaryOperation (NOT)");
+ if (context()->IsEffect()) {
+ // Unary NOT has no side effects so it's only necessary to visit the
+ // subexpression. Match the optimizing compiler by not branching.
+ VisitForEffect(expr->expression());
+ } else {
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+
+ // Notice that the labels are swapped.
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_false, &if_true, &fall_through);
+ if (context()->IsTest()) ForwardBailoutToChild(expr);
+ VisitForControl(expr->expression(), if_true, if_false, fall_through);
+ context()->Plug(if_false, if_true); // Labels swapped.
+ }
+ break;
+ }
+
+ case Token::TYPEOF: {
+ Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
+ { StackValueContext context(this);
+ VisitForTypeofValue(expr->expression());
+ }
+ __ CallRuntime(Runtime::kTypeof, 1);
+ context()->Plug(v0);
+ break;
+ }
+
+ case Token::ADD: {
+ Comment cmt(masm_, "[ UnaryOperation (ADD)");
+ VisitForAccumulatorValue(expr->expression());
+ Label no_conversion;
+ __ JumpIfSmi(result_register(), &no_conversion);
+ __ mov(a0, result_register());
+ ToNumberStub convert_stub;
+ __ CallStub(&convert_stub);
+ __ bind(&no_conversion);
+ context()->Plug(result_register());
+ break;
+ }
+
+ case Token::SUB:
+ EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
+ break;
+
+ case Token::BIT_NOT:
+ EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
+ const char* comment) {
+ // TODO(svenpanne): Allowing format strings in Comment would be nice here...
+ Comment cmt(masm_, comment);
+ bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+ UnaryOpStub stub(expr->op(), overwrite);
+ // GenericUnaryOpStub expects the argument to be in a0.
+ VisitForAccumulatorValue(expr->expression());
+ SetSourcePosition(expr->position());
+ __ mov(a0, result_register());
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
+ context()->Plug(v0);
}
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ CountOperation");
+ SetSourcePosition(expr->position());
+
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
+ // as the left-hand side.
+ if (!expr->expression()->IsValidLeftHandSide()) {
+ VisitForEffect(expr->expression());
+ return;
+ }
+
+ // Expression can only be a property, a global or a (parameter or local)
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->expression()->AsProperty();
+ // In case of a property we use the uninitialized expression context
+ // of the key to detect a named property.
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate expression and get value.
+ if (assign_type == VARIABLE) {
+ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
+ AccumulatorValueContext context(this);
+ EmitVariableLoad(expr->expression()->AsVariableProxy()->var());
+ } else {
+ // Reserve space for result of postfix operation.
+ if (expr->is_postfix() && !context()->IsEffect()) {
+ __ li(at, Operand(Smi::FromInt(0)));
+ __ push(at);
+ }
+ if (assign_type == NAMED_PROPERTY) {
+ // Put the object both on the stack and in the accumulator.
+ VisitForAccumulatorValue(prop->obj());
+ __ push(v0);
+ EmitNamedPropertyLoad(prop);
+ } else {
+ if (prop->is_arguments_access()) {
+ VariableProxy* obj_proxy = prop->obj()->AsVariableProxy();
+ __ lw(v0, EmitSlotSearch(obj_proxy->var()->AsSlot(), v0));
+ __ push(v0);
+ __ li(v0, Operand(prop->key()->AsLiteral()->handle()));
+ } else {
+ VisitForStackValue(prop->obj());
+ VisitForAccumulatorValue(prop->key());
+ }
+ __ lw(a1, MemOperand(sp, 0));
+ __ push(v0);
+ EmitKeyedPropertyLoad(prop);
+ }
+ }
+
+ // We need a second deoptimization point after loading the value
+ // in case evaluating the property load my have a side effect.
+ if (assign_type == VARIABLE) {
+ PrepareForBailout(expr->expression(), TOS_REG);
+ } else {
+ PrepareForBailoutForId(expr->CountId(), TOS_REG);
+ }
+
+ // Call ToNumber only if operand is not a smi.
+ Label no_conversion;
+ __ JumpIfSmi(v0, &no_conversion);
+ __ mov(a0, v0);
+ ToNumberStub convert_stub;
+ __ CallStub(&convert_stub);
+ __ bind(&no_conversion);
+
+ // Save result for postfix expressions.
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ // Save the result on the stack. If we have a named or keyed property
+ // we store the result under the receiver that is currently on top
+ // of the stack.
+ switch (assign_type) {
+ case VARIABLE:
+ __ push(v0);
+ break;
+ case NAMED_PROPERTY:
+ __ sw(v0, MemOperand(sp, kPointerSize));
+ break;
+ case KEYED_PROPERTY:
+ __ sw(v0, MemOperand(sp, 2 * kPointerSize));
+ break;
+ }
+ }
+ }
+ __ mov(a0, result_register());
+
+ // Inline smi case if we are in a loop.
+ Label stub_call, done;
+ JumpPatchSite patch_site(masm_);
+
+ int count_value = expr->op() == Token::INC ? 1 : -1;
+ __ li(a1, Operand(Smi::FromInt(count_value)));
+
+ if (ShouldInlineSmiCase(expr->op())) {
+ __ AdduAndCheckForOverflow(v0, a0, a1, t0);
+ __ BranchOnOverflow(&stub_call, t0); // Do stub on overflow.
+
+ // We could eliminate this smi check if we split the code at
+ // the first smi check before calling ToNumber.
+ patch_site.EmitJumpIfSmi(v0, &done);
+ __ bind(&stub_call);
+ }
+
+ // Record position before stub call.
+ SetSourcePosition(expr->position());
+
+ BinaryOpStub stub(Token::ADD, NO_OVERWRITE);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->CountId());
+ __ bind(&done);
+
+ // Store the value returned in v0.
+ switch (assign_type) {
+ case VARIABLE:
+ if (expr->is_postfix()) {
+ { EffectContext context(this);
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context.Plug(v0);
+ }
+ // For all contexts except EffectConstant we have the result on
+ // top of the stack.
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Token::ASSIGN);
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ context()->Plug(v0);
+ }
+ break;
+ case NAMED_PROPERTY: {
+ __ mov(a0, result_register()); // Value.
+ __ li(a2, Operand(prop->key()->AsLiteral()->handle())); // Name.
+ __ pop(a1); // Receiver.
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->StoreIC_Initialize_Strict()
+ : isolate()->builtins()->StoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(v0);
+ }
+ break;
+ }
+ case KEYED_PROPERTY: {
+ __ mov(a0, result_register()); // Value.
+ __ pop(a1); // Key.
+ __ pop(a2); // Receiver.
+ Handle<Code> ic = is_strict_mode()
+ ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
+ : isolate()->builtins()->KeyedStoreIC_Initialize();
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
+ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
+ if (expr->is_postfix()) {
+ if (!context()->IsEffect()) {
+ context()->PlugTOS();
+ }
+ } else {
+ context()->Plug(v0);
+ }
+ break;
+ }
+ }
}
void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
- UNIMPLEMENTED_MIPS();
+ VariableProxy* proxy = expr->AsVariableProxy();
+ if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) {
+ Comment cmnt(masm_, "Global variable");
+ __ lw(a0, GlobalObjectOperand());
+ __ li(a2, Operand(proxy->name()));
+ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
+ // Use a regular load, not a contextual load, to avoid a reference
+ // error.
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
+ PrepareForBailout(expr, TOS_REG);
+ context()->Plug(v0);
+ } else if (proxy != NULL &&
+ proxy->var()->AsSlot() != NULL &&
+ proxy->var()->AsSlot()->type() == Slot::LOOKUP) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ Slot* slot = proxy->var()->AsSlot();
+ EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
+ __ li(a0, Operand(proxy->name()));
+ __ Push(cp, a0);
+ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ PrepareForBailout(expr, TOS_REG);
+ __ bind(&done);
+
+ context()->Plug(v0);
+ } else {
+ // This expression cannot throw a reference error at the top level.
+ context()->HandleExpression(expr);
+ }
}
@@ -660,50 +4050,305 @@ bool FullCodeGenerator::TryLiteralCompare(Token::Value op,
Label* if_true,
Label* if_false,
Label* fall_through) {
- UNIMPLEMENTED_MIPS();
- return false;
+ if (op != Token::EQ && op != Token::EQ_STRICT) return false;
+
+ // Check for the pattern: typeof <expression> == <string literal>.
+ Literal* right_literal = right->AsLiteral();
+ if (right_literal == NULL) return false;
+ Handle<Object> right_literal_value = right_literal->handle();
+ if (!right_literal_value->IsString()) return false;
+ UnaryOperation* left_unary = left->AsUnaryOperation();
+ if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false;
+ Handle<String> check = Handle<String>::cast(right_literal_value);
+
+ { AccumulatorValueContext context(this);
+ VisitForTypeofValue(left_unary->expression());
+ }
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+
+ if (check->Equals(isolate()->heap()->number_symbol())) {
+ __ JumpIfSmi(v0, if_true);
+ __ lw(v0, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
+ Split(eq, v0, Operand(at), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->string_symbol())) {
+ __ JumpIfSmi(v0, if_false);
+ // Check for undetectable objects => false.
+ __ GetObjectType(v0, v0, a1);
+ __ Branch(if_false, ge, a1, Operand(FIRST_NONSTRING_TYPE));
+ __ lbu(a1, FieldMemOperand(v0, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(eq, a1, Operand(zero_reg),
+ if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->boolean_symbol())) {
+ __ LoadRoot(at, Heap::kTrueValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ __ LoadRoot(at, Heap::kFalseValueRootIndex);
+ Split(eq, v0, Operand(at), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->undefined_symbol())) {
+ __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ __ JumpIfSmi(v0, if_false);
+ // Check for undetectable objects => true.
+ __ lw(v0, FieldMemOperand(v0, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(v0, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(ne, a1, Operand(zero_reg), if_true, if_false, fall_through);
+ } else if (check->Equals(isolate()->heap()->function_symbol())) {
+ __ JumpIfSmi(v0, if_false);
+ __ GetObjectType(v0, a1, v0); // Leave map in a1.
+ Split(ge, v0, Operand(FIRST_FUNCTION_CLASS_TYPE),
+ if_true, if_false, fall_through);
+
+ } else if (check->Equals(isolate()->heap()->object_symbol())) {
+ __ JumpIfSmi(v0, if_false);
+ __ LoadRoot(at, Heap::kNullValueRootIndex);
+ __ Branch(if_true, eq, v0, Operand(at));
+ // Check for JS objects => true.
+ __ GetObjectType(v0, v0, a1);
+ __ Branch(if_false, lo, a1, Operand(FIRST_JS_OBJECT_TYPE));
+ __ lbu(a1, FieldMemOperand(v0, Map::kInstanceTypeOffset));
+ __ Branch(if_false, hs, a1, Operand(FIRST_FUNCTION_CLASS_TYPE));
+ // Check for undetectable objects => false.
+ __ lbu(a1, FieldMemOperand(v0, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(eq, a1, Operand(zero_reg), if_true, if_false, fall_through);
+ } else {
+ if (if_false != fall_through) __ jmp(if_false);
+ }
+
+ return true;
}
void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ CompareOperation");
+ SetSourcePosition(expr->position());
+
+ // Always perform the comparison for its control flow. Pack the result
+ // into the expression's context after the comparison is performed.
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ // First we try a fast inlined version of the compare when one of
+ // the operands is a literal.
+ Token::Value op = expr->op();
+ Expression* left = expr->left();
+ Expression* right = expr->right();
+ if (TryLiteralCompare(op, left, right, if_true, if_false, fall_through)) {
+ context()->Plug(if_true, if_false);
+ return;
+ }
+
+ VisitForStackValue(expr->left());
+ switch (op) {
+ case Token::IN:
+ VisitForStackValue(expr->right());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
+ PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ Split(eq, v0, Operand(t0), if_true, if_false, fall_through);
+ break;
+
+ case Token::INSTANCEOF: {
+ VisitForStackValue(expr->right());
+ InstanceofStub stub(InstanceofStub::kNoFlags);
+ __ CallStub(&stub);
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ // The stub returns 0 for true.
+ Split(eq, v0, Operand(zero_reg), if_true, if_false, fall_through);
+ break;
+ }
+
+ default: {
+ VisitForAccumulatorValue(expr->right());
+ Condition cc = eq;
+ bool strict = false;
+ switch (op) {
+ case Token::EQ_STRICT:
+ strict = true;
+ // Fall through.
+ case Token::EQ:
+ cc = eq;
+ __ mov(a0, result_register());
+ __ pop(a1);
+ break;
+ case Token::LT:
+ cc = lt;
+ __ mov(a0, result_register());
+ __ pop(a1);
+ break;
+ case Token::GT:
+ // Reverse left and right sides to obtain ECMA-262 conversion order.
+ cc = lt;
+ __ mov(a1, result_register());
+ __ pop(a0);
+ break;
+ case Token::LTE:
+ // Reverse left and right sides to obtain ECMA-262 conversion order.
+ cc = ge;
+ __ mov(a1, result_register());
+ __ pop(a0);
+ break;
+ case Token::GTE:
+ cc = ge;
+ __ mov(a0, result_register());
+ __ pop(a1);
+ break;
+ case Token::IN:
+ case Token::INSTANCEOF:
+ default:
+ UNREACHABLE();
+ }
+
+ bool inline_smi_code = ShouldInlineSmiCase(op);
+ JumpPatchSite patch_site(masm_);
+ if (inline_smi_code) {
+ Label slow_case;
+ __ Or(a2, a0, Operand(a1));
+ patch_site.EmitJumpIfNotSmi(a2, &slow_case);
+ Split(cc, a1, Operand(a0), if_true, if_false, NULL);
+ __ bind(&slow_case);
+ }
+ // Record position and call the compare IC.
+ SetSourcePosition(expr->position());
+ Handle<Code> ic = CompareIC::GetUninitialized(op);
+ EmitCallIC(ic, &patch_site, expr->id());
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ Split(cc, v0, Operand(zero_reg), if_true, if_false, fall_through);
+ }
+ }
+
+ // Convert the result of the comparison into one expected for this
+ // expression's context.
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) {
- UNIMPLEMENTED_MIPS();
+ Comment cmnt(masm_, "[ CompareToNull");
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ context()->PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ VisitForAccumulatorValue(expr->expression());
+ PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
+ __ mov(a0, result_register());
+ __ LoadRoot(a1, Heap::kNullValueRootIndex);
+ if (expr->is_strict()) {
+ Split(eq, a0, Operand(a1), if_true, if_false, fall_through);
+ } else {
+ __ Branch(if_true, eq, a0, Operand(a1));
+ __ LoadRoot(a1, Heap::kUndefinedValueRootIndex);
+ __ Branch(if_true, eq, a0, Operand(a1));
+ __ And(at, a0, Operand(kSmiTagMask));
+ __ Branch(if_false, eq, at, Operand(zero_reg));
+ // It can be an undetectable object.
+ __ lw(a1, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset));
+ __ And(a1, a1, Operand(1 << Map::kIsUndetectable));
+ Split(ne, a1, Operand(zero_reg), if_true, if_false, fall_through);
+ }
+ context()->Plug(if_true, if_false);
}
void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
- UNIMPLEMENTED_MIPS();
+ __ lw(v0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ context()->Plug(v0);
}
Register FullCodeGenerator::result_register() {
- UNIMPLEMENTED_MIPS();
return v0;
}
Register FullCodeGenerator::context_register() {
- UNIMPLEMENTED_MIPS();
return cp;
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
- UNIMPLEMENTED_MIPS();
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ RelocInfo::Mode mode,
+ unsigned ast_id) {
+ ASSERT(mode == RelocInfo::CODE_TARGET ||
+ mode == RelocInfo::CODE_TARGET_CONTEXT);
+ Counters* counters = isolate()->counters();
+ switch (ic->kind()) {
+ case Code::LOAD_IC:
+ __ IncrementCounter(counters->named_load_full(), 1, a1, a2);
+ break;
+ case Code::KEYED_LOAD_IC:
+ __ IncrementCounter(counters->keyed_load_full(), 1, a1, a2);
+ break;
+ case Code::STORE_IC:
+ __ IncrementCounter(counters->named_store_full(), 1, a1, a2);
+ break;
+ case Code::KEYED_STORE_IC:
+ __ IncrementCounter(counters->keyed_store_full(), 1, a1, a2);
+ default:
+ break;
+ }
+ if (ast_id == kNoASTId || mode == RelocInfo::CODE_TARGET_CONTEXT) {
+ __ Call(ic, mode);
+ } else {
+ ASSERT(mode == RelocInfo::CODE_TARGET);
+ mode = RelocInfo::CODE_TARGET_WITH_ID;
+ __ CallWithAstId(ic, mode, ast_id);
+ }
+}
+
+
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ JumpPatchSite* patch_site,
+ unsigned ast_id) {
+ Counters* counters = isolate()->counters();
+ switch (ic->kind()) {
+ case Code::LOAD_IC:
+ __ IncrementCounter(counters->named_load_full(), 1, a1, a2);
+ break;
+ case Code::KEYED_LOAD_IC:
+ __ IncrementCounter(counters->keyed_load_full(), 1, a1, a2);
+ break;
+ case Code::STORE_IC:
+ __ IncrementCounter(counters->named_store_full(), 1, a1, a2);
+ break;
+ case Code::KEYED_STORE_IC:
+ __ IncrementCounter(counters->keyed_store_full(), 1, a1, a2);
+ default:
+ break;
+ }
+
+ if (ast_id == kNoASTId) {
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ } else {
+ __ CallWithAstId(ic, RelocInfo::CODE_TARGET_WITH_ID, ast_id);
+ }
+ if (patch_site != NULL && patch_site->is_bound()) {
+ patch_site->EmitPatchInfo();
+ } else {
+ __ nop(); // Signals no inlined code.
+ }
}
void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
- UNIMPLEMENTED_MIPS();
+ ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
+ __ sw(value, MemOperand(fp, frame_offset));
}
void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
- UNIMPLEMENTED_MIPS();
+ __ lw(dst, ContextOperand(cp, context_index));
}
@@ -711,12 +4356,28 @@ void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
// Non-local control flow support.
void FullCodeGenerator::EnterFinallyBlock() {
- UNIMPLEMENTED_MIPS();
+ ASSERT(!result_register().is(a1));
+ // Store result register while executing finally block.
+ __ push(result_register());
+ // Cook return address in link register to stack (smi encoded Code* delta).
+ __ Subu(a1, ra, Operand(masm_->CodeObject()));
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ ASSERT_EQ(0, kSmiTag);
+ __ Addu(a1, a1, Operand(a1)); // Convert to smi.
+ __ push(a1);
}
void FullCodeGenerator::ExitFinallyBlock() {
- UNIMPLEMENTED_MIPS();
+ ASSERT(!result_register().is(a1));
+ // Restore result register from stack.
+ __ pop(a1);
+ // Uncook return address and return.
+ __ pop(result_register());
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
+ __ sra(a1, a1, 1); // Un-smi-tag value.
+ __ Addu(at, a1, Operand(masm_->CodeObject()));
+ __ Jump(at);
}
diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc
index fa8a7bb7..12c81c21 100644
--- a/src/mips/ic-mips.cc
+++ b/src/mips/ic-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -31,7 +31,7 @@
#if defined(V8_TARGET_ARCH_MIPS)
-#include "codegen-inl.h"
+#include "codegen.h"
#include "code-stubs.h"
#include "ic-inl.h"
#include "runtime.h"
@@ -48,175 +48,1410 @@ namespace internal {
#define __ ACCESS_MASM(masm)
+static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
+ Register type,
+ Label* global_object) {
+ // Register usage:
+ // type: holds the receiver instance type on entry.
+ __ Branch(global_object, eq, type, Operand(JS_GLOBAL_OBJECT_TYPE));
+ __ Branch(global_object, eq, type, Operand(JS_BUILTINS_OBJECT_TYPE));
+ __ Branch(global_object, eq, type, Operand(JS_GLOBAL_PROXY_TYPE));
+}
+
+
+// Generated code falls through if the receiver is a regular non-global
+// JS object with slow properties and no interceptors.
+static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register elements,
+ Register scratch0,
+ Register scratch1,
+ Label* miss) {
+ // Register usage:
+ // receiver: holds the receiver on entry and is unchanged.
+ // elements: holds the property dictionary on fall through.
+ // Scratch registers:
+ // scratch0: used to holds the receiver map.
+ // scratch1: used to holds the receiver instance type, receiver bit mask
+ // and elements map.
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // Check that the receiver is a valid JS object.
+ __ GetObjectType(receiver, scratch0, scratch1);
+ __ Branch(miss, lt, scratch1, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // If this assert fails, we have to check upper bound too.
+ ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+
+ GenerateGlobalInstanceTypeCheck(masm, scratch1, miss);
+
+ // Check that the global object does not require access checks.
+ __ lbu(scratch1, FieldMemOperand(scratch0, Map::kBitFieldOffset));
+ __ And(scratch1, scratch1, Operand((1 << Map::kIsAccessCheckNeeded) |
+ (1 << Map::kHasNamedInterceptor)));
+ __ Branch(miss, ne, scratch1, Operand(zero_reg));
+
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(scratch0, Heap::kHashTableMapRootIndex);
+ __ Branch(miss, ne, scratch1, Operand(scratch0));
+}
+
+
+// Helper function used from LoadIC/CallIC GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// result: Register for the result. It is only updated if a jump to the miss
+// label is not done. Can be the same as elements or name clobbering
+// one of these in the case of not jumping to the miss label.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+// The address returned from GenerateStringDictionaryProbes() in scratch2
+// is used.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register result,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry check that the value is a normal
+ // property.
+ __ bind(&done); // scratch2 == elements + 4 * index.
+ const int kElementsStartOffset = StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ And(at,
+ scratch1,
+ Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
+ __ Branch(miss, ne, at, Operand(zero_reg));
+
+ // Get the value at the masked, scaled index and return.
+ __ lw(result,
+ FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
+}
+
+
+// Helper function used from StoreIC::GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// value: The value to store.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+// The address returned from GenerateStringDictionaryProbes() in scratch2
+// is used.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register value,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry in the dictionary check that the value
+ // is a normal property that is not read only.
+ __ bind(&done); // scratch2 == elements + 4 * index.
+ const int kElementsStartOffset = StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask
+ = (PropertyDetails::TypeField::mask() |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ And(at, scratch1, Operand(kTypeAndReadOnlyMask));
+ __ Branch(miss, ne, at, Operand(zero_reg));
+
+ // Store the value at the masked, scaled index and return.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ Addu(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
+ __ sw(value, MemOperand(scratch2));
+
+ // Update the write barrier. Make sure not to clobber the value.
+ __ mov(scratch1, value);
+ __ RecordWrite(elements, scratch2, scratch1);
+}
+
+
+static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register key,
+ Register result,
+ Register reg0,
+ Register reg1,
+ Register reg2) {
+ // Register use:
+ //
+ // elements - holds the slow-case elements of the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the same as 'key' or 'result'.
+ // Unchanged on bailout so 'key' or 'result' can be used
+ // in further computation.
+ //
+ // Scratch registers:
+ //
+ // reg0 - holds the untagged key on entry and holds the hash once computed.
+ //
+ // reg1 - Used to hold the capacity mask of the dictionary.
+ //
+ // reg2 - Used for the index into the dictionary.
+ // at - Temporary (avoid MacroAssembler instructions also using 'at').
+ Label done;
+
+ // Compute the hash code from the untagged key. This must be kept in sync
+ // with ComputeIntegerHash in utils.h.
+ //
+ // hash = ~hash + (hash << 15);
+ __ nor(reg1, reg0, zero_reg);
+ __ sll(at, reg0, 15);
+ __ addu(reg0, reg1, at);
+
+ // hash = hash ^ (hash >> 12);
+ __ srl(at, reg0, 12);
+ __ xor_(reg0, reg0, at);
+
+ // hash = hash + (hash << 2);
+ __ sll(at, reg0, 2);
+ __ addu(reg0, reg0, at);
+
+ // hash = hash ^ (hash >> 4);
+ __ srl(at, reg0, 4);
+ __ xor_(reg0, reg0, at);
+
+ // hash = hash * 2057;
+ __ li(reg1, Operand(2057));
+ __ mul(reg0, reg0, reg1);
+
+ // hash = hash ^ (hash >> 16);
+ __ srl(at, reg0, 16);
+ __ xor_(reg0, reg0, at);
+
+ // Compute the capacity mask.
+ __ lw(reg1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset));
+ __ sra(reg1, reg1, kSmiTagSize);
+ __ Subu(reg1, reg1, Operand(1));
+
+ // Generate an unrolled loop that performs a few probes before giving up.
+ static const int kProbes = 4;
+ for (int i = 0; i < kProbes; i++) {
+ // Use reg2 for index calculations and keep the hash intact in reg0.
+ __ mov(reg2, reg0);
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (i > 0) {
+ __ Addu(reg2, reg2, Operand(NumberDictionary::GetProbeOffset(i)));
+ }
+ __ and_(reg2, reg2, reg1);
+
+ // Scale the index by multiplying by the element size.
+ ASSERT(NumberDictionary::kEntrySize == 3);
+ __ sll(at, reg2, 1); // 2x.
+ __ addu(reg2, reg2, at); // reg2 = reg2 * 3.
+
+ // Check if the key is identical to the name.
+ __ sll(at, reg2, kPointerSizeLog2);
+ __ addu(reg2, elements, at);
+
+ __ lw(at, FieldMemOperand(reg2, NumberDictionary::kElementsStartOffset));
+ if (i != kProbes - 1) {
+ __ Branch(&done, eq, key, Operand(at));
+ } else {
+ __ Branch(miss, ne, key, Operand(at));
+ }
+ }
+
+ __ bind(&done);
+ // Check that the value is a normal property.
+ // reg2: elements + (index * kPointerSize).
+ const int kDetailsOffset =
+ NumberDictionary::kElementsStartOffset + 2 * kPointerSize;
+ __ lw(reg1, FieldMemOperand(reg2, kDetailsOffset));
+ __ And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask())));
+ __ Branch(miss, ne, at, Operand(zero_reg));
+
+ // Get the value at the masked, scaled index and return.
+ const int kValueOffset =
+ NumberDictionary::kElementsStartOffset + kPointerSize;
+ __ lw(result, FieldMemOperand(reg2, kValueOffset));
+}
+
+
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ Label miss;
+
+ StubCompiler::GenerateLoadArrayLength(masm, a0, a3, &miss);
+ __ bind(&miss);
+ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
void LoadIC::GenerateStringLength(MacroAssembler* masm, bool support_wrappers) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- lr : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ Label miss;
+
+ StubCompiler::GenerateLoadStringLength(masm, a0, a1, a3, &miss,
+ support_wrappers);
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- lr : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ Label miss;
+
+ StubCompiler::GenerateLoadFunctionPrototype(masm, a0, a1, a3, &miss);
+ __ bind(&miss);
+ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
-// Defined in ic.cc.
-Object* CallIC_Miss(Arguments args);
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register map,
+ Register scratch,
+ int interceptor_bit,
+ Label* slow) {
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, slow);
+ // Get the map of the receiver.
+ __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ // Check bit field.
+ __ lbu(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ And(at, scratch, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
+ __ Branch(slow, ne, at, Operand(zero_reg));
+ // Check that the object is some kind of JS object EXCEPT JS Value type.
+ // In the case that the object is a value-wrapper object,
+ // we enter the runtime system to make sure that indexing into string
+ // objects work as intended.
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
+ __ lbu(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ Branch(slow, lt, scratch, Operand(JS_OBJECT_TYPE));
+}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
- UNIMPLEMENTED_MIPS();
+// Loads an indexed element from a fast case array.
+// If not_fast_array is NULL, doesn't perform the elements map check.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register elements,
+ Register scratch1,
+ Register scratch2,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ //
+ // receiver - holds the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // elements - holds the elements of the receiver on exit.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the the same as 'receiver' or 'key'.
+ // Unchanged on bailout so 'receiver' and 'key' can be safely
+ // used by further computation.
+ //
+ // Scratch registers:
+ //
+ // scratch1 - used to hold elements map and elements length.
+ // Holds the elements map if not_fast_array branch is taken.
+ //
+ // scratch2 - used to hold the loaded value.
+
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ if (not_fast_array != NULL) {
+ // Check that the object is in fast mode (not dictionary).
+ __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kFixedArrayMapRootIndex);
+ __ Branch(not_fast_array, ne, scratch1, Operand(at));
+ } else {
+ __ AssertFastElements(elements);
+ }
+
+ // Check that the key (index) is within bounds.
+ __ lw(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ Branch(out_of_range, hs, key, Operand(scratch1));
+
+ // Fast case: Do the load.
+ __ Addu(scratch1, elements,
+ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // The key is a smi.
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ sll(at, key, kPointerSizeLog2 - kSmiTagSize);
+ __ addu(at, at, scratch1);
+ __ lw(scratch2, MemOperand(at));
+
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ // In case the loaded value is the_hole we have to consult GetProperty
+ // to ensure the prototype chain is searched.
+ __ Branch(out_of_range, eq, scratch2, Operand(at));
+ __ mov(result, scratch2);
}
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
- UNIMPLEMENTED_MIPS();
+// Checks whether a key is an array index string or a symbol string.
+// Falls through if a key is a symbol.
+static void GenerateKeyStringCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_symbol) {
+ // The key is not a smi.
+ // Is it a string?
+ __ GetObjectType(key, map, hash);
+ __ Branch(not_symbol, ge, hash, Operand(FIRST_NONSTRING_TYPE));
+
+ // Is the string an array index, with cached numeric value?
+ __ lw(hash, FieldMemOperand(key, String::kHashFieldOffset));
+ __ And(at, hash, Operand(String::kContainsCachedArrayIndexMask));
+ __ Branch(index_string, eq, at, Operand(zero_reg));
+
+ // Is the string a symbol?
+ // map: key map
+ __ lbu(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ ASSERT(kSymbolTag != 0);
+ __ And(at, hash, Operand(kIsSymbolMask));
+ __ Branch(not_symbol, eq, at, Operand(zero_reg));
}
-void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
- UNIMPLEMENTED_MIPS();
+// Defined in ic.cc.
+Object* CallIC_Miss(Arguments args);
+
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a1 : receiver
+ // -- a2 : name
+ // -----------------------------------
+ Label number, non_number, non_string, boolean, probe, miss;
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(kind,
+ NOT_IN_LOOP,
+ MONOMORPHIC,
+ extra_ic_state,
+ NORMAL,
+ argc);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a1, a2, a3, t0, t1);
+
+ // If the stub cache probing failed, the receiver might be a value.
+ // For value objects, we use the map of the prototype objects for
+ // the corresponding JSValue for the cache and that is what we need
+ // to probe.
+ //
+ // Check for number.
+ __ JumpIfSmi(a1, &number, t1);
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(&non_number, ne, a3, Operand(HEAP_NUMBER_TYPE));
+ __ bind(&number);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::NUMBER_FUNCTION_INDEX, a1);
+ __ Branch(&probe);
+
+ // Check for string.
+ __ bind(&non_number);
+ __ Branch(&non_string, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE));
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::STRING_FUNCTION_INDEX, a1);
+ __ Branch(&probe);
+
+ // Check for boolean.
+ __ bind(&non_string);
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ __ Branch(&boolean, eq, a1, Operand(t0));
+ __ LoadRoot(t1, Heap::kFalseValueRootIndex);
+ __ Branch(&miss, ne, a1, Operand(t1));
+ __ bind(&boolean);
+ StubCompiler::GenerateLoadGlobalFunctionPrototype(
+ masm, Context::BOOLEAN_FUNCTION_INDEX, a1);
+
+ // Probe the stub cache for the value object.
+ __ bind(&probe);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a1, a2, a3, t0, t1);
+
+ __ bind(&miss);
}
-void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
- UNIMPLEMENTED_MIPS();
+static void GenerateFunctionTailCall(MacroAssembler* masm,
+ int argc,
+ Label* miss,
+ Register scratch) {
+ // a1: function
+
+ // Check that the value isn't a smi.
+ __ JumpIfSmi(a1, miss);
+
+ // Check that the value is a JSFunction.
+ __ GetObjectType(a1, scratch, scratch);
+ __ Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE));
+
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(a1, actual, JUMP_FUNCTION);
}
-void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
- UNIMPLEMENTED_MIPS();
+static void GenerateCallNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Get the receiver of the function from the stack into a1.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ GenerateStringDictionaryReceiverCheck(masm, a1, a0, a3, t0, &miss);
+
+ // a0: elements
+ // Search the dictionary - put result in register a1.
+ GenerateDictionaryLoad(masm, &miss, a0, a2, a1, a3, t0);
+
+ GenerateFunctionTailCall(masm, argc, &miss, t0);
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
}
-void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
- UNIMPLEMENTED_MIPS();
+static void GenerateCallMiss(MacroAssembler* masm,
+ int argc,
+ IC::UtilityId id,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ if (id == IC::kCallIC_Miss) {
+ __ IncrementCounter(isolate->counters()->call_miss(), 1, a3, t0);
+ } else {
+ __ IncrementCounter(isolate->counters()->keyed_call_miss(), 1, a3, t0);
+ }
+
+ // Get the receiver of the function from the stack.
+ __ lw(a3, MemOperand(sp, argc*kPointerSize));
+
+ __ EnterInternalFrame();
+
+ // Push the receiver and the name of the function.
+ __ Push(a3, a2);
+
+ // Call the entry.
+ __ li(a0, Operand(2));
+ __ li(a1, Operand(ExternalReference(IC_Utility(id), isolate)));
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
+
+ // Move result to a1 and leave the internal frame.
+ __ mov(a1, v0);
+ __ LeaveInternalFrame();
+
+ // Check if the receiver is a global object of some sort.
+ // This can happen only for regular CallIC but not KeyedCallIC.
+ if (id == IC::kCallIC_Miss) {
+ Label invoke, global;
+ __ lw(a2, MemOperand(sp, argc * kPointerSize));
+ __ andi(t0, a2, kSmiTagMask);
+ __ Branch(&invoke, eq, t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, a3);
+ __ Branch(&global, eq, a3, Operand(JS_GLOBAL_OBJECT_TYPE));
+ __ Branch(&invoke, ne, a3, Operand(JS_BUILTINS_OBJECT_TYPE));
+
+ // Patch the receiver on the stack.
+ __ bind(&global);
+ __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a2, MemOperand(sp, argc * kPointerSize));
+ __ bind(&invoke);
+ }
+ // Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ ParameterCount actual(argc);
+ __ InvokeFunction(a1,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
}
-// Defined in ic.cc.
-Object* LoadIC_Miss(Arguments args);
+void CallIC::GenerateMiss(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
-void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state);
}
-void LoadIC::GenerateNormal(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into a1.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+ GenerateMiss(masm, argc, extra_ic_state);
}
-void LoadIC::GenerateMiss(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
+ GenerateMiss(masm, argc, Code::kNoExtraICState);
}
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- UNIMPLEMENTED_MIPS();
- return false;
+void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState);
}
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- UNIMPLEMENTED_MIPS();
- return false;
+void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into a1.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ Label do_call, slow_call, slow_load, slow_reload_receiver;
+ Label check_number_dictionary, check_string, lookup_monomorphic_cache;
+ Label index_smi, index_string;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(a2, &check_string);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, a1, a0, a3, Map::kHasIndexedInterceptor, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, a1, a2, t0, a3, a0, a1, &check_number_dictionary, &slow_load);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1, a0, a3);
+
+ __ bind(&do_call);
+ // receiver in a1 is not used after this point.
+ // a2: key
+ // a1: function
+
+ GenerateFunctionTailCall(masm, argc, &slow_call, a0);
+
+ __ bind(&check_number_dictionary);
+ // a2: key
+ // a3: elements map
+ // t0: elements pointer
+ // Check whether the elements is a number dictionary.
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&slow_load, ne, a3, Operand(at));
+ __ sra(a0, a2, kSmiTagSize);
+ // a0: untagged index
+ GenerateNumberDictionaryLoad(masm, &slow_load, t0, a2, a1, a0, a3, t1);
+ __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, a0, a3);
+ __ jmp(&do_call);
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, a0, a3);
+ __ EnterInternalFrame();
+ __ push(a2); // Save the key.
+ __ Push(a1, a2); // Pass the receiver and the key.
+ __ CallRuntime(Runtime::kKeyedGetProperty, 2);
+ __ pop(a2); // Restore the key.
+ __ LeaveInternalFrame();
+ __ mov(a1, v0);
+ __ jmp(&do_call);
+
+ __ bind(&check_string);
+ GenerateKeyStringCheck(masm, a2, a0, a3, &index_string, &slow_call);
+
+ // The key is known to be a symbol.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(
+ masm, a1, a0, a3, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
+
+ __ lw(a0, FieldMemOperand(a1, JSObject::kPropertiesOffset));
+ __ lw(a3, FieldMemOperand(a0, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&lookup_monomorphic_cache, ne, a3, Operand(at));
+
+ GenerateDictionaryLoad(masm, &slow_load, a0, a2, a1, a3, t0);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1, a0, a3);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1, a0, a3);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor symbol,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub,
+ // that will get fetched next time.
+ __ IncrementCounter(counters->keyed_call_generic_slow(), 1, a0, a3);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_string);
+ __ IndexFromHash(a3, a2);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
}
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- UNIMPLEMENTED_MIPS();
- return false;
+void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Check if the name is a string.
+ Label miss;
+ __ JumpIfSmi(a2, &miss);
+ __ IsObjectJSStringType(a2, a0, &miss);
+
+ GenerateCallNormal(masm, argc);
+ __ bind(&miss);
+ GenerateMiss(masm, argc);
}
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- UNIMPLEMENTED_MIPS();
- return false;
+// Defined in ic.cc.
+Object* LoadIC_Miss(Arguments args);
+
+void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+
+ // Probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
+ NOT_IN_LOOP,
+ MONOMORPHIC);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a0, a2, a3, t0, t1);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
}
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- UNIMPLEMENTED_MIPS();
- return false;
+void LoadIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- lr : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ Label miss;
+
+ GenerateStringDictionaryReceiverCheck(masm, a0, a1, a3, t0, &miss);
+
+ // a1: elements
+ GenerateDictionaryLoad(masm, &miss, a1, a2, v0, a3, t0);
+ __ Ret();
+
+ // Cache miss: Jump to runtime.
+ __ bind(&miss);
+ GenerateMiss(masm);
}
-Object* KeyedLoadIC_Miss(Arguments args);
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- a0 : receiver
+ // -- sp[0] : receiver
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, a3, t0);
+
+ __ mov(a3, a0);
+ __ Push(a3, a2);
+
+ // Perform tail call to the entry.
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss), isolate);
+ __ TailCallExternalReference(ref, 2, 1);
+}
-void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Isolate* isolate = masm->isolate();
+
+ __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, a3, t0);
+
+ __ Push(a1, a0);
+
+ // Perform tail call to the entry.
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric), isolate)
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
+
+ __ TailCallExternalReference(ref, 2, 1);
}
void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+
+ __ Push(a1, a0);
+
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
}
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label slow, check_string, index_smi, index_string, property_array_property;
+ Label probe_dictionary, check_number_dictionary;
+
+ Register key = a0;
+ Register receiver = a1;
+
+ Isolate* isolate = masm->isolate();
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(key, &check_string);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, receiver, a2, a3, Map::kHasIndexedInterceptor, &slow);
+
+ // Check the "has fast elements" bit in the receiver's map which is
+ // now in a2.
+ __ lbu(a3, FieldMemOperand(a2, Map::kBitField2Offset));
+ __ And(at, a3, Operand(1 << Map::kHasFastElements));
+ __ Branch(&check_number_dictionary, eq, at, Operand(zero_reg));
+
+ GenerateFastArrayLoad(
+ masm, receiver, key, t0, a3, a2, v0, NULL, &slow);
+
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1, a2, a3);
+ __ Ret();
+
+ __ bind(&check_number_dictionary);
+ __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ lw(a3, FieldMemOperand(t0, JSObject::kMapOffset));
+
+ // Check whether the elements is a number dictionary.
+ // a0: key
+ // a3: elements map
+ // t0: elements
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&slow, ne, a3, Operand(at));
+ __ sra(a2, a0, kSmiTagSize);
+ GenerateNumberDictionaryLoad(masm, &slow, t0, a0, v0, a2, a3, t1);
+ __ Ret();
+
+ // Slow case, key and receiver still in a0 and a1.
+ __ bind(&slow);
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_slow(),
+ 1,
+ a2,
+ a3);
+ GenerateRuntimeGetProperty(masm);
+
+ __ bind(&check_string);
+ GenerateKeyStringCheck(masm, key, a2, a3, &index_string, &slow);
+
+ GenerateKeyedLoadReceiverCheck(
+ masm, receiver, a2, a3, Map::kHasIndexedInterceptor, &slow);
+
+
+ // If the receiver is a fast-case object, check the keyed lookup
+ // cache. Otherwise probe the dictionary.
+ __ lw(a3, FieldMemOperand(a1, JSObject::kPropertiesOffset));
+ __ lw(t0, FieldMemOperand(a3, HeapObject::kMapOffset));
+ __ LoadRoot(at, Heap::kHashTableMapRootIndex);
+ __ Branch(&probe_dictionary, eq, t0, Operand(at));
+
+ // Load the map of the receiver, compute the keyed lookup cache hash
+ // based on 32 bits of the map pointer and the string hash.
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ sra(a3, a2, KeyedLookupCache::kMapHashShift);
+ __ lw(t0, FieldMemOperand(a0, String::kHashFieldOffset));
+ __ sra(at, t0, String::kHashShift);
+ __ xor_(a3, a3, at);
+ __ And(a3, a3, Operand(KeyedLookupCache::kCapacityMask));
+
+ // Load the key (consisting of map and symbol) from the cache and
+ // check for match.
+ ExternalReference cache_keys =
+ ExternalReference::keyed_lookup_cache_keys(isolate);
+ __ li(t0, Operand(cache_keys));
+ __ sll(at, a3, kPointerSizeLog2 + 1);
+ __ addu(t0, t0, at);
+ __ lw(t1, MemOperand(t0)); // Move t0 to symbol.
+ __ Addu(t0, t0, Operand(kPointerSize));
+ __ Branch(&slow, ne, a2, Operand(t1));
+ __ lw(t1, MemOperand(t0));
+ __ Branch(&slow, ne, a0, Operand(t1));
+
+ // Get field offset.
+ // a0 : key
+ // a1 : receiver
+ // a2 : receiver's map
+ // a3 : lookup cache index
+ ExternalReference cache_field_offsets =
+ ExternalReference::keyed_lookup_cache_field_offsets(isolate);
+ __ li(t0, Operand(cache_field_offsets));
+ __ sll(at, a3, kPointerSizeLog2);
+ __ addu(at, t0, at);
+ __ lw(t1, MemOperand(at));
+ __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset));
+ __ Subu(t1, t1, t2);
+ __ Branch(&property_array_property, ge, t1, Operand(zero_reg));
+
+ // Load in-object property.
+ __ lbu(t2, FieldMemOperand(a2, Map::kInstanceSizeOffset));
+ __ addu(t2, t2, t1); // Index from start of object.
+ __ Subu(a1, a1, Operand(kHeapObjectTag)); // Remove the heap tag.
+ __ sll(at, t2, kPointerSizeLog2);
+ __ addu(at, a1, at);
+ __ lw(v0, MemOperand(at));
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
+ 1,
+ a2,
+ a3);
+ __ Ret();
+
+ // Load property array property.
+ __ bind(&property_array_property);
+ __ lw(a1, FieldMemOperand(a1, JSObject::kPropertiesOffset));
+ __ Addu(a1, a1, FixedArray::kHeaderSize - kHeapObjectTag);
+ __ sll(t0, t1, kPointerSizeLog2);
+ __ Addu(t0, t0, a1);
+ __ lw(v0, MemOperand(t0));
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
+ 1,
+ a2,
+ a3);
+ __ Ret();
+
+
+ // Do a quick inline probe of the receiver's dictionary, if it
+ // exists.
+ __ bind(&probe_dictionary);
+ // a1: receiver
+ // a0: key
+ // a3: elements
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ lbu(a2, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ GenerateGlobalInstanceTypeCheck(masm, a2, &slow);
+ // Load the property to v0.
+ GenerateDictionaryLoad(masm, &slow, a3, a0, v0, a2, t0);
+ __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(),
+ 1,
+ a2,
+ a3);
+ __ Ret();
+
+ __ bind(&index_string);
+ __ IndexFromHash(a3, key);
+ // Now jump to the place where smi keys are handled.
+ __ Branch(&index_smi);
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key (index)
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ Register receiver = a1;
+ Register index = a0;
+ Register scratch1 = a2;
+ Register scratch2 = a3;
+ Register result = v0;
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ &miss, // When index out of range.
+ STRING_INDEX_IS_ARRAY_INDEX);
+ char_at_generator.GenerateFast(masm);
+ __ Ret();
+
+ StubRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm, call_helper);
+
+ __ bind(&miss);
+ GenerateMiss(masm, false);
}
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
StrictModeFlag strict_mode) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(a2, a1, a0);
+ __ li(a1, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
+ __ li(a0, Operand(Smi::FromInt(strict_mode))); // Strict mode.
+ __ Push(a1, a0);
+
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
}
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
StrictModeFlag strict_mode) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ Label slow, fast, array, extra, exit;
+
+ // Register usage.
+ Register value = a0;
+ Register key = a1;
+ Register receiver = a2;
+ Register elements = a3; // Elements array of the receiver.
+ // t0 is used as ip in the arm version.
+ // t3-t4 are used as temporaries.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(key, &slow);
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(receiver, &slow);
+
+ // Get the map of the object.
+ __ lw(t3, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ // Check that the receiver does not require access checks. We need
+ // to do this because this generic stub does not perform map checks.
+ __ lbu(t0, FieldMemOperand(t3, Map::kBitFieldOffset));
+ __ And(t0, t0, Operand(1 << Map::kIsAccessCheckNeeded));
+ __ Branch(&slow, ne, t0, Operand(zero_reg));
+ // Check if the object is a JS array or not.
+ __ lbu(t3, FieldMemOperand(t3, Map::kInstanceTypeOffset));
+
+ __ Branch(&array, eq, t3, Operand(JS_ARRAY_TYPE));
+ // Check that the object is some kind of JS object.
+ __ Branch(&slow, lt, t3, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Object case: Check key against length in the elements array.
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ // Check that the object is in fast mode and writable.
+ __ lw(t3, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kFixedArrayMapRootIndex);
+ __ Branch(&slow, ne, t3, Operand(t0));
+ // Check array bounds. Both the key and the length of FixedArray are smis.
+ __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ Branch(&fast, lo, key, Operand(t0));
+ // Fall thru to slow if un-tagged index >= length.
+
+ // Slow case, handle jump to runtime.
+ __ bind(&slow);
+
+ // Entry registers are intact.
+ // a0: value.
+ // a1: key.
+ // a2: receiver.
+
+ GenerateRuntimeSetProperty(masm, strict_mode);
+
+ // Extra capacity case: Check if there is extra capacity to
+ // perform the store and update the length. Used for adding one
+ // element to the array by writing to array[array.length].
+
+ __ bind(&extra);
+ // Only support writing to array[array.length].
+ __ Branch(&slow, ne, key, Operand(t0));
+ // Check for room in the elements backing store.
+ // Both the key and the length of FixedArray are smis.
+ __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ Branch(&slow, hs, key, Operand(t0));
+ // Calculate key + 1 as smi.
+ ASSERT_EQ(0, kSmiTag);
+ __ Addu(t3, key, Operand(Smi::FromInt(1)));
+ __ sw(t3, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Branch(&fast);
+
+
+ // Array case: Get the length and the elements array from the JS
+ // array. Check that the array is in fast mode (and writable); if it
+ // is the length is always a smi.
+
+ __ bind(&array);
+ __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ __ lw(t3, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(t0, Heap::kFixedArrayMapRootIndex);
+ __ Branch(&slow, ne, t3, Operand(t0));
+
+ // Check the key against the length in the array.
+ __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Branch(&extra, hs, key, Operand(t0));
+ // Fall through to fast case.
+
+ __ bind(&fast);
+ // Fast case, store the value to the elements backing store.
+ __ Addu(t4, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ sll(t1, key, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(t4, t4, Operand(t1));
+ __ sw(value, MemOperand(t4));
+ // Skip write barrier if the written value is a smi.
+ __ JumpIfSmi(value, &exit);
+
+ // Update write barrier for the elements array address.
+ __ Subu(t3, t4, Operand(elements));
+
+ __ RecordWrite(elements, Operand(t3), t4, t5);
+ __ bind(&exit);
+
+ __ mov(v0, a0); // Return the value written.
+ __ Ret();
}
void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label slow;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(a1, &slow);
+
+ // Check that the key is an array index, that is Uint32.
+ __ And(t0, a0, Operand(kSmiTagMask | kSmiSignMask));
+ __ Branch(&slow, ne, t0, Operand(zero_reg));
+
+ // Get the map of the receiver.
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ lbu(a3, FieldMemOperand(a2, Map::kBitFieldOffset));
+ __ And(a3, a3, Operand(kSlowCaseBitFieldMask));
+ __ Branch(&slow, ne, a3, Operand(1 << Map::kHasIndexedInterceptor));
+ // Everything is fine, call runtime.
+ __ Push(a1, a0); // Receiver, key.
+
+ // Perform tail call to the entry.
+ __ TailCallExternalReference(ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor), masm->isolate()), 2, 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm, false);
}
-void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ __ Push(a2, a1, a0);
+
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ // Push receiver, key and value for runtime call.
+ // We can't use MultiPush as the order of the registers is important.
+ __ Push(a2, a1, a0);
+
+ // The slow case calls into the runtime to complete the store without causing
+ // an IC miss that would otherwise cause a transition to the generic stub.
+ ExternalReference ref =
+ ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
+
+ __ TailCallExternalReference(ref, 3, 1);
}
void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
StrictModeFlag strict_mode) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ // Get the receiver from the stack and probe the stub cache.
+ Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
+ NOT_IN_LOOP,
+ MONOMORPHIC,
+ strict_mode);
+ Isolate::Current()->stub_cache()->GenerateProbe(
+ masm, flags, a1, a2, a3, t0, t1);
+
+ // Cache miss: Jump to runtime.
+ GenerateMiss(masm);
}
void StoreIC::GenerateMiss(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ __ Push(a1, a2, a0);
+ // Perform tail call to the entry.
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss),
+ masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
}
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ //
+ // This accepts as a receiver anything JSObject::SetElementsLength accepts
+ // (currently anything except for external and pixel arrays which means
+ // anything with elements of FixedArray type.), but currently is restricted
+ // to JSArray.
+ // Value must be a number, but only smis are accepted as the most common case.
+
+ Label miss;
+
+ Register receiver = a1;
+ Register value = a0;
+ Register scratch = a3;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the object is a JS array.
+ __ GetObjectType(receiver, scratch, scratch);
+ __ Branch(&miss, ne, scratch, Operand(JS_ARRAY_TYPE));
+
+ // Check that elements are FixedArray.
+ // We rely on StoreIC_ArrayLength below to deal with all types of
+ // fast elements (including COW).
+ __ lw(scratch, FieldMemOperand(receiver, JSArray::kElementsOffset));
+ __ GetObjectType(scratch, scratch, scratch);
+ __ Branch(&miss, ne, scratch, Operand(FIXED_ARRAY_TYPE));
+
+ // Check that value is a smi.
+ __ JumpIfNotSmi(value, &miss);
+
+ // Prepare tail call to StoreIC_ArrayLength.
+ __ Push(receiver, value);
+
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength),
+ masm->isolate());
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&miss);
+
+ GenerateMiss(masm);
}
void StoreIC::GenerateNormal(MacroAssembler* masm) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateStringDictionaryReceiverCheck(masm, a1, a3, t0, t1, &miss);
+
+ GenerateDictionaryStore(masm, &miss, a3, a2, a0, t0, t1);
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->store_normal_hit(), 1, t0, t1);
+ __ Ret();
+
+ __ bind(&miss);
+ __ IncrementCounter(counters->store_normal_miss(), 1, t0, t1);
+ GenerateMiss(masm);
}
void StoreIC::GenerateGlobalProxy(MacroAssembler* masm,
StrictModeFlag strict_mode) {
- UNIMPLEMENTED_MIPS();
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ __ Push(a1, a2, a0);
+
+ __ li(a1, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
+ __ li(a0, Operand(Smi::FromInt(strict_mode)));
+ __ Push(a1, a0);
+
+ // Do tail-call to runtime routine.
+ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
}
@@ -224,18 +1459,119 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm,
Condition CompareIC::ComputeCondition(Token::Value op) {
- UNIMPLEMENTED_MIPS();
- return kNoCondition;
+ switch (op) {
+ case Token::EQ_STRICT:
+ case Token::EQ:
+ return eq;
+ case Token::LT:
+ return lt;
+ case Token::GT:
+ // Reverse left and right operands to obtain ECMA-262 conversion order.
+ return lt;
+ case Token::LTE:
+ // Reverse left and right operands to obtain ECMA-262 conversion order.
+ return ge;
+ case Token::GTE:
+ return ge;
+ default:
+ UNREACHABLE();
+ return kNoCondition;
+ }
}
void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) {
- UNIMPLEMENTED_MIPS();
+ HandleScope scope;
+ Handle<Code> rewritten;
+ State previous_state = GetState();
+ State state = TargetState(previous_state, false, x, y);
+ if (state == GENERIC) {
+ CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS, a1, a0);
+ rewritten = stub.GetCode();
+ } else {
+ ICCompareStub stub(op_, state);
+ rewritten = stub.GetCode();
+ }
+ set_target(*rewritten);
+
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[CompareIC (%s->%s)#%s]\n",
+ GetStateName(previous_state),
+ GetStateName(state),
+ Token::Name(op_));
+ }
+#endif
+
+ // Activate inlined smi code.
+ if (previous_state == UNINITIALIZED) {
+ PatchInlinedSmiCode(address());
+ }
}
void PatchInlinedSmiCode(Address address) {
- // Currently there is no smi inlining in the MIPS full code generator.
+ Address andi_instruction_address =
+ address + Assembler::kCallTargetAddressOffset;
+
+ // If the instruction following the call is not a andi at, rx, #yyy, nothing
+ // was inlined.
+ Instr instr = Assembler::instr_at(andi_instruction_address);
+ if (!Assembler::IsAndImmediate(instr)) {
+ return;
+ }
+
+ // The delta to the start of the map check instruction and the
+ // condition code uses at the patched jump.
+ int delta = Assembler::GetImmediate16(instr);
+ delta += Assembler::GetRs(instr) * kImm16Mask;
+ // If the delta is 0 the instruction is andi at, zero_reg, #0 which also
+ // signals that nothing was inlined.
+ if (delta == 0) {
+ return;
+ }
+
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[ patching ic at %p, andi=%p, delta=%d\n",
+ address, andi_instruction_address, delta);
+ }
+#endif
+
+ Address patch_address =
+ andi_instruction_address - delta * Instruction::kInstrSize;
+ Instr instr_at_patch = Assembler::instr_at(patch_address);
+ Instr branch_instr =
+ Assembler::instr_at(patch_address + Instruction::kInstrSize);
+ ASSERT(Assembler::IsAndImmediate(instr_at_patch));
+ ASSERT_EQ(0, Assembler::GetImmediate16(instr_at_patch));
+ ASSERT(Assembler::IsBranch(branch_instr));
+ if (Assembler::IsBeq(branch_instr)) {
+ // This is patching a "jump if not smi" site to be active.
+ // Changing:
+ // andi at, rx, 0
+ // Branch <target>, eq, at, Operand(zero_reg)
+ // to:
+ // andi at, rx, #kSmiTagMask
+ // Branch <target>, ne, at, Operand(zero_reg)
+ CodePatcher patcher(patch_address, 2);
+ Register reg = Register::from_code(Assembler::GetRs(instr_at_patch));
+ patcher.masm()->andi(at, reg, kSmiTagMask);
+ patcher.ChangeBranchCondition(ne);
+ } else {
+ ASSERT(Assembler::IsBne(branch_instr));
+ // This is patching a "jump if smi" site to be active.
+ // Changing:
+ // andi at, rx, 0
+ // Branch <target>, ne, at, Operand(zero_reg)
+ // to:
+ // andi at, rx, #kSmiTagMask
+ // Branch <target>, eq, at, Operand(zero_reg)
+ CodePatcher patcher(patch_address, 2);
+ Register reg = Register::from_code(Assembler::GetRs(instr_at_patch));
+ patcher.masm()->andi(at, reg, kSmiTagMask);
+ patcher.ChangeBranchCondition(eq);
+ }
}
diff --git a/src/mips/lithium-codegen-mips.h b/src/mips/lithium-codegen-mips.h
index 345d912c..2aec6845 100644
--- a/src/mips/lithium-codegen-mips.h
+++ b/src/mips/lithium-codegen-mips.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h
index e11dfabb..ebc1e43b 100644
--- a/src/mips/lithium-mips.h
+++ b/src/mips/lithium-mips.h
@@ -78,7 +78,7 @@ class LInstruction: public ZoneObject {
bool HasEnvironment() const {
UNIMPLEMENTED();
- return NULL;
+ return false;
}
virtual void PrintTo(StringStream* stream) const { UNIMPLEMENTED(); }
@@ -213,15 +213,13 @@ class LOsrEntry: public LInstruction {
class LChunk: public ZoneObject {
public:
- explicit LChunk(CompilationInfo* info, HGraph* graph) { }
+ explicit LChunk(HGraph* graph) { }
HGraph* graph() const {
UNIMPLEMENTED();
return NULL;
}
- CompilationInfo* info() const { return NULL; }
-
const ZoneList<LPointerMap*>* pointer_maps() const {
UNIMPLEMENTED();
return NULL;
@@ -271,6 +269,11 @@ class LChunk: public ZoneObject {
void MarkEmptyBlocks() { UNIMPLEMENTED(); }
+ CompilationInfo* info() const {
+ UNIMPLEMENTED();
+ return NULL;
+ }
+
#ifdef DEBUG
void Verify() { UNIMPLEMENTED(); }
#endif
@@ -279,7 +282,7 @@ class LChunk: public ZoneObject {
class LChunkBuilder BASE_EMBEDDED {
public:
- LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) { }
+ LChunkBuilder(CompilationInfo*&, HGraph* graph, LAllocator* allocator) { }
// Build the sequence for the graph.
LChunk* Build() {
diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc
index bd4ab480..8b342a28 100644
--- a/src/mips/macro-assembler-mips.cc
+++ b/src/mips/macro-assembler-mips.cc
@@ -25,29 +25,32 @@
// (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 <limits.h> // For LONG_MIN, LONG_MAX
+#include <limits.h> // For LONG_MIN, LONG_MAX.
#include "v8.h"
#if defined(V8_TARGET_ARCH_MIPS)
#include "bootstrapper.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "debug.h"
#include "runtime.h"
namespace v8 {
namespace internal {
-MacroAssembler::MacroAssembler(void* buffer, int size)
- : Assembler(buffer, size),
+MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
+ : Assembler(arg_isolate, buffer, size),
generating_stub_(false),
- allow_stub_calls_(true),
- code_object_(HEAP->undefined_value()) {
+ allow_stub_calls_(true) {
+ if (isolate() != NULL) {
+ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
+ isolate());
+ }
}
-// Arguments macros
+// Arguments macros.
#define COND_TYPED_ARGS Condition cond, Register r1, const Operand& r2
#define COND_ARGS cond, r1, r2
@@ -161,7 +164,7 @@ void MacroAssembler::StoreRoot(Register source,
void MacroAssembler::RecordWriteHelper(Register object,
Register address,
Register scratch) {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// Check that the object is not in new space.
Label not_in_new_space;
InNewSpace(object, scratch, ne, &not_in_new_space);
@@ -190,6 +193,77 @@ void MacroAssembler::RecordWriteHelper(Register object,
sw(scratch, MemOperand(object, Page::kDirtyFlagOffset));
}
+// Push and pop all registers that can hold pointers.
+void MacroAssembler::PushSafepointRegisters() {
+ // Safepoints expect a block of kNumSafepointRegisters values on the
+ // stack, so adjust the stack for unsaved registers.
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ ASSERT(num_unsaved >= 0);
+ Subu(sp, sp, Operand(num_unsaved * kPointerSize));
+ MultiPush(kSafepointSavedRegisters);
+}
+
+void MacroAssembler::PopSafepointRegisters() {
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ MultiPop(kSafepointSavedRegisters);
+ Addu(sp, sp, Operand(num_unsaved * kPointerSize));
+}
+
+void MacroAssembler::PushSafepointRegistersAndDoubles() {
+ PushSafepointRegisters();
+ Subu(sp, sp, Operand(FPURegister::kNumAllocatableRegisters * kDoubleSize));
+ for (int i = 0; i < FPURegister::kNumAllocatableRegisters; i+=2) {
+ FPURegister reg = FPURegister::FromAllocationIndex(i);
+ sdc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+}
+
+void MacroAssembler::PopSafepointRegistersAndDoubles() {
+ for (int i = 0; i < FPURegister::kNumAllocatableRegisters; i+=2) {
+ FPURegister reg = FPURegister::FromAllocationIndex(i);
+ ldc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+ Addu(sp, sp, Operand(FPURegister::kNumAllocatableRegisters * kDoubleSize));
+ PopSafepointRegisters();
+}
+
+void MacroAssembler::StoreToSafepointRegistersAndDoublesSlot(Register src,
+ Register dst) {
+ sw(src, SafepointRegistersAndDoublesSlot(dst));
+}
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register src, Register dst) {
+ sw(src, SafepointRegisterSlot(dst));
+}
+
+
+void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
+ lw(dst, SafepointRegisterSlot(src));
+}
+
+
+int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
+ // The registers are pushed starting with the highest encoding,
+ // which means that lowest encodings are closest to the stack pointer.
+ return kSafepointRegisterStackIndexMap[reg_code];
+}
+
+
+MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) {
+ return MemOperand(sp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
+}
+
+
+MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
+ // General purpose registers are pushed last on the stack.
+ int doubles_size = FPURegister::kNumAllocatableRegisters * kDoubleSize;
+ int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
+ return MemOperand(sp, doubles_size + register_offset);
+}
+
+
+
void MacroAssembler::InNewSpace(Register object,
Register scratch,
@@ -230,7 +304,7 @@ void MacroAssembler::RecordWrite(Register object,
// Clobber all input registers when running with the debug-code flag
// turned on to provoke errors.
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
li(object, Operand(BitCast<int32_t>(kZapValue)));
li(scratch0, Operand(BitCast<int32_t>(kZapValue)));
li(scratch1, Operand(BitCast<int32_t>(kZapValue)));
@@ -262,7 +336,7 @@ void MacroAssembler::RecordWrite(Register object,
// Clobber all input registers when running with the debug-code flag
// turned on to provoke errors.
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
li(object, Operand(BitCast<int32_t>(kZapValue)));
li(address, Operand(BitCast<int32_t>(kZapValue)));
li(scratch, Operand(BitCast<int32_t>(kZapValue)));
@@ -271,7 +345,7 @@ void MacroAssembler::RecordWrite(Register object,
// -----------------------------------------------------------------------------
-// Allocation support
+// Allocation support.
void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
@@ -297,15 +371,15 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
lw(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset));
// Check the context is a global context.
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// TODO(119): Avoid push(holder_reg)/pop(holder_reg).
- Push(holder_reg); // Temporarily save holder on the stack.
+ push(holder_reg); // Temporarily save holder on the stack.
// Read the first word and compare to the global_context_map.
lw(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset));
LoadRoot(at, Heap::kGlobalContextMapRootIndex);
Check(eq, "JSGlobalObject::global_context should be a global context.",
holder_reg, Operand(at));
- Pop(holder_reg); // Restore holder.
+ pop(holder_reg); // Restore holder.
}
// Check if both contexts are the same.
@@ -313,9 +387,9 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
Branch(&same_contexts, eq, scratch, Operand(at));
// Check the context is a global context.
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// TODO(119): Avoid push(holder_reg)/pop(holder_reg).
- Push(holder_reg); // Temporarily save holder on the stack.
+ push(holder_reg); // Temporarily save holder on the stack.
mov(holder_reg, at); // Move at to its holding place.
LoadRoot(at, Heap::kNullValueRootIndex);
Check(ne, "JSGlobalProxy::context() should not be null.",
@@ -326,7 +400,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
Check(eq, "JSGlobalObject::global_context should be a global context.",
holder_reg, Operand(at));
// Restore at is not needed. at is reloaded below.
- Pop(holder_reg); // Restore holder.
+ pop(holder_reg); // Restore holder.
// Restore at to holder's context.
lw(at, FieldMemOperand(holder_reg, JSGlobalProxy::kContextOffset));
}
@@ -346,7 +420,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
// ---------------------------------------------------------------------------
-// Instruction macros
+// Instruction macros.
void MacroAssembler::Addu(Register rd, Register rs, const Operand& rt) {
if (rt.is_reg()) {
@@ -500,6 +574,15 @@ void MacroAssembler::Nor(Register rd, Register rs, const Operand& rt) {
}
+void MacroAssembler::Neg(Register rs, const Operand& rt) {
+ ASSERT(rt.is_reg());
+ ASSERT(!at.is(rs));
+ ASSERT(!at.is(rt.rm()));
+ li(at, -1);
+ xor_(rs, rt.rm(), at);
+}
+
+
void MacroAssembler::Slt(Register rd, Register rs, const Operand& rt) {
if (rt.is_reg()) {
slt(rd, rs, rt.rm());
@@ -581,24 +664,13 @@ void MacroAssembler::li(Register rd, Operand j, bool gen2instr) {
}
// We need always the same number of instructions as we may need to patch
// this code to load another value which may need 2 instructions to load.
- if (is_int16(j.imm32_)) {
- nop();
- addiu(rd, zero_reg, j.imm32_);
- } else if (!(j.imm32_ & kHiMask)) {
- nop();
- ori(rd, zero_reg, j.imm32_);
- } else if (!(j.imm32_ & kImm16Mask)) {
- nop();
- lui(rd, (j.imm32_ & kHiMask) >> kLuiShift);
- } else {
- lui(rd, (j.imm32_ & kHiMask) >> kLuiShift);
- ori(rd, rd, (j.imm32_ & kImm16Mask));
- }
+ lui(rd, (j.imm32_ & kHiMask) >> kLuiShift);
+ ori(rd, rd, (j.imm32_ & kImm16Mask));
}
}
-// Exception-generating instructions and debugging support
+// Exception-generating instructions and debugging support.
void MacroAssembler::stop(const char* msg) {
// TO_UPGRADE: Just a break for now. Maybe we could upgrade it.
// We use the 0x54321 value to be able to find it easily when reading memory.
@@ -727,11 +799,11 @@ void MacroAssembler::Cvt_d_uw(FPURegister fd, Register rs) {
ASSERT(!rs.is(t9));
ASSERT(!rs.is(t8));
- // Save rs's MSB to t8
+ // Save rs's MSB to t8.
And(t8, rs, 0x80000000);
// Remove rs's MSB.
And(t9, rs, 0x7FFFFFFF);
- // Move t9 to fd
+ // Move t9 to fd.
mtc1(t9, fd);
// Convert fd to a real FP value.
@@ -839,7 +911,7 @@ void MacroAssembler::ConvertToInt32(Register source,
Subu(scratch2, scratch2, Operand(zero_exponent));
// Dest already has a Smi zero.
Branch(&done, lt, scratch2, Operand(zero_reg));
- if (!Isolate::Current()->cpu_features()->IsSupported(FPU)) {
+ if (!CpuFeatures::IsSupported(FPU)) {
// We have a shifted exponent between 0 and 30 in scratch2.
srl(dest, scratch2, HeapNumber::kExponentShift);
// We now have the exponent in dest. Subtract from 30 to get
@@ -848,7 +920,7 @@ void MacroAssembler::ConvertToInt32(Register source,
subu(dest, at, dest);
}
bind(&right_exponent);
- if (Isolate::Current()->cpu_features()->IsSupported(FPU)) {
+ if (CpuFeatures::IsSupported(FPU)) {
CpuFeatures::Scope scope(FPU);
// MIPS FPU instructions implementing double precision to integer
// conversion using round to zero. Since the FP value was qualified
@@ -898,6 +970,102 @@ void MacroAssembler::ConvertToInt32(Register source,
}
+void MacroAssembler::EmitOutOfInt32RangeTruncate(Register result,
+ Register input_high,
+ Register input_low,
+ Register scratch) {
+ Label done, normal_exponent, restore_sign;
+ // Extract the biased exponent in result.
+ Ext(result,
+ input_high,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+
+ // Check for Infinity and NaNs, which should return 0.
+ Subu(scratch, result, HeapNumber::kExponentMask);
+ movz(result, zero_reg, scratch);
+ Branch(&done, eq, scratch, Operand(zero_reg));
+
+ // Express exponent as delta to (number of mantissa bits + 31).
+ Subu(result,
+ result,
+ Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 31));
+
+ // If the delta is strictly positive, all bits would be shifted away,
+ // which means that we can return 0.
+ Branch(&normal_exponent, le, result, Operand(zero_reg));
+ mov(result, zero_reg);
+ Branch(&done);
+
+ bind(&normal_exponent);
+ const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1;
+ // Calculate shift.
+ Addu(scratch, result, Operand(kShiftBase + HeapNumber::kMantissaBits));
+
+ // Save the sign.
+ Register sign = result;
+ result = no_reg;
+ And(sign, input_high, Operand(HeapNumber::kSignMask));
+
+ // On ARM shifts > 31 bits are valid and will result in zero. On MIPS we need
+ // to check for this specific case.
+ Label high_shift_needed, high_shift_done;
+ Branch(&high_shift_needed, lt, scratch, Operand(32));
+ mov(input_high, zero_reg);
+ Branch(&high_shift_done);
+ bind(&high_shift_needed);
+
+ // Set the implicit 1 before the mantissa part in input_high.
+ Or(input_high,
+ input_high,
+ Operand(1 << HeapNumber::kMantissaBitsInTopWord));
+ // Shift the mantissa bits to the correct position.
+ // We don't need to clear non-mantissa bits as they will be shifted away.
+ // If they weren't, it would mean that the answer is in the 32bit range.
+ sllv(input_high, input_high, scratch);
+
+ bind(&high_shift_done);
+
+ // Replace the shifted bits with bits from the lower mantissa word.
+ Label pos_shift, shift_done;
+ li(at, 32);
+ subu(scratch, at, scratch);
+ Branch(&pos_shift, ge, scratch, Operand(zero_reg));
+
+ // Negate scratch.
+ Subu(scratch, zero_reg, scratch);
+ sllv(input_low, input_low, scratch);
+ Branch(&shift_done);
+
+ bind(&pos_shift);
+ srlv(input_low, input_low, scratch);
+
+ bind(&shift_done);
+ Or(input_high, input_high, Operand(input_low));
+ // Restore sign if necessary.
+ mov(scratch, sign);
+ result = sign;
+ sign = no_reg;
+ Subu(result, zero_reg, input_high);
+ movz(result, input_high, scratch);
+ bind(&done);
+}
+
+
+void MacroAssembler::GetLeastBitsFromSmi(Register dst,
+ Register src,
+ int num_least_bits) {
+ Ext(dst, src, kSmiTagSize, num_least_bits);
+}
+
+
+void MacroAssembler::GetLeastBitsFromInt32(Register dst,
+ Register src,
+ int num_least_bits) {
+ And(dst, src, Operand((1 << num_least_bits) - 1));
+}
+
+
// Emulated condtional branches do not emit a nop in the branch delay slot.
//
// BRANCH_ARGS_CHECK checks that conditional jump arguments are correct.
@@ -937,7 +1105,7 @@ void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs,
case ne:
bne(rs, r2, offset);
break;
- // Signed comparison
+ // Signed comparison.
case greater:
if (r2.is(zero_reg)) {
bgtz(rs, offset);
@@ -1028,7 +1196,7 @@ void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs,
li(r2, rt);
bne(rs, r2, offset);
break;
- // Signed comparison
+ // Signed comparison.
case greater:
if (rt.imm32_ == 0) {
bgtz(rs, offset);
@@ -1170,7 +1338,7 @@ void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
offset = shifted_branch_offset(L, false);
bne(rs, r2, offset);
break;
- // Signed comparison
+ // Signed comparison.
case greater:
if (r2.is(zero_reg)) {
offset = shifted_branch_offset(L, false);
@@ -1276,7 +1444,7 @@ void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
offset = shifted_branch_offset(L, false);
bne(rs, r2, offset);
break;
- // Signed comparison
+ // Signed comparison.
case greater:
if (rt.imm32_ == 0) {
offset = shifted_branch_offset(L, false);
@@ -1444,7 +1612,7 @@ void MacroAssembler::BranchAndLink(int16_t offset, Condition cond, Register rs,
bal(offset);
break;
- // Signed comparison
+ // Signed comparison.
case greater:
slt(scratch, r2, rs);
addiu(scratch, scratch, -1);
@@ -1539,7 +1707,7 @@ void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs,
bal(offset);
break;
- // Signed comparison
+ // Signed comparison.
case greater:
slt(scratch, r2, rs);
addiu(scratch, scratch, -1);
@@ -1642,7 +1810,7 @@ void MacroAssembler::Jump(const Operand& target,
Branch(2, NegateCondition(cond), rs, rt);
j(target.imm32_); // Will generate only one instruction.
}
- } else { // MustUseReg(target)
+ } else { // MustUseReg(target).
li(t9, target);
if (cond == cc_always) {
jr(t9);
@@ -1658,15 +1826,28 @@ void MacroAssembler::Jump(const Operand& target,
}
+int MacroAssembler::CallSize(Handle<Code> code, RelocInfo::Mode rmode) {
+ return 4 * kInstrSize;
+}
+
+
+int MacroAssembler::CallSize(Register reg) {
+ return 2 * kInstrSize;
+}
+
+
// Note: To call gcc-compiled C code on mips, you must call thru t9.
void MacroAssembler::Call(const Operand& target, BranchDelaySlot bdslot) {
BlockTrampolinePoolScope block_trampoline_pool(this);
if (target.is_reg()) {
jalr(target.rm());
- } else { // !target.is_reg()
+ } else { // !target.is_reg().
if (!MustUseReg(target.rmode_)) {
jal(target.imm32_);
- } else { // MustUseReg(target)
+ } else { // MustUseReg(target).
+ // Must record previous source positions before the
+ // li() generates a new code target.
+ positions_recorder()->WriteRecordedPositions();
li(t9, target);
jalr(t9);
}
@@ -1690,7 +1871,7 @@ void MacroAssembler::Call(const Operand& target,
Branch(2, NegateCondition(cond), rs, rt);
jalr(target.rm());
}
- } else { // !target.is_reg()
+ } else { // !target.is_reg().
if (!MustUseReg(target.rmode_)) {
if (cond == cc_always) {
jal(target.imm32_);
@@ -1714,6 +1895,20 @@ void MacroAssembler::Call(const Operand& target,
}
+void MacroAssembler::CallWithAstId(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ unsigned ast_id,
+ Condition cond,
+ Register r1,
+ const Operand& r2) {
+ ASSERT(rmode == RelocInfo::CODE_TARGET_WITH_ID);
+ ASSERT(ast_id != kNoASTId);
+ ASSERT(ast_id_for_reloc_info_ == kNoASTId);
+ ast_id_for_reloc_info_ = ast_id;
+ Call(reinterpret_cast<intptr_t>(code.location()), rmode, cond, r1, r2);
+}
+
+
void MacroAssembler::Drop(int count,
Condition cond,
Register reg,
@@ -1779,13 +1974,6 @@ void MacroAssembler::Call(Label* target) {
}
-void MacroAssembler::Move(Register dst, Register src) {
- if (!dst.is(src)) {
- mov(dst, src);
- }
-}
-
-
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
@@ -1800,7 +1988,7 @@ void MacroAssembler::DebugBreak() {
// ---------------------------------------------------------------------------
-// Exception handling
+// Exception handling.
void MacroAssembler::PushTryHandler(CodeLocation try_location,
HandlerType type) {
@@ -1868,6 +2056,159 @@ void MacroAssembler::PopTryHandler() {
}
+void MacroAssembler::Throw(Register value) {
+ // v0 is expected to hold the exception.
+ Move(v0, value);
+
+ // Adjust this code if not the case.
+ STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
+
+ // Drop the sp to the top of the handler.
+ li(a3, Operand(ExternalReference(Isolate::k_handler_address,
+ isolate())));
+ lw(sp, MemOperand(a3));
+
+ // Restore the next handler and frame pointer, discard handler state.
+ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
+ pop(a2);
+ sw(a2, MemOperand(a3));
+ STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
+ MultiPop(a3.bit() | fp.bit());
+
+ // Before returning we restore the context from the frame pointer if
+ // not NULL. The frame pointer is NULL in the exception handler of a
+ // JS entry frame.
+ // Set cp to NULL if fp is NULL.
+ Label done;
+ Branch(USE_DELAY_SLOT, &done, eq, fp, Operand(zero_reg));
+ mov(cp, zero_reg); // In branch delay slot.
+ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ bind(&done);
+
+#ifdef DEBUG
+ // When emitting debug_code, set ra as return address for the jump.
+ // 5 instructions: add: 1, pop: 2, jump: 2.
+ const int kOffsetRaInstructions = 5;
+ Label find_ra;
+
+ if (emit_debug_code()) {
+ // Compute ra for the Jump(t9).
+ const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize;
+
+ // This branch-and-link sequence is needed to get the current PC on mips,
+ // saved to the ra register. Then adjusted for instruction count.
+ bal(&find_ra); // bal exposes branch-delay.
+ nop(); // Branch delay slot nop.
+ bind(&find_ra);
+ addiu(ra, ra, kOffsetRaBytes);
+ }
+#endif
+
+ STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
+ pop(t9); // 2 instructions: lw, add sp.
+ Jump(t9); // 2 instructions: jr, nop (in delay slot).
+
+ if (emit_debug_code()) {
+ // Make sure that the expected number of instructions were generated.
+ ASSERT_EQ(kOffsetRaInstructions,
+ InstructionsGeneratedSince(&find_ra));
+ }
+}
+
+
+void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
+ Register value) {
+ // Adjust this code if not the case.
+ STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
+
+ // v0 is expected to hold the exception.
+ Move(v0, value);
+
+ // Drop sp to the top stack handler.
+ li(a3, Operand(ExternalReference(Isolate::k_handler_address, isolate())));
+ lw(sp, MemOperand(a3));
+
+ // Unwind the handlers until the ENTRY handler is found.
+ Label loop, done;
+ bind(&loop);
+ // Load the type of the current stack handler.
+ const int kStateOffset = StackHandlerConstants::kStateOffset;
+ lw(a2, MemOperand(sp, kStateOffset));
+ Branch(&done, eq, a2, Operand(StackHandler::ENTRY));
+ // Fetch the next handler in the list.
+ const int kNextOffset = StackHandlerConstants::kNextOffset;
+ lw(sp, MemOperand(sp, kNextOffset));
+ jmp(&loop);
+ bind(&done);
+
+ // Set the top handler address to next handler past the current ENTRY handler.
+ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
+ pop(a2);
+ sw(a2, MemOperand(a3));
+
+ if (type == OUT_OF_MEMORY) {
+ // Set external caught exception to false.
+ ExternalReference external_caught(
+ Isolate::k_external_caught_exception_address, isolate());
+ li(a0, Operand(false, RelocInfo::NONE));
+ li(a2, Operand(external_caught));
+ sw(a0, MemOperand(a2));
+
+ // Set pending exception and v0 to out of memory exception.
+ Failure* out_of_memory = Failure::OutOfMemoryException();
+ li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+ li(a2, Operand(ExternalReference(Isolate::k_pending_exception_address,
+ isolate())));
+ sw(v0, MemOperand(a2));
+ }
+
+ // Stack layout at this point. See also StackHandlerConstants.
+ // sp -> state (ENTRY)
+ // fp
+ // ra
+
+ // Discard handler state (a2 is not used) and restore frame pointer.
+ STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
+ MultiPop(a2.bit() | fp.bit()); // a2: discarded state.
+ // Before returning we restore the context from the frame pointer if
+ // not NULL. The frame pointer is NULL in the exception handler of a
+ // JS entry frame.
+ Label cp_null;
+ Branch(USE_DELAY_SLOT, &cp_null, eq, fp, Operand(zero_reg));
+ mov(cp, zero_reg); // In the branch delay slot.
+ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ bind(&cp_null);
+
+#ifdef DEBUG
+ // When emitting debug_code, set ra as return address for the jump.
+ // 5 instructions: add: 1, pop: 2, jump: 2.
+ const int kOffsetRaInstructions = 5;
+ Label find_ra;
+
+ if (emit_debug_code()) {
+ // Compute ra for the Jump(t9).
+ const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize;
+
+ // This branch-and-link sequence is needed to get the current PC on mips,
+ // saved to the ra register. Then adjusted for instruction count.
+ bal(&find_ra); // bal exposes branch-delay slot.
+ nop(); // Branch delay slot nop.
+ bind(&find_ra);
+ addiu(ra, ra, kOffsetRaBytes);
+ }
+#endif
+ STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
+ pop(t9); // 2 instructions: lw, add sp.
+ Jump(t9); // 2 instructions: jr, nop (in delay slot).
+
+ if (emit_debug_code()) {
+ // Make sure that the expected number of instructions were generated.
+ ASSERT_EQ(kOffsetRaInstructions,
+ InstructionsGeneratedSince(&find_ra));
+ }
+}
+
+
void MacroAssembler::AllocateInNewSpace(int object_size,
Register result,
Register scratch1,
@@ -1875,7 +2216,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
Label* gc_required,
AllocationFlags flags) {
if (!FLAG_inline_new) {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// Trash the registers to simulate an allocation failure.
li(result, 0x7091);
li(scratch1, 0x7191);
@@ -1923,7 +2264,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
lw(result, MemOperand(topaddr));
lw(t9, MemOperand(topaddr, kPointerSize));
} else {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// Assert that result actually contains top on entry. t9 is used
// immediately below so this use of t9 does not cause difference with
// respect to register content between debug and release mode.
@@ -1954,7 +2295,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
Label* gc_required,
AllocationFlags flags) {
if (!FLAG_inline_new) {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// Trash the registers to simulate an allocation failure.
li(result, 0x7091);
li(scratch1, 0x7191);
@@ -1992,7 +2333,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
lw(result, MemOperand(topaddr));
lw(t9, MemOperand(topaddr, kPointerSize));
} else {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
// Assert that result actually contains top on entry. t9 is used
// immediately below so this use of t9 does not cause difference with
// respect to register content between debug and release mode.
@@ -2015,7 +2356,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
Branch(gc_required, Ugreater, scratch2, Operand(t9));
// Update allocation top. result temporarily holds the new top.
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
And(t9, scratch2, Operand(kObjectAlignmentMask));
Check(eq, "Unaligned allocation in new space", t9, Operand(zero_reg));
}
@@ -2206,12 +2547,70 @@ void MacroAssembler::CopyFields(Register dst,
}
+void MacroAssembler::CopyBytes(Register src,
+ Register dst,
+ Register length,
+ Register scratch) {
+ Label align_loop, align_loop_1, word_loop, byte_loop, byte_loop_1, done;
+
+ // Align src before copying in word size chunks.
+ bind(&align_loop);
+ Branch(&done, eq, length, Operand(zero_reg));
+ bind(&align_loop_1);
+ And(scratch, src, kPointerSize - 1);
+ Branch(&word_loop, eq, scratch, Operand(zero_reg));
+ lbu(scratch, MemOperand(src));
+ Addu(src, src, 1);
+ sb(scratch, MemOperand(dst));
+ Addu(dst, dst, 1);
+ Subu(length, length, Operand(1));
+ Branch(&byte_loop_1, ne, length, Operand(zero_reg));
+
+ // Copy bytes in word size chunks.
+ bind(&word_loop);
+ if (emit_debug_code()) {
+ And(scratch, src, kPointerSize - 1);
+ Assert(eq, "Expecting alignment for CopyBytes",
+ scratch, Operand(zero_reg));
+ }
+ Branch(&byte_loop, lt, length, Operand(kPointerSize));
+ lw(scratch, MemOperand(src));
+ Addu(src, src, kPointerSize);
+
+ // TODO(kalmard) check if this can be optimized to use sw in most cases.
+ // Can't use unaligned access - copy byte by byte.
+ sb(scratch, MemOperand(dst, 0));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 1));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 2));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 3));
+ Addu(dst, dst, 4);
+
+ Subu(length, length, Operand(kPointerSize));
+ Branch(&word_loop);
+
+ // Copy the last bytes if any left.
+ bind(&byte_loop);
+ Branch(&done, eq, length, Operand(zero_reg));
+ bind(&byte_loop_1);
+ lbu(scratch, MemOperand(src));
+ Addu(src, src, 1);
+ sb(scratch, MemOperand(dst));
+ Addu(dst, dst, 1);
+ Subu(length, length, Operand(1));
+ Branch(&byte_loop_1, ne, length, Operand(zero_reg));
+ bind(&done);
+}
+
+
void MacroAssembler::CheckMap(Register obj,
Register scratch,
Handle<Map> map,
Label* fail,
- bool is_heap_object) {
- if (!is_heap_object) {
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
JumpIfSmi(obj, fail);
}
lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
@@ -2220,12 +2619,27 @@ void MacroAssembler::CheckMap(Register obj,
}
+void MacroAssembler::DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
+ Jump(success, RelocInfo::CODE_TARGET, eq, scratch, Operand(map));
+ bind(&fail);
+}
+
+
void MacroAssembler::CheckMap(Register obj,
Register scratch,
Heap::RootListIndex index,
Label* fail,
- bool is_heap_object) {
- if (!is_heap_object) {
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
JumpIfSmi(obj, fail);
}
lw(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
@@ -2234,8 +2648,74 @@ void MacroAssembler::CheckMap(Register obj,
}
+void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
+ CpuFeatures::Scope scope(FPU);
+ if (IsMipsSoftFloatABI) {
+ Move(dst, v0, v1);
+ } else {
+ Move(dst, f0); // Reg f0 is o32 ABI FP return value.
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) {
+ CpuFeatures::Scope scope(FPU);
+ if (!IsMipsSoftFloatABI) {
+ Move(f12, dreg);
+ } else {
+ Move(a0, a1, dreg);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1,
+ DoubleRegister dreg2) {
+ CpuFeatures::Scope scope(FPU);
+ if (!IsMipsSoftFloatABI) {
+ if (dreg2.is(f12)) {
+ ASSERT(!dreg1.is(f14));
+ Move(f14, dreg2);
+ Move(f12, dreg1);
+ } else {
+ Move(f12, dreg1);
+ Move(f14, dreg2);
+ }
+ } else {
+ Move(a0, a1, dreg1);
+ Move(a2, a3, dreg2);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg,
+ Register reg) {
+ CpuFeatures::Scope scope(FPU);
+ if (!IsMipsSoftFloatABI) {
+ Move(f12, dreg);
+ Move(a2, reg);
+ } else {
+ Move(a2, reg);
+ Move(a0, a1, dreg);
+ }
+}
+
+
+void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) {
+ // This macro takes the dst register to make the code more readable
+ // at the call sites. However, the dst register has to be t1 to
+ // follow the calling convention which requires the call type to be
+ // in t1.
+ ASSERT(dst.is(t1));
+ if (call_kind == CALL_AS_FUNCTION) {
+ li(dst, Operand(Smi::FromInt(1)));
+ } else {
+ li(dst, Operand(Smi::FromInt(0)));
+ }
+}
+
+
// -----------------------------------------------------------------------------
-// JavaScript invokes
+// JavaScript invokes.
void MacroAssembler::InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual,
@@ -2243,7 +2723,8 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Register code_reg,
Label* done,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
bool definitely_matches = false;
Label regular_invoke;
@@ -2278,13 +2759,11 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
li(a2, Operand(expected.immediate()));
}
}
+ } else if (actual.is_immediate()) {
+ Branch(&regular_invoke, eq, expected.reg(), Operand(actual.immediate()));
+ li(a0, Operand(actual.immediate()));
} else {
- if (actual.is_immediate()) {
- Branch(&regular_invoke, eq, expected.reg(), Operand(actual.immediate()));
- li(a0, Operand(actual.immediate()));
- } else {
- Branch(&regular_invoke, eq, expected.reg(), Operand(actual.reg()));
- }
+ Branch(&regular_invoke, eq, expected.reg(), Operand(actual.reg()));
}
if (!definitely_matches) {
@@ -2296,10 +2775,13 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Handle<Code> adaptor =
isolate()->builtins()->ArgumentsAdaptorTrampoline();
if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
+ SetCallKind(t1, call_kind);
Call(adaptor, RelocInfo::CODE_TARGET);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ call_wrapper.AfterCall();
jmp(done);
} else {
+ SetCallKind(t1, call_kind);
Jump(adaptor, RelocInfo::CODE_TARGET);
}
bind(&regular_invoke);
@@ -2311,15 +2793,18 @@ void MacroAssembler::InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
Label done;
InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag,
- post_call_generator);
+ call_wrapper, call_kind);
if (flag == CALL_FUNCTION) {
+ SetCallKind(t1, call_kind);
Call(code);
} else {
ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(t1, call_kind);
Jump(code);
}
// Continue here if InvokePrologue does handle the invocation due to
@@ -2332,13 +2817,17 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
const ParameterCount& expected,
const ParameterCount& actual,
RelocInfo::Mode rmode,
- InvokeFlag flag) {
+ InvokeFlag flag,
+ CallKind call_kind) {
Label done;
- InvokePrologue(expected, actual, code, no_reg, &done, flag);
+ InvokePrologue(expected, actual, code, no_reg, &done, flag,
+ NullCallWrapper(), call_kind);
if (flag == CALL_FUNCTION) {
+ SetCallKind(t1, call_kind);
Call(code, rmode);
} else {
+ SetCallKind(t1, call_kind);
Jump(code, rmode);
}
// Continue here if InvokePrologue does handle the invocation due to
@@ -2350,7 +2839,8 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
void MacroAssembler::InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
// Contract with called JS functions requires that function is passed in a1.
ASSERT(function.is(a1));
Register expected_reg = a2;
@@ -2365,7 +2855,7 @@ void MacroAssembler::InvokeFunction(Register function,
lw(code_reg, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
- InvokeCode(code_reg, expected, actual, flag, post_call_generator);
+ InvokeCode(code_reg, expected, actual, flag, call_wrapper, call_kind);
}
@@ -2478,7 +2968,7 @@ void MacroAssembler::GetObjectType(Register object,
// -----------------------------------------------------------------------------
-// Runtime calls
+// Runtime calls.
void MacroAssembler::CallStub(CodeStub* stub, Condition cond,
Register r1, const Operand& r2) {
@@ -2487,11 +2977,136 @@ void MacroAssembler::CallStub(CodeStub* stub, Condition cond,
}
+MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond,
+ Register r1, const Operand& r2) {
+ ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs.
+ Object* result;
+ { MaybeObject* maybe_result = stub->TryGetCode();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Call(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond, r1, r2);
+ return result;
+}
+
+
+
void MacroAssembler::TailCallStub(CodeStub* stub) {
- ASSERT(allow_stub_calls()); // stub calls are not allowed in some stubs
+ ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs.
Jump(stub->GetCode(), RelocInfo::CODE_TARGET);
}
+MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub,
+ Condition cond,
+ Register r1,
+ const Operand& r2) {
+ ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs.
+ Object* result;
+ { MaybeObject* maybe_result = stub->TryGetCode();
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Jump(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond, r1, r2);
+ return result;
+}
+
+
+static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
+ return ref0.address() - ref1.address();
+}
+
+
+MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(
+ ExternalReference function, int stack_space) {
+ ExternalReference next_address =
+ ExternalReference::handle_scope_next_address();
+ const int kNextOffset = 0;
+ const int kLimitOffset = AddressOffset(
+ ExternalReference::handle_scope_limit_address(),
+ next_address);
+ const int kLevelOffset = AddressOffset(
+ ExternalReference::handle_scope_level_address(),
+ next_address);
+
+ // Allocate HandleScope in callee-save registers.
+ li(s3, Operand(next_address));
+ lw(s0, MemOperand(s3, kNextOffset));
+ lw(s1, MemOperand(s3, kLimitOffset));
+ lw(s2, MemOperand(s3, kLevelOffset));
+ Addu(s2, s2, Operand(1));
+ sw(s2, MemOperand(s3, kLevelOffset));
+
+ // The O32 ABI requires us to pass a pointer in a0 where the returned struct
+ // (4 bytes) will be placed. This is also built into the Simulator.
+ // Set up the pointer to the returned value (a0). It was allocated in
+ // EnterExitFrame.
+ addiu(a0, fp, ExitFrameConstants::kStackSpaceOffset);
+
+ // Native call returns to the DirectCEntry stub which redirects to the
+ // return address pushed on stack (could have moved after GC).
+ // DirectCEntry stub itself is generated early and never moves.
+ DirectCEntryStub stub;
+ stub.GenerateCall(this, function);
+
+ // As mentioned above, on MIPS a pointer is returned - we need to dereference
+ // it to get the actual return value (which is also a pointer).
+ lw(v0, MemOperand(v0));
+
+ Label promote_scheduled_exception;
+ Label delete_allocated_handles;
+ Label leave_exit_frame;
+
+ // If result is non-zero, dereference to get the result value
+ // otherwise set it to undefined.
+ Label skip;
+ LoadRoot(a0, Heap::kUndefinedValueRootIndex);
+ Branch(&skip, eq, v0, Operand(zero_reg));
+ lw(a0, MemOperand(v0));
+ bind(&skip);
+ mov(v0, a0);
+
+ // No more valid handles (the result handle was the last one). Restore
+ // previous handle scope.
+ sw(s0, MemOperand(s3, kNextOffset));
+ if (emit_debug_code()) {
+ lw(a1, MemOperand(s3, kLevelOffset));
+ Check(eq, "Unexpected level after return from api call", a1, Operand(s2));
+ }
+ Subu(s2, s2, Operand(1));
+ sw(s2, MemOperand(s3, kLevelOffset));
+ lw(at, MemOperand(s3, kLimitOffset));
+ Branch(&delete_allocated_handles, ne, s1, Operand(at));
+
+ // Check if the function scheduled an exception.
+ bind(&leave_exit_frame);
+ LoadRoot(t0, Heap::kTheHoleValueRootIndex);
+ li(at, Operand(ExternalReference::scheduled_exception_address(isolate())));
+ lw(t1, MemOperand(at));
+ Branch(&promote_scheduled_exception, ne, t0, Operand(t1));
+ li(s0, Operand(stack_space));
+ LeaveExitFrame(false, s0);
+ Ret();
+
+ bind(&promote_scheduled_exception);
+ MaybeObject* result = TryTailCallExternalReference(
+ ExternalReference(Runtime::kPromoteScheduledException, isolate()), 0, 1);
+ if (result->IsFailure()) {
+ return result;
+ }
+
+ // HandleScope limit has changed. Delete allocated extensions.
+ bind(&delete_allocated_handles);
+ sw(s1, MemOperand(s3, kLimitOffset));
+ mov(s0, v0);
+ mov(a0, v0);
+ PrepareCallCFunction(1, s1);
+ li(a0, Operand(ExternalReference::isolate_address()));
+ CallCFunction(ExternalReference::delete_handle_scope_extensions(isolate()),
+ 1);
+ mov(v0, s0);
+ jmp(&leave_exit_frame);
+
+ return result;
+}
+
void MacroAssembler::IllegalOperation(int num_arguments) {
if (num_arguments > 0) {
@@ -2554,7 +3169,6 @@ void MacroAssembler::ObjectToDoubleFPURegister(Register object,
}
-
void MacroAssembler::SmiToDoubleFPURegister(Register smi,
FPURegister value,
Register scratch1) {
@@ -2564,6 +3178,84 @@ void MacroAssembler::SmiToDoubleFPURegister(Register smi,
}
+void MacroAssembler::AdduAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch) {
+ ASSERT(!dst.is(overflow_dst));
+ ASSERT(!dst.is(scratch));
+ ASSERT(!overflow_dst.is(scratch));
+ ASSERT(!overflow_dst.is(left));
+ ASSERT(!overflow_dst.is(right));
+ ASSERT(!left.is(right));
+
+ // TODO(kalmard) There must be a way to optimize dst == left and dst == right
+ // cases.
+
+ if (dst.is(left)) {
+ addu(overflow_dst, left, right);
+ xor_(dst, overflow_dst, left);
+ xor_(scratch, overflow_dst, right);
+ and_(scratch, scratch, dst);
+ mov(dst, overflow_dst);
+ mov(overflow_dst, scratch);
+ } else if (dst.is(right)) {
+ addu(overflow_dst, left, right);
+ xor_(dst, overflow_dst, right);
+ xor_(scratch, overflow_dst, left);
+ and_(scratch, scratch, dst);
+ mov(dst, overflow_dst);
+ mov(overflow_dst, scratch);
+ } else {
+ addu(dst, left, right);
+ xor_(overflow_dst, dst, left);
+ xor_(scratch, dst, right);
+ and_(overflow_dst, scratch, overflow_dst);
+ }
+}
+
+
+void MacroAssembler::SubuAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch) {
+ ASSERT(!dst.is(overflow_dst));
+ ASSERT(!dst.is(scratch));
+ ASSERT(!overflow_dst.is(scratch));
+ ASSERT(!overflow_dst.is(left));
+ ASSERT(!overflow_dst.is(right));
+ ASSERT(!left.is(right));
+ ASSERT(!scratch.is(left));
+ ASSERT(!scratch.is(right));
+
+ // TODO(kalmard) There must be a way to optimize dst == left and dst == right
+ // cases.
+
+ if (dst.is(left)) {
+ subu(overflow_dst, left, right);
+ xor_(scratch, overflow_dst, left);
+ xor_(dst, left, right);
+ and_(scratch, scratch, dst);
+ mov(dst, overflow_dst);
+ mov(overflow_dst, scratch);
+ } else if (dst.is(right)) {
+ subu(overflow_dst, left, right);
+ xor_(dst, left, right);
+ xor_(scratch, overflow_dst, left);
+ and_(scratch, scratch, dst);
+ mov(dst, overflow_dst);
+ mov(overflow_dst, scratch);
+ } else {
+ subu(dst, left, right);
+ xor_(overflow_dst, dst, left);
+ xor_(scratch, left, right);
+ and_(overflow_dst, scratch, overflow_dst);
+ }
+}
+
+
void MacroAssembler::CallRuntime(const Runtime::Function* f,
int num_arguments) {
// All parameters are on the stack. v0 has the return value after call.
@@ -2623,6 +3315,16 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext,
JumpToExternalReference(ext);
}
+MaybeObject* MacroAssembler::TryTailCallExternalReference(
+ const ExternalReference& ext, int num_arguments, int result_size) {
+ // TODO(1236192): Most runtime routines don't need the number of
+ // arguments passed in because it is constant. At some point we
+ // should remove this need and make the runtime routine entry code
+ // smarter.
+ li(a0, num_arguments);
+ return TryJumpToExternalReference(ext);
+}
+
void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
int num_arguments,
@@ -2640,15 +3342,24 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) {
}
+MaybeObject* MacroAssembler::TryJumpToExternalReference(
+ const ExternalReference& builtin) {
+ li(a1, Operand(builtin));
+ CEntryStub stub(1);
+ return TryTailCallStub(&stub);
+}
+
+
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags,
- PostCallGenerator* post_call_generator) {
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper) {
GetBuiltinEntry(t9, id);
- if (flags == CALL_JS) {
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(t9));
Call(t9);
- if (post_call_generator != NULL) post_call_generator->Generate();
+ call_wrapper.AfterCall();
} else {
- ASSERT(flags == JUMP_JS);
+ ASSERT(flag == JUMP_FUNCTION);
Jump(t9);
}
}
@@ -2708,18 +3419,18 @@ void MacroAssembler::DecrementCounter(StatsCounter* counter, int value,
// -----------------------------------------------------------------------------
-// Debugging
+// Debugging.
void MacroAssembler::Assert(Condition cc, const char* msg,
Register rs, Operand rt) {
- if (FLAG_debug_code)
+ if (emit_debug_code())
Check(cc, msg, rs, rt);
}
void MacroAssembler::AssertRegisterIsRoot(Register reg,
Heap::RootListIndex index) {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
LoadRoot(at, index);
Check(eq, "Register did not match expected root", reg, Operand(at));
}
@@ -2727,10 +3438,10 @@ void MacroAssembler::AssertRegisterIsRoot(Register reg,
void MacroAssembler::AssertFastElements(Register elements) {
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
ASSERT(!elements.is(at));
Label ok;
- Push(elements);
+ push(elements);
lw(elements, FieldMemOperand(elements, HeapObject::kMapOffset));
LoadRoot(at, Heap::kFixedArrayMapRootIndex);
Branch(&ok, eq, elements, Operand(at));
@@ -2738,7 +3449,7 @@ void MacroAssembler::AssertFastElements(Register elements) {
Branch(&ok, eq, elements, Operand(at));
Abort("JSObject with fast elements map has slow elements");
bind(&ok);
- Pop(elements);
+ pop(elements);
}
}
@@ -2748,7 +3459,7 @@ void MacroAssembler::Check(Condition cc, const char* msg,
Label L;
Branch(&L, cc, rs, rt);
Abort(msg);
- // will not return here
+ // Will not return here.
bind(&L);
}
@@ -2774,11 +3485,11 @@ void MacroAssembler::Abort(const char* msg) {
AllowStubCallsScope allow_scope(this, true);
li(a0, Operand(p0));
- Push(a0);
+ push(a0);
li(a0, Operand(Smi::FromInt(p1 - p0)));
- Push(a0);
+ push(a0);
CallRuntime(Runtime::kAbort, 2);
- // will not return here
+ // Will not return here.
if (is_trampoline_pool_blocked()) {
// If the calling code cares about the exact number of
// instructions generated, we insert padding here to keep the size
@@ -2805,11 +3516,22 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
lw(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
lw(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
}
- // The context may be an intermediate context, not a function context.
- lw(dst, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
- } else { // Slot is in the current function context.
- // The context may be an intermediate context, not a function context.
- lw(dst, MemOperand(cp, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ } else {
+ // Slot is in the current function context. Move it into the
+ // destination register in case we store into it (the write barrier
+ // cannot be allowed to destroy the context in esi).
+ Move(dst, cp);
+ }
+
+ // We should not have found a 'with' context by walking the context chain
+ // (i.e., the static scope chain and runtime context chain do not agree).
+ // A variable occurring in such a scope should have slot type LOOKUP and
+ // not CONTEXT.
+ if (emit_debug_code()) {
+ lw(t9, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+ Check(eq, "Yo dawg, I heard you liked function contexts "
+ "so I put function contexts in all your contexts",
+ dst, Operand(t9));
}
}
@@ -2830,9 +3552,9 @@ void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
Register scratch) {
// Load the initial map. The global functions all have initial maps.
lw(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
- if (FLAG_debug_code) {
+ if (emit_debug_code()) {
Label ok, fail;
- CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, false);
+ CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK);
Branch(&ok);
bind(&fail);
Abort("Global functions must have initial map");
@@ -2862,38 +3584,34 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) {
}
-void MacroAssembler::EnterExitFrame(Register hold_argc,
- Register hold_argv,
- Register hold_function,
- bool save_doubles) {
- // a0 is argc.
- sll(t8, a0, kPointerSizeLog2);
- addu(hold_argv, sp, t8);
- addiu(hold_argv, hold_argv, -kPointerSize);
-
- // Compute callee's stack pointer before making changes and save it as
- // t9 register so that it is restored as sp register on exit, thereby
- // popping the args.
- // t9 = sp + kPointerSize * #args
- addu(t9, sp, t8);
-
- // Compute the argv pointer and keep it in a callee-saved register.
- // This only seems to be needed for crankshaft and may cause problems
- // so it's disabled for now.
- // Subu(s6, t9, Operand(kPointerSize));
+void MacroAssembler::EnterExitFrame(bool save_doubles,
+ int stack_space) {
+ // Setup the frame structure on the stack.
+ STATIC_ASSERT(2 * kPointerSize == ExitFrameConstants::kCallerSPDisplacement);
+ STATIC_ASSERT(1 * kPointerSize == ExitFrameConstants::kCallerPCOffset);
+ STATIC_ASSERT(0 * kPointerSize == ExitFrameConstants::kCallerFPOffset);
- // Align the stack at this point.
- AlignStack(0);
+ // This is how the stack will look:
+ // fp + 2 (==kCallerSPDisplacement) - old stack's end
+ // [fp + 1 (==kCallerPCOffset)] - saved old ra
+ // [fp + 0 (==kCallerFPOffset)] - saved old fp
+ // [fp - 1 (==kSPOffset)] - sp of the called function
+ // [fp - 2 (==kCodeOffset)] - CodeObject
+ // fp - (2 + stack_space + alignment) == sp == [fp - kSPOffset] - top of the
+ // new stack (will contain saved ra)
// Save registers.
- addiu(sp, sp, -12);
- sw(t9, MemOperand(sp, 8));
- sw(ra, MemOperand(sp, 4));
- sw(fp, MemOperand(sp, 0));
- mov(fp, sp); // Setup new frame pointer.
+ addiu(sp, sp, -4 * kPointerSize);
+ sw(ra, MemOperand(sp, 3 * kPointerSize));
+ sw(fp, MemOperand(sp, 2 * kPointerSize));
+ addiu(fp, sp, 2 * kPointerSize); // Setup new frame pointer.
- li(t8, Operand(CodeObject()));
- Push(t8); // Accessed from ExitFrame::code_slot.
+ if (emit_debug_code()) {
+ sw(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset));
+ }
+
+ li(t8, Operand(CodeObject())); // Accessed from ExitFrame::code_slot.
+ sw(t8, MemOperand(fp, ExitFrameConstants::kCodeOffset));
// Save the frame pointer and the context in top.
li(t8, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate())));
@@ -2901,47 +3619,49 @@ void MacroAssembler::EnterExitFrame(Register hold_argc,
li(t8, Operand(ExternalReference(Isolate::k_context_address, isolate())));
sw(cp, MemOperand(t8));
- // Setup argc and the builtin function in callee-saved registers.
- mov(hold_argc, a0);
- mov(hold_function, a1);
-
- // Optionally save all double registers.
+ const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
if (save_doubles) {
-#ifdef DEBUG
- int frame_alignment = ActivationFrameAlignment();
-#endif
- // The stack alignment code above made sp unaligned, so add space for one
- // more double register and use aligned addresses.
+ // The stack must be allign to 0 modulo 8 for stores with sdc1.
ASSERT(kDoubleSize == frame_alignment);
- // Mark the frame as containing doubles by pushing a non-valid return
- // address, i.e. 0.
- ASSERT(ExitFrameConstants::kMarkerOffset == -2 * kPointerSize);
- push(zero_reg); // Marker and alignment word.
- int space = FPURegister::kNumRegisters * kDoubleSize + kPointerSize;
+ if (frame_alignment > 0) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ And(sp, sp, Operand(-frame_alignment)); // Align stack.
+ }
+ int space = FPURegister::kNumRegisters * kDoubleSize;
Subu(sp, sp, Operand(space));
// Remember: we only need to save every 2nd double FPU value.
for (int i = 0; i < FPURegister::kNumRegisters; i+=2) {
FPURegister reg = FPURegister::from_code(i);
- sdc1(reg, MemOperand(sp, i * kDoubleSize + kPointerSize));
+ sdc1(reg, MemOperand(sp, i * kDoubleSize));
}
- // Note that f0 will be accessible at fp - 2*kPointerSize -
- // FPURegister::kNumRegisters * kDoubleSize, since the code slot and the
- // alignment word were pushed after the fp.
}
+
+ // Reserve place for the return address, stack space and an optional slot
+ // (used by the DirectCEntryStub to hold the return value if a struct is
+ // returned) and align the frame preparing for calling the runtime function.
+ ASSERT(stack_space >= 0);
+ Subu(sp, sp, Operand((stack_space + 2) * kPointerSize));
+ if (frame_alignment > 0) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ And(sp, sp, Operand(-frame_alignment)); // Align stack.
+ }
+
+ // Set the exit frame sp value to point just before the return address
+ // location.
+ addiu(at, sp, kPointerSize);
+ sw(at, MemOperand(fp, ExitFrameConstants::kSPOffset));
}
-void MacroAssembler::LeaveExitFrame(bool save_doubles) {
+void MacroAssembler::LeaveExitFrame(bool save_doubles,
+ Register argument_count) {
// Optionally restore all double registers.
if (save_doubles) {
- // TODO(regis): Use vldrm instruction.
// Remember: we only need to restore every 2nd double FPU value.
+ lw(t8, MemOperand(fp, ExitFrameConstants::kSPOffset));
for (int i = 0; i < FPURegister::kNumRegisters; i+=2) {
FPURegister reg = FPURegister::from_code(i);
- // Register f30-f31 is just below the marker.
- const int offset = ExitFrameConstants::kMarkerOffset;
- ldc1(reg, MemOperand(fp,
- (i - FPURegister::kNumRegisters) * kDoubleSize + offset));
+ ldc1(reg, MemOperand(t8, i * kDoubleSize + kPointerSize));
}
}
@@ -2958,11 +3678,13 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) {
// Pop the arguments, restore registers, and return.
mov(sp, fp); // Respect ABI stack constraint.
- lw(fp, MemOperand(sp, 0));
- lw(ra, MemOperand(sp, 4));
- lw(sp, MemOperand(sp, 8));
- jr(ra);
- nop(); // Branch delay slot nop.
+ lw(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset));
+ lw(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset));
+ addiu(sp, sp, 8);
+ if (argument_count.is_valid()) {
+ sll(t8, argument_count, kPointerSizeLog2);
+ addu(sp, sp, t8);
+ }
}
@@ -2996,39 +3718,24 @@ int MacroAssembler::ActivationFrameAlignment() {
#endif // defined(V8_HOST_ARCH_MIPS)
}
-
-void MacroAssembler::AlignStack(int offset) {
- // On MIPS an offset of 0 aligns to 0 modulo 8 bytes,
- // and an offset of 1 aligns to 4 modulo 8 bytes.
-#if defined(V8_HOST_ARCH_MIPS)
- // Running on the real platform. Use the alignment as mandated by the local
- // environment.
- // Note: This will break if we ever start generating snapshots on one MIPS
- // platform for another MIPS platform with a different alignment.
- int activation_frame_alignment = OS::ActivationFrameAlignment();
-#else // defined(V8_HOST_ARCH_MIPS)
- // If we are using the simulator then we should always align to the expected
- // alignment. As the simulator is used to generate snapshots we do not know
- // if the target platform will need alignment, so we will always align at
- // this point here.
- int activation_frame_alignment = 2 * kPointerSize;
-#endif // defined(V8_HOST_ARCH_MIPS)
- if (activation_frame_alignment != kPointerSize) {
- // This code needs to be made more general if this assert doesn't hold.
- ASSERT(activation_frame_alignment == 2 * kPointerSize);
- if (offset == 0) {
- andi(t8, sp, activation_frame_alignment - 1);
- Push(zero_reg, eq, t8, zero_reg);
- } else {
- andi(t8, sp, activation_frame_alignment - 1);
- addiu(t8, t8, -4);
- Push(zero_reg, eq, t8, zero_reg);
+void MacroAssembler::AssertStackIsAligned() {
+ if (emit_debug_code()) {
+ const int frame_alignment = ActivationFrameAlignment();
+ const int frame_alignment_mask = frame_alignment - 1;
+
+ if (frame_alignment > kPointerSize) {
+ Label alignment_as_expected;
+ ASSERT(IsPowerOf2(frame_alignment));
+ andi(at, sp, frame_alignment_mask);
+ Branch(&alignment_as_expected, eq, at, Operand(zero_reg));
+ // Don't use Check here, as it will call Runtime_Abort re-entering here.
+ stop("Unexpected stack alignment");
+ bind(&alignment_as_expected);
+ }
}
- }
}
-
void MacroAssembler::JumpIfNotPowerOfTwoOrZero(
Register reg,
Register scratch,
@@ -3078,6 +3785,18 @@ void MacroAssembler::AbortIfNotSmi(Register object) {
}
+void MacroAssembler::AbortIfNotString(Register object) {
+ STATIC_ASSERT(kSmiTag == 0);
+ And(t0, object, Operand(kSmiTagMask));
+ Assert(ne, "Operand is not a string", t0, Operand(zero_reg));
+ push(object);
+ lw(object, FieldMemOperand(object, HeapObject::kMapOffset));
+ lbu(object, FieldMemOperand(object, Map::kInstanceTypeOffset));
+ Assert(lo, "Operand is not a string", object, Operand(FIRST_NONSTRING_TYPE));
+ pop(object);
+}
+
+
void MacroAssembler::AbortIfNotRootValue(Register src,
Heap::RootListIndex root_value_index,
const char* message) {
@@ -3169,9 +3888,6 @@ static const int kRegisterPassedArguments = 4;
void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
int frame_alignment = ActivationFrameAlignment();
- // Reserve space for Isolate address which is always passed as last parameter
- num_arguments += 1;
-
// Up to four simple arguments are passed in registers a0..a3.
// Those four arguments must have reserved argument slots on the stack for
// mips, even though those argument slots are not normally used.
@@ -3198,7 +3914,7 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
void MacroAssembler::CallCFunction(ExternalReference function,
int num_arguments) {
- CallCFunctionHelper(no_reg, function, at, num_arguments);
+ CallCFunctionHelper(no_reg, function, t8, num_arguments);
}
@@ -3216,21 +3932,6 @@ void MacroAssembler::CallCFunctionHelper(Register function,
ExternalReference function_reference,
Register scratch,
int num_arguments) {
- // Push Isolate address as the last argument.
- if (num_arguments < kRegisterPassedArguments) {
- Register arg_to_reg[] = {a0, a1, a2, a3};
- Register r = arg_to_reg[num_arguments];
- li(r, Operand(ExternalReference::isolate_address()));
- } else {
- int stack_passed_arguments = num_arguments - kRegisterPassedArguments +
- (StandardFrameConstants::kCArgsSlotsSize /
- kPointerSize);
- // Push Isolate address on the stack after the arguments.
- li(scratch, Operand(ExternalReference::isolate_address()));
- sw(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
- }
- num_arguments += 1;
-
// Make sure that the stack is aligned before calling a C function unless
// running in the simulator. The simulator has its own alignment check which
// provides more information.
@@ -3257,13 +3958,12 @@ void MacroAssembler::CallCFunctionHelper(Register function,
// Just call directly. The function called cannot cause a GC, or
// allow preemption, so the return address in the link register
// stays correct.
- if (!function.is(t9)) {
- mov(t9, function);
- function = t9;
- }
if (function.is(no_reg)) {
- li(t9, Operand(function_reference));
+ function = t9;
+ li(function, Operand(function_reference));
+ } else if (!function.is(t9)) {
+ mov(t9, function);
function = t9;
}
@@ -3286,12 +3986,22 @@ void MacroAssembler::CallCFunctionHelper(Register function,
#undef BRANCH_ARGS_CHECK
-#ifdef ENABLE_DEBUGGER_SUPPORT
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ lw(descriptors,
+ FieldMemOperand(map, Map::kInstanceDescriptorsOrBitField3Offset));
+ Label not_smi;
+ JumpIfNotSmi(descriptors, &not_smi);
+ li(descriptors, Operand(FACTORY->empty_descriptor_array()));
+ bind(&not_smi);
+}
+
+
CodePatcher::CodePatcher(byte* address, int instructions)
: address_(address),
instructions_(instructions),
size_(instructions * Assembler::kInstrSize),
- masm_(address, size_ + Assembler::kGap) {
+ masm_(Isolate::Current(), address, size_ + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
// The size is adjusted with kGap on order for the assembler to generate size
// bytes of instructions without failing with buffer size constraints.
@@ -3309,8 +4019,8 @@ CodePatcher::~CodePatcher() {
}
-void CodePatcher::Emit(Instr x) {
- masm()->emit(x);
+void CodePatcher::Emit(Instr instr) {
+ masm()->emit(instr);
}
@@ -3319,7 +4029,26 @@ void CodePatcher::Emit(Address addr) {
}
-#endif // ENABLE_DEBUGGER_SUPPORT
+void CodePatcher::ChangeBranchCondition(Condition cond) {
+ Instr instr = Assembler::instr_at(masm_.pc_);
+ ASSERT(Assembler::IsBranch(instr));
+ uint32_t opcode = Assembler::GetOpcodeField(instr);
+ // Currently only the 'eq' and 'ne' cond values are supported and the simple
+ // branch instructions (with opcode being the branch type).
+ // There are some special cases (see Assembler::IsBranch()) so extending this
+ // would be tricky.
+ ASSERT(opcode == BEQ ||
+ opcode == BNE ||
+ opcode == BLEZ ||
+ opcode == BGTZ ||
+ opcode == BEQL ||
+ opcode == BNEL ||
+ opcode == BLEZL ||
+ opcode == BGTZL);
+ opcode = (cond == eq) ? BEQ : BNE;
+ instr = (instr & ~kOpcodeMask) | opcode;
+ masm_.emit(instr);
+}
} } // namespace v8::internal
diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h
index 7ff9e17b..bcb459ee 100644
--- a/src/mips/macro-assembler-mips.h
+++ b/src/mips/macro-assembler-mips.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,13 +30,13 @@
#include "assembler.h"
#include "mips/assembler-mips.h"
+#include "v8globals.h"
namespace v8 {
namespace internal {
// Forward declaration.
class JumpTarget;
-class PostCallGenerator;
// Reserved Register Usage Summary.
//
@@ -53,17 +53,12 @@ class PostCallGenerator;
// Registers aliases
// cp is assumed to be a callee saved register.
const Register roots = s6; // Roots array pointer.
-const Register cp = s7; // JavaScript context pointer
-const Register fp = s8_fp; // Alias fp
-// Register used for condition evaluation.
+const Register cp = s7; // JavaScript context pointer.
+const Register fp = s8_fp; // Alias for fp.
+// Registers used for condition evaluation.
const Register condReg1 = s4;
const Register condReg2 = s5;
-enum InvokeJSFlags {
- CALL_JS,
- JUMP_JS
-};
-
// Flags used for the AllocateInNewSpace functions.
enum AllocationFlags {
@@ -98,15 +93,19 @@ enum BranchDelaySlot {
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
public:
- MacroAssembler(void* buffer, int size);
+ // The isolate parameter can be NULL if the macro assembler should
+ // not use isolate-dependent functionality. In this case, it's the
+ // responsibility of the caller to never invoke such function on the
+ // macro assembler.
+ MacroAssembler(Isolate* isolate, void* buffer, int size);
-// Arguments macros
+// Arguments macros.
#define COND_TYPED_ARGS Condition cond, Register r1, const Operand& r2
#define COND_ARGS cond, r1, r2
-// ** Prototypes
+// Prototypes.
-// * Prototypes for functions with no target (eg Ret()).
+// Prototypes for functions with no target (eg Ret()).
#define DECLARE_NOTARGET_PROTOTYPE(Name) \
void Name(BranchDelaySlot bd = PROTECT); \
void Name(COND_TYPED_ARGS, BranchDelaySlot bd = PROTECT); \
@@ -114,7 +113,7 @@ class MacroAssembler: public Assembler {
Name(COND_ARGS, bd); \
}
-// * Prototypes for functions with a target.
+// Prototypes for functions with a target.
// Cases when relocation may be needed.
#define DECLARE_RELOC_PROTOTYPE(Name, target_type) \
@@ -152,7 +151,7 @@ class MacroAssembler: public Assembler {
Name(target, COND_ARGS, bd); \
}
-// ** Target prototypes.
+// Target prototypes.
#define DECLARE_JUMP_CALL_PROTOTYPES(Name) \
DECLARE_NORELOC_PROTOTYPE(Name, Register) \
@@ -181,6 +180,16 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
#undef DECLARE_JUMP_CALL_PROTOTYPES
#undef DECLARE_BRANCH_PROTOTYPES
+ void CallWithAstId(Handle<Code> code,
+ RelocInfo::Mode rmode,
+ unsigned ast_id,
+ Condition cond = al,
+ Register r1 = zero_reg,
+ const Operand& r2 = Operand(zero_reg));
+
+ int CallSize(Register reg);
+ int CallSize(Handle<Code> code, RelocInfo::Mode rmode);
+
// Emit code to discard a non-negative number of pointer-sized elements
// from the stack, clobbering only the sp register.
void Drop(int count,
@@ -198,9 +207,28 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
void Swap(Register reg1, Register reg2, Register scratch = no_reg);
void Call(Label* target);
- // May do nothing if the registers are identical.
- void Move(Register dst, Register src);
+ inline void Move(Register dst, Register src) {
+ if (!dst.is(src)) {
+ mov(dst, src);
+ }
+ }
+
+ inline void Move(FPURegister dst, FPURegister src) {
+ if (!dst.is(src)) {
+ mov_d(dst, src);
+ }
+ }
+
+ inline void Move(Register dst_low, Register dst_high, FPURegister src) {
+ mfc1(dst_low, src);
+ mfc1(dst_high, FPURegister::from_code(src.code() + 1));
+ }
+
+ inline void Move(FPURegister dst, Register src_low, Register src_high) {
+ mtc1(src_low, dst);
+ mtc1(src_high, FPURegister::from_code(dst.code() + 1));
+ }
// Jump unconditionally to given label.
// We NEED a nop in the branch delay slot, as it used by v8, for example in
@@ -262,7 +290,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// ---------------------------------------------------------------------------
- // Inline caching support
+ // Inline caching support.
// Generate code for checking access rights - used for security checks
// on access to global objects across environments. The holder register
@@ -306,7 +334,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// ---------------------------------------------------------------------------
- // Allocation support
+ // Allocation support.
// Allocate an object in new space. The object_size is specified
// either in bytes or in words if the allocation flag SIZE_IN_WORDS
@@ -373,7 +401,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Label* gc_required);
// ---------------------------------------------------------------------------
- // Instruction macros
+ // Instruction macros.
#define DEFINE_INSTRUCTION(instr) \
void instr(Register rd, Register rs, const Operand& rt); \
@@ -405,6 +433,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
DEFINE_INSTRUCTION(Or);
DEFINE_INSTRUCTION(Xor);
DEFINE_INSTRUCTION(Nor);
+ DEFINE_INSTRUCTION2(Neg);
DEFINE_INSTRUCTION(Slt);
DEFINE_INSTRUCTION(Sltu);
@@ -416,12 +445,12 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
#undef DEFINE_INSTRUCTION2
- //------------Pseudo-instructions-------------
+ // ---------------------------------------------------------------------------
+ // Pseudo-instructions.
void mov(Register rd, Register rt) { or_(rd, rt, zero_reg); }
-
- // load int32 in the rd register
+ // Load int32 in the rd register.
void li(Register rd, Operand j, bool gen2instr = false);
inline void li(Register rd, int32_t j, bool gen2instr = false) {
li(rd, Operand(j), gen2instr);
@@ -430,100 +459,88 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
li(dst, Operand(value), gen2instr);
}
- // Exception-generating instructions and debugging support
+ // Exception-generating instructions and debugging support.
void stop(const char* msg);
-
// Push multiple registers on the stack.
// Registers are saved in numerical order, with higher numbered registers
- // saved in higher memory addresses
+ // saved in higher memory addresses.
void MultiPush(RegList regs);
void MultiPushReversed(RegList regs);
- void Push(Register src) {
+ // Lower case push() for compatibility with arch-independent code.
+ void push(Register src) {
Addu(sp, sp, Operand(-kPointerSize));
sw(src, MemOperand(sp, 0));
}
- // Push two registers. Pushes leftmost register first (to highest address).
- void Push(Register src1, Register src2, Condition cond = al) {
- ASSERT(cond == al); // Do not support conditional versions yet.
+ // Push two registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2) {
Subu(sp, sp, Operand(2 * kPointerSize));
sw(src1, MemOperand(sp, 1 * kPointerSize));
sw(src2, MemOperand(sp, 0 * kPointerSize));
}
- // Push three registers. Pushes leftmost register first (to highest address).
- void Push(Register src1, Register src2, Register src3, Condition cond = al) {
- ASSERT(cond == al); // Do not support conditional versions yet.
- Addu(sp, sp, Operand(3 * -kPointerSize));
+ // Push three registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2, Register src3) {
+ Subu(sp, sp, Operand(3 * kPointerSize));
sw(src1, MemOperand(sp, 2 * kPointerSize));
sw(src2, MemOperand(sp, 1 * kPointerSize));
sw(src3, MemOperand(sp, 0 * kPointerSize));
}
- // Push four registers. Pushes leftmost register first (to highest address).
- void Push(Register src1, Register src2,
- Register src3, Register src4, Condition cond = al) {
- ASSERT(cond == al); // Do not support conditional versions yet.
- Addu(sp, sp, Operand(4 * -kPointerSize));
+ // Push four registers. Pushes leftmost register first (to highest address).
+ void Push(Register src1, Register src2, Register src3, Register src4) {
+ Subu(sp, sp, Operand(4 * kPointerSize));
sw(src1, MemOperand(sp, 3 * kPointerSize));
sw(src2, MemOperand(sp, 2 * kPointerSize));
sw(src3, MemOperand(sp, 1 * kPointerSize));
sw(src4, MemOperand(sp, 0 * kPointerSize));
}
- inline void push(Register src) { Push(src); }
- inline void pop(Register src) { Pop(src); }
-
void Push(Register src, Condition cond, Register tst1, Register tst2) {
- // Since we don't have conditionnal execution we use a Branch.
+ // Since we don't have conditional execution we use a Branch.
Branch(3, cond, tst1, Operand(tst2));
- Addu(sp, sp, Operand(-kPointerSize));
+ Subu(sp, sp, Operand(kPointerSize));
sw(src, MemOperand(sp, 0));
}
-
// Pops multiple values from the stack and load them in the
// registers specified in regs. Pop order is the opposite as in MultiPush.
void MultiPop(RegList regs);
void MultiPopReversed(RegList regs);
- void Pop(Register dst) {
+
+ // Lower case pop() for compatibility with arch-independent code.
+ void pop(Register dst) {
lw(dst, MemOperand(sp, 0));
Addu(sp, sp, Operand(kPointerSize));
}
+
+ // Pop two registers. Pops rightmost register first (from lower address).
+ void Pop(Register src1, Register src2) {
+ ASSERT(!src1.is(src2));
+ lw(src2, MemOperand(sp, 0 * kPointerSize));
+ lw(src1, MemOperand(sp, 1 * kPointerSize));
+ Addu(sp, sp, 2 * kPointerSize);
+ }
+
void Pop(uint32_t count = 1) {
Addu(sp, sp, Operand(count * kPointerSize));
}
- // ---------------------------------------------------------------------------
- // These functions are only used by crankshaft, so they are currently
- // unimplemented.
-
// Push and pop the registers that can hold pointers, as defined by the
// RegList constant kSafepointSavedRegisters.
- void PushSafepointRegisters() {
- UNIMPLEMENTED_MIPS();
- }
-
- void PopSafepointRegisters() {
- UNIMPLEMENTED_MIPS();
- }
-
- void PushSafepointRegistersAndDoubles() {
- UNIMPLEMENTED_MIPS();
- }
-
- void PopSafepointRegistersAndDoubles() {
- UNIMPLEMENTED_MIPS();
- }
-
- static int SafepointRegisterStackIndex(int reg_code) {
- UNIMPLEMENTED_MIPS();
- return 0;
- }
-
- // ---------------------------------------------------------------------------
+ void PushSafepointRegisters();
+ void PopSafepointRegisters();
+ void PushSafepointRegistersAndDoubles();
+ void PopSafepointRegistersAndDoubles();
+ // Store value in register src in the safepoint stack slot for
+ // register dst.
+ void StoreToSafepointRegisterSlot(Register src, Register dst);
+ void StoreToSafepointRegistersAndDoublesSlot(Register src, Register dst);
+ // Load the value of the src register from its safepoint stack slot
+ // into register dst.
+ void LoadFromSafepointRegisterSlot(Register dst, Register src);
// MIPS32 R2 instruction macro.
void Ins(Register rt, Register rs, uint16_t pos, uint16_t size);
@@ -548,8 +565,19 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
FPURegister double_scratch,
Label *not_int32);
+ // Helper for EmitECMATruncate.
+ // This will truncate a floating-point value outside of the singed 32bit
+ // integer range to a 32bit signed integer.
+ // Expects the double value loaded in input_high and input_low.
+ // Exits with the answer in 'result'.
+ // Note that this code does not work for values in the 32bit range!
+ void EmitOutOfInt32RangeTruncate(Register result,
+ Register input_high,
+ Register input_low,
+ Register scratch);
+
// -------------------------------------------------------------------------
- // Activation frames
+ // Activation frames.
void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); }
void LeaveInternalFrame() { LeaveFrame(StackFrame::INTERNAL); }
@@ -558,23 +586,21 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
// Enter exit frame.
- // Expects the number of arguments in register a0 and
- // the builtin function to call in register a1.
- // On output hold_argc, hold_function, and hold_argv are setup.
- void EnterExitFrame(Register hold_argc,
- Register hold_argv,
- Register hold_function,
- bool save_doubles);
-
- // Leave the current exit frame. Expects the return value in v0.
- void LeaveExitFrame(bool save_doubles);
+ // argc - argument count to be dropped by LeaveExitFrame.
+ // save_doubles - saves FPU registers on stack, currently disabled.
+ // stack_space - extra stack space.
+ void EnterExitFrame(bool save_doubles,
+ int stack_space = 0);
- // Align the stack by optionally pushing a Smi zero.
- void AlignStack(int offset); // TODO(mips) : remove this function.
+ // Leave the current exit frame.
+ void LeaveExitFrame(bool save_doubles, Register arg_count);
// Get the actual activation frame alignment for target environment.
static int ActivationFrameAlignment();
+ // Make sure the stack is aligned. Only emits code in debug mode.
+ void AssertStackIsAligned();
+
void LoadContext(Register dst, int context_chain_length);
void LoadGlobalFunction(int index, Register function);
@@ -586,27 +612,35 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Register scratch);
// -------------------------------------------------------------------------
- // JavaScript invokes
+ // JavaScript invokes.
+
+ // Setup call kind marking in t1. The method takes t1 as an
+ // explicit first parameter to make the code more readable at the
+ // call sites.
+ void SetCallKind(Register dst, CallKind kind);
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper = NullCallWrapper(),
+ CallKind call_kind = CALL_AS_METHOD);
void InvokeCode(Handle<Code> code,
const ParameterCount& expected,
const ParameterCount& actual,
RelocInfo::Mode rmode,
- InvokeFlag flag);
+ InvokeFlag flag,
+ CallKind call_kind = CALL_AS_METHOD);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
void InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper = NullCallWrapper(),
+ CallKind call_kind = CALL_AS_METHOD);
void InvokeFunction(JSFunction* function,
const ParameterCount& actual,
@@ -628,14 +662,14 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
#ifdef ENABLE_DEBUGGER_SUPPORT
// -------------------------------------------------------------------------
- // Debugger Support
+ // Debugger Support.
void DebugBreak();
#endif
// -------------------------------------------------------------------------
- // Exception handling
+ // Exception handling.
// Push a new try handler and link into try handler chain.
// The return address must be passed in register ra.
@@ -646,9 +680,24 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// Must preserve the result register.
void PopTryHandler();
+ // Passes thrown value (in v0) to the handler of top of the try handler chain.
+ void Throw(Register value);
+
+ // Propagates an uncatchable exception to the top of the current JS stack's
+ // handler chain.
+ void ThrowUncatchable(UncatchableExceptionType type, Register value);
+
// Copies a fixed number of fields of heap objects from src to dst.
void CopyFields(Register dst, Register src, RegList temps, int field_count);
+ // Copies a number of bytes from src to dst. All registers are clobbered. On
+ // exit src and dst will point to the place just after where the last byte was
+ // read or written and length will be zero.
+ void CopyBytes(Register src,
+ Register dst,
+ Register length,
+ Register scratch);
+
// -------------------------------------------------------------------------
// Support functions.
@@ -669,18 +718,27 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// Check if the map of an object is equal to a specified map (either
// given directly or as an index into the root list) and branch to
// label if not. Skip the smi check if not required (object is known
- // to be a heap object)
+ // to be a heap object).
void CheckMap(Register obj,
Register scratch,
Handle<Map> map,
Label* fail,
- bool is_heap_object);
+ SmiCheckType smi_check_type);
void CheckMap(Register obj,
Register scratch,
Heap::RootListIndex index,
Label* fail,
- bool is_heap_object);
+ SmiCheckType smi_check_type);
+
+ // Check if the map of an object is equal to a specified map and branch to a
+ // specified target if equal. Skip the smi check if not required (object is
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Register scratch,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
// Generates code for reporting that an illegal operation has
// occurred.
@@ -692,6 +750,10 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// index - holds the overwritten index on exit.
void IndexFromHash(Register hash, Register index);
+ // Get the number of least significant bits from a register.
+ void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
+ void GetLeastBitsFromInt32(Register dst, Register src, int mun_least_bits);
+
// Load the value of a number object into a FPU double register. If the
// object is not a number a jump to the label not_number is performed
// and the FPU double register is unchanged.
@@ -712,15 +774,70 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Register scratch1);
// -------------------------------------------------------------------------
- // Runtime calls
+ // Overflow handling functions.
+ // Usage: first call the appropriate arithmetic function, then call one of the
+ // jump functions with the overflow_dst register as the second parameter.
+
+ void AdduAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch = at);
+
+ void SubuAndCheckForOverflow(Register dst,
+ Register left,
+ Register right,
+ Register overflow_dst,
+ Register scratch = at);
+
+ void BranchOnOverflow(Label* label,
+ Register overflow_check,
+ BranchDelaySlot bd = PROTECT) {
+ Branch(label, lt, overflow_check, Operand(zero_reg), bd);
+ }
+
+ void BranchOnNoOverflow(Label* label,
+ Register overflow_check,
+ BranchDelaySlot bd = PROTECT) {
+ Branch(label, ge, overflow_check, Operand(zero_reg), bd);
+ }
+
+ void RetOnOverflow(Register overflow_check, BranchDelaySlot bd = PROTECT) {
+ Ret(lt, overflow_check, Operand(zero_reg), bd);
+ }
+
+ void RetOnNoOverflow(Register overflow_check, BranchDelaySlot bd = PROTECT) {
+ Ret(ge, overflow_check, Operand(zero_reg), bd);
+ }
+
+ // -------------------------------------------------------------------------
+ // Runtime calls.
// Call a code stub.
void CallStub(CodeStub* stub, Condition cond = cc_always,
Register r1 = zero_reg, const Operand& r2 = Operand(zero_reg));
+ // Call a code stub and return the code object called. Try to generate
+ // the code if necessary. Do not perform a GC but instead return a retry
+ // after GC failure.
+ MUST_USE_RESULT MaybeObject* TryCallStub(CodeStub* stub,
+ Condition cond = cc_always,
+ Register r1 = zero_reg,
+ const Operand& r2 =
+ Operand(zero_reg));
+
// Tail call a code stub (jump).
void TailCallStub(CodeStub* stub);
+ // Tail call a code stub (jump) and return the code object called. Try to
+ // generate the code if necessary. Do not perform a GC but instead return
+ // a retry after GC failure.
+ MUST_USE_RESULT MaybeObject* TryTailCallStub(CodeStub* stub,
+ Condition cond = cc_always,
+ Register r1 = zero_reg,
+ const Operand& r2 =
+ Operand(zero_reg));
+
void CallJSExitStub(CodeStub* stub);
// Call a runtime routine.
@@ -741,6 +858,12 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
int num_arguments,
int result_size);
+ // Tail call of a runtime routine (jump). Try to generate the code if
+ // necessary. Do not perform a GC but instead return a retry after GC
+ // failure.
+ MUST_USE_RESULT MaybeObject* TryTailCallExternalReference(
+ const ExternalReference& ext, int num_arguments, int result_size);
+
// Convenience function: tail call a runtime routine (jump).
void TailCallRuntime(Runtime::FunctionId fid,
int num_arguments,
@@ -768,15 +891,31 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// function).
void CallCFunction(ExternalReference function, int num_arguments);
void CallCFunction(Register function, Register scratch, int num_arguments);
+ void GetCFunctionDoubleResult(const DoubleRegister dst);
+
+ // There are two ways of passing double arguments on MIPS, depending on
+ // whether soft or hard floating point ABI is used. These functions
+ // abstract parameter passing for the three different ways we call
+ // C functions from generated code.
+ void SetCallCDoubleArguments(DoubleRegister dreg);
+ void SetCallCDoubleArguments(DoubleRegister dreg1, DoubleRegister dreg2);
+ void SetCallCDoubleArguments(DoubleRegister dreg, Register reg);
+
+ // Calls an API function. Allocates HandleScope, extracts returned value
+ // from handle and propagates exceptions. Restores context.
+ MaybeObject* TryCallApiFunctionAndReturn(ExternalReference function,
+ int stack_space);
// Jump to the builtin routine.
void JumpToExternalReference(const ExternalReference& builtin);
+ MaybeObject* TryJumpToExternalReference(const ExternalReference& ext);
+
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags,
- PostCallGenerator* post_call_generator = NULL);
+ InvokeFlag flag,
+ const CallWrapper& call_wrapper = NullCallWrapper());
// Store the code object for the given builtin in the target register and
// setup the function in a1.
@@ -787,14 +926,17 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
struct Unresolved {
int pc;
- uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders.
+ uint32_t flags; // See Bootstrapper::FixupFlags decoders/encoders.
const char* name;
};
- Handle<Object> CodeObject() { return code_object_; }
+ Handle<Object> CodeObject() {
+ ASSERT(!code_object_.is_null());
+ return code_object_;
+ }
// -------------------------------------------------------------------------
- // StatsCounter support
+ // StatsCounter support.
void SetCounter(StatsCounter* counter, int value,
Register scratch1, Register scratch2);
@@ -805,7 +947,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
// -------------------------------------------------------------------------
- // Debugging
+ // Debugging.
// Calls Abort(msg) if the condition cc is not satisfied.
// Use --debug_code to enable.
@@ -826,7 +968,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
bool allow_stub_calls() { return allow_stub_calls_; }
// ---------------------------------------------------------------------------
- // Number utilities
+ // Number utilities.
// Check whether the value of reg is a power of two and not zero. If not
// control continues at the label not_power_of_two. If reg is a power of two
@@ -837,7 +979,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Label* not_power_of_two_or_zero);
// -------------------------------------------------------------------------
- // Smi utilities
+ // Smi utilities.
// Try to convert int32 to smi. If the value is to large, preserve
// the original value and jump to not_a_smi. Destroys scratch and
@@ -888,13 +1030,16 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
void AbortIfSmi(Register object);
void AbortIfNotSmi(Register object);
+ // Abort execution if argument is a string. Used in debug code.
+ void AbortIfNotString(Register object);
+
// Abort execution if argument is not the root value with the given index.
void AbortIfNotRootValue(Register src,
Heap::RootListIndex root_value_index,
const char* message);
// ---------------------------------------------------------------------------
- // HeapNumber utilities
+ // HeapNumber utilities.
void JumpIfNotHeapNumber(Register object,
Register heap_number_map,
@@ -902,7 +1047,7 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Label* on_not_heap_number);
// -------------------------------------------------------------------------
- // String utilities
+ // String utilities.
// Checks if both instance types are sequential ASCII strings and jumps to
// label if either is not.
@@ -935,6 +1080,8 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Register scratch2,
Label* failure);
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+
private:
void CallCFunctionHelper(Register function,
ExternalReference function_reference,
@@ -959,7 +1106,8 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Register code_reg,
Label* done,
InvokeFlag flag,
- PostCallGenerator* post_call_generator = NULL);
+ const CallWrapper& call_wrapper = NullCallWrapper(),
+ CallKind call_kind = CALL_AS_METHOD);
// Get the code for the given builtin. Returns if able to resolve
// the function in the 'resolved' flag.
@@ -975,15 +1123,22 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
Register scratch1,
Register scratch2);
+ // Compute memory operands for safepoint stack slots.
+ static int SafepointRegisterStackIndex(int reg_code);
+ MemOperand SafepointRegisterSlot(Register reg);
+ MemOperand SafepointRegistersAndDoublesSlot(Register reg);
bool generating_stub_;
bool allow_stub_calls_;
// This handle will be patched with the code object on installation.
Handle<Object> code_object_;
+
+ // Needs access to SafepointRegisterStackIndex for optimized frame
+ // traversal.
+ friend class OptimizedFrame;
};
-#ifdef ENABLE_DEBUGGER_SUPPORT
// The code patcher is used to patch (typically) small parts of code e.g. for
// debugging and other types of instrumentation. When using the code patcher
// the exact number of bytes specified must be emitted. It is not legal to emit
@@ -998,29 +1153,21 @@ class CodePatcher {
MacroAssembler* masm() { return &masm_; }
// Emit an instruction directly.
- void Emit(Instr x);
+ void Emit(Instr instr);
// Emit an address directly.
void Emit(Address addr);
+ // Change the condition part of an instruction leaving the rest of the current
+ // instruction unchanged.
+ void ChangeBranchCondition(Condition cond);
+
private:
byte* address_; // The address of the code being patched.
int instructions_; // Number of instructions of the expected patch size.
int size_; // Number of bytes of the expected patch size.
MacroAssembler masm_; // Macro assembler used to generate the code.
};
-#endif // ENABLE_DEBUGGER_SUPPORT
-
-
-// Helper class for generating code or data associated with the code
-// right after a call instruction. As an example this can be used to
-// generate safepoint data after calls for crankshaft.
-class PostCallGenerator {
- public:
- PostCallGenerator() { }
- virtual ~PostCallGenerator() { }
- virtual void Generate() = 0;
-};
// -----------------------------------------------------------------------------
@@ -1042,6 +1189,16 @@ static inline MemOperand FieldMemOperand(Register object, int offset) {
}
+// Generate a MemOperand for storing arguments 5..N on the stack
+// when calling CallCFunction().
+static inline MemOperand CFunctionArgumentOperand(int index) {
+ ASSERT(index > StandardFrameConstants::kCArgSlotCount);
+ // Argument 5 takes the slot just past the four Arg-slots.
+ int offset =
+ (index - 5) * kPointerSize + StandardFrameConstants::kCArgsSlotsSize;
+ return MemOperand(sp, offset);
+}
+
#ifdef GENERATED_CODE_COVERAGE
#define CODE_COVERAGE_STRINGIFY(x) #x
@@ -1055,4 +1212,3 @@ static inline MemOperand FieldMemOperand(Register object, int offset) {
} } // namespace v8::internal
#endif // V8_MIPS_MACRO_ASSEMBLER_MIPS_H_
-
diff --git a/src/mips/regexp-macro-assembler-mips.cc b/src/mips/regexp-macro-assembler-mips.cc
index 9f9e976a..cfc8f651 100644
--- a/src/mips/regexp-macro-assembler-mips.cc
+++ b/src/mips/regexp-macro-assembler-mips.cc
@@ -56,49 +56,58 @@ namespace internal {
* - sp : points to tip of C stack.
*
* The remaining registers are free for computations.
- *
* Each call to a public method should retain this convention.
+ *
* The stack will have the following structure:
- * - direct_call (if 1, direct call from JavaScript code, if 0 call
- * through the runtime system)
- * - stack_area_base (High end of the memory area to use as
- * backtracking stack)
- * - int* capture_array (int[num_saved_registers_], for output).
- * - stack frame header (16 bytes in size)
- * --- sp when called ---
- * - link address
- * - backup of registers s0..s7
- * - end of input (Address of end of string)
- * - start of input (Address of first character in string)
- * - start index (character index of start)
- * --- frame pointer ----
- * - void* input_string (location of a handle containing the string)
- * - Offset of location before start of input (effectively character
- * position -1). Used to initialize capture registers to a non-position.
- * - At start (if 1, we are starting at the start of the
- * string, otherwise 0)
- * - register 0 (Only positions must be stored in the first
- * - register 1 num_saved_registers_ registers)
- * - ...
- * - register num_registers-1
- * --- sp ---
+ *
+ * - fp[56] direct_call (if 1, direct call from JavaScript code,
+ * if 0, call through the runtime system).
+ * - fp[52] stack_area_base (High end of the memory area to use as
+ * backtracking stack).
+ * - fp[48] int* capture_array (int[num_saved_registers_], for output).
+ * - fp[44] secondary link/return address used by native call.
+ * --- sp when called ---
+ * - fp[40] return address (lr).
+ * - fp[36] old frame pointer (r11).
+ * - fp[0..32] backup of registers s0..s7.
+ * --- frame pointer ----
+ * - fp[-4] end of input (Address of end of string).
+ * - fp[-8] start of input (Address of first character in string).
+ * - fp[-12] start index (character index of start).
+ * - fp[-16] void* input_string (location of a handle containing the string).
+ * - fp[-20] Offset of location before start of input (effectively character
+ * position -1). Used to initialize capture registers to a
+ * non-position.
+ * - fp[-24] At start (if 1, we are starting at the start of the
+ * string, otherwise 0)
+ * - fp[-28] register 0 (Only positions must be stored in the first
+ * - register 1 num_saved_registers_ registers)
+ * - ...
+ * - register num_registers-1
+ * --- sp ---
*
* The first num_saved_registers_ registers are initialized to point to
* "character -1" in the string (i.e., char_size() bytes before the first
* character of the string). The remaining registers start out as garbage.
*
* The data up to the return address must be placed there by the calling
- * code, by calling the code entry as cast to a function with the signature:
+ * code and the remaining arguments are passed in registers, e.g. by calling the
+ * code entry as cast to a function with the signature:
* int (*match)(String* input_string,
* int start_index,
* Address start,
* Address end,
+ * Address secondary_return_address, // Only used by native call.
* int* capture_output_array,
- * bool at_start,
* byte* stack_area_base,
- * bool direct_call)
+ * bool direct_call = false)
* The call is performed by NativeRegExpMacroAssembler::Execute()
- * (in regexp-macro-assembler.cc).
+ * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
+ * in mips/simulator-mips.h.
+ * When calling as a non-direct call (i.e., from C++ code), the return address
+ * area is overwritten with the ra register by the RegExp code. When doing a
+ * direct call from generated code, the return address is placed there by
+ * the calling code, as in a normal exit frame.
*/
#define __ ACCESS_MASM(masm_)
@@ -106,7 +115,7 @@ namespace internal {
RegExpMacroAssemblerMIPS::RegExpMacroAssemblerMIPS(
Mode mode,
int registers_to_save)
- : masm_(new MacroAssembler(NULL, kRegExpCodeSize)),
+ : masm_(new MacroAssembler(Isolate::Current(), NULL, kRegExpCodeSize)),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
@@ -114,9 +123,15 @@ RegExpMacroAssemblerMIPS::RegExpMacroAssemblerMIPS(
start_label_(),
success_label_(),
backtrack_label_(),
- exit_label_() {
+ exit_label_(),
+ internal_failure_label_() {
ASSERT_EQ(0, registers_to_save % 2);
__ jmp(&entry_label_); // We'll write the entry code later.
+ // If the code gets too big or corrupted, an internal exception will be
+ // raised, and we will exit right away.
+ __ bind(&internal_failure_label_);
+ __ li(v0, Operand(FAILURE));
+ __ Ret();
__ bind(&start_label_); // And then continue from here.
}
@@ -131,6 +146,7 @@ RegExpMacroAssemblerMIPS::~RegExpMacroAssemblerMIPS() {
exit_label_.Unuse();
check_preempt_label_.Unuse();
stack_overflow_label_.Unuse();
+ internal_failure_label_.Unuse();
}
@@ -140,47 +156,75 @@ int RegExpMacroAssemblerMIPS::stack_limit_slack() {
void RegExpMacroAssemblerMIPS::AdvanceCurrentPosition(int by) {
- UNIMPLEMENTED_MIPS();
+ if (by != 0) {
+ __ Addu(current_input_offset(),
+ current_input_offset(), Operand(by * char_size()));
+ }
}
void RegExpMacroAssemblerMIPS::AdvanceRegister(int reg, int by) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(reg >= 0);
+ ASSERT(reg < num_registers_);
+ if (by != 0) {
+ __ lw(a0, register_location(reg));
+ __ Addu(a0, a0, Operand(by));
+ __ sw(a0, register_location(reg));
+ }
}
void RegExpMacroAssemblerMIPS::Backtrack() {
- UNIMPLEMENTED_MIPS();
+ CheckPreemption();
+ // Pop Code* offset from backtrack stack, add Code* and jump to location.
+ Pop(a0);
+ __ Addu(a0, a0, code_pointer());
+ __ Jump(Operand(a0));
}
void RegExpMacroAssemblerMIPS::Bind(Label* label) {
- UNIMPLEMENTED_MIPS();
+ __ bind(label);
}
void RegExpMacroAssemblerMIPS::CheckCharacter(uint32_t c, Label* on_equal) {
- UNIMPLEMENTED_MIPS();
+ BranchOrBacktrack(on_equal, eq, current_character(), Operand(c));
}
void RegExpMacroAssemblerMIPS::CheckCharacterGT(uc16 limit, Label* on_greater) {
- UNIMPLEMENTED_MIPS();
+ BranchOrBacktrack(on_greater, gt, current_character(), Operand(limit));
}
void RegExpMacroAssemblerMIPS::CheckAtStart(Label* on_at_start) {
- UNIMPLEMENTED_MIPS();
+ Label not_at_start;
+ // Did we start the match at the start of the string at all?
+ __ lw(a0, MemOperand(frame_pointer(), kAtStart));
+ BranchOrBacktrack(&not_at_start, eq, a0, Operand(zero_reg));
+
+ // If we did, are we still at the start of the input?
+ __ lw(a1, MemOperand(frame_pointer(), kInputStart));
+ __ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
+ BranchOrBacktrack(on_at_start, eq, a0, Operand(a1));
+ __ bind(&not_at_start);
}
void RegExpMacroAssemblerMIPS::CheckNotAtStart(Label* on_not_at_start) {
- UNIMPLEMENTED_MIPS();
+ // Did we start the match at the start of the string at all?
+ __ lw(a0, MemOperand(frame_pointer(), kAtStart));
+ BranchOrBacktrack(on_not_at_start, eq, a0, Operand(zero_reg));
+ // If we did, are we still at the start of the input?
+ __ lw(a1, MemOperand(frame_pointer(), kInputStart));
+ __ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
+ BranchOrBacktrack(on_not_at_start, ne, a0, Operand(a1));
}
void RegExpMacroAssemblerMIPS::CheckCharacterLT(uc16 limit, Label* on_less) {
- UNIMPLEMENTED_MIPS();
+ BranchOrBacktrack(on_less, lt, current_character(), Operand(limit));
}
@@ -188,26 +232,212 @@ void RegExpMacroAssemblerMIPS::CheckCharacters(Vector<const uc16> str,
int cp_offset,
Label* on_failure,
bool check_end_of_string) {
- UNIMPLEMENTED_MIPS();
+ if (on_failure == NULL) {
+ // Instead of inlining a backtrack for each test, (re)use the global
+ // backtrack target.
+ on_failure = &backtrack_label_;
+ }
+
+ if (check_end_of_string) {
+ // Is last character of required match inside string.
+ CheckPosition(cp_offset + str.length() - 1, on_failure);
+ }
+
+ __ Addu(a0, end_of_input_address(), Operand(current_input_offset()));
+ if (cp_offset != 0) {
+ int byte_offset = cp_offset * char_size();
+ __ Addu(a0, a0, Operand(byte_offset));
+ }
+
+ // a0 : Address of characters to match against str.
+ int stored_high_byte = 0;
+ for (int i = 0; i < str.length(); i++) {
+ if (mode_ == ASCII) {
+ __ lbu(a1, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ ASSERT(str[i] <= String::kMaxAsciiCharCode);
+ BranchOrBacktrack(on_failure, ne, a1, Operand(str[i]));
+ } else {
+ __ lhu(a1, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ uc16 match_char = str[i];
+ int match_high_byte = (match_char >> 8);
+ if (match_high_byte == 0) {
+ BranchOrBacktrack(on_failure, ne, a1, Operand(str[i]));
+ } else {
+ if (match_high_byte != stored_high_byte) {
+ __ li(a2, Operand(match_high_byte));
+ stored_high_byte = match_high_byte;
+ }
+ __ Addu(a3, a2, Operand(match_char & 0xff));
+ BranchOrBacktrack(on_failure, ne, a1, Operand(a3));
+ }
+ }
+ }
}
void RegExpMacroAssemblerMIPS::CheckGreedyLoop(Label* on_equal) {
- UNIMPLEMENTED_MIPS();
+ Label backtrack_non_equal;
+ __ lw(a0, MemOperand(backtrack_stackpointer(), 0));
+ __ Branch(&backtrack_non_equal, ne, current_input_offset(), Operand(a0));
+ __ Addu(backtrack_stackpointer(),
+ backtrack_stackpointer(),
+ Operand(kPointerSize));
+ __ bind(&backtrack_non_equal);
+ BranchOrBacktrack(on_equal, eq, current_input_offset(), Operand(a0));
}
void RegExpMacroAssemblerMIPS::CheckNotBackReferenceIgnoreCase(
int start_reg,
Label* on_no_match) {
- UNIMPLEMENTED_MIPS();
+ Label fallthrough;
+ __ lw(a0, register_location(start_reg)); // Index of start of capture.
+ __ lw(a1, register_location(start_reg + 1)); // Index of end of capture.
+ __ Subu(a1, a1, a0); // Length of capture.
+
+ // If length is zero, either the capture is empty or it is not participating.
+ // In either case succeed immediately.
+ __ Branch(&fallthrough, eq, a1, Operand(zero_reg));
+
+ __ Addu(t5, a1, current_input_offset());
+ // Check that there are enough characters left in the input.
+ BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
+
+ if (mode_ == ASCII) {
+ Label success;
+ Label fail;
+ Label loop_check;
+
+ // a0 - offset of start of capture.
+ // a1 - length of capture.
+ __ Addu(a0, a0, Operand(end_of_input_address()));
+ __ Addu(a2, end_of_input_address(), Operand(current_input_offset()));
+ __ Addu(a1, a0, Operand(a1));
+
+ // a0 - Address of start of capture.
+ // a1 - Address of end of capture.
+ // a2 - Address of current input position.
+
+ Label loop;
+ __ bind(&loop);
+ __ lbu(a3, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ __ lbu(t0, MemOperand(a2, 0));
+ __ addiu(a2, a2, char_size());
+
+ __ Branch(&loop_check, eq, t0, Operand(a3));
+
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ __ Or(a3, a3, Operand(0x20)); // Convert capture character to lower-case.
+ __ Or(t0, t0, Operand(0x20)); // Also convert input character.
+ __ Branch(&fail, ne, t0, Operand(a3));
+ __ Subu(a3, a3, Operand('a'));
+ __ Branch(&fail, hi, a3, Operand('z' - 'a')); // Is a3 a lowercase letter?
+
+ __ bind(&loop_check);
+ __ Branch(&loop, lt, a0, Operand(a1));
+ __ jmp(&success);
+
+ __ bind(&fail);
+ GoTo(on_no_match);
+
+ __ bind(&success);
+ // Compute new value of character position after the matched part.
+ __ Subu(current_input_offset(), a2, end_of_input_address());
+ } else {
+ ASSERT(mode_ == UC16);
+ // Put regexp engine registers on stack.
+ RegList regexp_registers_to_retain = current_input_offset().bit() |
+ current_character().bit() | backtrack_stackpointer().bit();
+ __ MultiPush(regexp_registers_to_retain);
+
+ int argument_count = 4;
+ __ PrepareCallCFunction(argument_count, a2);
+
+ // a0 - offset of start of capture.
+ // a1 - length of capture.
+
+ // Put arguments into arguments registers.
+ // Parameters are
+ // a0: Address byte_offset1 - Address captured substring's start.
+ // a1: Address byte_offset2 - Address of current character position.
+ // a2: size_t byte_length - length of capture in bytes(!).
+ // a3: Isolate* isolate.
+
+ // Address of start of capture.
+ __ Addu(a0, a0, Operand(end_of_input_address()));
+ // Length of capture.
+ __ mov(a2, a1);
+ // Save length in callee-save register for use on return.
+ __ mov(s3, a1);
+ // Address of current input position.
+ __ Addu(a1, current_input_offset(), Operand(end_of_input_address()));
+ // Isolate.
+ __ li(a3, Operand(ExternalReference::isolate_address()));
+
+ ExternalReference function =
+ ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate());
+ __ CallCFunction(function, argument_count);
+
+ // Restore regexp engine registers.
+ __ MultiPop(regexp_registers_to_retain);
+ __ li(code_pointer(), Operand(masm_->CodeObject()));
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+
+ // Check if function returned non-zero for success or zero for failure.
+ BranchOrBacktrack(on_no_match, eq, v0, Operand(zero_reg));
+ // On success, increment position by length of capture.
+ __ Addu(current_input_offset(), current_input_offset(), Operand(s3));
+ }
+
+ __ bind(&fallthrough);
}
void RegExpMacroAssemblerMIPS::CheckNotBackReference(
int start_reg,
Label* on_no_match) {
- UNIMPLEMENTED_MIPS();
+ Label fallthrough;
+ Label success;
+
+ // Find length of back-referenced capture.
+ __ lw(a0, register_location(start_reg));
+ __ lw(a1, register_location(start_reg + 1));
+ __ Subu(a1, a1, a0); // Length to check.
+ // Succeed on empty capture (including no capture).
+ __ Branch(&fallthrough, eq, a1, Operand(zero_reg));
+
+ __ Addu(t5, a1, current_input_offset());
+ // Check that there are enough characters left in the input.
+ BranchOrBacktrack(on_no_match, gt, t5, Operand(zero_reg));
+
+ // Compute pointers to match string and capture string.
+ __ Addu(a0, a0, Operand(end_of_input_address()));
+ __ Addu(a2, end_of_input_address(), Operand(current_input_offset()));
+ __ Addu(a1, a1, Operand(a0));
+
+ Label loop;
+ __ bind(&loop);
+ if (mode_ == ASCII) {
+ __ lbu(a3, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ __ lbu(t0, MemOperand(a2, 0));
+ __ addiu(a2, a2, char_size());
+ } else {
+ ASSERT(mode_ == UC16);
+ __ lhu(a3, MemOperand(a0, 0));
+ __ addiu(a0, a0, char_size());
+ __ lhu(t0, MemOperand(a2, 0));
+ __ addiu(a2, a2, char_size());
+ }
+ BranchOrBacktrack(on_no_match, ne, a3, Operand(t0));
+ __ Branch(&loop, lt, a0, Operand(a1));
+
+ // Move current character position to position after match.
+ __ Subu(current_input_offset(), a2, end_of_input_address());
+ __ bind(&fallthrough);
}
@@ -220,21 +450,23 @@ void RegExpMacroAssemblerMIPS::CheckNotRegistersEqual(int reg1,
void RegExpMacroAssemblerMIPS::CheckNotCharacter(uint32_t c,
Label* on_not_equal) {
- UNIMPLEMENTED_MIPS();
+ BranchOrBacktrack(on_not_equal, ne, current_character(), Operand(c));
}
void RegExpMacroAssemblerMIPS::CheckCharacterAfterAnd(uint32_t c,
uint32_t mask,
Label* on_equal) {
- UNIMPLEMENTED_MIPS();
+ __ And(a0, current_character(), Operand(mask));
+ BranchOrBacktrack(on_equal, eq, a0, Operand(c));
}
void RegExpMacroAssemblerMIPS::CheckNotCharacterAfterAnd(uint32_t c,
uint32_t mask,
Label* on_not_equal) {
- UNIMPLEMENTED_MIPS();
+ __ And(a0, current_character(), Operand(mask));
+ BranchOrBacktrack(on_not_equal, ne, a0, Operand(c));
}
@@ -249,24 +481,360 @@ void RegExpMacroAssemblerMIPS::CheckNotCharacterAfterMinusAnd(
bool RegExpMacroAssemblerMIPS::CheckSpecialCharacterClass(uc16 type,
Label* on_no_match) {
- UNIMPLEMENTED_MIPS();
- return false;
+ // Range checks (c in min..max) are generally implemented by an unsigned
+ // (c - min) <= (max - min) check.
+ switch (type) {
+ case 's':
+ // Match space-characters.
+ if (mode_ == ASCII) {
+ // ASCII space characters are '\t'..'\r' and ' '.
+ Label success;
+ __ Branch(&success, eq, current_character(), Operand(' '));
+ // Check range 0x09..0x0d.
+ __ Subu(a0, current_character(), Operand('\t'));
+ BranchOrBacktrack(on_no_match, hi, a0, Operand('\r' - '\t'));
+ __ bind(&success);
+ return true;
+ }
+ return false;
+ case 'S':
+ // Match non-space characters.
+ if (mode_ == ASCII) {
+ // ASCII space characters are '\t'..'\r' and ' '.
+ BranchOrBacktrack(on_no_match, eq, current_character(), Operand(' '));
+ __ Subu(a0, current_character(), Operand('\t'));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand('\r' - '\t'));
+ return true;
+ }
+ return false;
+ case 'd':
+ // Match ASCII digits ('0'..'9').
+ __ Subu(a0, current_character(), Operand('0'));
+ BranchOrBacktrack(on_no_match, hi, a0, Operand('9' - '0'));
+ return true;
+ case 'D':
+ // Match non ASCII-digits.
+ __ Subu(a0, current_character(), Operand('0'));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand('9' - '0'));
+ return true;
+ case '.': {
+ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029).
+ __ Xor(a0, current_character(), Operand(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c.
+ __ Subu(a0, a0, Operand(0x0b));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand(0x0c - 0x0b));
+ if (mode_ == UC16) {
+ // Compare original value to 0x2028 and 0x2029, using the already
+ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
+ // 0x201d (0x2028 - 0x0b) or 0x201e.
+ __ Subu(a0, a0, Operand(0x2028 - 0x0b));
+ BranchOrBacktrack(on_no_match, ls, a0, Operand(1));
+ }
+ return true;
+ }
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029).
+ __ Xor(a0, current_character(), Operand(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c.
+ __ Subu(a0, a0, Operand(0x0b));
+ if (mode_ == ASCII) {
+ BranchOrBacktrack(on_no_match, hi, a0, Operand(0x0c - 0x0b));
+ } else {
+ Label done;
+ BranchOrBacktrack(&done, ls, a0, Operand(0x0c - 0x0b));
+ // Compare original value to 0x2028 and 0x2029, using the already
+ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
+ // 0x201d (0x2028 - 0x0b) or 0x201e.
+ __ Subu(a0, a0, Operand(0x2028 - 0x0b));
+ BranchOrBacktrack(on_no_match, hi, a0, Operand(1));
+ __ bind(&done);
+ }
+ return true;
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ BranchOrBacktrack(on_no_match, hi, current_character(), Operand('z'));
+ }
+ ExternalReference map = ExternalReference::re_word_character_map();
+ __ li(a0, Operand(map));
+ __ Addu(a0, a0, current_character());
+ __ lbu(a0, MemOperand(a0, 0));
+ BranchOrBacktrack(on_no_match, eq, a0, Operand(zero_reg));
+ return true;
+ }
+ case 'W': {
+ Label done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ Branch(&done, hi, current_character(), Operand('z'));
+ }
+ ExternalReference map = ExternalReference::re_word_character_map();
+ __ li(a0, Operand(map));
+ __ Addu(a0, a0, current_character());
+ __ lbu(a0, MemOperand(a0, 0));
+ BranchOrBacktrack(on_no_match, ne, a0, Operand(zero_reg));
+ if (mode_ != ASCII) {
+ __ bind(&done);
+ }
+ return true;
+ }
+ case '*':
+ // Match any character.
+ return true;
+ // No custom implementation (yet): s(UC16), S(UC16).
+ default:
+ return false;
+ }
}
void RegExpMacroAssemblerMIPS::Fail() {
- UNIMPLEMENTED_MIPS();
+ __ li(v0, Operand(FAILURE));
+ __ jmp(&exit_label_);
}
Handle<HeapObject> RegExpMacroAssemblerMIPS::GetCode(Handle<String> source) {
- UNIMPLEMENTED_MIPS();
- return Handle<HeapObject>::null();
+ if (masm_->has_exception()) {
+ // If the code gets corrupted due to long regular expressions and lack of
+ // space on trampolines, an internal exception flag is set. If this case
+ // is detected, we will jump into exit sequence right away.
+ __ bind_to(&entry_label_, internal_failure_label_.pos());
+ } else {
+ // Finalize code - write the entry point code now we know how many
+ // registers we need.
+
+ // Entry code:
+ __ bind(&entry_label_);
+ // Push arguments
+ // Save callee-save registers.
+ // Start new stack frame.
+ // Store link register in existing stack-cell.
+ // Order here should correspond to order of offset constants in header file.
+ RegList registers_to_retain = s0.bit() | s1.bit() | s2.bit() |
+ s3.bit() | s4.bit() | s5.bit() | s6.bit() | s7.bit() | fp.bit();
+ RegList argument_registers = a0.bit() | a1.bit() | a2.bit() | a3.bit();
+ __ MultiPush(argument_registers | registers_to_retain | ra.bit());
+ // Set frame pointer in space for it if this is not a direct call
+ // from generated code.
+ __ Addu(frame_pointer(), sp, Operand(4 * kPointerSize));
+ __ push(a0); // Make room for "position - 1" constant (value irrelevant).
+ __ push(a0); // Make room for "at start" constant (value irrelevant).
+
+ // Check if we have space on the stack for registers.
+ Label stack_limit_hit;
+ Label stack_ok;
+
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(masm_->isolate());
+ __ li(a0, Operand(stack_limit));
+ __ lw(a0, MemOperand(a0));
+ __ Subu(a0, sp, a0);
+ // Handle it if the stack pointer is already below the stack limit.
+ __ Branch(&stack_limit_hit, le, a0, Operand(zero_reg));
+ // Check if there is room for the variable number of registers above
+ // the stack limit.
+ __ Branch(&stack_ok, hs, a0, Operand(num_registers_ * kPointerSize));
+ // Exit with OutOfMemory exception. There is not enough space on the stack
+ // for our working registers.
+ __ li(v0, Operand(EXCEPTION));
+ __ jmp(&exit_label_);
+
+ __ bind(&stack_limit_hit);
+ CallCheckStackGuardState(a0);
+ // If returned value is non-zero, we exit with the returned value as result.
+ __ Branch(&exit_label_, ne, v0, Operand(zero_reg));
+
+ __ bind(&stack_ok);
+ // Allocate space on stack for registers.
+ __ Subu(sp, sp, Operand(num_registers_ * kPointerSize));
+ // Load string end.
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ // Load input start.
+ __ lw(a0, MemOperand(frame_pointer(), kInputStart));
+ // Find negative length (offset of start relative to end).
+ __ Subu(current_input_offset(), a0, end_of_input_address());
+ // Set a0 to address of char before start of the input string
+ // (effectively string position -1).
+ __ lw(a1, MemOperand(frame_pointer(), kStartIndex));
+ __ Subu(a0, current_input_offset(), Operand(char_size()));
+ __ sll(t5, a1, (mode_ == UC16) ? 1 : 0);
+ __ Subu(a0, a0, t5);
+ // Store this value in a local variable, for use when clearing
+ // position registers.
+ __ sw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
+
+ // Determine whether the start index is zero, that is at the start of the
+ // string, and store that value in a local variable.
+ __ mov(t5, a1);
+ __ li(a1, Operand(1));
+ __ movn(a1, zero_reg, t5);
+ __ sw(a1, MemOperand(frame_pointer(), kAtStart));
+
+ if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
+ // Fill saved registers with initial value = start offset - 1.
+
+ // Address of register 0.
+ __ Addu(a1, frame_pointer(), Operand(kRegisterZero));
+ __ li(a2, Operand(num_saved_registers_));
+ Label init_loop;
+ __ bind(&init_loop);
+ __ sw(a0, MemOperand(a1));
+ __ Addu(a1, a1, Operand(-kPointerSize));
+ __ Subu(a2, a2, Operand(1));
+ __ Branch(&init_loop, ne, a2, Operand(zero_reg));
+ }
+
+ // Initialize backtrack stack pointer.
+ __ lw(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackHighEnd));
+ // Initialize code pointer register
+ __ li(code_pointer(), Operand(masm_->CodeObject()));
+ // Load previous char as initial value of current character register.
+ Label at_start;
+ __ lw(a0, MemOperand(frame_pointer(), kAtStart));
+ __ Branch(&at_start, ne, a0, Operand(zero_reg));
+ LoadCurrentCharacterUnchecked(-1, 1); // Load previous char.
+ __ jmp(&start_label_);
+ __ bind(&at_start);
+ __ li(current_character(), Operand('\n'));
+ __ jmp(&start_label_);
+
+
+ // Exit code:
+ if (success_label_.is_linked()) {
+ // Save captures when successful.
+ __ bind(&success_label_);
+ if (num_saved_registers_ > 0) {
+ // Copy captures to output.
+ __ lw(a1, MemOperand(frame_pointer(), kInputStart));
+ __ lw(a0, MemOperand(frame_pointer(), kRegisterOutput));
+ __ lw(a2, MemOperand(frame_pointer(), kStartIndex));
+ __ Subu(a1, end_of_input_address(), a1);
+ // a1 is length of input in bytes.
+ if (mode_ == UC16) {
+ __ srl(a1, a1, 1);
+ }
+ // a1 is length of input in characters.
+ __ Addu(a1, a1, Operand(a2));
+ // a1 is length of string in characters.
+
+ ASSERT_EQ(0, num_saved_registers_ % 2);
+ // Always an even number of capture registers. This allows us to
+ // unroll the loop once to add an operation between a load of a register
+ // and the following use of that register.
+ for (int i = 0; i < num_saved_registers_; i += 2) {
+ __ lw(a2, register_location(i));
+ __ lw(a3, register_location(i + 1));
+ if (mode_ == UC16) {
+ __ sra(a2, a2, 1);
+ __ Addu(a2, a2, a1);
+ __ sra(a3, a3, 1);
+ __ Addu(a3, a3, a1);
+ } else {
+ __ Addu(a2, a1, Operand(a2));
+ __ Addu(a3, a1, Operand(a3));
+ }
+ __ sw(a2, MemOperand(a0));
+ __ Addu(a0, a0, kPointerSize);
+ __ sw(a3, MemOperand(a0));
+ __ Addu(a0, a0, kPointerSize);
+ }
+ }
+ __ li(v0, Operand(SUCCESS));
+ }
+ // Exit and return v0.
+ __ bind(&exit_label_);
+ // Skip sp past regexp registers and local variables..
+ __ mov(sp, frame_pointer());
+ // Restore registers s0..s7 and return (restoring ra to pc).
+ __ MultiPop(registers_to_retain | ra.bit());
+ __ Ret();
+
+ // Backtrack code (branch target for conditional backtracks).
+ if (backtrack_label_.is_linked()) {
+ __ bind(&backtrack_label_);
+ Backtrack();
+ }
+
+ Label exit_with_exception;
+
+ // Preempt-code.
+ if (check_preempt_label_.is_linked()) {
+ SafeCallTarget(&check_preempt_label_);
+ // Put regexp engine registers on stack.
+ RegList regexp_registers_to_retain = current_input_offset().bit() |
+ current_character().bit() | backtrack_stackpointer().bit();
+ __ MultiPush(regexp_registers_to_retain);
+ CallCheckStackGuardState(a0);
+ __ MultiPop(regexp_registers_to_retain);
+ // If returning non-zero, we should end execution with the given
+ // result as return value.
+ __ Branch(&exit_label_, ne, v0, Operand(zero_reg));
+
+ // String might have moved: Reload end of string from frame.
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ __ li(code_pointer(), Operand(masm_->CodeObject()));
+ SafeReturn();
+ }
+
+ // Backtrack stack overflow code.
+ if (stack_overflow_label_.is_linked()) {
+ SafeCallTarget(&stack_overflow_label_);
+ // Reached if the backtrack-stack limit has been hit.
+ // Put regexp engine registers on stack first.
+ RegList regexp_registers = current_input_offset().bit() |
+ current_character().bit();
+ __ MultiPush(regexp_registers);
+ Label grow_failed;
+ // Call GrowStack(backtrack_stackpointer(), &stack_base)
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, a0);
+ __ mov(a0, backtrack_stackpointer());
+ __ Addu(a1, frame_pointer(), Operand(kStackHighEnd));
+ __ li(a2, Operand(ExternalReference::isolate_address()));
+ ExternalReference grow_stack =
+ ExternalReference::re_grow_stack(masm_->isolate());
+ __ CallCFunction(grow_stack, num_arguments);
+ // Restore regexp registers.
+ __ MultiPop(regexp_registers);
+ // If return NULL, we have failed to grow the stack, and
+ // must exit with a stack-overflow exception.
+ __ Branch(&exit_with_exception, eq, v0, Operand(zero_reg));
+ // Otherwise use return value as new stack pointer.
+ __ mov(backtrack_stackpointer(), v0);
+ // Restore saved registers and continue.
+ __ li(code_pointer(), Operand(masm_->CodeObject()));
+ __ lw(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
+ SafeReturn();
+ }
+
+ if (exit_with_exception.is_linked()) {
+ // If any of the code above needed to exit with an exception.
+ __ bind(&exit_with_exception);
+ // Exit with Result EXCEPTION(-1) to signal thrown exception.
+ __ li(v0, Operand(EXCEPTION));
+ __ jmp(&exit_label_);
+ }
+ }
+
+ CodeDesc code_desc;
+ masm_->GetCode(&code_desc);
+ Handle<Code> code = FACTORY->NewCode(code_desc,
+ Code::ComputeFlags(Code::REGEXP),
+ masm_->CodeObject());
+ LOG(Isolate::Current(), RegExpCodeCreateEvent(*code, *source));
+ return Handle<HeapObject>::cast(code);
}
void RegExpMacroAssemblerMIPS::GoTo(Label* to) {
- UNIMPLEMENTED_MIPS();
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
}
@@ -281,13 +849,15 @@ void RegExpMacroAssemblerMIPS::IfRegisterGE(int reg,
void RegExpMacroAssemblerMIPS::IfRegisterLT(int reg,
int comparand,
Label* if_lt) {
- UNIMPLEMENTED_MIPS();
+ __ lw(a0, register_location(reg));
+ BranchOrBacktrack(if_lt, lt, a0, Operand(comparand));
}
void RegExpMacroAssemblerMIPS::IfRegisterEqPos(int reg,
Label* if_eq) {
- UNIMPLEMENTED_MIPS();
+ __ lw(a0, register_location(reg));
+ BranchOrBacktrack(if_eq, eq, a0, Operand(current_input_offset()));
}
@@ -301,23 +871,47 @@ void RegExpMacroAssemblerMIPS::LoadCurrentCharacter(int cp_offset,
Label* on_end_of_input,
bool check_bounds,
int characters) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
+ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works).
+ if (check_bounds) {
+ CheckPosition(cp_offset + characters - 1, on_end_of_input);
+ }
+ LoadCurrentCharacterUnchecked(cp_offset, characters);
}
void RegExpMacroAssemblerMIPS::PopCurrentPosition() {
- UNIMPLEMENTED_MIPS();
+ Pop(current_input_offset());
}
void RegExpMacroAssemblerMIPS::PopRegister(int register_index) {
- UNIMPLEMENTED_MIPS();
+ Pop(a0);
+ __ sw(a0, register_location(register_index));
}
-
void RegExpMacroAssemblerMIPS::PushBacktrack(Label* label) {
- UNIMPLEMENTED_MIPS();
+ if (label->is_bound()) {
+ int target = label->pos();
+ __ li(a0, Operand(target + Code::kHeaderSize - kHeapObjectTag));
+ } else {
+ Label after_constant;
+ __ Branch(&after_constant);
+ int offset = masm_->pc_offset();
+ int cp_offset = offset + Code::kHeaderSize - kHeapObjectTag;
+ __ emit(0);
+ masm_->label_at_put(label, offset);
+ __ bind(&after_constant);
+ if (is_int16(cp_offset)) {
+ __ lw(a0, MemOperand(code_pointer(), cp_offset));
+ } else {
+ __ Addu(a0, code_pointer(), cp_offset);
+ __ lw(a0, MemOperand(a0, 0));
+ }
+ }
+ Push(a0);
+ CheckStackLimit();
}
@@ -328,55 +922,90 @@ void RegExpMacroAssemblerMIPS::PushCurrentPosition() {
void RegExpMacroAssemblerMIPS::PushRegister(int register_index,
StackCheckFlag check_stack_limit) {
- UNIMPLEMENTED_MIPS();
+ __ lw(a0, register_location(register_index));
+ Push(a0);
+ if (check_stack_limit) CheckStackLimit();
}
void RegExpMacroAssemblerMIPS::ReadCurrentPositionFromRegister(int reg) {
- UNIMPLEMENTED_MIPS();
+ __ lw(current_input_offset(), register_location(reg));
}
void RegExpMacroAssemblerMIPS::ReadStackPointerFromRegister(int reg) {
- UNIMPLEMENTED_MIPS();
+ __ lw(backtrack_stackpointer(), register_location(reg));
+ __ lw(a0, MemOperand(frame_pointer(), kStackHighEnd));
+ __ Addu(backtrack_stackpointer(), backtrack_stackpointer(), Operand(a0));
}
void RegExpMacroAssemblerMIPS::SetCurrentPositionFromEnd(int by) {
- UNIMPLEMENTED_MIPS();
+ Label after_position;
+ __ Branch(&after_position,
+ ge,
+ current_input_offset(),
+ Operand(-by * char_size()));
+ __ li(current_input_offset(), -by * char_size());
+ // On RegExp code entry (where this operation is used), the character before
+ // the current position is expected to be already loaded.
+ // We have advanced the position, so it's safe to read backwards.
+ LoadCurrentCharacterUnchecked(-1, 1);
+ __ bind(&after_position);
}
void RegExpMacroAssemblerMIPS::SetRegister(int register_index, int to) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
+ __ li(a0, Operand(to));
+ __ sw(a0, register_location(register_index));
}
void RegExpMacroAssemblerMIPS::Succeed() {
- UNIMPLEMENTED_MIPS();
+ __ jmp(&success_label_);
}
void RegExpMacroAssemblerMIPS::WriteCurrentPositionToRegister(int reg,
int cp_offset) {
- UNIMPLEMENTED_MIPS();
+ if (cp_offset == 0) {
+ __ sw(current_input_offset(), register_location(reg));
+ } else {
+ __ Addu(a0, current_input_offset(), Operand(cp_offset * char_size()));
+ __ sw(a0, register_location(reg));
+ }
}
void RegExpMacroAssemblerMIPS::ClearRegisters(int reg_from, int reg_to) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(reg_from <= reg_to);
+ __ lw(a0, MemOperand(frame_pointer(), kInputStartMinusOne));
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ __ sw(a0, register_location(reg));
+ }
}
void RegExpMacroAssemblerMIPS::WriteStackPointerToRegister(int reg) {
- UNIMPLEMENTED_MIPS();
+ __ lw(a1, MemOperand(frame_pointer(), kStackHighEnd));
+ __ Subu(a0, backtrack_stackpointer(), a1);
+ __ sw(a0, register_location(reg));
}
// Private methods:
void RegExpMacroAssemblerMIPS::CallCheckStackGuardState(Register scratch) {
- UNIMPLEMENTED_MIPS();
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, scratch);
+ __ mov(a2, frame_pointer());
+ // Code* of self.
+ __ li(a1, Operand(masm_->CodeObject()));
+ // a0 becomes return address pointer.
+ ExternalReference stack_guard_check =
+ ExternalReference::re_check_stack_guard_state(masm_->isolate());
+ CallCFunctionUsingStub(stack_guard_check, num_arguments);
}
@@ -388,22 +1017,101 @@ static T& frame_entry(Address re_frame, int frame_offset) {
int RegExpMacroAssemblerMIPS::CheckStackGuardState(Address* return_address,
- Code* re_code,
- Address re_frame) {
- UNIMPLEMENTED_MIPS();
+ Code* re_code,
+ Address re_frame) {
+ Isolate* isolate = frame_entry<Isolate*>(re_frame, kIsolate);
+ ASSERT(isolate == Isolate::Current());
+ if (isolate->stack_guard()->IsStackOverflow()) {
+ isolate->StackOverflow();
+ return EXCEPTION;
+ }
+
+ // If not real stack overflow the stack guard was used to interrupt
+ // execution for another purpose.
+
+ // If this is a direct call from JavaScript retry the RegExp forcing the call
+ // through the runtime system. Currently the direct call cannot handle a GC.
+ if (frame_entry<int>(re_frame, kDirectCall) == 1) {
+ return RETRY;
+ }
+
+ // Prepare for possible GC.
+ HandleScope handles;
+ Handle<Code> code_handle(re_code);
+
+ Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
+ // Current string.
+ bool is_ascii = subject->IsAsciiRepresentation();
+
+ ASSERT(re_code->instruction_start() <= *return_address);
+ ASSERT(*return_address <=
+ re_code->instruction_start() + re_code->instruction_size());
+
+ MaybeObject* result = Execution::HandleStackGuardInterrupt();
+
+ if (*code_handle != re_code) { // Return address no longer valid.
+ int delta = *code_handle - re_code;
+ // Overwrite the return address on the stack.
+ *return_address += delta;
+ }
+
+ if (result->IsException()) {
+ return EXCEPTION;
+ }
+
+ // String might have changed.
+ if (subject->IsAsciiRepresentation() != is_ascii) {
+ // If we changed between an ASCII and an UC16 string, the specialized
+ // code cannot be used, and we need to restart regexp matching from
+ // scratch (including, potentially, compiling a new version of the code).
+ return RETRY;
+ }
+
+ // Otherwise, the content of the string might have moved. It must still
+ // be a sequential or external string with the same content.
+ // Update the start and end pointers in the stack frame to the current
+ // location (whether it has actually moved or not).
+ ASSERT(StringShape(*subject).IsSequential() ||
+ StringShape(*subject).IsExternal());
+
+ // The original start address of the characters to match.
+ const byte* start_address = frame_entry<const byte*>(re_frame, kInputStart);
+
+ // Find the current start address of the same character at the current string
+ // position.
+ int start_index = frame_entry<int>(re_frame, kStartIndex);
+ const byte* new_address = StringCharacterPosition(*subject, start_index);
+
+ if (start_address != new_address) {
+ // If there is a difference, update the object pointer and start and end
+ // addresses in the RegExp stack frame to match the new value.
+ const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd);
+ int byte_length = end_address - start_address;
+ frame_entry<const String*>(re_frame, kInputString) = *subject;
+ frame_entry<const byte*>(re_frame, kInputStart) = new_address;
+ frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length;
+ }
+
return 0;
}
MemOperand RegExpMacroAssemblerMIPS::register_location(int register_index) {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
+ ASSERT(register_index < (1<<30));
+ if (num_registers_ <= register_index) {
+ num_registers_ = register_index + 1;
+ }
+ return MemOperand(frame_pointer(),
+ kRegisterZero - register_index * kPointerSize);
}
void RegExpMacroAssemblerMIPS::CheckPosition(int cp_offset,
Label* on_outside_input) {
- UNIMPLEMENTED_MIPS();
+ BranchOrBacktrack(on_outside_input,
+ ge,
+ current_input_offset(),
+ Operand(-cp_offset * char_size()));
}
@@ -411,61 +1119,126 @@ void RegExpMacroAssemblerMIPS::BranchOrBacktrack(Label* to,
Condition condition,
Register rs,
const Operand& rt) {
- UNIMPLEMENTED_MIPS();
+ if (condition == al) { // Unconditional.
+ if (to == NULL) {
+ Backtrack();
+ return;
+ }
+ __ jmp(to);
+ return;
+ }
+ if (to == NULL) {
+ __ Branch(&backtrack_label_, condition, rs, rt);
+ return;
+ }
+ __ Branch(to, condition, rs, rt);
}
void RegExpMacroAssemblerMIPS::SafeCall(Label* to, Condition cond, Register rs,
const Operand& rt) {
- UNIMPLEMENTED_MIPS();
+ __ BranchAndLink(to, cond, rs, rt);
}
void RegExpMacroAssemblerMIPS::SafeReturn() {
- UNIMPLEMENTED_MIPS();
+ __ pop(ra);
+ __ Addu(t5, ra, Operand(masm_->CodeObject()));
+ __ Jump(t5);
}
void RegExpMacroAssemblerMIPS::SafeCallTarget(Label* name) {
- UNIMPLEMENTED_MIPS();
+ __ bind(name);
+ __ Subu(ra, ra, Operand(masm_->CodeObject()));
+ __ push(ra);
}
void RegExpMacroAssemblerMIPS::Push(Register source) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(!source.is(backtrack_stackpointer()));
+ __ Addu(backtrack_stackpointer(),
+ backtrack_stackpointer(),
+ Operand(-kPointerSize));
+ __ sw(source, MemOperand(backtrack_stackpointer()));
}
void RegExpMacroAssemblerMIPS::Pop(Register target) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(!target.is(backtrack_stackpointer()));
+ __ lw(target, MemOperand(backtrack_stackpointer()));
+ __ Addu(backtrack_stackpointer(), backtrack_stackpointer(), kPointerSize);
}
void RegExpMacroAssemblerMIPS::CheckPreemption() {
- UNIMPLEMENTED_MIPS();
+ // Check for preemption.
+ ExternalReference stack_limit =
+ ExternalReference::address_of_stack_limit(masm_->isolate());
+ __ li(a0, Operand(stack_limit));
+ __ lw(a0, MemOperand(a0));
+ SafeCall(&check_preempt_label_, ls, sp, Operand(a0));
}
void RegExpMacroAssemblerMIPS::CheckStackLimit() {
- UNIMPLEMENTED_MIPS();
+ ExternalReference stack_limit =
+ ExternalReference::address_of_regexp_stack_limit(masm_->isolate());
+
+ __ li(a0, Operand(stack_limit));
+ __ lw(a0, MemOperand(a0));
+ SafeCall(&stack_overflow_label_, ls, backtrack_stackpointer(), Operand(a0));
}
void RegExpMacroAssemblerMIPS::CallCFunctionUsingStub(
ExternalReference function,
int num_arguments) {
- UNIMPLEMENTED_MIPS();
+ // Must pass all arguments in registers. The stub pushes on the stack.
+ ASSERT(num_arguments <= 4);
+ __ li(code_pointer(), Operand(function));
+ RegExpCEntryStub stub;
+ __ CallStub(&stub);
+ if (OS::ActivationFrameAlignment() != 0) {
+ __ lw(sp, MemOperand(sp, 16));
+ }
+ __ li(code_pointer(), Operand(masm_->CodeObject()));
}
void RegExpMacroAssemblerMIPS::LoadCurrentCharacterUnchecked(int cp_offset,
- int characters) {
- UNIMPLEMENTED_MIPS();
+ int characters) {
+ Register offset = current_input_offset();
+ if (cp_offset != 0) {
+ __ Addu(a0, current_input_offset(), Operand(cp_offset * char_size()));
+ offset = a0;
+ }
+ // We assume that we cannot do unaligned loads on MIPS, so this function
+ // must only be used to load a single character at a time.
+ ASSERT(characters == 1);
+ __ Addu(t5, end_of_input_address(), Operand(offset));
+ if (mode_ == ASCII) {
+ __ lbu(current_character(), MemOperand(t5, 0));
+ } else {
+ ASSERT(mode_ == UC16);
+ __ lhu(current_character(), MemOperand(t5, 0));
+ }
}
void RegExpCEntryStub::Generate(MacroAssembler* masm_) {
- UNIMPLEMENTED_MIPS();
+ int stack_alignment = OS::ActivationFrameAlignment();
+ if (stack_alignment < kPointerSize) stack_alignment = kPointerSize;
+ // Stack is already aligned for call, so decrement by alignment
+ // to make room for storing the return address.
+ __ Subu(sp, sp, Operand(stack_alignment));
+ __ sw(ra, MemOperand(sp, 0));
+ __ mov(a0, sp);
+ __ mov(t9, t1);
+ __ Call(t9);
+ __ lw(ra, MemOperand(sp, 0));
+ __ Addu(sp, sp, Operand(stack_alignment));
+ __ Jump(Operand(ra));
}
diff --git a/src/mips/regexp-macro-assembler-mips.h b/src/mips/regexp-macro-assembler-mips.h
index 7310c9d4..ad7ada54 100644
--- a/src/mips/regexp-macro-assembler-mips.h
+++ b/src/mips/regexp-macro-assembler-mips.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -121,10 +121,11 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
static const int kStoredRegisters = kFramePointer;
// Return address (stored from link register, read into pc on return).
static const int kReturnAddress = kStoredRegisters + 9 * kPointerSize;
+ static const int kSecondaryReturnAddress = kReturnAddress + kPointerSize;
// Stack frame header.
static const int kStackFrameHeader = kReturnAddress + kPointerSize;
// Stack parameters placed by caller.
- static const int kRegisterOutput = kStackFrameHeader + 16;
+ static const int kRegisterOutput = kStackFrameHeader + 20;
static const int kStackHighEnd = kRegisterOutput + kPointerSize;
static const int kDirectCall = kStackHighEnd + kPointerSize;
static const int kIsolate = kDirectCall + kPointerSize;
@@ -183,7 +184,7 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
// Register holding pointer to the current code object.
inline Register code_pointer() { return t1; }
- // Byte size of chars in the string to match (decided by the Mode argument)
+ // Byte size of chars in the string to match (decided by the Mode argument).
inline int char_size() { return static_cast<int>(mode_); }
// Equivalent to a conditional branch to the label, unless the label
@@ -228,7 +229,7 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
int num_registers_;
// Number of registers to output at the end (the saved registers
- // are always 0..num_saved_registers_-1)
+ // are always 0..num_saved_registers_-1).
int num_saved_registers_;
// Labels used internally.
@@ -239,6 +240,7 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler {
Label exit_label_;
Label check_preempt_label_;
Label stack_overflow_label_;
+ Label internal_failure_label_;
};
#endif // V8_INTERPRETED_REGEXP
diff --git a/src/mips/register-allocator-mips-inl.h b/src/mips/register-allocator-mips-inl.h
deleted file mode 100644
index bbfb31dd..00000000
--- a/src/mips/register-allocator-mips-inl.h
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2010 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_IA32_REGISTER_ALLOCATOR_MIPS_INL_H_
-#define V8_IA32_REGISTER_ALLOCATOR_MIPS_INL_H_
-
-#include "v8.h"
-#include "mips/assembler-mips.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// RegisterAllocator implementation.
-
-bool RegisterAllocator::IsReserved(Register reg) {
- // The code for this test relies on the order of register codes.
- return reg.is(cp) || reg.is(s8_fp) || reg.is(sp);
-}
-
-
-int RegisterAllocator::ToNumber(Register reg) {
- ASSERT(reg.is_valid() && !IsReserved(reg));
- const int kNumbers[] = {
- 0, // zero_reg
- 1, // at
- 2, // v0
- 3, // v1
- 4, // a0
- 5, // a1
- 6, // a2
- 7, // a3
- 8, // t0
- 9, // t1
- 10, // t2
- 11, // t3
- 12, // t4
- 13, // t5
- 14, // t
- 15, // t7
- 16, // t8
- 17, // t9
- 18, // s0
- 19, // s1
- 20, // s2
- 21, // s3
- 22, // s4
- 23, // s5
- 24, // s6
- 25, // s7
- 26, // k0
- 27, // k1
- 28, // gp
- 29, // sp
- 30, // s8_fp
- 31, // ra
- };
- return kNumbers[reg.code()];
-}
-
-
-Register RegisterAllocator::ToRegister(int num) {
- ASSERT(num >= 0 && num < kNumRegisters);
- const Register kRegisters[] = {
- zero_reg,
- at,
- v0,
- v1,
- a0,
- a1,
- a2,
- a3,
- t0,
- t1,
- t2,
- t3,
- t4,
- t5,
- t6,
- t7,
- s0,
- s1,
- s2,
- s3,
- s4,
- s5,
- s6,
- s7,
- t8,
- t9,
- k0,
- k1,
- gp,
- sp,
- s8_fp,
- ra
- };
- return kRegisters[num];
-}
-
-
-void RegisterAllocator::Initialize() {
- Reset();
-}
-
-
-} } // namespace v8::internal
-
-#endif // V8_IA32_REGISTER_ALLOCATOR_MIPS_INL_H_
-
diff --git a/src/mips/simulator-mips.cc b/src/mips/simulator-mips.cc
index 50ad7a18..68fb7ce8 100644
--- a/src/mips/simulator-mips.cc
+++ b/src/mips/simulator-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -35,7 +35,7 @@
#include "disasm.h"
#include "assembler.h"
-#include "globals.h" // Need the BitCast
+#include "globals.h" // Need the BitCast.
#include "mips/constants-mips.h"
#include "mips/simulator-mips.h"
@@ -46,7 +46,7 @@
namespace v8 {
namespace internal {
-// Utils functions
+// Utils functions.
bool HaveSameSign(int32_t a, int32_t b) {
return ((a ^ b) >= 0);
}
@@ -139,7 +139,7 @@ void MipsDebugger::Stop(Instruction* instr) {
}
-#else // ndef GENERATED_CODE_COVERAGE
+#else // GENERATED_CODE_COVERAGE
#define UNSUPPORTED() printf("Unsupported instruction.\n");
@@ -263,15 +263,15 @@ void MipsDebugger::PrintAllRegs() {
#define REG_INFO(n) Registers::Name(n), GetRegisterValue(n), GetRegisterValue(n)
PrintF("\n");
- // at, v0, a0
+ // at, v0, a0.
PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
REG_INFO(1), REG_INFO(2), REG_INFO(4));
- // v1, a1
+ // v1, a1.
PrintF("%26s\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
"", REG_INFO(3), REG_INFO(5));
- // a2
+ // a2.
PrintF("%26s\t%26s\t%3s: 0x%08x %10d\n", "", "", REG_INFO(6));
- // a3
+ // a3.
PrintF("%26s\t%26s\t%3s: 0x%08x %10d\n", "", "", REG_INFO(7));
PrintF("\n");
// t0-t7, s0-s7
@@ -280,16 +280,16 @@ void MipsDebugger::PrintAllRegs() {
REG_INFO(8+i), REG_INFO(16+i));
}
PrintF("\n");
- // t8, k0, LO
+ // t8, k0, LO.
PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
REG_INFO(24), REG_INFO(26), REG_INFO(32));
- // t9, k1, HI
+ // t9, k1, HI.
PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
REG_INFO(25), REG_INFO(27), REG_INFO(33));
- // sp, fp, gp
+ // sp, fp, gp.
PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
REG_INFO(29), REG_INFO(30), REG_INFO(28));
- // pc
+ // pc.
PrintF("%3s: 0x%08x %10d\t%3s: 0x%08x %10d\n",
REG_INFO(31), REG_INFO(34));
@@ -307,7 +307,7 @@ void MipsDebugger::PrintAllRegsIncludingFPU() {
PrintAllRegs();
PrintF("\n\n");
- // f0, f1, f2, ... f31
+ // f0, f1, f2, ... f31.
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(0) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(2) );
PrintF("%3s,%3s: 0x%08x%08x %16.4e\n", FPU_REG_INFO(4) );
@@ -345,7 +345,7 @@ void MipsDebugger::Debug() {
char arg2[ARG_SIZE + 1];
char* argv[3] = { cmd, arg1, arg2 };
- // make sure to have a proper terminating character if reaching the limit
+ // Make sure to have a proper terminating character if reaching the limit.
cmd[COMMAND_SIZE] = 0;
arg1[ARG_SIZE] = 0;
arg2[ARG_SIZE] = 0;
@@ -358,10 +358,10 @@ void MipsDebugger::Debug() {
if (last_pc != sim_->get_pc()) {
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
- // use a reasonably large buffer
+ // Use a reasonably large buffer.
v8::internal::EmbeddedVector<char, 256> buffer;
dasm.InstructionDecode(buffer,
- reinterpret_cast<byte_*>(sim_->get_pc()));
+ reinterpret_cast<byte*>(sim_->get_pc()));
PrintF(" 0x%08x %s\n", sim_->get_pc(), buffer.start());
last_pc = sim_->get_pc();
}
@@ -475,7 +475,7 @@ void MipsDebugger::Debug() {
if (strcmp(cmd, "stack") == 0) {
cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp));
- } else { // "mem"
+ } else { // Command "mem".
int32_t value;
if (!GetValue(arg1, &value)) {
PrintF("%s unrecognized\n", arg1);
@@ -496,35 +496,62 @@ void MipsDebugger::Debug() {
end = cur + words;
while (cur < end) {
- PrintF(" 0x%08x: 0x%08x %10d\n",
+ PrintF(" 0x%08x: 0x%08x %10d",
reinterpret_cast<intptr_t>(cur), *cur, *cur);
+ HeapObject* obj = reinterpret_cast<HeapObject*>(*cur);
+ int value = *cur;
+ Heap* current_heap = v8::internal::Isolate::Current()->heap();
+ if (current_heap->Contains(obj) || ((value & 1) == 0)) {
+ PrintF(" (");
+ if ((value & 1) == 0) {
+ PrintF("smi %d", value / 2);
+ } else {
+ obj->ShortPrint();
+ }
+ PrintF(")");
+ }
+ PrintF("\n");
cur++;
}
- } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0)) {
+ } else if ((strcmp(cmd, "disasm") == 0) ||
+ (strcmp(cmd, "dpc") == 0) ||
+ (strcmp(cmd, "di") == 0)) {
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
- // use a reasonably large buffer
+ // Use a reasonably large buffer.
v8::internal::EmbeddedVector<char, 256> buffer;
- byte_* cur = NULL;
- byte_* end = NULL;
+ byte* cur = NULL;
+ byte* end = NULL;
if (argc == 1) {
- cur = reinterpret_cast<byte_*>(sim_->get_pc());
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
end = cur + (10 * Instruction::kInstrSize);
} else if (argc == 2) {
- int32_t value;
- if (GetValue(arg1, &value)) {
- cur = reinterpret_cast<byte_*>(value);
- // no length parameter passed, assume 10 instructions
- end = cur + (10 * Instruction::kInstrSize);
+ int regnum = Registers::Number(arg1);
+ if (regnum != kInvalidRegister || strncmp(arg1, "0x", 2) == 0) {
+ // The argument is an address or a register name.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(value);
+ // Disassemble 10 instructions at <arg1>.
+ end = cur + (10 * Instruction::kInstrSize);
+ }
+ } else {
+ // The argument is the number of instructions.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * Instruction::kInstrSize);
+ }
}
} else {
int32_t value1;
int32_t value2;
if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
- cur = reinterpret_cast<byte_*>(value1);
+ cur = reinterpret_cast<byte*>(value1);
end = cur + (value2 * Instruction::kInstrSize);
}
}
@@ -561,25 +588,25 @@ void MipsDebugger::Debug() {
} else if (strcmp(cmd, "unstop") == 0) {
PrintF("Unstop command not implemented on MIPS.");
} else if ((strcmp(cmd, "stat") == 0) || (strcmp(cmd, "st") == 0)) {
- // Print registers and disassemble
+ // Print registers and disassemble.
PrintAllRegs();
PrintF("\n");
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
- // use a reasonably large buffer
+ // Use a reasonably large buffer.
v8::internal::EmbeddedVector<char, 256> buffer;
- byte_* cur = NULL;
- byte_* end = NULL;
+ byte* cur = NULL;
+ byte* end = NULL;
if (argc == 1) {
- cur = reinterpret_cast<byte_*>(sim_->get_pc());
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
end = cur + (10 * Instruction::kInstrSize);
} else if (argc == 2) {
int32_t value;
if (GetValue(arg1, &value)) {
- cur = reinterpret_cast<byte_*>(value);
+ cur = reinterpret_cast<byte*>(value);
// no length parameter passed, assume 10 instructions
end = cur + (10 * Instruction::kInstrSize);
}
@@ -587,7 +614,7 @@ void MipsDebugger::Debug() {
int32_t value1;
int32_t value2;
if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
- cur = reinterpret_cast<byte_*>(value1);
+ cur = reinterpret_cast<byte*>(value1);
end = cur + (value2 * Instruction::kInstrSize);
}
}
@@ -615,8 +642,10 @@ void MipsDebugger::Debug() {
PrintF("flags\n");
PrintF(" print flags\n");
PrintF("disasm [<instructions>]\n");
- PrintF("disasm [[<address>] <instructions>]\n");
- PrintF(" disassemble code, default is 10 instructions from pc\n");
+ PrintF("disasm [<address/register>]\n");
+ PrintF("disasm [[<address/register>] <instructions>]\n");
+ PrintF(" disassemble code, default is 10 instructions\n");
+ PrintF(" from pc (alias 'di')\n");
PrintF("gdb\n");
PrintF(" enter gdb\n");
PrintF("break <address>\n");
@@ -689,8 +718,8 @@ void Simulator::FlushICache(v8::internal::HashMap* i_cache,
CachePage* Simulator::GetCachePage(v8::internal::HashMap* i_cache, void* page) {
v8::internal::HashMap::Entry* entry = i_cache->Lookup(page,
- ICacheHash(page),
- true);
+ ICacheHash(page),
+ true);
if (entry->value == NULL) {
CachePage* new_page = new CachePage();
entry->value = new_page;
@@ -738,23 +767,23 @@ void Simulator::CheckICache(v8::internal::HashMap* i_cache,
}
-void Simulator::Initialize() {
- if (Isolate::Current()->simulator_initialized()) return;
- Isolate::Current()->set_simulator_initialized(true);
- ::v8::internal::ExternalReference::set_redirector(&RedirectExternalReference);
+void Simulator::Initialize(Isolate* isolate) {
+ if (isolate->simulator_initialized()) return;
+ isolate->set_simulator_initialized(true);
+ ::v8::internal::ExternalReference::set_redirector(isolate,
+ &RedirectExternalReference);
}
-Simulator::Simulator() : isolate_(Isolate::Current()) {
+Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
i_cache_ = isolate_->simulator_i_cache();
if (i_cache_ == NULL) {
i_cache_ = new v8::internal::HashMap(&ICacheMatch);
isolate_->set_simulator_i_cache(i_cache_);
}
- Initialize();
+ Initialize(isolate);
// Setup simulator support first. Some of this information is needed to
// setup the architecture state.
- stack_size_ = 1 * 1024*1024; // allocate 1MB for stack
stack_ = reinterpret_cast<char*>(malloc(stack_size_));
pc_modified_ = false;
icount_ = 0;
@@ -852,17 +881,14 @@ void* Simulator::RedirectExternalReference(void* external_function,
// Get the active Simulator for the current thread.
Simulator* Simulator::current(Isolate* isolate) {
v8::internal::Isolate::PerIsolateThreadData* isolate_data =
- Isolate::CurrentPerIsolateThreadData();
- if (isolate_data == NULL) {
- Isolate::EnterDefaultIsolate();
- isolate_data = Isolate::CurrentPerIsolateThreadData();
- }
+ isolate->FindOrAllocatePerThreadDataForThisThread();
+ ASSERT(isolate_data != NULL);
ASSERT(isolate_data != NULL);
Simulator* sim = isolate_data->simulator();
if (sim == NULL) {
// TODO(146): delete the simulator object when a thread/isolate goes away.
- sim = new Simulator();
+ sim = new Simulator(isolate);
isolate_data->set_simulator(sim);
}
return sim;
@@ -877,7 +903,7 @@ void Simulator::set_register(int reg, int32_t value) {
pc_modified_ = true;
}
- // zero register always hold 0.
+ // Zero register always holds 0.
registers_[reg] = (reg == 0) ? 0 : value;
}
@@ -937,6 +963,87 @@ double Simulator::get_fpu_register_double(int fpureg) const {
}
+// For use in calls that take two double values, constructed either
+// from a0-a3 or f12 and f14.
+void Simulator::GetFpArgs(double* x, double* y) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ *y = get_fpu_register_double(14);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+
+ // Registers a0 and a1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ memcpy(x, buffer, sizeof(buffer));
+
+ // Registers a2 and a3 -> y.
+ reg_buffer[0] = get_register(a2);
+ reg_buffer[1] = get_register(a3);
+ memcpy(y, buffer, sizeof(buffer));
+ }
+}
+
+
+// For use in calls that take one double value, constructed either
+// from a0 and a1 or f12.
+void Simulator::GetFpArgs(double* x) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ // Registers a0 and a1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ memcpy(x, buffer, sizeof(buffer));
+ }
+}
+
+
+// For use in calls that take one double value constructed either
+// from a0 and a1 or f12 and one integer value.
+void Simulator::GetFpArgs(double* x, int32_t* y) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ *y = get_register(a2);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ // Registers 0 and 1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ memcpy(x, buffer, sizeof(buffer));
+
+ // Register 2 -> y.
+ reg_buffer[0] = get_register(a2);
+ memcpy(y, buffer, sizeof(*y));
+ }
+}
+
+
+// The return value is either in v0/v1 or f0.
+void Simulator::SetFpResult(const double& result) {
+ if (!IsMipsSoftFloatABI) {
+ set_fpu_register_double(0, result);
+ } else {
+ char buffer[2 * sizeof(registers_[0])];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ memcpy(buffer, &result, sizeof(buffer));
+ // Copy result to v0 and v1.
+ set_register(v0, reg_buffer[0]);
+ set_register(v1, reg_buffer[1]);
+ }
+}
+
+
// Helper functions for setting and testing the FCSR register's bits.
void Simulator::set_fcsr_bit(uint32_t cc, bool value) {
if (value) {
@@ -995,7 +1102,7 @@ int32_t Simulator::get_pc() const {
int Simulator::ReadW(int32_t addr, Instruction* instr) {
if (addr >=0 && addr < 0x400) {
- // this has to be a NULL-dereference
+ // This has to be a NULL-dereference, drop into debugger.
MipsDebugger dbg(this);
dbg.Debug();
}
@@ -1003,8 +1110,9 @@ int Simulator::ReadW(int32_t addr, Instruction* instr) {
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
return *ptr;
}
- PrintF("Unaligned read at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
MipsDebugger dbg(this);
dbg.Debug();
return 0;
@@ -1013,7 +1121,7 @@ int Simulator::ReadW(int32_t addr, Instruction* instr) {
void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
if (addr >= 0 && addr < 0x400) {
- // this has to be a NULL-dereference
+ // This has to be a NULL-dereference, drop into debugger.
MipsDebugger dbg(this);
dbg.Debug();
}
@@ -1022,8 +1130,9 @@ void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
*ptr = value;
return;
}
- PrintF("Unaligned write at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
MipsDebugger dbg(this);
dbg.Debug();
}
@@ -1034,8 +1143,9 @@ double Simulator::ReadD(int32_t addr, Instruction* instr) {
double* ptr = reinterpret_cast<double*>(addr);
return *ptr;
}
- PrintF("Unaligned (double) read at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned (double) read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
OS::Abort();
return 0;
}
@@ -1047,8 +1157,9 @@ void Simulator::WriteD(int32_t addr, double value, Instruction* instr) {
*ptr = value;
return;
}
- PrintF("Unaligned (double) write at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned (double) write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
OS::Abort();
}
@@ -1058,8 +1169,9 @@ uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) {
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
return *ptr;
}
- PrintF("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
OS::Abort();
return 0;
}
@@ -1070,8 +1182,9 @@ int16_t Simulator::ReadH(int32_t addr, Instruction* instr) {
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
return *ptr;
}
- PrintF("Unaligned signed halfword read at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned signed halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
OS::Abort();
return 0;
}
@@ -1083,8 +1196,9 @@ void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) {
*ptr = value;
return;
}
- PrintF("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned unsigned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
OS::Abort();
}
@@ -1095,8 +1209,9 @@ void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) {
*ptr = value;
return;
}
- PrintF("Unaligned halfword write at 0x%08x, pc=%p\n", addr,
- reinterpret_cast<void*>(instr));
+ PrintF("Unaligned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n",
+ addr,
+ reinterpret_cast<intptr_t>(instr));
OS::Abort();
}
@@ -1158,6 +1273,14 @@ typedef double (*SimulatorRuntimeFPCall)(int32_t arg0,
int32_t arg2,
int32_t arg3);
+// This signature supports direct call in to API function native callback
+// (refer to InvocationCallback in v8.h).
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectApiCall)(int32_t arg0);
+
+// This signature supports direct call to accessor getter callback.
+typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectGetterCall)(int32_t arg0,
+ int32_t arg1);
+
// Software interrupt instructions are used by the simulator to call into the
// C-based V8 runtime. They are also used for debugging with simulator.
void Simulator::SoftwareInterrupt(Instruction* instr) {
@@ -1169,11 +1292,6 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
// We first check if we met a call_rt_redirected.
if (instr->InstructionBits() == rtCallRedirInstr) {
- // Check if stack is aligned. Error if not aligned is reported below to
- // include information on the function called.
- bool stack_aligned =
- (get_register(sp)
- & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0;
Redirection* redirection = Redirection::FromSwiInstruction(instr);
int32_t arg0 = get_register(a0);
int32_t arg1 = get_register(a1);
@@ -1188,58 +1306,122 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
// stack check here.
int32_t* stack_pointer = reinterpret_cast<int32_t*>(get_register(sp));
int32_t* stack = reinterpret_cast<int32_t*>(stack_);
- if (stack_pointer >= stack && stack_pointer < stack + stack_size_) {
- arg4 = stack_pointer[0];
- arg5 = stack_pointer[1];
+ if (stack_pointer >= stack && stack_pointer < stack + stack_size_ - 5) {
+ // Args 4 and 5 are on the stack after the reserved space for args 0..3.
+ arg4 = stack_pointer[4];
+ arg5 = stack_pointer[5];
+ }
+
+ bool fp_call =
+ (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_CALL) ||
+ (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL);
+
+ if (!IsMipsSoftFloatABI) {
+ // With the hard floating point calling convention, double
+ // arguments are passed in FPU registers. Fetch the arguments
+ // from there and call the builtin using soft floating point
+ // convention.
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ arg2 = get_fpu_register(f14);
+ arg3 = get_fpu_register(f15);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ arg2 = get_register(a2);
+ break;
+ default:
+ break;
+ }
}
+
// This is dodgy but it works because the C entry stubs are never moved.
// See comment in codegen-arm.cc and bug 1242173.
int32_t saved_ra = get_register(ra);
intptr_t external =
- reinterpret_cast<int32_t>(redirection->external_function());
+ reinterpret_cast<intptr_t>(redirection->external_function());
// Based on CpuFeatures::IsSupported(FPU), Mips will use either hardware
// FPU, or gcc soft-float routines. Hardware FPU is simulated in this
// simulator. Soft-float has additional abstraction of ExternalReference,
- // to support serialization. Finally, when simulated on x86 host, the
- // x86 softfloat routines are used, and this Redirection infrastructure
- // lets simulated-mips make calls into x86 C code.
- // When doing that, the 'double' return type must be handled differently
- // than the usual int64_t return. The data is returned in different
- // registers and cannot be cast from one type to the other. However, the
- // calling arguments are passed the same way in both cases.
- if (redirection->type() == ExternalReference::FP_RETURN_CALL) {
+ // to support serialization.
+ if (fp_call) {
SimulatorRuntimeFPCall target =
- reinterpret_cast<SimulatorRuntimeFPCall>(external);
- if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
- PrintF("Call to host function at %p with args %08x:%08x %08x:%08x",
- FUNCTION_ADDR(target), arg0, arg1, arg2, arg3);
- if (!stack_aligned) {
- PrintF(" with unaligned stack %08x\n", get_register(sp));
+ reinterpret_cast<SimulatorRuntimeFPCall>(external);
+ if (::v8::internal::FLAG_trace_sim) {
+ double dval0, dval1;
+ int32_t ival;
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ GetFpArgs(&dval0, &dval1);
+ PrintF("Call to host function at %p with args %f, %f",
+ FUNCTION_ADDR(target), dval0, dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ GetFpArgs(&dval0);
+ PrintF("Call to host function at %p with arg %f",
+ FUNCTION_ADDR(target), dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ GetFpArgs(&dval0, &ival);
+ PrintF("Call to host function at %p with args %f, %d",
+ FUNCTION_ADDR(target), dval0, ival);
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
- PrintF("\n");
}
double result = target(arg0, arg1, arg2, arg3);
- // fp result -> registers v0 and v1.
- int32_t gpreg_pair[2];
- memcpy(&gpreg_pair[0], &result, 2 * sizeof(int32_t));
- set_register(v0, gpreg_pair[0]);
- set_register(v1, gpreg_pair[1]);
+ if (redirection->type() != ExternalReference::BUILTIN_COMPARE_CALL) {
+ SetFpResult(result);
+ } else {
+ int32_t gpreg_pair[2];
+ memcpy(&gpreg_pair[0], &result, 2 * sizeof(int32_t));
+ set_register(v0, gpreg_pair[0]);
+ set_register(v1, gpreg_pair[1]);
+ }
} else if (redirection->type() == ExternalReference::DIRECT_API_CALL) {
- PrintF("Mips does not yet support ExternalReference::DIRECT_API_CALL\n");
- ASSERT(redirection->type() != ExternalReference::DIRECT_API_CALL);
+ // See DirectCEntryStub::GenerateCall for explanation of register usage.
+ SimulatorRuntimeDirectApiCall target =
+ reinterpret_cast<SimulatorRuntimeDirectApiCall>(external);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x\n",
+ FUNCTION_ADDR(target), arg1);
+ }
+ v8::Handle<v8::Value> result = target(arg1);
+ *(reinterpret_cast<int*>(arg0)) = (int32_t) *result;
+ set_register(v0, arg0);
} else if (redirection->type() == ExternalReference::DIRECT_GETTER_CALL) {
- PrintF("Mips does not support ExternalReference::DIRECT_GETTER_CALL\n");
- ASSERT(redirection->type() != ExternalReference::DIRECT_GETTER_CALL);
+ // See DirectCEntryStub::GenerateCall for explanation of register usage.
+ SimulatorRuntimeDirectGetterCall target =
+ reinterpret_cast<SimulatorRuntimeDirectGetterCall>(external);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Call to host function at %p args %08x %08x\n",
+ FUNCTION_ADDR(target), arg1, arg2);
+ }
+ v8::Handle<v8::Value> result = target(arg1, arg2);
+ *(reinterpret_cast<int*>(arg0)) = (int32_t) *result;
+ set_register(v0, arg0);
} else {
- // Builtin call.
- ASSERT(redirection->type() == ExternalReference::BUILTIN_CALL);
SimulatorRuntimeCall target =
- reinterpret_cast<SimulatorRuntimeCall>(external);
- if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
+ reinterpret_cast<SimulatorRuntimeCall>(external);
+ if (::v8::internal::FLAG_trace_sim) {
PrintF(
- "Call to host function at %p: %08x, %08x, %08x, %08x, %08x, %08x",
+ "Call to host function at %p "
+ "args %08x, %08x, %08x, %08x, %08x, %08x\n",
FUNCTION_ADDR(target),
arg0,
arg1,
@@ -1247,12 +1429,7 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
arg3,
arg4,
arg5);
- if (!stack_aligned) {
- PrintF(" with unaligned stack %08x\n", get_register(sp));
- }
- PrintF("\n");
}
-
int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
set_register(v0, static_cast<int32_t>(result));
set_register(v1, static_cast<int32_t>(result >> 32));
@@ -1263,8 +1440,8 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
set_register(ra, saved_ra);
set_pc(get_register(ra));
- } else if (func == BREAK && code >= 0 && code < 16) {
- // First 16 break_ codes interpreted as debug markers.
+ } else if (func == BREAK && code >= 0 && code < 32) {
+ // First 32 break_ codes interpreted as debug-markers/watchpoints.
MipsDebugger dbg(this);
++break_count_;
PrintF("\n---- break %d marker: %3d (instr count: %8d) ----------"
@@ -1314,9 +1491,9 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
const int32_t fs_reg = instr->FsValue();
- // ---------- Configuration
+ // ---------- Configuration.
switch (op) {
- case COP1: // Coprocessor instructions
+ case COP1: // Coprocessor instructions.
switch (instr->RsFieldRaw()) {
case BC1: // Handled in DecodeTypeImmed, should never come here.
UNREACHABLE();
@@ -1365,7 +1542,7 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
} else {
// Logical right-rotate of a word by a fixed number of bits. This
// is special case of SRL instruction, added in MIPS32 Release 2.
- // RS field is equal to 00001
+ // RS field is equal to 00001.
alu_out = (rt_u >> sa) | (rt_u << (32 - sa));
}
break;
@@ -1383,7 +1560,7 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
} else {
// Logical right-rotate of a word by a variable number of bits.
// This is special case od SRLV instruction, added in MIPS32
- // Release 2. SA field is equal to 00001
+ // Release 2. SA field is equal to 00001.
alu_out = (rt_u >> rs_u) | (rt_u << (32 - rs_u));
}
break;
@@ -1402,10 +1579,6 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case MULTU:
u64hilo = static_cast<uint64_t>(rs_u) * static_cast<uint64_t>(rt_u);
break;
- case DIV:
- case DIVU:
- exceptions[kDivideByZero] = rt == 0;
- break;
case ADD:
if (HaveSameSign(rs, rt)) {
if (rs > 0) {
@@ -1450,7 +1623,7 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case SLTU:
alu_out = rs_u < rt_u ? 1 : 0;
break;
- // Break and trap instructions
+ // Break and trap instructions.
case BREAK:
do_interrupt = true;
@@ -1478,6 +1651,10 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case MOVCI:
// No action taken on decode.
break;
+ case DIV:
+ case DIVU:
+ // div and divu never raise exceptions.
+ break;
default:
UNREACHABLE();
};
@@ -1497,7 +1674,7 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
case SPECIAL3:
switch (instr->FunctionFieldRaw()) {
case INS: { // Mips32r2 instruction.
- // Interpret Rd field as 5-bit msb of insert.
+ // Interpret rd field as 5-bit msb of insert.
uint16_t msb = rd_reg;
// Interpret sa field as 5-bit lsb of insert.
uint16_t lsb = sa;
@@ -1507,7 +1684,7 @@ void Simulator::ConfigureTypeRegister(Instruction* instr,
break;
}
case EXT: { // Mips32r2 instruction.
- // Interpret Rd field as 5-bit msb of extract.
+ // Interpret rd field as 5-bit msb of extract.
uint16_t msb = rd_reg;
// Interpret sa field as 5-bit lsb of extract.
uint16_t lsb = sa;
@@ -1543,7 +1720,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
int64_t i64hilo = 0;
uint64_t u64hilo = 0;
- // ALU output
+ // ALU output.
// It should not be used as is. Instructions using it should always
// initialize it first.
int32_t alu_out = 0x12345678;
@@ -1551,7 +1728,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
// For break and trap instructions.
bool do_interrupt = false;
- // For jr and jalr
+ // For jr and jalr.
// Get current pc.
int32_t current_pc = get_pc();
// Next pc
@@ -1568,11 +1745,11 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
// ---------- Raise exceptions triggered.
SignalExceptions();
- // ---------- Execution
+ // ---------- Execution.
switch (op) {
case COP1:
switch (instr->RsFieldRaw()) {
- case BC1: // branch on coprocessor condition
+ case BC1: // Branch on coprocessor condition.
UNREACHABLE();
break;
case CFC1:
@@ -1802,7 +1979,7 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
Instruction* branch_delay_instr = reinterpret_cast<Instruction*>(
current_pc+Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr);
- set_register(31, current_pc + 2* Instruction::kInstrSize);
+ set_register(31, current_pc + 2 * Instruction::kInstrSize);
set_pc(next_pc);
pc_modified_ = true;
break;
@@ -1817,13 +1994,19 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
set_register(HI, static_cast<int32_t>(u64hilo >> 32));
break;
case DIV:
- // Divide by zero was checked in the configuration step.
- set_register(LO, rs / rt);
- set_register(HI, rs % rt);
+ // Divide by zero was not checked in the configuration step - div and
+ // divu do not raise exceptions. On division by 0, the result will
+ // be UNPREDICTABLE.
+ if (rt != 0) {
+ set_register(LO, rs / rt);
+ set_register(HI, rs % rt);
+ }
break;
case DIVU:
- set_register(LO, rs_u / rt_u);
- set_register(HI, rs_u % rt_u);
+ if (rt_u != 0) {
+ set_register(LO, rs_u / rt_u);
+ set_register(HI, rs_u % rt_u);
+ }
break;
// Break and trap instructions.
case BREAK:
@@ -1842,9 +2025,9 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
if (rt) set_register(rd_reg, rs);
break;
case MOVCI: {
- uint32_t cc = instr->FCccValue();
+ uint32_t cc = instr->FBccValue();
uint32_t fcsr_cc = get_fcsr_condition_bit(cc);
- if (instr->Bit(16)) { // Read Tf bit
+ if (instr->Bit(16)) { // Read Tf bit.
if (test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
} else {
if (!test_fcsr_bit(fcsr_cc)) set_register(rd_reg, rs);
@@ -1893,17 +2076,17 @@ void Simulator::DecodeTypeRegister(Instruction* instr) {
}
-// Type 2: instructions using a 16 bytes immediate. (eg: addi, beq)
+// Type 2: instructions using a 16 bytes immediate. (eg: addi, beq).
void Simulator::DecodeTypeImmediate(Instruction* instr) {
// Instruction fields.
Opcode op = instr->OpcodeFieldRaw();
int32_t rs = get_register(instr->RsValue());
uint32_t rs_u = static_cast<uint32_t>(rs);
- int32_t rt_reg = instr->RtValue(); // destination register
+ int32_t rt_reg = instr->RtValue(); // Destination register.
int32_t rt = get_register(rt_reg);
int16_t imm16 = instr->Imm16Value();
- int32_t ft_reg = instr->FtValue(); // destination register
+ int32_t ft_reg = instr->FtValue(); // Destination register.
// Zero extended immediate.
uint32_t oe_imm16 = 0xffff & imm16;
@@ -1927,10 +2110,10 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
// Used for memory instructions.
int32_t addr = 0x0;
- // Value to be written in memory
+ // Value to be written in memory.
uint32_t mem_value = 0x0;
- // ---------- Configuration (and execution for REGIMM)
+ // ---------- Configuration (and execution for REGIMM).
switch (op) {
// ------------- COP1. Coprocessor instructions.
case COP1:
@@ -1941,7 +2124,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
cc_value = test_fcsr_bit(fcsr_cc);
do_branch = (instr->FBtrueValue()) ? cc_value : !cc_value;
execute_branch_delay_instruction = true;
- // Set next_pc
+ // Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
} else {
@@ -1952,7 +2135,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
UNREACHABLE();
};
break;
- // ------------- REGIMM class
+ // ------------- REGIMM class.
case REGIMM:
switch (instr->RtFieldRaw()) {
case BLTZ:
@@ -1977,7 +2160,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case BGEZAL:
// Branch instructions common part.
execute_branch_delay_instruction = true;
- // Set next_pc
+ // Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
if (instr->IsLinkingInstruction()) {
@@ -1989,8 +2172,8 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
default:
break;
};
- break; // case REGIMM
- // ------------- Branch instructions
+ break; // case REGIMM.
+ // ------------- Branch instructions.
// When comparing to zero, the encoding of rt field is always 0, so we don't
// need to replace rt with zero.
case BEQ:
@@ -2005,7 +2188,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case BGTZ:
do_branch = rs > 0;
break;
- // ------------- Arithmetic instructions
+ // ------------- Arithmetic instructions.
case ADDI:
if (HaveSameSign(rs, se_imm16)) {
if (rs > 0) {
@@ -2038,7 +2221,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case LUI:
alu_out = (oe_imm16 << 16);
break;
- // ------------- Memory instructions
+ // ------------- Memory instructions.
case LB:
addr = rs + se_imm16;
alu_out = ReadB(addr);
@@ -2048,7 +2231,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
alu_out = ReadH(addr, instr);
break;
case LWL: {
- // al_offset is an offset of the effective address within an aligned word
+ // al_offset is offset of the effective address within an aligned word.
uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
uint8_t byte_shift = kPointerAlignmentMask - al_offset;
uint32_t mask = (1 << byte_shift * 8) - 1;
@@ -2071,7 +2254,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
alu_out = ReadHU(addr, instr);
break;
case LWR: {
- // al_offset is an offset of the effective address within an aligned word
+ // al_offset is offset of the effective address within an aligned word.
uint8_t al_offset = (rs + se_imm16) & kPointerAlignmentMask;
uint8_t byte_shift = kPointerAlignmentMask - al_offset;
uint32_t mask = al_offset ? (~0 << (byte_shift + 1) * 8) : 0;
@@ -2126,16 +2309,16 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
// ---------- Raise exceptions triggered.
SignalExceptions();
- // ---------- Execution
+ // ---------- Execution.
switch (op) {
- // ------------- Branch instructions
+ // ------------- Branch instructions.
case BEQ:
case BNE:
case BLEZ:
case BGTZ:
// Branch instructions common part.
execute_branch_delay_instruction = true;
- // Set next_pc
+ // Set next_pc.
if (do_branch) {
next_pc = current_pc + (imm16 << 2) + Instruction::kInstrSize;
if (instr->IsLinkingInstruction()) {
@@ -2145,7 +2328,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
next_pc = current_pc + 2 * Instruction::kInstrSize;
}
break;
- // ------------- Arithmetic instructions
+ // ------------- Arithmetic instructions.
case ADDI:
case ADDIU:
case SLTI:
@@ -2156,7 +2339,7 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
case LUI:
set_register(rt_reg, alu_out);
break;
- // ------------- Memory instructions
+ // ------------- Memory instructions.
case LB:
case LH:
case LWL:
@@ -2216,26 +2399,26 @@ void Simulator::DecodeTypeImmediate(Instruction* instr) {
}
-// Type 3: instructions using a 26 bytes immediate. (eg: j, jal)
+// Type 3: instructions using a 26 bytes immediate. (eg: j, jal).
void Simulator::DecodeTypeJump(Instruction* instr) {
// Get current pc.
int32_t current_pc = get_pc();
// Get unchanged bits of pc.
int32_t pc_high_bits = current_pc & 0xf0000000;
- // Next pc
+ // Next pc.
int32_t next_pc = pc_high_bits | (instr->Imm26Value() << 2);
- // Execute branch delay slot
+ // Execute branch delay slot.
// We don't check for end_sim_pc. First it should not be met as the current pc
// is valid. Secondly a jump should always execute its branch delay slot.
Instruction* branch_delay_instr =
- reinterpret_cast<Instruction*>(current_pc+Instruction::kInstrSize);
+ reinterpret_cast<Instruction*>(current_pc + Instruction::kInstrSize);
BranchDelayInstructionDecode(branch_delay_instr);
// Update pc and ra if necessary.
// Do this after the branch delay execution.
if (instr->IsLinkingInstruction()) {
- set_register(31, current_pc + 2* Instruction::kInstrSize);
+ set_register(31, current_pc + 2 * Instruction::kInstrSize);
}
set_pc(next_pc);
pc_modified_ = true;
@@ -2251,11 +2434,11 @@ void Simulator::InstructionDecode(Instruction* instr) {
if (::v8::internal::FLAG_trace_sim) {
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
- // use a reasonably large buffer
+ // Use a reasonably large buffer.
v8::internal::EmbeddedVector<char, 256> buffer;
- dasm.InstructionDecode(buffer, reinterpret_cast<byte_*>(instr));
+ dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr));
PrintF(" 0x%08x %s\n", reinterpret_cast<intptr_t>(instr),
- buffer.start());
+ buffer.start());
}
switch (instr->InstructionType()) {
@@ -2310,10 +2493,10 @@ void Simulator::Execute() {
}
-int32_t Simulator::Call(byte_* entry, int argument_count, ...) {
+int32_t Simulator::Call(byte* entry, int argument_count, ...) {
va_list parameters;
va_start(parameters, argument_count);
- // Setup arguments
+ // Setup arguments.
// First four arguments passed in registers.
ASSERT(argument_count >= 4);
@@ -2338,7 +2521,7 @@ int32_t Simulator::Call(byte_* entry, int argument_count, ...) {
va_end(parameters);
set_register(sp, entry_stack);
- // Prepare to execute the code at entry
+ // Prepare to execute the code at entry.
set_register(pc, reinterpret_cast<int32_t>(entry));
// Put down marker for end of simulation. The simulator will stop simulation
// when the PC reaches this value. By saving the "end simulation" value into
@@ -2374,7 +2557,7 @@ int32_t Simulator::Call(byte_* entry, int argument_count, ...) {
set_register(gp, callee_saved_value);
set_register(fp, callee_saved_value);
- // Start the simulation
+ // Start the simulation.
Execute();
// Check that the callee-saved registers have been preserved.
diff --git a/src/mips/simulator-mips.h b/src/mips/simulator-mips.h
index 0cd9bbe7..21476dcc 100644
--- a/src/mips/simulator-mips.h
+++ b/src/mips/simulator-mips.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -50,14 +50,15 @@ namespace internal {
entry(p0, p1, p2, p3, p4)
typedef int (*mips_regexp_matcher)(String*, int, const byte*, const byte*,
- void*, int*, Address, int, Isolate*);
+ void*, int*, Address, int, Isolate*);
+
// Call the generated regexp code directly. The code at the entry address
// should act as a function matching the type arm_regexp_matcher.
// The fifth argument is a dummy that reserves the space used for
// the return address added by the ExitFrame in native calls.
#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7) \
- (FUNCTION_CAST<mips_regexp_matcher>(entry)( \
+ (FUNCTION_CAST<mips_regexp_matcher>(entry)( \
p0, p1, p2, p3, NULL, p4, p5, p6, p7))
#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
@@ -68,7 +69,8 @@ typedef int (*mips_regexp_matcher)(String*, int, const byte*, const byte*,
// just use the C stack limit.
class SimulatorStack : public v8::internal::AllStatic {
public:
- static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
return c_limit;
}
@@ -95,6 +97,7 @@ class SimulatorStack : public v8::internal::AllStatic {
// Running with a simulator.
#include "hashmap.h"
+#include "assembler.h"
namespace v8 {
namespace internal {
@@ -151,7 +154,7 @@ class Simulator {
sp,
s8,
ra,
- // LO, HI, and pc
+ // LO, HI, and pc.
LO,
HI,
pc, // pc must be the last register.
@@ -164,13 +167,13 @@ class Simulator {
// Generated code will always use doubles. So we will only use even registers.
enum FPURegister {
f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11,
- f12, f13, f14, f15, // f12 and f14 are arguments FPURegisters
+ f12, f13, f14, f15, // f12 and f14 are arguments FPURegisters.
f16, f17, f18, f19, f20, f21, f22, f23, f24, f25,
f26, f27, f28, f29, f30, f31,
kNumFPURegisters
};
- Simulator();
+ explicit Simulator(Isolate* isolate);
~Simulator();
// The currently executing Simulator instance. Potentially there can be one
@@ -182,7 +185,7 @@ class Simulator {
// instruction.
void set_register(int reg, int32_t value);
int32_t get_register(int reg) const;
- // Same for FPURegisters
+ // Same for FPURegisters.
void set_fpu_register(int fpureg, int32_t value);
void set_fpu_register_float(int fpureg, float value);
void set_fpu_register_double(int fpureg, double value);
@@ -205,7 +208,7 @@ class Simulator {
void Execute();
// Call on program start.
- static void Initialize();
+ static void Initialize(Isolate* isolate);
// V8 generally calls into generated JS code with 5 parameters and into
// generated RegExp code with 7 parameters. This is a convenience function,
@@ -304,7 +307,6 @@ class Simulator {
int size);
static CachePage* GetCachePage(v8::internal::HashMap* i_cache, void* page);
-
enum Exception {
none,
kIntegerOverflow,
@@ -321,9 +323,12 @@ class Simulator {
static void* RedirectExternalReference(void* external_function,
ExternalReference::Type type);
- // Used for real time calls that takes two double values as arguments and
- // returns a double.
- void SetFpResult(double result);
+ // For use in calls that take double value arguments.
+ void GetFpArgs(double* x, double* y);
+ void GetFpArgs(double* x);
+ void GetFpArgs(double* x, int32_t* y);
+ void SetFpResult(const double& result);
+
// Architecture state.
// Registers.
@@ -334,35 +339,36 @@ class Simulator {
uint32_t FCSR_;
// Simulator support.
+ // Allocate 1MB for stack.
+ static const size_t stack_size_ = 1 * 1024*1024;
char* stack_;
- size_t stack_size_;
bool pc_modified_;
int icount_;
int break_count_;
- // Icache simulation
+ // Icache simulation.
v8::internal::HashMap* i_cache_;
+ v8::internal::Isolate* isolate_;
+
// Registered breakpoints.
Instruction* break_pc_;
Instr break_instr_;
-
- v8::internal::Isolate* isolate_;
};
// When running with the simulator transition into simulated execution at this
// point.
#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
-reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
+ reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
FUNCTION_ADDR(entry), 5, p0, p1, p2, p3, p4))
#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7) \
- Simulator::current(Isolate::Current())->Call( \
- entry, 9, p0, p1, p2, p3, NULL, p4, p5, p6, p7)
+ Simulator::current(Isolate::Current())->Call( \
+ entry, 9, p0, p1, p2, p3, NULL, p4, p5, p6, p7)
-#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
- try_catch_address == NULL ? \
+#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
+ try_catch_address == NULL ? \
NULL : *(reinterpret_cast<TryCatch**>(try_catch_address))
@@ -373,8 +379,9 @@ reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
// trouble down the line.
class SimulatorStack : public v8::internal::AllStatic {
public:
- static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
- return Simulator::current(Isolate::Current())->StackLimit();
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
+ return Simulator::current(isolate)->StackLimit();
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc
index 1a495581..47428a83 100644
--- a/src/mips/stub-cache-mips.cc
+++ b/src/mips/stub-cache-mips.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -30,7 +30,7 @@
#if defined(V8_TARGET_ARCH_MIPS)
#include "ic-inl.h"
-#include "codegen-inl.h"
+#include "codegen.h"
#include "stub-cache.h"
namespace v8 {
@@ -39,6 +39,124 @@ namespace internal {
#define __ ACCESS_MASM(masm)
+static void ProbeTable(Isolate* isolate,
+ MacroAssembler* masm,
+ Code::Flags flags,
+ StubCache::Table table,
+ Register name,
+ Register offset,
+ Register scratch,
+ Register scratch2) {
+ ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
+ ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
+
+ uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address());
+ uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address());
+
+ // Check the relative positions of the address fields.
+ ASSERT(value_off_addr > key_off_addr);
+ ASSERT((value_off_addr - key_off_addr) % 4 == 0);
+ ASSERT((value_off_addr - key_off_addr) < (256 * 4));
+
+ Label miss;
+ Register offsets_base_addr = scratch;
+
+ // Check that the key in the entry matches the name.
+ __ li(offsets_base_addr, Operand(key_offset));
+ __ sll(scratch2, offset, 1);
+ __ addu(scratch2, offsets_base_addr, scratch2);
+ __ lw(scratch2, MemOperand(scratch2));
+ __ Branch(&miss, ne, name, Operand(scratch2));
+
+ // Get the code entry from the cache.
+ __ Addu(offsets_base_addr, offsets_base_addr,
+ Operand(value_off_addr - key_off_addr));
+ __ sll(scratch2, offset, 1);
+ __ addu(scratch2, offsets_base_addr, scratch2);
+ __ lw(scratch2, MemOperand(scratch2));
+
+ // Check that the flags match what we're looking for.
+ __ lw(scratch2, FieldMemOperand(scratch2, Code::kFlagsOffset));
+ __ And(scratch2, scratch2, Operand(~Code::kFlagsNotUsedInLookup));
+ __ Branch(&miss, ne, scratch2, Operand(flags));
+
+ // Re-load code entry from cache.
+ __ sll(offset, offset, 1);
+ __ addu(offset, offset, offsets_base_addr);
+ __ lw(offset, MemOperand(offset));
+
+ // Jump to the first instruction in the code stub.
+ __ Addu(offset, offset, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ Jump(offset);
+
+ // Miss: fall through.
+ __ bind(&miss);
+}
+
+
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// must always call a backup property check that is complete.
+// This function is safe to call if the receiver has fast properties.
+// Name must be a symbol and receiver must be a heap object.
+MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ String* name,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(name->IsSymbol());
+ Counters* counters = masm->isolate()->counters();
+ __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
+ __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+
+ Label done;
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ Register map = scratch1;
+ __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ lbu(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ And(at, scratch0, Operand(kInterceptorOrAccessCheckNeededMask));
+ __ Branch(miss_label, ne, at, Operand(zero_reg));
+
+
+ // Check that receiver is a JSObject.
+ __ lbu(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ Branch(miss_label, lt, scratch0, Operand(FIRST_JS_OBJECT_TYPE));
+
+ // Load properties array.
+ Register properties = scratch0;
+ __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ // Check that the properties array is a dictionary.
+ __ lw(map, FieldMemOperand(properties, HeapObject::kMapOffset));
+ Register tmp = properties;
+ __ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
+ __ Branch(miss_label, ne, map, Operand(tmp));
+
+ // Restore the temporarily used register.
+ __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+
+ MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup(
+ masm,
+ miss_label,
+ &done,
+ receiver,
+ properties,
+ name,
+ scratch1);
+ if (result->IsFailure()) return result;
+
+ __ bind(&done);
+ __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
+
+ return result;
+}
+
+
void StubCache::GenerateProbe(MacroAssembler* masm,
Code::Flags flags,
Register receiver,
@@ -46,20 +164,96 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register scratch,
Register extra,
Register extra2) {
- UNIMPLEMENTED_MIPS();
+ Isolate* isolate = masm->isolate();
+ Label miss;
+
+ // Make sure that code is valid. The shifting code relies on the
+ // entry size being 8.
+ ASSERT(sizeof(Entry) == 8);
+
+ // Make sure the flags does not name a specific type.
+ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
+
+ // Make sure that there are no register conflicts.
+ ASSERT(!scratch.is(receiver));
+ ASSERT(!scratch.is(name));
+ ASSERT(!extra.is(receiver));
+ ASSERT(!extra.is(name));
+ ASSERT(!extra.is(scratch));
+ ASSERT(!extra2.is(receiver));
+ ASSERT(!extra2.is(name));
+ ASSERT(!extra2.is(scratch));
+ ASSERT(!extra2.is(extra));
+
+ // Check scratch, extra and extra2 registers are valid.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(!extra.is(no_reg));
+ ASSERT(!extra2.is(no_reg));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss, t0);
+
+ // Get the map of the receiver and compute the hash.
+ __ lw(scratch, FieldMemOperand(name, String::kHashFieldOffset));
+ __ lw(t8, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ Addu(scratch, scratch, Operand(t8));
+ __ Xor(scratch, scratch, Operand(flags));
+ __ And(scratch,
+ scratch,
+ Operand((kPrimaryTableSize - 1) << kHeapObjectTagSize));
+
+ // Probe the primary table.
+ ProbeTable(isolate, masm, flags, kPrimary, name, scratch, extra, extra2);
+
+ // Primary miss: Compute hash for secondary probe.
+ __ Subu(scratch, scratch, Operand(name));
+ __ Addu(scratch, scratch, Operand(flags));
+ __ And(scratch,
+ scratch,
+ Operand((kSecondaryTableSize - 1) << kHeapObjectTagSize));
+
+ // Probe the secondary table.
+ ProbeTable(isolate, masm, flags, kSecondary, name, scratch, extra, extra2);
+
+ // Cache miss: Fall-through and let caller handle the miss by
+ // entering the runtime system.
+ __ bind(&miss);
}
void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
int index,
Register prototype) {
- UNIMPLEMENTED_MIPS();
+ // Load the global or builtins object from the current context.
+ __ lw(prototype, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ // Load the global context from the global or builtins object.
+ __ lw(prototype,
+ FieldMemOperand(prototype, GlobalObject::kGlobalContextOffset));
+ // Load the function from the global context.
+ __ lw(prototype, MemOperand(prototype, Context::SlotOffset(index)));
+ // Load the initial map. The global functions all have initial maps.
+ __ lw(prototype,
+ FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset));
+ // Load the prototype from the initial map.
+ __ lw(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
}
void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
MacroAssembler* masm, int index, Register prototype, Label* miss) {
- UNIMPLEMENTED_MIPS();
+ Isolate* isolate = masm->isolate();
+ // Check we're still in the same context.
+ __ lw(prototype, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ ASSERT(!prototype.is(at));
+ __ li(at, isolate->global());
+ __ Branch(miss, ne, prototype, Operand(at));
+ // Get the global function with the given index.
+ JSFunction* function =
+ JSFunction::cast(isolate->global_context()->get(index));
+ // Load its initial map. The global functions all have initial maps.
+ __ li(prototype, Handle<Map>(function->initial_map()));
+ // Load the prototype from the initial map.
+ __ lw(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
}
@@ -69,7 +263,18 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
Register dst, Register src,
JSObject* holder, int index) {
- UNIMPLEMENTED_MIPS();
+ // Adjust for the number of properties stored in the holder.
+ index -= holder->map()->inobject_properties();
+ if (index < 0) {
+ // Get the property straight out of the holder.
+ int offset = holder->map()->instance_size() + (index * kPointerSize);
+ __ lw(dst, FieldMemOperand(src, offset));
+ } else {
+ // Calculate the offset into the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ __ lw(dst, FieldMemOperand(src, JSObject::kPropertiesOffset));
+ __ lw(dst, FieldMemOperand(dst, offset));
+ }
}
@@ -77,7 +282,41 @@ void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
Register receiver,
Register scratch,
Label* miss_label) {
- UNIMPLEMENTED_MIPS();
+ // Check that the receiver isn't a smi.
+ __ And(scratch, receiver, Operand(kSmiTagMask));
+ __ Branch(miss_label, eq, scratch, Operand(zero_reg));
+
+ // Check that the object is a JS array.
+ __ GetObjectType(receiver, scratch, scratch);
+ __ Branch(miss_label, ne, scratch, Operand(JS_ARRAY_TYPE));
+
+ // Load length directly from the JS array.
+ __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Ret();
+}
+
+
+// Generate code to check if an object is a string. If the object is a
+// heap object, its map's instance type is left in the scratch1 register.
+// If this is not needed, scratch1 and scratch2 may be the same register.
+static void GenerateStringCheck(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* smi,
+ Label* non_string_object) {
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, smi, t0);
+
+ // Check that the object is a string.
+ __ lw(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ And(scratch2, scratch1, Operand(kIsNotStringMask));
+ // The cast is to resolve the overload for the argument of 0x0.
+ __ Branch(non_string_object,
+ ne,
+ scratch2,
+ Operand(static_cast<int32_t>(kStringTag)));
}
@@ -91,7 +330,28 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
Register scratch2,
Label* miss,
bool support_wrappers) {
- UNIMPLEMENTED_MIPS();
+ Label check_wrapper;
+
+ // Check if the object is a string leaving the instance type in the
+ // scratch1 register.
+ GenerateStringCheck(masm, receiver, scratch1, scratch2, miss,
+ support_wrappers ? &check_wrapper : miss);
+
+ // Load length directly from the string.
+ __ lw(v0, FieldMemOperand(receiver, String::kLengthOffset));
+ __ Ret();
+
+ if (support_wrappers) {
+ // Check if the object is a JSValue wrapper.
+ __ bind(&check_wrapper);
+ __ Branch(miss, ne, scratch1, Operand(JS_VALUE_TYPE));
+
+ // Unwrap the value and check if the wrapped value is a string.
+ __ lw(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
+ __ lw(v0, FieldMemOperand(scratch1, String::kLengthOffset));
+ __ Ret();
+ }
}
@@ -100,7 +360,9 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
Register scratch1,
Register scratch2,
Label* miss_label) {
- UNIMPLEMENTED_MIPS();
+ __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
+ __ mov(v0, scratch1);
+ __ Ret();
}
@@ -115,15 +377,254 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
Register name_reg,
Register scratch,
Label* miss_label) {
- UNIMPLEMENTED_MIPS();
+ // a0 : value.
+ Label exit;
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver_reg, miss_label, scratch);
+
+ // Check that the map of the receiver hasn't changed.
+ __ lw(scratch, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
+ __ Branch(miss_label, ne, scratch, Operand(Handle<Map>(object->map())));
+
+ // Perform global security token check if needed.
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label);
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ // Perform map transition for the receiver if necessary.
+ if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
+ // The properties must be extended before we can store the value.
+ // We jump to a runtime call that extends the properties array.
+ __ push(receiver_reg);
+ __ li(a2, Operand(Handle<Map>(transition)));
+ __ Push(a2, a0);
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
+ masm->isolate()),
+ 3, 1);
+ return;
+ }
+
+ if (transition != NULL) {
+ // Update the map of the object; no write barrier updating is
+ // needed because the map is never in new space.
+ __ li(t0, Operand(Handle<Map>(transition)));
+ __ sw(t0, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
+ }
+
+ // Adjust for the number of properties stored in the object. Even in the
+ // face of a transition we can use the old map here because the size of the
+ // object and the number of in-object properties is not going to change.
+ index -= object->map()->inobject_properties();
+
+ if (index < 0) {
+ // Set the property straight into the object.
+ int offset = object->map()->instance_size() + (index * kPointerSize);
+ __ sw(a0, FieldMemOperand(receiver_reg, offset));
+
+ // Skip updating write barrier if storing a smi.
+ __ JumpIfSmi(a0, &exit, scratch);
+
+ // Update the write barrier for the array address.
+ // Pass the now unused name_reg as a scratch register.
+ __ RecordWrite(receiver_reg, Operand(offset), name_reg, scratch);
+ } else {
+ // Write to the properties array.
+ int offset = index * kPointerSize + FixedArray::kHeaderSize;
+ // Get the properties array.
+ __ lw(scratch, FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
+ __ sw(a0, FieldMemOperand(scratch, offset));
+
+ // Skip updating write barrier if storing a smi.
+ __ JumpIfSmi(a0, &exit);
+
+ // Update the write barrier for the array address.
+ // Ok to clobber receiver_reg and name_reg, since we return.
+ __ RecordWrite(scratch, Operand(offset), name_reg, receiver_reg);
+ }
+
+ // Return the value (register v0).
+ __ bind(&exit);
+ __ mov(v0, a0);
+ __ Ret();
}
void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC);
+ Code* code = NULL;
+ if (kind == Code::LOAD_IC) {
+ code = masm->isolate()->builtins()->builtin(Builtins::kLoadIC_Miss);
+ } else {
+ code = masm->isolate()->builtins()->builtin(Builtins::kKeyedLoadIC_Miss);
+ }
+
+ Handle<Code> ic(code);
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+}
+
+
+static void GenerateCallFunction(MacroAssembler* masm,
+ Object* object,
+ const ParameterCount& arguments,
+ Label* miss) {
+ // ----------- S t a t e -------------
+ // -- a0: receiver
+ // -- a1: function to call
+ // -----------------------------------
+ // Check that the function really is a function.
+ __ JumpIfSmi(a1, miss);
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(miss, ne, a3, Operand(JS_FUNCTION_TYPE));
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ lw(a3, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, arguments.immediate() * kPointerSize));
+ }
+
+ // Invoke the function.
+ __ InvokeFunction(a1, arguments, JUMP_FUNCTION);
+}
+
+
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ JSObject* holder_obj) {
+ __ push(name);
+ InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
+ ASSERT(!masm->isolate()->heap()->InNewSpace(interceptor));
+ Register scratch = name;
+ __ li(scratch, Operand(Handle<Object>(interceptor)));
+ __ Push(scratch, receiver, holder);
+ __ lw(scratch, FieldMemOperand(scratch, InterceptorInfo::kDataOffset));
+ __ push(scratch);
+}
+
+
+static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ JSObject* holder_obj) {
+ PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
+ masm->isolate());
+ __ li(a0, Operand(5));
+ __ li(a1, Operand(ref));
+
+ CEntryStub stub(1);
+ __ CallStub(&stub);
}
+static const int kFastApiCallArguments = 3;
+
+
+// Reserves space for the extra arguments to FastHandleApiCall in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiDirectCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm,
+ Register scratch) {
+ ASSERT(Smi::FromInt(0) == 0);
+ for (int i = 0; i < kFastApiCallArguments; i++) {
+ __ push(zero_reg);
+ }
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm) {
+ __ Drop(kFastApiCallArguments);
+}
+
+
+static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm,
+ const CallOptimization& optimization,
+ int argc) {
+ // ----------- S t a t e -------------
+ // -- sp[0] : holder (set by CheckPrototypes)
+ // -- sp[4] : callee js function
+ // -- sp[8] : call data
+ // -- sp[12] : last js argument
+ // -- ...
+ // -- sp[(argc + 3) * 4] : first js argument
+ // -- sp[(argc + 4) * 4] : receiver
+ // -----------------------------------
+ // Get the function and setup the context.
+ JSFunction* function = optimization.constant_function();
+ __ li(t1, Operand(Handle<JSFunction>(function)));
+ __ lw(cp, FieldMemOperand(t1, JSFunction::kContextOffset));
+
+ // Pass the additional arguments FastHandleApiCall expects.
+ Object* call_data = optimization.api_call_info()->data();
+ Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info());
+ if (masm->isolate()->heap()->InNewSpace(call_data)) {
+ __ li(a0, api_call_info_handle);
+ __ lw(t2, FieldMemOperand(a0, CallHandlerInfo::kDataOffset));
+ } else {
+ __ li(t2, Operand(Handle<Object>(call_data)));
+ }
+
+ // Store js function and call data.
+ __ sw(t1, MemOperand(sp, 1 * kPointerSize));
+ __ sw(t2, MemOperand(sp, 2 * kPointerSize));
+
+ // a2 points to call data as expected by Arguments
+ // (refer to layout above).
+ __ Addu(a2, sp, Operand(2 * kPointerSize));
+
+ Object* callback = optimization.api_call_info()->callback();
+ Address api_function_address = v8::ToCData<Address>(callback);
+ ApiFunction fun(api_function_address);
+
+ const int kApiStackSpace = 4;
+
+ __ EnterExitFrame(false, kApiStackSpace);
+
+ // NOTE: the O32 abi requires a0 to hold a special pointer when returning a
+ // struct from the function (which is currently the case). This means we pass
+ // the first argument in a1 instead of a0. TryCallApiFunctionAndReturn
+ // will handle setting up a0.
+
+ // a1 = v8::Arguments&
+ // Arguments is built at sp + 1 (sp is a reserved spot for ra).
+ __ Addu(a1, sp, kPointerSize);
+
+ // v8::Arguments::implicit_args = data
+ __ sw(a2, MemOperand(a1, 0 * kPointerSize));
+ // v8::Arguments::values = last argument
+ __ Addu(t0, a2, Operand(argc * kPointerSize));
+ __ sw(t0, MemOperand(a1, 1 * kPointerSize));
+ // v8::Arguments::length_ = argc
+ __ li(t0, Operand(argc));
+ __ sw(t0, MemOperand(a1, 2 * kPointerSize));
+ // v8::Arguments::is_construct_call = 0
+ __ sw(zero_reg, MemOperand(a1, 3 * kPointerSize));
+
+ // Emitting a stub call may try to allocate (if the code is not
+ // already generated). Do not allow the assembler to perform a
+ // garbage collection but instead return the allocation failure
+ // object.
+ const int kStackUnwindSpace = argc + kFastApiCallArguments + 1;
+ ExternalReference ref =
+ ExternalReference(&fun,
+ ExternalReference::DIRECT_API_CALL,
+ masm->isolate());
+ return masm->TryCallApiFunctionAndReturn(ref, kStackUnwindSpace);
+}
+
class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
@@ -133,32 +634,150 @@ class CallInterceptorCompiler BASE_EMBEDDED {
arguments_(arguments),
name_(name) {}
- void Compile(MacroAssembler* masm,
- JSObject* object,
- JSObject* holder,
- String* name,
- LookupResult* lookup,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- Label* miss) {
- UNIMPLEMENTED_MIPS();
- }
-
- private:
- void CompileCacheable(MacroAssembler* masm,
+ MaybeObject* Compile(MacroAssembler* masm,
JSObject* object,
+ JSObject* holder,
+ String* name,
+ LookupResult* lookup,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
- JSObject* interceptor_holder,
- LookupResult* lookup,
- String* name,
- const CallOptimization& optimization,
- Label* miss_label) {
- UNIMPLEMENTED_MIPS();
+ Label* miss) {
+ ASSERT(holder->HasNamedInterceptor());
+ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ CallOptimization optimization(lookup);
+
+ if (optimization.is_constant_call()) {
+ return CompileCacheable(masm,
+ object,
+ receiver,
+ scratch1,
+ scratch2,
+ scratch3,
+ holder,
+ lookup,
+ name,
+ optimization,
+ miss);
+ } else {
+ CompileRegular(masm,
+ object,
+ receiver,
+ scratch1,
+ scratch2,
+ scratch3,
+ name,
+ holder,
+ miss);
+ return masm->isolate()->heap()->undefined_value();
+ }
+ }
+
+ private:
+ MaybeObject* CompileCacheable(MacroAssembler* masm,
+ JSObject* object,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ JSObject* interceptor_holder,
+ LookupResult* lookup,
+ String* name,
+ const CallOptimization& optimization,
+ Label* miss_label) {
+ ASSERT(optimization.is_constant_call());
+ ASSERT(!lookup->holder()->IsGlobalObject());
+
+ Counters* counters = masm->isolate()->counters();
+
+ int depth1 = kInvalidProtoDepth;
+ int depth2 = kInvalidProtoDepth;
+ bool can_do_fast_api_call = false;
+ if (optimization.is_simple_api_call() &&
+ !lookup->holder()->IsGlobalObject()) {
+ depth1 =
+ optimization.GetPrototypeDepthOfExpectedType(object,
+ interceptor_holder);
+ if (depth1 == kInvalidProtoDepth) {
+ depth2 =
+ optimization.GetPrototypeDepthOfExpectedType(interceptor_holder,
+ lookup->holder());
+ }
+ can_do_fast_api_call = (depth1 != kInvalidProtoDepth) ||
+ (depth2 != kInvalidProtoDepth);
+ }
+
+ __ IncrementCounter(counters->call_const_interceptor(), 1,
+ scratch1, scratch2);
+
+ if (can_do_fast_api_call) {
+ __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1,
+ scratch1, scratch2);
+ ReserveSpaceForFastApiCall(masm, scratch1);
+ }
+
+ // Check that the maps from receiver to interceptor's holder
+ // haven't changed and thus we can invoke interceptor.
+ Label miss_cleanup;
+ Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver,
+ interceptor_holder, scratch1,
+ scratch2, scratch3, name, depth1, miss);
+
+ // Invoke an interceptor and if it provides a value,
+ // branch to |regular_invoke|.
+ Label regular_invoke;
+ LoadWithInterceptor(masm, receiver, holder, interceptor_holder, scratch2,
+ &regular_invoke);
+
+ // Interceptor returned nothing for this property. Try to use cached
+ // constant function.
+
+ // Check that the maps from interceptor's holder to constant function's
+ // holder haven't changed and thus we can use cached constant function.
+ if (interceptor_holder != lookup->holder()) {
+ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
+ lookup->holder(), scratch1,
+ scratch2, scratch3, name, depth2, miss);
+ } else {
+ // CheckPrototypes has a side effect of fetching a 'holder'
+ // for API (object which is instanceof for the signature). It's
+ // safe to omit it here, as if present, it should be fetched
+ // by the previous CheckPrototypes.
+ ASSERT(depth2 == kInvalidProtoDepth);
+ }
+
+ // Invoke function.
+ if (can_do_fast_api_call) {
+ MaybeObject* result = GenerateFastApiDirectCall(masm,
+ optimization,
+ arguments_.immediate());
+ if (result->IsFailure()) return result;
+ } else {
+ __ InvokeFunction(optimization.constant_function(), arguments_,
+ JUMP_FUNCTION);
+ }
+
+ // Deferred code for fast API call case---clean preallocated space.
+ if (can_do_fast_api_call) {
+ __ bind(&miss_cleanup);
+ FreeSpaceForFastApiCall(masm);
+ __ Branch(miss_label);
+ }
+
+ // Invoke a regular function.
+ __ bind(&regular_invoke);
+ if (can_do_fast_api_call) {
+ FreeSpaceForFastApiCall(masm);
+ }
+
+ return masm->isolate()->heap()->undefined_value();
}
void CompileRegular(MacroAssembler* masm,
@@ -170,7 +789,31 @@ class CallInterceptorCompiler BASE_EMBEDDED {
String* name,
JSObject* interceptor_holder,
Label* miss_label) {
- UNIMPLEMENTED_MIPS();
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3, name,
+ miss_label);
+
+ // Call a runtime function to load the interceptor property.
+ __ EnterInternalFrame();
+ // Save the name_ register across the call.
+ __ push(name_);
+
+ PushInterceptorArguments(masm,
+ receiver,
+ holder,
+ name_,
+ interceptor_holder);
+
+ __ CallExternalReference(
+ ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
+ masm->isolate()),
+ 5);
+
+ // Restore the name_ register.
+ __ pop(name_);
+ __ LeaveInternalFrame();
}
void LoadWithInterceptor(MacroAssembler* masm,
@@ -179,7 +822,23 @@ class CallInterceptorCompiler BASE_EMBEDDED {
JSObject* holder_obj,
Register scratch,
Label* interceptor_succeeded) {
- UNIMPLEMENTED_MIPS();
+ __ EnterInternalFrame();
+
+ __ Push(holder, name_);
+
+ CompileCallLoadPropertyWithInterceptor(masm,
+ receiver,
+ holder,
+ name_,
+ holder_obj);
+
+ __ pop(name_); // Restore the name.
+ __ pop(receiver); // Restore the holder.
+ __ LeaveInternalFrame();
+
+ // If interceptor returns no-result sentinel, call the constant function.
+ __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex);
+ __ Branch(interceptor_succeeded, ne, v0, Operand(scratch));
}
StubCompiler* stub_compiler_;
@@ -188,6 +847,175 @@ class CallInterceptorCompiler BASE_EMBEDDED {
};
+
+// Generate code to check that a global property cell is empty. Create
+// the property cell at compilation time if no cell exists for the
+// property.
+MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell(
+ MacroAssembler* masm,
+ GlobalObject* global,
+ String* name,
+ Register scratch,
+ Label* miss) {
+ Object* probe;
+ { MaybeObject* maybe_probe = global->EnsurePropertyCell(name);
+ if (!maybe_probe->ToObject(&probe)) return maybe_probe;
+ }
+ JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe);
+ ASSERT(cell->value()->IsTheHole());
+ __ li(scratch, Operand(Handle<Object>(cell)));
+ __ lw(scratch,
+ FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset));
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(miss, ne, scratch, Operand(at));
+ return cell;
+}
+
+
+// Calls GenerateCheckPropertyCell for each global object in the prototype chain
+// from object to (but not including) holder.
+MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells(
+ MacroAssembler* masm,
+ JSObject* object,
+ JSObject* holder,
+ String* name,
+ Register scratch,
+ Label* miss) {
+ JSObject* current = object;
+ while (current != holder) {
+ if (current->IsGlobalObject()) {
+ // Returns a cell or a failure.
+ MaybeObject* result = GenerateCheckPropertyCell(
+ masm,
+ GlobalObject::cast(current),
+ name,
+ scratch,
+ miss);
+ if (result->IsFailure()) return result;
+ }
+ ASSERT(current->IsJSObject());
+ current = JSObject::cast(current->GetPrototype());
+ }
+ return NULL;
+}
+
+
+// Convert and store int passed in register ival to IEEE 754 single precision
+// floating point value at memory location (dst + 4 * wordoffset)
+// If FPU is available use it for conversion.
+static void StoreIntAsFloat(MacroAssembler* masm,
+ Register dst,
+ Register wordoffset,
+ Register ival,
+ Register fval,
+ Register scratch1,
+ Register scratch2) {
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ mtc1(ival, f0);
+ __ cvt_s_w(f0, f0);
+ __ sll(scratch1, wordoffset, 2);
+ __ addu(scratch1, dst, scratch1);
+ __ swc1(f0, MemOperand(scratch1, 0));
+ } else {
+ // FPU is not available, do manual conversions.
+
+ Label not_special, done;
+ // Move sign bit from source to destination. This works because the sign
+ // bit in the exponent word of the double has the same position and polarity
+ // as the 2's complement sign bit in a Smi.
+ ASSERT(kBinary32SignMask == 0x80000000u);
+
+ __ And(fval, ival, Operand(kBinary32SignMask));
+ // Negate value if it is negative.
+ __ subu(scratch1, zero_reg, ival);
+ __ movn(ival, scratch1, fval);
+
+ // We have -1, 0 or 1, which we treat specially. Register ival contains
+ // absolute value: it is either equal to 1 (special case of -1 and 1),
+ // greater than 1 (not a special case) or less than 1 (special case of 0).
+ __ Branch(&not_special, gt, ival, Operand(1));
+
+ // For 1 or -1 we need to or in the 0 exponent (biased).
+ static const uint32_t exponent_word_for_1 =
+ kBinary32ExponentBias << kBinary32ExponentShift;
+
+ __ Xor(scratch1, ival, Operand(1));
+ __ li(scratch2, exponent_word_for_1);
+ __ or_(scratch2, fval, scratch2);
+ __ movz(fval, scratch2, scratch1); // Only if ival is equal to 1.
+ __ Branch(&done);
+
+ __ bind(&not_special);
+ // Count leading zeros.
+ // Gets the wrong answer for 0, but we already checked for that case above.
+ Register zeros = scratch2;
+ __ clz(zeros, ival);
+
+ // Compute exponent and or it into the exponent register.
+ __ li(scratch1, (kBitsPerInt - 1) + kBinary32ExponentBias);
+ __ subu(scratch1, scratch1, zeros);
+
+ __ sll(scratch1, scratch1, kBinary32ExponentShift);
+ __ or_(fval, fval, scratch1);
+
+ // Shift up the source chopping the top bit off.
+ __ Addu(zeros, zeros, Operand(1));
+ // This wouldn't work for 1 and -1 as the shift would be 32 which means 0.
+ __ sllv(ival, ival, zeros);
+ // And the top (top 20 bits).
+ __ srl(scratch1, ival, kBitsPerInt - kBinary32MantissaBits);
+ __ or_(fval, fval, scratch1);
+
+ __ bind(&done);
+
+ __ sll(scratch1, wordoffset, 2);
+ __ addu(scratch1, dst, scratch1);
+ __ sw(fval, MemOperand(scratch1, 0));
+ }
+}
+
+
+// Convert unsigned integer with specified number of leading zeroes in binary
+// representation to IEEE 754 double.
+// Integer to convert is passed in register hiword.
+// Resulting double is returned in registers hiword:loword.
+// This functions does not work correctly for 0.
+static void GenerateUInt2Double(MacroAssembler* masm,
+ Register hiword,
+ Register loword,
+ Register scratch,
+ int leading_zeroes) {
+ const int meaningful_bits = kBitsPerInt - leading_zeroes - 1;
+ const int biased_exponent = HeapNumber::kExponentBias + meaningful_bits;
+
+ const int mantissa_shift_for_hi_word =
+ meaningful_bits - HeapNumber::kMantissaBitsInTopWord;
+
+ const int mantissa_shift_for_lo_word =
+ kBitsPerInt - mantissa_shift_for_hi_word;
+
+ __ li(scratch, biased_exponent << HeapNumber::kExponentShift);
+ if (mantissa_shift_for_hi_word > 0) {
+ __ sll(loword, hiword, mantissa_shift_for_lo_word);
+ __ srl(hiword, hiword, mantissa_shift_for_hi_word);
+ __ or_(hiword, scratch, hiword);
+ } else {
+ __ mov(loword, zero_reg);
+ __ sll(hiword, hiword, mantissa_shift_for_hi_word);
+ __ or_(hiword, scratch, hiword);
+ }
+
+ // If least significant bit of biased exponent was not 1 it was corrupted
+ // by most significant bit of mantissa so we should fix that.
+ if (!(biased_exponent & 1)) {
+ __ li(scratch, 1 << HeapNumber::kExponentShift);
+ __ nor(scratch, scratch, scratch);
+ __ and_(hiword, hiword, scratch);
+ }
+}
+
+
#undef __
#define __ ACCESS_MASM(masm())
@@ -201,8 +1029,132 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
String* name,
int save_at_depth,
Label* miss) {
- UNIMPLEMENTED_MIPS();
- return no_reg;
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg.
+ Register reg = object_reg;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ sw(reg, MemOperand(sp));
+ }
+
+ // Check the maps in the prototype chain.
+ // Traverse the prototype chain from the object and do map checks.
+ JSObject* current = object;
+ while (current != holder) {
+ depth++;
+
+ // Only global objects and objects that do not require access
+ // checks are allowed in stubs.
+ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
+
+ ASSERT(current->GetPrototype()->IsJSObject());
+ JSObject* prototype = JSObject::cast(current->GetPrototype());
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsSymbol()) {
+ MaybeObject* maybe_lookup_result = heap()->LookupSymbol(name);
+ Object* lookup_result = NULL; // Initialization to please compiler.
+ if (!maybe_lookup_result->ToObject(&lookup_result)) {
+ set_failure(Failure::cast(maybe_lookup_result));
+ return reg;
+ }
+ name = String::cast(lookup_result);
+ }
+ ASSERT(current->property_dictionary()->FindEntry(name) ==
+ StringDictionary::kNotFound);
+
+ MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch1,
+ scratch2);
+ if (negative_lookup->IsFailure()) {
+ set_failure(Failure::cast(negative_lookup));
+ return reg;
+ }
+
+ __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // From now the object is in holder_reg.
+ __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
+ } else if (heap()->InNewSpace(prototype)) {
+ // Get the map of the current object.
+ __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+
+ // Branch on the result of the map check.
+ __ Branch(miss, ne, scratch1, Operand(Handle<Map>(current->map())));
+
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ // Restore scratch register to be the map of the object. In the
+ // new space case below, we load the prototype from the map in
+ // the scratch register.
+ __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ }
+
+ reg = holder_reg; // From now the object is in holder_reg.
+ // The prototype is in new space; we cannot store a reference
+ // to it in the code. Load it from the map.
+ __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
+ } else {
+ // Check the map of the current object.
+ __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ // Branch on the result of the map check.
+ __ Branch(miss, ne, scratch1, Operand(Handle<Map>(current->map())));
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ }
+ // The prototype is in old space; load it directly.
+ reg = holder_reg; // From now the object is in holder_reg.
+ __ li(reg, Operand(Handle<JSObject>(prototype)));
+ }
+
+ if (save_at_depth == depth) {
+ __ sw(reg, MemOperand(sp));
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+
+ // Check the holder map.
+ __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ Branch(miss, ne, scratch1, Operand(Handle<Map>(current->map())));
+
+ // Log the check depth.
+ LOG(masm()->isolate(), IntEvent("check-maps-depth", depth + 1));
+ // Perform security check for access to the global object.
+ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
+ if (holder->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ };
+
+ // If we've skipped any global objects, it's not enough to verify
+ // that their maps haven't changed. We also need to check that the
+ // property cell for the property is still empty.
+
+ MaybeObject* result = GenerateCheckPropertyCells(masm(),
+ object,
+ holder,
+ name,
+ scratch1,
+ miss);
+ if (result->IsFailure()) set_failure(Failure::cast(result));
+
+ // Return the register containing the holder.
+ return reg;
}
@@ -215,7 +1167,16 @@ void StubCompiler::GenerateLoadField(JSObject* object,
int index,
String* name,
Label* miss) {
- UNIMPLEMENTED_MIPS();
+ // Check that the receiver isn't a smi.
+ __ And(scratch1, receiver, Operand(kSmiTagMask));
+ __ Branch(miss, eq, scratch1, Operand(zero_reg));
+
+ // Check that the maps haven't changed.
+ Register reg =
+ CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3,
+ name, miss);
+ GenerateFastPropertyLoad(masm(), v0, reg, holder, index);
+ __ Ret();
}
@@ -228,7 +1189,17 @@ void StubCompiler::GenerateLoadConstant(JSObject* object,
Object* value,
String* name,
Label* miss) {
- UNIMPLEMENTED_MIPS();
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss, scratch1);
+
+ // Check that the maps haven't changed.
+ Register reg =
+ CheckPrototypes(object, receiver, holder,
+ scratch1, scratch2, scratch3, name, miss);
+
+ // Return the constant value.
+ __ li(v0, Operand(Handle<Object>(value)));
+ __ Ret();
}
@@ -242,8 +1213,56 @@ MaybeObject* StubCompiler::GenerateLoadCallback(JSObject* object,
AccessorInfo* callback,
String* name,
Label* miss) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss, scratch1);
+
+ // Check that the maps haven't changed.
+ Register reg =
+ CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3,
+ name, miss);
+
+ // Build AccessorInfo::args_ list on the stack and push property name below
+ // the exit frame to make GC aware of them and store pointers to them.
+ __ push(receiver);
+ __ mov(scratch2, sp); // scratch2 = AccessorInfo::args_
+ Handle<AccessorInfo> callback_handle(callback);
+ if (heap()->InNewSpace(callback_handle->data())) {
+ __ li(scratch3, callback_handle);
+ __ lw(scratch3, FieldMemOperand(scratch3, AccessorInfo::kDataOffset));
+ } else {
+ __ li(scratch3, Handle<Object>(callback_handle->data()));
+ }
+ __ Push(reg, scratch3, name_reg);
+ __ mov(a2, scratch2); // Saved in case scratch2 == a1.
+ __ mov(a1, sp); // a1 (first argument - see note below) = Handle<String>
+
+ Address getter_address = v8::ToCData<Address>(callback->getter());
+ ApiFunction fun(getter_address);
+
+ // NOTE: the O32 abi requires a0 to hold a special pointer when returning a
+ // struct from the function (which is currently the case). This means we pass
+ // the arguments in a1-a2 instead of a0-a1. TryCallApiFunctionAndReturn
+ // will handle setting up a0.
+
+ const int kApiStackSpace = 1;
+
+ __ EnterExitFrame(false, kApiStackSpace);
+ // Create AccessorInfo instance on the stack above the exit frame with
+ // scratch2 (internal::Object **args_) as the data.
+ __ sw(a2, MemOperand(sp, kPointerSize));
+ // a2 (second argument - see note above) = AccessorInfo&
+ __ Addu(a2, sp, kPointerSize);
+
+ // Emitting a stub call may try to allocate (if the code is not
+ // already generated). Do not allow the assembler to perform a
+ // garbage collection but instead return the allocation failure
+ // object.
+ ExternalReference ref =
+ ExternalReference(&fun,
+ ExternalReference::DIRECT_GETTER_CALL,
+ masm()->isolate());
+ // 4 args - will be freed later by LeaveExitFrame.
+ return masm()->TryCallApiFunctionAndReturn(ref, 4);
}
@@ -257,12 +1276,143 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
Register scratch3,
String* name,
Label* miss) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(interceptor_holder->HasNamedInterceptor());
+ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, miss);
+
+ // So far the most popular follow ups for interceptor loads are FIELD
+ // and CALLBACKS, so inline only them, other cases may be added
+ // later.
+ bool compile_followup_inline = false;
+ if (lookup->IsProperty() && lookup->IsCacheable()) {
+ if (lookup->type() == FIELD) {
+ compile_followup_inline = true;
+ } else if (lookup->type() == CALLBACKS &&
+ lookup->GetCallbackObject()->IsAccessorInfo() &&
+ AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) {
+ compile_followup_inline = true;
+ }
+ }
+
+ if (compile_followup_inline) {
+ // Compile the interceptor call, followed by inline code to load the
+ // property from further up the prototype chain if the call fails.
+ // Check that the maps haven't changed.
+ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss);
+ ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
+
+ // Save necessary data before invoking an interceptor.
+ // Requires a frame to make GC aware of pushed pointers.
+ __ EnterInternalFrame();
+
+ if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
+ // CALLBACKS case needs a receiver to be passed into C++ callback.
+ __ Push(receiver, holder_reg, name_reg);
+ } else {
+ __ Push(holder_reg, name_reg);
+ }
+
+ // Invoke an interceptor. Note: map checks from receiver to
+ // interceptor's holder has been compiled before (see a caller
+ // of this method).
+ CompileCallLoadPropertyWithInterceptor(masm(),
+ receiver,
+ holder_reg,
+ name_reg,
+ interceptor_holder);
+
+ // Check if interceptor provided a value for property. If it's
+ // the case, return immediately.
+ Label interceptor_failed;
+ __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex);
+ __ Branch(&interceptor_failed, eq, v0, Operand(scratch1));
+ __ LeaveInternalFrame();
+ __ Ret();
+
+ __ bind(&interceptor_failed);
+ __ pop(name_reg);
+ __ pop(holder_reg);
+ if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
+ __ pop(receiver);
+ }
+
+ __ LeaveInternalFrame();
+
+ // Check that the maps from interceptor's holder to lookup's holder
+ // haven't changed. And load lookup's holder into |holder| register.
+ if (interceptor_holder != lookup->holder()) {
+ holder_reg = CheckPrototypes(interceptor_holder,
+ holder_reg,
+ lookup->holder(),
+ scratch1,
+ scratch2,
+ scratch3,
+ name,
+ miss);
+ }
+
+ if (lookup->type() == FIELD) {
+ // We found FIELD property in prototype chain of interceptor's holder.
+ // Retrieve a field from field's holder.
+ GenerateFastPropertyLoad(masm(), v0, holder_reg,
+ lookup->holder(), lookup->GetFieldIndex());
+ __ Ret();
+ } else {
+ // We found CALLBACKS property in prototype chain of interceptor's
+ // holder.
+ ASSERT(lookup->type() == CALLBACKS);
+ ASSERT(lookup->GetCallbackObject()->IsAccessorInfo());
+ AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
+ ASSERT(callback != NULL);
+ ASSERT(callback->getter() != NULL);
+
+ // Tail call to runtime.
+ // Important invariant in CALLBACKS case: the code above must be
+ // structured to never clobber |receiver| register.
+ __ li(scratch2, Handle<AccessorInfo>(callback));
+ // holder_reg is either receiver or scratch1.
+ if (!receiver.is(holder_reg)) {
+ ASSERT(scratch1.is(holder_reg));
+ __ Push(receiver, holder_reg);
+ __ lw(scratch3,
+ FieldMemOperand(scratch2, AccessorInfo::kDataOffset));
+ __ Push(scratch3, scratch2, name_reg);
+ } else {
+ __ push(receiver);
+ __ lw(scratch3,
+ FieldMemOperand(scratch2, AccessorInfo::kDataOffset));
+ __ Push(holder_reg, scratch3, scratch2, name_reg);
+ }
+
+ ExternalReference ref =
+ ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
+ masm()->isolate());
+ __ TailCallExternalReference(ref, 5, 1);
+ }
+ } else { // !compile_followup_inline
+ // Call the runtime system to load the interceptor.
+ // Check that the maps haven't changed.
+ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
+ scratch1, scratch2, scratch3,
+ name, miss);
+ PushInterceptorArguments(masm(), receiver, holder_reg,
+ name_reg, interceptor_holder);
+
+ ExternalReference ref = ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), masm()->isolate());
+ __ TailCallExternalReference(ref, 5, 1);
+ }
}
void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
- UNIMPLEMENTED_MIPS();
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ Branch(miss, ne, a2, Operand(Handle<String>(name)));
+ }
}
@@ -270,20 +1420,63 @@ void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
JSObject* holder,
String* name,
Label* miss) {
- UNIMPLEMENTED_MIPS();
+ ASSERT(holder->IsGlobalObject());
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ // Get the receiver from the stack.
+ __ lw(a0, MemOperand(sp, argc * kPointerSize));
+
+ // If the object is the holder then we know that it's a global
+ // object which can only happen for contextual calls. In this case,
+ // the receiver cannot be a smi.
+ if (object != holder) {
+ __ JumpIfSmi(a0, miss);
+ }
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(object, a0, holder, a3, a1, t0, name, miss);
}
void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
JSFunction* function,
Label* miss) {
- UNIMPLEMENTED_MIPS();
+ // Get the value from the cell.
+ __ li(a3, Operand(Handle<JSGlobalPropertyCell>(cell)));
+ __ lw(a1, FieldMemOperand(a3, JSGlobalPropertyCell::kValueOffset));
+
+ // Check that the cell contains the same function.
+ if (heap()->InNewSpace(function)) {
+ // We can't embed a pointer to a function in new space so we have
+ // to verify that the shared function info is unchanged. This has
+ // the nice side effect that multiple closures based on the same
+ // function can all use this call IC. Before we load through the
+ // function, we have to verify that it still is a function.
+ __ JumpIfSmi(a1, miss);
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(miss, ne, a3, Operand(JS_FUNCTION_TYPE));
+
+ // Check the shared function info. Make sure it hasn't changed.
+ __ li(a3, Handle<SharedFunctionInfo>(function->shared()));
+ __ lw(t0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ Branch(miss, ne, t0, Operand(a3));
+ } else {
+ __ Branch(miss, ne, a1, Operand(Handle<JSFunction>(function)));
+ }
}
MaybeObject* CallStubCompiler::GenerateMissBranch() {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ MaybeObject* maybe_obj =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_ic_state_);
+ Object* obj;
+ if (!maybe_obj->ToObject(&obj)) return maybe_obj;
+ __ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
+ return obj;
}
@@ -291,8 +1484,34 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object,
JSObject* holder,
int index,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ const int argc = arguments().immediate();
+
+ // Get the receiver of the function from the stack into a0.
+ __ lw(a0, MemOperand(sp, argc * kPointerSize));
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(a0, &miss, t0);
+
+ // Do the right check and compute the holder register.
+ Register reg = CheckPrototypes(object, a0, holder, a1, a3, t0, name, &miss);
+ GenerateFastPropertyLoad(masm(), a1, reg, holder, index);
+
+ GenerateCallFunction(masm(), object, arguments(), &miss);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(FIELD, name);
}
@@ -301,8 +1520,160 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value();
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ Register receiver = a1;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ lw(receiver, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), receiver,
+ holder, a3, v0, t0, name, &miss);
+
+ if (argc == 0) {
+ // Nothing to do, just return the length.
+ __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Drop(argc + 1);
+ __ Ret();
+ } else {
+ Label call_builtin;
+
+ Register elements = a3;
+ Register end_elements = t1;
+
+ // Get the elements array of the object.
+ __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode and writable.
+ __ CheckMap(elements,
+ v0,
+ Heap::kFixedArrayMapRootIndex,
+ &call_builtin,
+ DONT_DO_SMI_CHECK);
+
+ if (argc == 1) { // Otherwise fall through to call the builtin.
+ Label exit, with_write_barrier, attempt_to_grow_elements;
+
+ // Get the array's length into v0 and calculate new length.
+ __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Addu(v0, v0, Operand(Smi::FromInt(argc)));
+
+ // Get the element's length.
+ __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+
+ // Check if we could survive without allocation.
+ __ Branch(&attempt_to_grow_elements, gt, v0, Operand(t0));
+
+ // Save new length.
+ __ sw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+
+ // Push the element.
+ __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize));
+ // We may need a register containing the address end_elements below,
+ // so write back the value in end_elements.
+ __ sll(end_elements, v0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(end_elements, elements, end_elements);
+ const int kEndElementsOffset =
+ FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize;
+ __ sw(t0, MemOperand(end_elements, kEndElementsOffset));
+ __ Addu(end_elements, end_elements, kPointerSize);
+
+ // Check for a smi.
+ __ JumpIfNotSmi(t0, &with_write_barrier);
+ __ bind(&exit);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&with_write_barrier);
+ __ InNewSpace(elements, t0, eq, &exit);
+ __ RecordWriteHelper(elements, end_elements, t0);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&attempt_to_grow_elements);
+ // v0: array's length + 1.
+ // t0: elements' length.
+
+ if (!FLAG_inline_new) {
+ __ Branch(&call_builtin);
+ }
+
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address(
+ masm()->isolate());
+ ExternalReference new_space_allocation_limit =
+ ExternalReference::new_space_allocation_limit_address(
+ masm()->isolate());
+
+ const int kAllocationDelta = 4;
+ // Load top and check if it is the end of elements.
+ __ sll(end_elements, v0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(end_elements, elements, end_elements);
+ __ Addu(end_elements, end_elements, Operand(kEndElementsOffset));
+ __ li(t3, Operand(new_space_allocation_top));
+ __ lw(t2, MemOperand(t3));
+ __ Branch(&call_builtin, ne, end_elements, Operand(t2));
+
+ __ li(t5, Operand(new_space_allocation_limit));
+ __ lw(t5, MemOperand(t5));
+ __ Addu(t2, t2, Operand(kAllocationDelta * kPointerSize));
+ __ Branch(&call_builtin, hi, t2, Operand(t5));
+
+ // We fit and could grow elements.
+ // Update new_space_allocation_top.
+ __ sw(t2, MemOperand(t3));
+ // Push the argument.
+ __ lw(t2, MemOperand(sp, (argc - 1) * kPointerSize));
+ __ sw(t2, MemOperand(end_elements));
+ // Fill the rest with holes.
+ __ LoadRoot(t2, Heap::kTheHoleValueRootIndex);
+ for (int i = 1; i < kAllocationDelta; i++) {
+ __ sw(t2, MemOperand(end_elements, i * kPointerSize));
+ }
+
+ // Update elements' and array's sizes.
+ __ sw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Addu(t0, t0, Operand(Smi::FromInt(kAllocationDelta)));
+ __ sw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
+
+ // Elements are in new space, so write barrier is not required.
+ __ Drop(argc + 1);
+ __ Ret();
+ }
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush,
+ masm()->isolate()),
+ argc + 1,
+ 1);
+ }
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(function);
}
@@ -311,8 +1682,87 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray() || cell != NULL) return heap()->undefined_value();
+
+ Label miss, return_undefined, call_builtin;
+
+ Register receiver = a1;
+ Register elements = a3;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ lw(receiver, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(receiver, &miss);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object),
+ receiver, holder, elements, t0, v0, name, &miss);
+
+ // Get the elements array of the object.
+ __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode and writable.
+ __ CheckMap(elements,
+ v0,
+ Heap::kFixedArrayMapRootIndex,
+ &call_builtin,
+ DONT_DO_SMI_CHECK);
+
+ // Get the array's length into t0 and calculate new length.
+ __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+ __ Subu(t0, t0, Operand(Smi::FromInt(1)));
+ __ Branch(&return_undefined, lt, t0, Operand(zero_reg));
+
+ // Get the last element.
+ __ LoadRoot(t2, Heap::kTheHoleValueRootIndex);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ // We can't address the last element in one operation. Compute the more
+ // expensive shift first, and use an offset later on.
+ __ sll(t1, t0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(elements, elements, t1);
+ __ lw(v0, MemOperand(elements, FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Branch(&call_builtin, eq, v0, Operand(t2));
+
+ // Set the array's length.
+ __ sw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
+
+ // Fill with the hole.
+ __ sw(t2, MemOperand(elements, FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&return_undefined);
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop,
+ masm()->isolate()),
+ argc + 1,
+ 1);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(function);
}
@@ -322,8 +1772,84 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ if (!object->IsString() || cell != NULL) return heap()->undefined_value();
+
+ const int argc = arguments().immediate();
+
+ Label miss;
+ Label name_miss;
+ Label index_out_of_range;
+
+ Label* index_out_of_range_label = &index_out_of_range;
+
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
+ index_out_of_range_label = &miss;
+ }
+
+ GenerateNameCheck(name, &name_miss);
+
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ v0,
+ &miss);
+ ASSERT(object != holder);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), v0, holder,
+ a1, a3, t0, name, &miss);
+
+ Register receiver = a1;
+ Register index = t1;
+ Register scratch = a3;
+ Register result = v0;
+ __ lw(receiver, MemOperand(sp, argc * kPointerSize));
+ if (argc > 0) {
+ __ lw(index, MemOperand(sp, (argc - 1) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ StringCharCodeAtGenerator char_code_at_generator(receiver,
+ index,
+ scratch,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ index_out_of_range_label,
+ STRING_INDEX_IS_NUMBER);
+ char_code_at_generator.GenerateFast(masm());
+ __ Drop(argc + 1);
+ __ Ret();
+
+ StubRuntimeCallHelper call_helper;
+ char_code_at_generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(v0, Heap::kNanValueRootIndex);
+ __ Drop(argc + 1);
+ __ Ret();
+ }
+
+ __ bind(&miss);
+ // Restore function name in a2.
+ __ li(a2, Handle<String>(name));
+ __ bind(&name_miss);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(function);
}
@@ -333,8 +1859,85 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ // If object is not a string, bail out to regular call.
+ if (!object->IsString() || cell != NULL) return heap()->undefined_value();
+
+ const int argc = arguments().immediate();
+
+ Label miss;
+ Label name_miss;
+ Label index_out_of_range;
+ Label* index_out_of_range_label = &index_out_of_range;
+
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
+ index_out_of_range_label = &miss;
+ }
+
+ GenerateNameCheck(name, &name_miss);
+
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ v0,
+ &miss);
+ ASSERT(object != holder);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), v0, holder,
+ a1, a3, t0, name, &miss);
+
+ Register receiver = v0;
+ Register index = t1;
+ Register scratch1 = a1;
+ Register scratch2 = a3;
+ Register result = v0;
+ __ lw(receiver, MemOperand(sp, argc * kPointerSize));
+ if (argc > 0) {
+ __ lw(index, MemOperand(sp, (argc - 1) * kPointerSize));
+ } else {
+ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
+ }
+
+ StringCharAtGenerator char_at_generator(receiver,
+ index,
+ scratch1,
+ scratch2,
+ result,
+ &miss, // When not a string.
+ &miss, // When not a number.
+ index_out_of_range_label,
+ STRING_INDEX_IS_NUMBER);
+ char_at_generator.GenerateFast(masm());
+ __ Drop(argc + 1);
+ __ Ret();
+
+ StubRuntimeCallHelper call_helper;
+ char_at_generator.GenerateSlow(masm(), call_helper);
+
+ if (index_out_of_range.is_linked()) {
+ __ bind(&index_out_of_range);
+ __ LoadRoot(v0, Heap::kEmptyStringRootIndex);
+ __ Drop(argc + 1);
+ __ Ret();
+ }
+
+ __ bind(&miss);
+ // Restore function name in a2.
+ __ li(a2, Handle<String>(name));
+ __ bind(&name_miss);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(function);
}
@@ -344,8 +1947,69 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall(
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ const int argc = arguments().immediate();
+
+ // If the object is not a JSObject or we got an unexpected number of
+ // arguments, bail out to the regular call.
+ if (!object->IsJSObject() || argc != 1) return heap()->undefined_value();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell == NULL) {
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a1, &miss);
+
+ CheckPrototypes(JSObject::cast(object), a1, holder, v0, a3, t0, name,
+ &miss);
+ } else {
+ ASSERT(cell->value() == function);
+ GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the char code argument.
+ Register code = a1;
+ __ lw(code, MemOperand(sp, 0 * kPointerSize));
+
+ // Check the code is a smi.
+ Label slow;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(code, &slow);
+
+ // Convert the smi code to uint16.
+ __ And(code, code, Operand(Smi::FromInt(0xffff)));
+
+ StringCharFromCodeGenerator char_from_code_generator(code, v0);
+ char_from_code_generator.GenerateFast(masm());
+ __ Drop(argc + 1);
+ __ Ret();
+
+ StubRuntimeCallHelper call_helper;
+ char_from_code_generator.GenerateSlow(masm(), call_helper);
+
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ bind(&slow);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+ __ bind(&miss);
+ // a2: function name.
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
}
@@ -354,8 +2018,134 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ if (!CpuFeatures::IsSupported(FPU))
+ return heap()->undefined_value();
+ CpuFeatures::Scope scope_fpu(FPU);
+
+ const int argc = arguments().immediate();
+
+ // If the object is not a JSObject or we got an unexpected number of
+ // arguments, bail out to the regular call.
+ if (!object->IsJSObject() || argc != 1) return heap()->undefined_value();
+
+ Label miss, slow;
+ GenerateNameCheck(name, &miss);
+
+ if (cell == NULL) {
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a1, &miss);
+
+ CheckPrototypes(JSObject::cast(object), a1, holder, a0, a3, t0, name,
+ &miss);
+ } else {
+ ASSERT(cell->value() == function);
+ GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into v0.
+ __ lw(v0, MemOperand(sp, 0 * kPointerSize));
+
+ // If the argument is a smi, just return.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ And(t0, v0, Operand(kSmiTagMask));
+ __ Drop(argc + 1, eq, t0, Operand(zero_reg));
+ __ Ret(eq, t0, Operand(zero_reg));
+
+ __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
+
+ Label wont_fit_smi, no_fpu_error, restore_fcsr_and_return;
+
+ // If fpu is enabled, we use the floor instruction.
+
+ // Load the HeapNumber value.
+ __ ldc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset));
+
+ // Backup FCSR.
+ __ cfc1(a3, FCSR);
+ // Clearing FCSR clears the exception mask with no side-effects.
+ __ ctc1(zero_reg, FCSR);
+ // Convert the argument to an integer.
+ __ floor_w_d(f0, f0);
+
+ // Start checking for special cases.
+ // Get the argument exponent and clear the sign bit.
+ __ lw(t1, FieldMemOperand(v0, HeapNumber::kValueOffset + kPointerSize));
+ __ And(t2, t1, Operand(~HeapNumber::kSignMask));
+ __ srl(t2, t2, HeapNumber::kMantissaBitsInTopWord);
+
+ // Retrieve FCSR and check for fpu errors.
+ __ cfc1(t5, FCSR);
+ __ srl(t5, t5, kFCSRFlagShift);
+ // Flag 1 marks an inaccurate but still good result so we ignore it.
+ __ And(t5, t5, Operand(kFCSRFlagMask ^ 1));
+ __ Branch(&no_fpu_error, eq, t5, Operand(zero_reg));
+
+ // Check for NaN, Infinity, and -Infinity.
+ // They are invariant through a Math.Floor call, so just
+ // return the original argument.
+ __ Subu(t3, t2, Operand(HeapNumber::kExponentMask
+ >> HeapNumber::kMantissaBitsInTopWord));
+ __ Branch(&restore_fcsr_and_return, eq, t3, Operand(zero_reg));
+ // We had an overflow or underflow in the conversion. Check if we
+ // have a big exponent.
+ // If greater or equal, the argument is already round and in v0.
+ __ Branch(&restore_fcsr_and_return, ge, t3,
+ Operand(HeapNumber::kMantissaBits));
+ __ Branch(&wont_fit_smi);
+
+ __ bind(&no_fpu_error);
+ // Move the result back to v0.
+ __ mfc1(v0, f0);
+ // Check if the result fits into a smi.
+ __ Addu(a1, v0, Operand(0x40000000));
+ __ Branch(&wont_fit_smi, lt, a1, Operand(zero_reg));
+ // Tag the result.
+ STATIC_ASSERT(kSmiTag == 0);
+ __ sll(v0, v0, kSmiTagSize);
+
+ // Check for -0.
+ __ Branch(&restore_fcsr_and_return, ne, v0, Operand(zero_reg));
+ // t1 already holds the HeapNumber exponent.
+ __ And(t0, t1, Operand(HeapNumber::kSignMask));
+ // If our HeapNumber is negative it was -0, so load its address and return.
+ // Else v0 is loaded with 0, so we can also just return.
+ __ Branch(&restore_fcsr_and_return, eq, t0, Operand(zero_reg));
+ __ lw(v0, MemOperand(sp, 0 * kPointerSize));
+
+ __ bind(&restore_fcsr_and_return);
+ // Restore FCSR and return.
+ __ ctc1(a3, FCSR);
+
+ __ Drop(argc + 1);
+ __ Ret();
+
+ __ bind(&wont_fit_smi);
+ // Restore FCSR and fall to slow case.
+ __ ctc1(a3, FCSR);
+
+ __ bind(&slow);
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+ __ bind(&miss);
+ // a2: function name.
+ MaybeObject* obj = GenerateMissBranch();
+ if (obj->IsFailure()) return obj;
+
+ // Return the generated code.
+ return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
}
@@ -364,8 +2154,100 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : function name
+ // -- ra : return address
+ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- sp[argc * 4] : receiver
+ // -----------------------------------
+
+ const int argc = arguments().immediate();
+
+ // If the object is not a JSObject or we got an unexpected number of
+ // arguments, bail out to the regular call.
+ if (!object->IsJSObject() || argc != 1) return heap()->undefined_value();
+
+ Label miss;
+ GenerateNameCheck(name, &miss);
+
+ if (cell == NULL) {
+ __ lw(a1, MemOperand(sp, 1 * kPointerSize));
+
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfSmi(a1, &miss);
+
+ CheckPrototypes(JSObject::cast(object), a1, holder, v0, a3, t0, name,
+ &miss);
+ } else {
+ ASSERT(cell->value() == function);
+ GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+ }
+
+ // Load the (only) argument into v0.
+ __ lw(v0, MemOperand(sp, 0 * kPointerSize));
+
+ // Check if the argument is a smi.
+ Label not_smi;
+ STATIC_ASSERT(kSmiTag == 0);
+ __ JumpIfNotSmi(v0, &not_smi);
+
+ // Do bitwise not or do nothing depending on the sign of the
+ // argument.
+ __ sra(t0, v0, kBitsPerInt - 1);
+ __ Xor(a1, v0, t0);
+
+ // Add 1 or do nothing depending on the sign of the argument.
+ __ Subu(v0, a1, t0);
+
+ // If the result is still negative, go to the slow case.
+ // This only happens for the most negative smi.
+ Label slow;
+ __ Branch(&slow, lt, v0, Operand(zero_reg));
+
+ // Smi case done.
+ __ Drop(argc + 1);
+ __ Ret();
+
+ // Check if the argument is a heap number and load its exponent and
+ // sign.
+ __ bind(&not_smi);
+ __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
+ __ lw(a1, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+
+ // Check the sign of the argument. If the argument is positive,
+ // just return it.
+ Label negative_sign;
+ __ And(t0, a1, Operand(HeapNumber::kSignMask));
+ __ Branch(&negative_sign, ne, t0, Operand(zero_reg));
+ __ Drop(argc + 1);
+ __ Ret();
+
+ // If the argument is negative, clear the sign, and return a new
+ // number.
+ __ bind(&negative_sign);
+ __ Xor(a1, a1, Operand(HeapNumber::kSignMask));
+ __ lw(a3, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ LoadRoot(t2, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t0, t1, t2, &slow);
+ __ sw(a1, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ sw(a3, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ Drop(argc + 1);
+ __ Ret();
+
+ // Tail call the full function. We do not have to patch the receiver
+ // because the function makes no use of it.
+ __ bind(&slow);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+ __ bind(&miss);
+ // a2: function name.
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
}
@@ -376,8 +2258,51 @@ MaybeObject* CallStubCompiler::CompileFastApiCall(
JSGlobalPropertyCell* cell,
JSFunction* function,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+
+ Counters* counters = isolate()->counters();
+
+ ASSERT(optimization.is_simple_api_call());
+ // Bail out if object is a global object as we don't want to
+ // repatch it to global receiver.
+ if (object->IsGlobalObject()) return heap()->undefined_value();
+ if (cell != NULL) return heap()->undefined_value();
+ if (!object->IsJSObject()) return heap()->undefined_value();
+ int depth = optimization.GetPrototypeDepthOfExpectedType(
+ JSObject::cast(object), holder);
+ if (depth == kInvalidProtoDepth) return heap()->undefined_value();
+
+ Label miss, miss_before_stack_reserved;
+
+ GenerateNameCheck(name, &miss_before_stack_reserved);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(a1, &miss_before_stack_reserved);
+
+ __ IncrementCounter(counters->call_const(), 1, a0, a3);
+ __ IncrementCounter(counters->call_const_fast_api(), 1, a0, a3);
+
+ ReserveSpaceForFastApiCall(masm(), a0);
+
+ // Check that the maps haven't changed and find a Holder as a side effect.
+ CheckPrototypes(JSObject::cast(object), a1, holder, a0, a3, t0, name,
+ depth, &miss);
+
+ MaybeObject* result = GenerateFastApiDirectCall(masm(), optimization, argc);
+ if (result->IsFailure()) return result;
+
+ __ bind(&miss);
+ FreeSpaceForFastApiCall(masm());
+
+ __ bind(&miss_before_stack_reserved);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(function);
}
@@ -386,26 +2311,251 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
JSFunction* function,
String* name,
CheckType check) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ if (HasCustomCallGenerator(function)) {
+ MaybeObject* maybe_result = CompileCustomCall(
+ object, holder, NULL, function, name);
+ Object* result;
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ // Undefined means bail out to regular compiler.
+ if (!result->IsUndefined()) return result;
+ }
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ if (check != NUMBER_CHECK) {
+ __ And(t1, a1, Operand(kSmiTagMask));
+ __ Branch(&miss, eq, t1, Operand(zero_reg));
+ }
+
+ // Make sure that it's okay not to patch the on stack receiver
+ // unless we're doing a receiver map check.
+ ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
+
+ SharedFunctionInfo* function_info = function->shared();
+ switch (check) {
+ case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(masm()->isolate()->counters()->call_const(),
+ 1, a0, a3);
+
+ // Check that the maps haven't changed.
+ CheckPrototypes(JSObject::cast(object), a1, holder, a0, a3, t0, name,
+ &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ lw(a3, FieldMemOperand(a1, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, argc * kPointerSize));
+ }
+ break;
+
+ case STRING_CHECK:
+ if (!function->IsBuiltin() && !function_info->strict_mode()) {
+ // Calling non-strict non-builtins with a value as the receiver
+ // requires boxing.
+ __ jmp(&miss);
+ } else {
+ // Check that the object is a two-byte string or a symbol.
+ __ GetObjectType(a1, a3, a3);
+ __ Branch(&miss, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE));
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::STRING_FUNCTION_INDEX, a0, &miss);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), a0, holder, a3,
+ a1, t0, name, &miss);
+ }
+ break;
+
+ case NUMBER_CHECK: {
+ if (!function->IsBuiltin() && !function_info->strict_mode()) {
+ // Calling non-strict non-builtins with a value as the receiver
+ // requires boxing.
+ __ jmp(&miss);
+ } else {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ And(t1, a1, Operand(kSmiTagMask));
+ __ Branch(&fast, eq, t1, Operand(zero_reg));
+ __ GetObjectType(a1, a0, a0);
+ __ Branch(&miss, ne, a0, Operand(HEAP_NUMBER_TYPE));
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::NUMBER_FUNCTION_INDEX, a0, &miss);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), a0, holder, a3,
+ a1, t0, name, &miss);
+ }
+ break;
+ }
+
+ case BOOLEAN_CHECK: {
+ if (!function->IsBuiltin() && !function_info->strict_mode()) {
+ // Calling non-strict non-builtins with a value as the receiver
+ // requires boxing.
+ __ jmp(&miss);
+ } else {
+ Label fast;
+ // Check that the object is a boolean.
+ __ LoadRoot(t0, Heap::kTrueValueRootIndex);
+ __ Branch(&fast, eq, a1, Operand(t0));
+ __ LoadRoot(t0, Heap::kFalseValueRootIndex);
+ __ Branch(&miss, ne, a1, Operand(t0));
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateDirectLoadGlobalFunctionPrototype(
+ masm(), Context::BOOLEAN_FUNCTION_INDEX, a0, &miss);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), a0, holder, a3,
+ a1, t0, name, &miss);
+ }
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ }
+
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(function);
}
MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ LookupResult lookup;
+ LookupPostInterceptor(holder, name, &lookup);
+
+ // Get the receiver from the stack.
+ __ lw(a1, MemOperand(sp, argc * kPointerSize));
+
+ CallInterceptorCompiler compiler(this, arguments(), a2);
+ MaybeObject* result = compiler.Compile(masm(),
+ object,
+ holder,
+ name,
+ &lookup,
+ a1,
+ a3,
+ t0,
+ a0,
+ &miss);
+ if (result->IsFailure()) {
+ return result;
+ }
+
+ // Move returned value, the function to call, to a1.
+ __ mov(a1, v0);
+ // Restore receiver.
+ __ lw(a0, MemOperand(sp, argc * kPointerSize));
+
+ GenerateCallFunction(masm(), object, arguments(), &miss);
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(INTERCEPTOR, name);
}
-MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object,
- GlobalObject* holder,
- JSGlobalPropertyCell* cell,
- JSFunction* function,
- String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+MaybeObject* CallStubCompiler::CompileCallGlobal(
+ JSObject* object,
+ GlobalObject* holder,
+ JSGlobalPropertyCell* cell,
+ JSFunction* function,
+ String* name,
+ Code::ExtraICState extra_ic_state) {
+ // ----------- S t a t e -------------
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+
+ if (HasCustomCallGenerator(function)) {
+ MaybeObject* maybe_result = CompileCustomCall(
+ object, holder, cell, function, name);
+ Object* result;
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ // Undefined means bail out to regular compiler.
+ if (!result->IsUndefined()) return result;
+ }
+
+ Label miss;
+
+ GenerateNameCheck(name, &miss);
+
+ // Get the number of arguments.
+ const int argc = arguments().immediate();
+
+ GenerateGlobalReceiverCheck(object, holder, name, &miss);
+ GenerateLoadFunctionFromCell(cell, function, &miss);
+
+ // Patch the receiver on the stack with the global proxy if
+ // necessary.
+ if (object->IsGlobalObject()) {
+ __ lw(a3, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset));
+ __ sw(a3, MemOperand(sp, argc * kPointerSize));
+ }
+
+ // Setup the context (function already in r1).
+ __ lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
+
+ // Jump to the cached code (tail call).
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->call_global_inline(), 1, a3, t0);
+ ASSERT(function->is_compiled());
+ Handle<Code> code(function->code());
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ if (V8::UseCrankshaft()) {
+ UNIMPLEMENTED_MIPS();
+ } else {
+ __ InvokeCode(code, expected, arguments(), RelocInfo::CODE_TARGET,
+ JUMP_FUNCTION, call_kind);
+ }
+
+ // Handle call cache miss.
+ __ bind(&miss);
+ __ IncrementCounter(counters->call_global_inline_miss(), 1, a1, a3);
+ MaybeObject* maybe_result = GenerateMissBranch();
+ if (maybe_result->IsFailure()) return maybe_result;
+
+ // Return the generated code.
+ return GetCode(NORMAL, name);
}
@@ -413,39 +2563,205 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object,
int index,
Map* transition,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Name register might be clobbered.
+ GenerateStoreField(masm(),
+ object,
+ index,
+ transition,
+ a1, a2, a3,
+ &miss);
+ __ bind(&miss);
+ __ li(a2, Operand(Handle<String>(name))); // Restore name.
+ Handle<Code> ic = masm()->isolate()->builtins()->Builtins::StoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name);
}
MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object,
AccessorInfo* callback,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(a1, &miss);
+
+ // Check that the map of the object hasn't changed.
+ __ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ Branch(&miss, ne, a3, Operand(Handle<Map>(object->map())));
+
+ // Perform global security token check if needed.
+ if (object->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(a1, a3, &miss);
+ }
+
+ // Stub never generated for non-global objects that require access
+ // checks.
+ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
+
+ __ push(a1); // Receiver.
+ __ li(a3, Operand(Handle<AccessorInfo>(callback))); // Callback info.
+ __ Push(a3, a2, a0);
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_callback_property =
+ ExternalReference(IC_Utility(IC::kStoreCallbackProperty),
+ masm()->isolate());
+ __ TailCallExternalReference(store_callback_property, 4, 1);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(CALLBACKS, name);
}
MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the object isn't a smi.
+ __ JumpIfSmi(a1, &miss);
+
+ // Check that the map of the object hasn't changed.
+ __ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ Branch(&miss, ne, a3, Operand(Handle<Map>(receiver->map())));
+
+ // Perform global security token check if needed.
+ if (receiver->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(a1, a3, &miss);
+ }
+
+ // Stub is never generated for non-global objects that require access
+ // checks.
+ ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
+
+ __ Push(a1, a2, a0); // Receiver, name, value.
+
+ __ li(a0, Operand(Smi::FromInt(strict_mode_)));
+ __ push(a0); // Strict mode.
+
+ // Do tail-call to the runtime system.
+ ExternalReference store_ic_property =
+ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty),
+ masm()->isolate());
+ __ TailCallExternalReference(store_ic_property, 4, 1);
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ Handle<Code> ic = masm()->isolate()->builtins()->Builtins::StoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(INTERCEPTOR, name);
}
MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
JSGlobalPropertyCell* cell,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the map of the global has not changed.
+ __ lw(a3, FieldMemOperand(a1, HeapObject::kMapOffset));
+ __ Branch(&miss, ne, a3, Operand(Handle<Map>(object->map())));
+
+ // Check that the value in the cell is not the hole. If it is, this
+ // cell could have been deleted and reintroducing the global needs
+ // to update the property details in the property dictionary of the
+ // global object. We bail out to the runtime system to do that.
+ __ li(t0, Operand(Handle<JSGlobalPropertyCell>(cell)));
+ __ LoadRoot(t1, Heap::kTheHoleValueRootIndex);
+ __ lw(t2, FieldMemOperand(t0, JSGlobalPropertyCell::kValueOffset));
+ __ Branch(&miss, eq, t1, Operand(t2));
+
+ // Store the value in the cell.
+ __ sw(a0, FieldMemOperand(t0, JSGlobalPropertyCell::kValueOffset));
+ __ mov(v0, a0); // Stored value must be returned in v0.
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->named_store_global_inline(), 1, a1, a3);
+ __ Ret();
+
+ // Handle store cache miss.
+ __ bind(&miss);
+ __ IncrementCounter(counters->named_store_global_inline_miss(), 1, a1, a3);
+ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(NORMAL, name);
}
MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name,
JSObject* object,
JSObject* last) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the receiver is not a smi.
+ __ JumpIfSmi(a0, &miss);
+
+ // Check the maps of the full prototype chain.
+ CheckPrototypes(object, a0, last, a3, a1, t0, name, &miss);
+
+ // If the last object in the prototype chain is a global object,
+ // check that the global property cell is empty.
+ if (last->IsGlobalObject()) {
+ MaybeObject* cell = GenerateCheckPropertyCell(masm(),
+ GlobalObject::cast(last),
+ name,
+ a1,
+ &miss);
+ if (cell->IsFailure()) {
+ miss.Unuse();
+ return cell;
+ }
+ }
+
+ // Return undefined if maps of the full prototype chain is still the same.
+ __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
+ __ Ret();
+
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(NONEXISTENT, heap()->empty_string());
}
@@ -453,8 +2769,21 @@ MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object,
JSObject* holder,
int index,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ __ mov(v0, a0);
+
+ GenerateLoadField(object, holder, v0, a3, a1, t0, index, name, &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(FIELD, name);
}
@@ -462,8 +2791,25 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name,
JSObject* object,
JSObject* holder,
AccessorInfo* callback) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ MaybeObject* result = GenerateLoadCallback(object, holder, a0, a2, a3, a1, t0,
+ callback, name, &miss);
+ if (result->IsFailure()) {
+ miss.Unuse();
+ return result;
+ }
+
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(CALLBACKS, name);
}
@@ -471,16 +2817,50 @@ MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object,
JSObject* holder,
Object* value,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateLoadConstant(object, holder, a0, a3, a1, t0, value, name, &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(CONSTANT_FUNCTION, name);
}
MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* object,
JSObject* holder,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -- [sp] : receiver
+ // -----------------------------------
+ Label miss;
+
+ LookupResult lookup;
+ LookupPostInterceptor(holder, name, &lookup);
+ GenerateLoadInterceptor(object,
+ holder,
+ &lookup,
+ a0,
+ a2,
+ a3,
+ a1,
+ t0,
+ name,
+ &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(INTERCEPTOR, name);
}
@@ -489,8 +2869,45 @@ MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
JSGlobalPropertyCell* cell,
String* name,
bool is_dont_delete) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : receiver
+ // -- a2 : name
+ // -- ra : return address
+ // -----------------------------------
+ Label miss;
+
+ // If the object is the holder then we know that it's a global
+ // object which can only happen for contextual calls. In this case,
+ // the receiver cannot be a smi.
+ if (object != holder) {
+ __ And(t0, a0, Operand(kSmiTagMask));
+ __ Branch(&miss, eq, t0, Operand(zero_reg));
+ }
+
+ // Check that the map of the global has not changed.
+ CheckPrototypes(object, a0, holder, a3, t0, a1, name, &miss);
+
+ // Get the value from the cell.
+ __ li(a3, Operand(Handle<JSGlobalPropertyCell>(cell)));
+ __ lw(t0, FieldMemOperand(a3, JSGlobalPropertyCell::kValueOffset));
+
+ // Check for deleted property if property can actually be deleted.
+ if (!is_dont_delete) {
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Branch(&miss, eq, t0, Operand(at));
+ }
+
+ __ mov(v0, t0);
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->named_load_global_stub(), 1, a1, a3);
+ __ Ret();
+
+ __ bind(&miss);
+ __ IncrementCounter(counters->named_load_global_stub_miss(), 1, a1, a3);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(NORMAL, name);
}
@@ -498,8 +2915,21 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name,
JSObject* receiver,
JSObject* holder,
int index) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ // Check the key is the cached one.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ GenerateLoadField(receiver, holder, a1, a2, a3, t0, index, name, &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ return GetCode(FIELD, name);
}
@@ -508,8 +2938,27 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback(
JSObject* receiver,
JSObject* holder,
AccessorInfo* callback) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ // Check the key is the cached one.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ MaybeObject* result = GenerateLoadCallback(receiver, holder, a1, a0, a2, a3,
+ t0, callback, name, &miss);
+ if (result->IsFailure()) {
+ miss.Unuse();
+ return result;
+ }
+
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ return GetCode(CALLBACKS, name);
}
@@ -517,40 +2966,171 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name,
JSObject* receiver,
JSObject* holder,
Object* value) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ // Check the key is the cached one.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ GenerateLoadConstant(receiver, holder, a1, a2, a3, t0, value, name, &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(CONSTANT_FUNCTION, name);
}
MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
JSObject* holder,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ // Check the key is the cached one.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ LookupResult lookup;
+ LookupPostInterceptor(holder, name, &lookup);
+ GenerateLoadInterceptor(receiver,
+ holder,
+ &lookup,
+ a1,
+ a0,
+ a2,
+ a3,
+ t0,
+ name,
+ &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ return GetCode(INTERCEPTOR, name);
}
MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ // Check the key is the cached one.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ GenerateLoadArrayLength(masm(), a1, a2, &miss);
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ return GetCode(CALLBACKS, name);
}
MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->keyed_load_string_length(), 1, a2, a3);
+
+ // Check the key is the cached one.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ GenerateLoadStringLength(masm(), a1, a2, a3, &miss, true);
+ __ bind(&miss);
+ __ DecrementCounter(counters->keyed_load_string_length(), 1, a2, a3);
+
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ return GetCode(CALLBACKS, name);
}
MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->keyed_load_function_prototype(), 1, a2, a3);
+
+ // Check the name hasn't changed.
+ __ Branch(&miss, ne, a0, Operand(Handle<String>(name)));
+
+ GenerateLoadFunctionPrototype(masm(), a1, a2, a3, &miss);
+ __ bind(&miss);
+ __ DecrementCounter(counters->keyed_load_function_prototype(), 1, a2, a3);
+ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
+
+ return GetCode(CALLBACKS, name);
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+MaybeObject* KeyedLoadStubCompiler::CompileLoadFastElement(Map* receiver_map) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ MaybeObject* maybe_stub = KeyedLoadFastElementStub().TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(a1,
+ a2,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
+
+
+MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(a1, &miss);
+
+ int receiver_count = receiver_maps->length();
+ __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map(receiver_maps->at(current));
+ Handle<Code> code(handler_ics->at(current));
+ __ Jump(code, RelocInfo::CODE_TARGET, eq, a2, Operand(map));
+ }
+
+ __ bind(&miss);
+ Handle<Code> miss_ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -558,39 +3138,1143 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
int index,
Map* transition,
String* name) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ Label miss;
+
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->keyed_store_field(), 1, a3, t0);
+
+ // Check that the name has not changed.
+ __ Branch(&miss, ne, a1, Operand(Handle<String>(name)));
+
+ // a3 is used as scratch register. a1 and a2 keep their values if a jump to
+ // the miss label is generated.
+ GenerateStoreField(masm(),
+ object,
+ index,
+ transition,
+ a2, a1, a3,
+ &miss);
+ __ bind(&miss);
+
+ __ DecrementCounter(counters->keyed_store_field(), 1, a3, t0);
+ Handle<Code> ic = masm()->isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name);
}
-MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
- JSObject* receiver) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+MaybeObject* KeyedStoreStubCompiler::CompileStoreFastElement(
+ Map* receiver_map) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -- a3 : scratch
+ // -----------------------------------
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ MaybeObject* maybe_stub =
+ KeyedStoreFastElementStub(is_js_array).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(a2,
+ a3,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
+
+
+MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -- a3 : scratch
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(a2, &miss);
+
+ int receiver_count = receiver_maps->length();
+ __ lw(a3, FieldMemOperand(a2, HeapObject::kMapOffset));
+ for (int current = 0; current < receiver_count; ++current) {
+ Handle<Map> map(receiver_maps->at(current));
+ Handle<Code> code(handler_ics->at(current));
+ __ Jump(code, RelocInfo::CODE_TARGET, eq, a3, Operand(map));
+ }
+
+ __ bind(&miss);
+ Handle<Code> miss_ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+ // a0 : argc
+ // a1 : constructor
+ // ra : return address
+ // [sp] : last argument
+ Label generic_stub_call;
+
+ // Use t7 for holding undefined which is used in several places below.
+ __ LoadRoot(t7, Heap::kUndefinedValueRootIndex);
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Check to see whether there are any break points in the function code. If
+ // there are jump to the generic constructor stub which calls the actual
+ // code for the function thereby hitting the break points.
+ __ lw(t5, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
+ __ lw(a2, FieldMemOperand(t5, SharedFunctionInfo::kDebugInfoOffset));
+ __ Branch(&generic_stub_call, ne, a2, Operand(t7));
+#endif
+
+ // Load the initial map and verify that it is in fact a map.
+ // a1: constructor function
+ // t7: undefined
+ __ lw(a2, FieldMemOperand(a1, JSFunction::kPrototypeOrInitialMapOffset));
+ __ And(t0, a2, Operand(kSmiTagMask));
+ __ Branch(&generic_stub_call, eq, t0, Operand(zero_reg));
+ __ GetObjectType(a2, a3, t0);
+ __ Branch(&generic_stub_call, ne, t0, Operand(MAP_TYPE));
+
+#ifdef DEBUG
+ // Cannot construct functions this way.
+ // a0: argc
+ // a1: constructor function
+ // a2: initial map
+ // t7: undefined
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
+ __ Check(ne, "Function constructed by construct stub.",
+ a3, Operand(JS_FUNCTION_TYPE));
+#endif
+
+ // Now allocate the JSObject in new space.
+ // a0: argc
+ // a1: constructor function
+ // a2: initial map
+ // t7: undefined
+ __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset));
+ __ AllocateInNewSpace(a3,
+ t4,
+ t5,
+ t6,
+ &generic_stub_call,
+ SIZE_IN_WORDS);
+
+ // Allocated the JSObject, now initialize the fields. Map is set to initial
+ // map and properties and elements are set to empty fixed array.
+ // a0: argc
+ // a1: constructor function
+ // a2: initial map
+ // a3: object size (in words)
+ // t4: JSObject (not tagged)
+ // t7: undefined
+ __ LoadRoot(t6, Heap::kEmptyFixedArrayRootIndex);
+ __ mov(t5, t4);
+ __ sw(a2, MemOperand(t5, JSObject::kMapOffset));
+ __ sw(t6, MemOperand(t5, JSObject::kPropertiesOffset));
+ __ sw(t6, MemOperand(t5, JSObject::kElementsOffset));
+ __ Addu(t5, t5, Operand(3 * kPointerSize));
+ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
+ ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
+ ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
+
+
+ // Calculate the location of the first argument. The stack contains only the
+ // argc arguments.
+ __ sll(a1, a0, kPointerSizeLog2);
+ __ Addu(a1, a1, sp);
+
+ // Fill all the in-object properties with undefined.
+ // a0: argc
+ // a1: first argument
+ // a3: object size (in words)
+ // t4: JSObject (not tagged)
+ // t5: First in-object property of JSObject (not tagged)
+ // t7: undefined
+ // Fill the initialized properties with a constant value or a passed argument
+ // depending on the this.x = ...; assignment in the function.
+ SharedFunctionInfo* shared = function->shared();
+ for (int i = 0; i < shared->this_property_assignments_count(); i++) {
+ if (shared->IsThisPropertyAssignmentArgument(i)) {
+ Label not_passed, next;
+ // Check if the argument assigned to the property is actually passed.
+ int arg_number = shared->GetThisPropertyAssignmentArgument(i);
+ __ Branch(&not_passed, less_equal, a0, Operand(arg_number));
+ // Argument passed - find it on the stack.
+ __ lw(a2, MemOperand(a1, (arg_number + 1) * -kPointerSize));
+ __ sw(a2, MemOperand(t5));
+ __ Addu(t5, t5, kPointerSize);
+ __ jmp(&next);
+ __ bind(&not_passed);
+ // Set the property to undefined.
+ __ sw(t7, MemOperand(t5));
+ __ Addu(t5, t5, Operand(kPointerSize));
+ __ bind(&next);
+ } else {
+ // Set the property to the constant value.
+ Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i));
+ __ li(a2, Operand(constant));
+ __ sw(a2, MemOperand(t5));
+ __ Addu(t5, t5, kPointerSize);
+ }
+ }
+
+ // Fill the unused in-object property fields with undefined.
+ ASSERT(function->has_initial_map());
+ for (int i = shared->this_property_assignments_count();
+ i < function->initial_map()->inobject_properties();
+ i++) {
+ __ sw(t7, MemOperand(t5));
+ __ Addu(t5, t5, kPointerSize);
+ }
+
+ // a0: argc
+ // t4: JSObject (not tagged)
+ // Move argc to a1 and the JSObject to return to v0 and tag it.
+ __ mov(a1, a0);
+ __ mov(v0, t4);
+ __ Or(v0, v0, Operand(kHeapObjectTag));
+
+ // v0: JSObject
+ // a1: argc
+ // Remove caller arguments and receiver from the stack and return.
+ __ sll(t0, a1, kPointerSizeLog2);
+ __ Addu(sp, sp, t0);
+ __ Addu(sp, sp, Operand(kPointerSize));
+ Counters* counters = masm()->isolate()->counters();
+ __ IncrementCounter(counters->constructed_objects(), 1, a1, a2);
+ __ IncrementCounter(counters->constructed_objects_stub(), 1, a1, a2);
+ __ Ret();
+
+ // Jump to the generic stub in case the specialized code cannot handle the
+ // construction.
+ __ bind(&generic_stub_call);
+ Handle<Code> generic_construct_stub =
+ masm()->isolate()->builtins()->JSConstructStubGeneric();
+ __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode();
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
- JSObject* receiver_object,
- ExternalArrayType array_type,
- Code::Flags flags) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+MaybeObject* ExternalArrayLoadStubCompiler::CompileLoad(
+ JSObject*receiver, ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ MaybeObject* maybe_stub =
+ KeyedLoadExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(a1,
+ a2,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ return GetCode();
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
- JSObject* receiver_object,
- ExternalArrayType array_type,
- Code::Flags flags) {
- UNIMPLEMENTED_MIPS();
- return NULL;
+MaybeObject* ExternalArrayStoreStubCompiler::CompileStore(
+ JSObject* receiver, ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : name
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+ MaybeObject* maybe_stub =
+ KeyedStoreExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(a2,
+ a3,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+
+ return GetCode();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+static bool IsElementTypeSigned(ExternalArrayType array_type) {
+ switch (array_type) {
+ case kExternalByteArray:
+ case kExternalShortArray:
+ case kExternalIntArray:
+ return true;
+
+ case kExternalUnsignedByteArray:
+ case kExternalUnsignedShortArray:
+ case kExternalUnsignedIntArray:
+ return false;
+
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+
+void KeyedLoadStubCompiler::GenerateLoadExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss_force_generic, slow, failed_allocation;
+
+ Register key = a0;
+ Register receiver = a1;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(key, &miss_force_generic);
+
+ __ lw(a3, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ // a3: elements array
+
+ // Check that the index is in range.
+ __ lw(t1, FieldMemOperand(a3, ExternalArray::kLengthOffset));
+ __ sra(t2, key, kSmiTagSize);
+ // Unsigned comparison catches both negative and too-large values.
+ __ Branch(&miss_force_generic, Uless, t1, Operand(t2));
+
+ __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset));
+ // a3: base pointer of external storage
+
+ // We are not untagging smi key and instead work with it
+ // as if it was premultiplied by 2.
+ ASSERT((kSmiTag == 0) && (kSmiTagSize == 1));
+
+ Register value = a2;
+ switch (array_type) {
+ case kExternalByteArray:
+ __ srl(t2, key, 1);
+ __ addu(t3, a3, t2);
+ __ lb(value, MemOperand(t3, 0));
+ break;
+ case kExternalPixelArray:
+ case kExternalUnsignedByteArray:
+ __ srl(t2, key, 1);
+ __ addu(t3, a3, t2);
+ __ lbu(value, MemOperand(t3, 0));
+ break;
+ case kExternalShortArray:
+ __ addu(t3, a3, key);
+ __ lh(value, MemOperand(t3, 0));
+ break;
+ case kExternalUnsignedShortArray:
+ __ addu(t3, a3, key);
+ __ lhu(value, MemOperand(t3, 0));
+ break;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ __ sll(t2, key, 1);
+ __ addu(t3, a3, t2);
+ __ lw(value, MemOperand(t3, 0));
+ break;
+ case kExternalFloatArray:
+ __ sll(t3, t2, 2);
+ __ addu(t3, a3, t3);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ lwc1(f0, MemOperand(t3, 0));
+ } else {
+ __ lw(value, MemOperand(t3, 0));
+ }
+ break;
+ case kExternalDoubleArray:
+ __ sll(t2, key, 2);
+ __ addu(t3, a3, t2);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ ldc1(f0, MemOperand(t3, 0));
+ } else {
+ // t3: pointer to the beginning of the double we want to load.
+ __ lw(a2, MemOperand(t3, 0));
+ __ lw(a3, MemOperand(t3, Register::kSizeInBytes));
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ // For integer array types:
+ // a2: value
+ // For float array type:
+ // f0: value (if FPU is supported)
+ // a2: value (if FPU is not supported)
+ // For double array type:
+ // f0: value (if FPU is supported)
+ // a2/a3: value (if FPU is not supported)
+
+ if (array_type == kExternalIntArray) {
+ // For the Int and UnsignedInt array types, we need to see whether
+ // the value can be represented in a Smi. If not, we need to convert
+ // it to a HeapNumber.
+ Label box_int;
+ __ Subu(t3, value, Operand(0xC0000000)); // Non-smi value gives neg result.
+ __ Branch(&box_int, lt, t3, Operand(zero_reg));
+ // Tag integer as smi and return it.
+ __ sll(v0, value, kSmiTagSize);
+ __ Ret();
+
+ __ bind(&box_int);
+ // Allocate a HeapNumber for the result and perform int-to-double
+ // conversion.
+ // The arm version uses a temporary here to save r0, but we don't need to
+ // (a0 is not modified).
+ __ LoadRoot(t1, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, a3, t0, t1, &slow);
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ __ mtc1(value, f0);
+ __ cvt_d_w(f0, f0);
+ __ sdc1(f0, MemOperand(v0, HeapNumber::kValueOffset - kHeapObjectTag));
+ __ Ret();
+ } else {
+ Register dst1 = t2;
+ Register dst2 = t3;
+ FloatingPointHelper::Destination dest =
+ FloatingPointHelper::kCoreRegisters;
+ FloatingPointHelper::ConvertIntToDouble(masm,
+ value,
+ dest,
+ f0,
+ dst1,
+ dst2,
+ t1,
+ f2);
+ __ sw(dst1, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ sw(dst2, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ Ret();
+ }
+ } else if (array_type == kExternalUnsignedIntArray) {
+ // The test is different for unsigned int values. Since we need
+ // the value to be in the range of a positive smi, we can't
+ // handle either of the top two bits being set in the value.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ Label pl_box_int;
+ __ And(t2, value, Operand(0xC0000000));
+ __ Branch(&pl_box_int, ne, t2, Operand(zero_reg));
+
+ // It can fit in an Smi.
+ // Tag integer as smi and return it.
+ __ sll(v0, value, kSmiTagSize);
+ __ Ret();
+
+ __ bind(&pl_box_int);
+ // Allocate a HeapNumber for the result and perform int-to-double
+ // conversion. Don't use a0 and a1 as AllocateHeapNumber clobbers all
+ // registers - also when jumping due to exhausted young space.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t2, t3, t6, &slow);
+
+ // This is replaced by a macro:
+ // __ mtc1(value, f0); // LS 32-bits.
+ // __ mtc1(zero_reg, f1); // MS 32-bits are all zero.
+ // __ cvt_d_l(f0, f0); // Use 64 bit conv to get correct unsigned 32-bit.
+
+ __ Cvt_d_uw(f0, value);
+
+ __ sdc1(f0, MemOperand(v0, HeapNumber::kValueOffset - kHeapObjectTag));
+
+ __ Ret();
+ } else {
+ // Check whether unsigned integer fits into smi.
+ Label box_int_0, box_int_1, done;
+ __ And(t2, value, Operand(0x80000000));
+ __ Branch(&box_int_0, ne, t2, Operand(zero_reg));
+ __ And(t2, value, Operand(0x40000000));
+ __ Branch(&box_int_1, ne, t2, Operand(zero_reg));
+
+ // Tag integer as smi and return it.
+ __ sll(v0, value, kSmiTagSize);
+ __ Ret();
+
+ Register hiword = value; // a2.
+ Register loword = a3;
+
+ __ bind(&box_int_0);
+ // Integer does not have leading zeros.
+ GenerateUInt2Double(masm, hiword, loword, t0, 0);
+ __ Branch(&done);
+
+ __ bind(&box_int_1);
+ // Integer has one leading zero.
+ GenerateUInt2Double(masm, hiword, loword, t0, 1);
+
+
+ __ bind(&done);
+ // Integer was converted to double in registers hiword:loword.
+ // Wrap it into a HeapNumber. Don't use a0 and a1 as AllocateHeapNumber
+ // clobbers all registers - also when jumping due to exhausted young
+ // space.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(t2, t3, t5, t6, &slow);
+
+ __ sw(hiword, FieldMemOperand(t2, HeapNumber::kExponentOffset));
+ __ sw(loword, FieldMemOperand(t2, HeapNumber::kMantissaOffset));
+
+ __ mov(v0, t2);
+ __ Ret();
+ }
+ } else if (array_type == kExternalFloatArray) {
+ // For the floating-point array type, we need to always allocate a
+ // HeapNumber.
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Allocate a HeapNumber for the result. Don't use a0 and a1 as
+ // AllocateHeapNumber clobbers all registers - also when jumping due to
+ // exhausted young space.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t3, t5, t6, &slow);
+ // The float (single) value is already in fpu reg f0 (if we use float).
+ __ cvt_d_s(f0, f0);
+ __ sdc1(f0, MemOperand(v0, HeapNumber::kValueOffset - kHeapObjectTag));
+ __ Ret();
+ } else {
+ // Allocate a HeapNumber for the result. Don't use a0 and a1 as
+ // AllocateHeapNumber clobbers all registers - also when jumping due to
+ // exhausted young space.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t3, t5, t6, &slow);
+ // FPU is not available, do manual single to double conversion.
+
+ // a2: floating point value (binary32).
+ // v0: heap number for result
+
+ // Extract mantissa to t4.
+ __ And(t4, value, Operand(kBinary32MantissaMask));
+
+ // Extract exponent to t5.
+ __ srl(t5, value, kBinary32MantissaBits);
+ __ And(t5, t5, Operand(kBinary32ExponentMask >> kBinary32MantissaBits));
+
+ Label exponent_rebiased;
+ __ Branch(&exponent_rebiased, eq, t5, Operand(zero_reg));
+
+ __ li(t0, 0x7ff);
+ __ Xor(t1, t5, Operand(0xFF));
+ __ movz(t5, t0, t1); // Set t5 to 0x7ff only if t5 is equal to 0xff.
+ __ Branch(&exponent_rebiased, eq, t0, Operand(0xff));
+
+ // Rebias exponent.
+ __ Addu(t5,
+ t5,
+ Operand(-kBinary32ExponentBias + HeapNumber::kExponentBias));
+
+ __ bind(&exponent_rebiased);
+ __ And(a2, value, Operand(kBinary32SignMask));
+ value = no_reg;
+ __ sll(t0, t5, HeapNumber::kMantissaBitsInTopWord);
+ __ or_(a2, a2, t0);
+
+ // Shift mantissa.
+ static const int kMantissaShiftForHiWord =
+ kBinary32MantissaBits - HeapNumber::kMantissaBitsInTopWord;
+
+ static const int kMantissaShiftForLoWord =
+ kBitsPerInt - kMantissaShiftForHiWord;
+
+ __ srl(t0, t4, kMantissaShiftForHiWord);
+ __ or_(a2, a2, t0);
+ __ sll(a0, t4, kMantissaShiftForLoWord);
+
+ __ sw(a2, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ sw(a0, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ Ret();
+ }
+
+ } else if (array_type == kExternalDoubleArray) {
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Allocate a HeapNumber for the result. Don't use a0 and a1 as
+ // AllocateHeapNumber clobbers all registers - also when jumping due to
+ // exhausted young space.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t3, t5, t6, &slow);
+ // The double value is already in f0
+ __ sdc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset));
+ __ Ret();
+ } else {
+ // Allocate a HeapNumber for the result. Don't use a0 and a1 as
+ // AllocateHeapNumber clobbers all registers - also when jumping due to
+ // exhausted young space.
+ __ LoadRoot(t6, Heap::kHeapNumberMapRootIndex);
+ __ AllocateHeapNumber(v0, t3, t5, t6, &slow);
+
+ __ sw(a2, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ sw(a3, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ Ret();
+ }
+
+ } else {
+ // Tag integer as smi and return it.
+ __ sll(v0, value, kSmiTagSize);
+ __ Ret();
+ }
+
+ // Slow case, key and receiver still in a0 and a1.
+ __ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, a2, a3);
+
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+
+ __ Push(a1, a0);
+
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+
+ __ bind(&miss_force_generic);
+ Code* stub = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
+ // ---------- S t a t e --------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -----------------------------------
+
+ Label slow, check_heap_number, miss_force_generic;
+
+ // Register usage.
+ Register value = a0;
+ Register key = a1;
+ Register receiver = a2;
+ // a3 mostly holds the elements array or the destination external array.
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ __ lw(a3, FieldMemOperand(receiver, JSObject::kElementsOffset));
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(key, &miss_force_generic);
+
+ // Check that the index is in range.
+ __ SmiUntag(t0, key);
+ __ lw(t1, FieldMemOperand(a3, ExternalArray::kLengthOffset));
+ // Unsigned comparison catches both negative and too-large values.
+ __ Branch(&miss_force_generic, Ugreater_equal, t0, Operand(t1));
+
+ // Handle both smis and HeapNumbers in the fast path. Go to the
+ // runtime for all other kinds of values.
+ // a3: external array.
+ // t0: key (integer).
+
+ if (array_type == kExternalPixelArray) {
+ // Double to pixel conversion is only implemented in the runtime for now.
+ __ JumpIfNotSmi(value, &slow);
+ } else {
+ __ JumpIfNotSmi(value, &check_heap_number);
+ }
+ __ SmiUntag(t1, value);
+ __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset));
+
+ // a3: base pointer of external storage.
+ // t0: key (integer).
+ // t1: value (integer).
+
+ switch (array_type) {
+ case kExternalPixelArray: {
+ // Clamp the value to [0..255].
+ // v0 is used as a scratch register here.
+ Label done;
+ __ li(v0, Operand(255));
+ // Normal branch: nop in delay slot.
+ __ Branch(&done, gt, t1, Operand(v0));
+ // Use delay slot in this branch.
+ __ Branch(USE_DELAY_SLOT, &done, lt, t1, Operand(zero_reg));
+ __ mov(v0, zero_reg); // In delay slot.
+ __ mov(v0, t1); // Value is in range 0..255.
+ __ bind(&done);
+ __ mov(t1, v0);
+ __ addu(t8, a3, t0);
+ __ sb(t1, MemOperand(t8, 0));
+ }
+ break;
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ __ addu(t8, a3, t0);
+ __ sb(t1, MemOperand(t8, 0));
+ break;
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ __ sll(t8, t0, 1);
+ __ addu(t8, a3, t8);
+ __ sh(t1, MemOperand(t8, 0));
+ break;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ __ sll(t8, t0, 2);
+ __ addu(t8, a3, t8);
+ __ sw(t1, MemOperand(t8, 0));
+ break;
+ case kExternalFloatArray:
+ // Perform int-to-float conversion and store to memory.
+ StoreIntAsFloat(masm, a3, t0, t1, t2, t3, t4);
+ break;
+ case kExternalDoubleArray:
+ __ sll(t8, t0, 3);
+ __ addu(a3, a3, t8);
+ // a3: effective address of the double element
+ FloatingPointHelper::Destination destination;
+ if (CpuFeatures::IsSupported(FPU)) {
+ destination = FloatingPointHelper::kFPURegisters;
+ } else {
+ destination = FloatingPointHelper::kCoreRegisters;
+ }
+ FloatingPointHelper::ConvertIntToDouble(
+ masm, t1, destination,
+ f0, t2, t3, // These are: double_dst, dst1, dst2.
+ t0, f2); // These are: scratch2, single_scratch.
+ if (destination == FloatingPointHelper::kFPURegisters) {
+ CpuFeatures::Scope scope(FPU);
+ __ sdc1(f0, MemOperand(a3, 0));
+ } else {
+ __ sw(t2, MemOperand(a3, 0));
+ __ sw(t3, MemOperand(a3, Register::kSizeInBytes));
+ }
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ // Entry registers are intact, a0 holds the value which is the return value.
+ __ mov(v0, value);
+ __ Ret();
+
+ if (array_type != kExternalPixelArray) {
+ // a3: external array.
+ // t0: index (integer).
+ __ bind(&check_heap_number);
+ __ GetObjectType(value, t1, t2);
+ __ Branch(&slow, ne, t2, Operand(HEAP_NUMBER_TYPE));
+
+ __ lw(a3, FieldMemOperand(a3, ExternalArray::kExternalPointerOffset));
+
+ // a3: base pointer of external storage.
+ // t0: key (integer).
+
+ // The WebGL specification leaves the behavior of storing NaN and
+ // +/-Infinity into integer arrays basically undefined. For more
+ // reproducible behavior, convert these to zero.
+
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+
+ __ ldc1(f0, FieldMemOperand(a0, HeapNumber::kValueOffset));
+
+ if (array_type == kExternalFloatArray) {
+ __ cvt_s_d(f0, f0);
+ __ sll(t8, t0, 2);
+ __ addu(t8, a3, t8);
+ __ swc1(f0, MemOperand(t8, 0));
+ } else if (array_type == kExternalDoubleArray) {
+ __ sll(t8, t0, 3);
+ __ addu(t8, a3, t8);
+ __ sdc1(f0, MemOperand(t8, 0));
+ } else {
+ Label done;
+
+ // Need to perform float-to-int conversion.
+ // Test whether exponent equal to 0x7FF (infinity or NaN).
+
+ __ mfc1(t3, f1); // Move exponent word of double to t3 (as raw bits).
+ __ li(t1, Operand(0x7FF00000));
+ __ And(t3, t3, Operand(t1));
+ __ Branch(USE_DELAY_SLOT, &done, eq, t3, Operand(t1));
+ __ mov(t3, zero_reg); // In delay slot.
+
+ // Not infinity or NaN simply convert to int.
+ if (IsElementTypeSigned(array_type)) {
+ __ trunc_w_d(f0, f0);
+ __ mfc1(t3, f0);
+ } else {
+ __ Trunc_uw_d(f0, t3);
+ }
+
+ // t3: HeapNumber converted to integer
+ __ bind(&done);
+ switch (array_type) {
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ __ addu(t8, a3, t0);
+ __ sb(t3, MemOperand(t8, 0));
+ break;
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ __ sll(t8, t0, 1);
+ __ addu(t8, a3, t8);
+ __ sh(t3, MemOperand(t8, 0));
+ break;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ __ sll(t8, t0, 2);
+ __ addu(t8, a3, t8);
+ __ sw(t3, MemOperand(t8, 0));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ // Entry registers are intact, a0 holds the value
+ // which is the return value.
+ __ mov(v0, value);
+ __ Ret();
+ } else {
+ // FPU is not available, do manual conversions.
+
+ __ lw(t3, FieldMemOperand(value, HeapNumber::kExponentOffset));
+ __ lw(t4, FieldMemOperand(value, HeapNumber::kMantissaOffset));
+
+ if (array_type == kExternalFloatArray) {
+ Label done, nan_or_infinity_or_zero;
+ static const int kMantissaInHiWordShift =
+ kBinary32MantissaBits - HeapNumber::kMantissaBitsInTopWord;
+
+ static const int kMantissaInLoWordShift =
+ kBitsPerInt - kMantissaInHiWordShift;
+
+ // Test for all special exponent values: zeros, subnormal numbers, NaNs
+ // and infinities. All these should be converted to 0.
+ __ li(t5, HeapNumber::kExponentMask);
+ __ and_(t6, t3, t5);
+ __ Branch(&nan_or_infinity_or_zero, eq, t6, Operand(zero_reg));
+
+ __ xor_(t1, t6, t5);
+ __ li(t2, kBinary32ExponentMask);
+ __ movz(t6, t2, t1); // Only if t6 is equal to t5.
+ __ Branch(&nan_or_infinity_or_zero, eq, t6, Operand(t5));
+
+ // Rebias exponent.
+ __ srl(t6, t6, HeapNumber::kExponentShift);
+ __ Addu(t6,
+ t6,
+ Operand(kBinary32ExponentBias - HeapNumber::kExponentBias));
+
+ __ li(t1, Operand(kBinary32MaxExponent));
+ __ Slt(t1, t1, t6);
+ __ And(t2, t3, Operand(HeapNumber::kSignMask));
+ __ Or(t2, t2, Operand(kBinary32ExponentMask));
+ __ movn(t3, t2, t1); // Only if t6 is gt kBinary32MaxExponent.
+ __ Branch(&done, gt, t6, Operand(kBinary32MaxExponent));
+
+ __ Slt(t1, t6, Operand(kBinary32MinExponent));
+ __ And(t2, t3, Operand(HeapNumber::kSignMask));
+ __ movn(t3, t2, t1); // Only if t6 is lt kBinary32MinExponent.
+ __ Branch(&done, lt, t6, Operand(kBinary32MinExponent));
+
+ __ And(t7, t3, Operand(HeapNumber::kSignMask));
+ __ And(t3, t3, Operand(HeapNumber::kMantissaMask));
+ __ sll(t3, t3, kMantissaInHiWordShift);
+ __ or_(t7, t7, t3);
+ __ srl(t4, t4, kMantissaInLoWordShift);
+ __ or_(t7, t7, t4);
+ __ sll(t6, t6, kBinary32ExponentShift);
+ __ or_(t3, t7, t6);
+
+ __ bind(&done);
+ __ sll(t9, a1, 2);
+ __ addu(t9, a2, t9);
+ __ sw(t3, MemOperand(t9, 0));
+
+ // Entry registers are intact, a0 holds the value which is the return
+ // value.
+ __ mov(v0, value);
+ __ Ret();
+
+ __ bind(&nan_or_infinity_or_zero);
+ __ And(t7, t3, Operand(HeapNumber::kSignMask));
+ __ And(t3, t3, Operand(HeapNumber::kMantissaMask));
+ __ or_(t6, t6, t7);
+ __ sll(t3, t3, kMantissaInHiWordShift);
+ __ or_(t6, t6, t3);
+ __ srl(t4, t4, kMantissaInLoWordShift);
+ __ or_(t3, t6, t4);
+ __ Branch(&done);
+ } else if (array_type == kExternalDoubleArray) {
+ __ sll(t8, t0, 3);
+ __ addu(t8, a3, t8);
+ // t8: effective address of destination element.
+ __ sw(t4, MemOperand(t8, 0));
+ __ sw(t3, MemOperand(t8, Register::kSizeInBytes));
+ __ Ret();
+ } else {
+ bool is_signed_type = IsElementTypeSigned(array_type);
+ int meaningfull_bits = is_signed_type ? (kBitsPerInt - 1) : kBitsPerInt;
+ int32_t min_value = is_signed_type ? 0x80000000 : 0x00000000;
+
+ Label done, sign;
+
+ // Test for all special exponent values: zeros, subnormal numbers, NaNs
+ // and infinities. All these should be converted to 0.
+ __ li(t5, HeapNumber::kExponentMask);
+ __ and_(t6, t3, t5);
+ __ movz(t3, zero_reg, t6); // Only if t6 is equal to zero.
+ __ Branch(&done, eq, t6, Operand(zero_reg));
+
+ __ xor_(t2, t6, t5);
+ __ movz(t3, zero_reg, t2); // Only if t6 is equal to t5.
+ __ Branch(&done, eq, t6, Operand(t5));
+
+ // Unbias exponent.
+ __ srl(t6, t6, HeapNumber::kExponentShift);
+ __ Subu(t6, t6, Operand(HeapNumber::kExponentBias));
+ // If exponent is negative then result is 0.
+ __ slt(t2, t6, zero_reg);
+ __ movn(t3, zero_reg, t2); // Only if exponent is negative.
+ __ Branch(&done, lt, t6, Operand(zero_reg));
+
+ // If exponent is too big then result is minimal value.
+ __ slti(t1, t6, meaningfull_bits - 1);
+ __ li(t2, min_value);
+ __ movz(t3, t2, t1); // Only if t6 is ge meaningfull_bits - 1.
+ __ Branch(&done, ge, t6, Operand(meaningfull_bits - 1));
+
+ __ And(t5, t3, Operand(HeapNumber::kSignMask));
+ __ And(t3, t3, Operand(HeapNumber::kMantissaMask));
+ __ Or(t3, t3, Operand(1u << HeapNumber::kMantissaBitsInTopWord));
+
+ __ li(t9, HeapNumber::kMantissaBitsInTopWord);
+ __ subu(t6, t9, t6);
+ __ slt(t1, t6, zero_reg);
+ __ srlv(t2, t3, t6);
+ __ movz(t3, t2, t1); // Only if t6 is positive.
+ __ Branch(&sign, ge, t6, Operand(zero_reg));
+
+ __ subu(t6, zero_reg, t6);
+ __ sllv(t3, t3, t6);
+ __ li(t9, meaningfull_bits);
+ __ subu(t6, t9, t6);
+ __ srlv(t4, t4, t6);
+ __ or_(t3, t3, t4);
+
+ __ bind(&sign);
+ __ subu(t2, t3, zero_reg);
+ __ movz(t3, t2, t5); // Only if t5 is zero.
+
+ __ bind(&done);
+
+ // Result is in t3.
+ // This switch block should be exactly the same as above (FPU mode).
+ switch (array_type) {
+ case kExternalByteArray:
+ case kExternalUnsignedByteArray:
+ __ addu(t8, a3, t0);
+ __ sb(t3, MemOperand(t8, 0));
+ break;
+ case kExternalShortArray:
+ case kExternalUnsignedShortArray:
+ __ sll(t8, t0, 1);
+ __ addu(t8, a3, t8);
+ __ sh(t3, MemOperand(t8, 0));
+ break;
+ case kExternalIntArray:
+ case kExternalUnsignedIntArray:
+ __ sll(t8, t0, 2);
+ __ addu(t8, a3, t8);
+ __ sw(t3, MemOperand(t8, 0));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ }
+
+ // Slow case, key and receiver still in a0 and a1.
+ __ bind(&slow);
+ __ IncrementCounter(
+ masm->isolate()->counters()->keyed_load_external_array_slow(),
+ 1, a2, a3);
+ // Entry registers are intact.
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Handle<Code> slow_ic =
+ masm->isolate()->builtins()->KeyedStoreIC_Slow();
+ __ Jump(slow_ic, RelocInfo::CODE_TARGET);
+
+ // Miss case, call the runtime.
+ __ bind(&miss_force_generic);
+
+ // ---------- S t a t e --------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- ra : return address
+ // -- a0 : key
+ // -- a1 : receiver
+ // -----------------------------------
+ Label miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(a0, &miss_force_generic);
+
+ // Get the elements array.
+ __ lw(a2, FieldMemOperand(a1, JSObject::kElementsOffset));
+ __ AssertFastElements(a2);
+
+ // Check that the key is within bounds.
+ __ lw(a3, FieldMemOperand(a2, FixedArray::kLengthOffset));
+ __ Branch(&miss_force_generic, hs, a0, Operand(a3));
+
+ // Load the result and make sure it's not the hole.
+ __ Addu(a3, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ sll(t0, a0, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(t0, t0, a3);
+ __ lw(t0, MemOperand(t0));
+ __ LoadRoot(t1, Heap::kTheHoleValueRootIndex);
+ __ Branch(&miss_force_generic, eq, t0, Operand(t1));
+ __ mov(v0, t0);
+ __ Ret();
+
+ __ bind(&miss_force_generic);
+ Code* stub = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ __ Jump(Handle<Code>(stub), RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
+ bool is_js_array) {
+ // ----------- S t a t e -------------
+ // -- a0 : value
+ // -- a1 : key
+ // -- a2 : receiver
+ // -- ra : return address
+ // -- a3 : scratch
+ // -- a4 : scratch (elements)
+ // -----------------------------------
+ Label miss_force_generic;
+
+ Register value_reg = a0;
+ Register key_reg = a1;
+ Register receiver_reg = a2;
+ Register scratch = a3;
+ Register elements_reg = t0;
+ Register scratch2 = t1;
+ Register scratch3 = t2;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(a0, &miss_force_generic);
+
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ lw(elements_reg,
+ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
+ __ CheckMap(elements_reg,
+ scratch,
+ Heap::kFixedArrayMapRootIndex,
+ &miss_force_generic,
+ DONT_DO_SMI_CHECK);
+
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ lw(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
+ } else {
+ __ lw(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ }
+ // Compare smis.
+ __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch));
+
+ __ Addu(scratch,
+ elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ sll(scratch2, key_reg, kPointerSizeLog2 - kSmiTagSize);
+ __ Addu(scratch3, scratch2, scratch);
+ __ sw(value_reg, MemOperand(scratch3));
+ __ RecordWrite(scratch, Operand(scratch2), receiver_reg , elements_reg);
+
+ // value_reg (a0) is preserved.
+ // Done.
+ __ Ret();
+
+ __ bind(&miss_force_generic);
+ Handle<Code> ic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ Jump(ic, RelocInfo::CODE_TARGET);
}
diff --git a/src/mips/virtual-frame-mips.cc b/src/mips/virtual-frame-mips.cc
deleted file mode 100644
index 22fe9f06..00000000
--- a/src/mips/virtual-frame-mips.cc
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#if defined(V8_TARGET_ARCH_MIPS)
-
-#include "codegen-inl.h"
-#include "register-allocator-inl.h"
-#include "scopes.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-#define __ ACCESS_MASM(masm())
-
-void VirtualFrame::PopToA1A0() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::PopToA1() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::PopToA0() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::MergeTo(const VirtualFrame* expected,
- Condition cond,
- Register r1,
- const Operand& r2) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::MergeTo(VirtualFrame* expected,
- Condition cond,
- Register r1,
- const Operand& r2) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::MergeTOSTo(
- VirtualFrame::TopOfStack expected_top_of_stack_state,
- Condition cond,
- Register r1,
- const Operand& r2) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::Enter() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::Exit() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::AllocateStackSlots() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-
-void VirtualFrame::PushReceiverSlotAddress() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::PushTryHandler(HandlerType type) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallJSFunction(int arg_count) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallRuntime(const Runtime::Function* f, int arg_count) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void VirtualFrame::DebugBreak() {
- UNIMPLEMENTED_MIPS();
-}
-#endif
-
-
-void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flags,
- int arg_count) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallLoadIC(Handle<String> name, RelocInfo::Mode mode) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallKeyedLoadIC() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallKeyedStoreIC() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::CallCodeObject(Handle<Code> code,
- RelocInfo::Mode rmode,
- int dropped_args) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-// NO_TOS_REGISTERS, A0_TOS, A1_TOS, A1_A0_TOS, A0_A1_TOS.
-const bool VirtualFrame::kA0InUse[TOS_STATES] =
- { false, true, false, true, true };
-const bool VirtualFrame::kA1InUse[TOS_STATES] =
- { false, false, true, true, true };
-const int VirtualFrame::kVirtualElements[TOS_STATES] =
- { 0, 1, 1, 2, 2 };
-const Register VirtualFrame::kTopRegister[TOS_STATES] =
- { a0, a0, a1, a1, a0 };
-const Register VirtualFrame::kBottomRegister[TOS_STATES] =
- { a0, a0, a1, a0, a1 };
-const Register VirtualFrame::kAllocatedRegisters[
- VirtualFrame::kNumberOfAllocatedRegisters] = { a2, a3, t0, t1, t2 };
-// Popping is done by the transition implied by kStateAfterPop. Of course if
-// there were no stack slots allocated to registers then the physical SP must
-// be adjusted.
-const VirtualFrame::TopOfStack VirtualFrame::kStateAfterPop[TOS_STATES] =
- { NO_TOS_REGISTERS, NO_TOS_REGISTERS, NO_TOS_REGISTERS, A0_TOS, A1_TOS };
-// Pushing is done by the transition implied by kStateAfterPush. Of course if
-// the maximum number of registers was already allocated to the top of stack
-// slots then one register must be physically pushed onto the stack.
-const VirtualFrame::TopOfStack VirtualFrame::kStateAfterPush[TOS_STATES] =
- { A0_TOS, A1_A0_TOS, A0_A1_TOS, A0_A1_TOS, A1_A0_TOS };
-
-
-void VirtualFrame::Drop(int count) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::Pop() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitPop(Register reg) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::SpillAllButCopyTOSToA0() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::SpillAllButCopyTOSToA1() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::SpillAllButCopyTOSToA1A0() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-Register VirtualFrame::Peek() {
- UNIMPLEMENTED_MIPS();
- return no_reg;
-}
-
-
-Register VirtualFrame::Peek2() {
- UNIMPLEMENTED_MIPS();
- return no_reg;
-}
-
-
-void VirtualFrame::Dup() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::Dup2() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-Register VirtualFrame::PopToRegister(Register but_not_to_this_one) {
- UNIMPLEMENTED_MIPS();
- return no_reg;
-}
-
-
-void VirtualFrame::EnsureOneFreeTOSRegister() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitMultiPop(RegList regs) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::SetElementAt(Register reg, int this_far_down) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-Register VirtualFrame::GetTOSRegister() {
- UNIMPLEMENTED_MIPS();
- return no_reg;
-}
-
-
-void VirtualFrame::EmitPush(Operand operand, TypeInfo info) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitPush(MemOperand operand, TypeInfo info) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitPushRoot(Heap::RootListIndex index) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitMultiPush(RegList regs) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::EmitMultiPushReversed(RegList regs) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void VirtualFrame::SpillAll() {
- UNIMPLEMENTED_MIPS();
-}
-
-
-#undef __
-
-} } // namespace v8::internal
-
-#endif // V8_TARGET_ARCH_MIPS
diff --git a/src/mips/virtual-frame-mips.h b/src/mips/virtual-frame-mips.h
deleted file mode 100644
index cf30b093..00000000
--- a/src/mips/virtual-frame-mips.h
+++ /dev/null
@@ -1,530 +0,0 @@
-// Copyright 2010 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_MIPS_VIRTUAL_FRAME_MIPS_H_
-#define V8_MIPS_VIRTUAL_FRAME_MIPS_H_
-
-#include "register-allocator.h"
-
-namespace v8 {
-namespace internal {
-
-// This dummy class is only used to create invalid virtual frames.
-extern class InvalidVirtualFrameInitializer {}* kInvalidVirtualFrameInitializer;
-
-
-// -------------------------------------------------------------------------
-// Virtual frames
-//
-// The virtual frame is an abstraction of the physical stack frame. It
-// encapsulates the parameters, frame-allocated locals, and the expression
-// stack. It supports push/pop operations on the expression stack, as well
-// as random access to the expression stack elements, locals, and
-// parameters.
-
-class VirtualFrame : public ZoneObject {
- public:
- class RegisterAllocationScope;
- // A utility class to introduce a scope where the virtual frame is
- // expected to remain spilled. The constructor spills the code
- // generator's current frame, and keeps it spilled.
- class SpilledScope BASE_EMBEDDED {
- public:
- explicit SpilledScope(VirtualFrame* frame)
- : old_is_spilled_(
- Isolate::Current()->is_virtual_frame_in_spilled_scope()) {
- if (frame != NULL) {
- if (!old_is_spilled_) {
- frame->SpillAll();
- } else {
- frame->AssertIsSpilled();
- }
- }
- Isolate::Current()->set_is_virtual_frame_in_spilled_scope(true);
- }
- ~SpilledScope() {
- Isolate::Current()->set_is_virtual_frame_in_spilled_scope(
- old_is_spilled_);
- }
- static bool is_spilled() {
- return Isolate::Current()->is_virtual_frame_in_spilled_scope();
- }
-
- private:
- int old_is_spilled_;
-
- SpilledScope() {}
-
- friend class RegisterAllocationScope;
- };
-
- class RegisterAllocationScope BASE_EMBEDDED {
- public:
- // A utility class to introduce a scope where the virtual frame
- // is not spilled, ie. where register allocation occurs. Eventually
- // when RegisterAllocationScope is ubiquitous it can be removed
- // along with the (by then unused) SpilledScope class.
- inline explicit RegisterAllocationScope(CodeGenerator* cgen);
- inline ~RegisterAllocationScope();
-
- private:
- CodeGenerator* cgen_;
- bool old_is_spilled_;
-
- RegisterAllocationScope() {}
- };
-
- // An illegal index into the virtual frame.
- static const int kIllegalIndex = -1;
-
- // Construct an initial virtual frame on entry to a JS function.
- inline VirtualFrame();
-
- // Construct an invalid virtual frame, used by JumpTargets.
- explicit inline VirtualFrame(InvalidVirtualFrameInitializer* dummy);
-
- // Construct a virtual frame as a clone of an existing one.
- explicit inline VirtualFrame(VirtualFrame* original);
-
- inline CodeGenerator* cgen() const;
- inline MacroAssembler* masm();
-
- // The number of elements on the virtual frame.
- int element_count() const { return element_count_; }
-
- // The height of the virtual expression stack.
- inline int height() const;
-
- bool is_used(int num) {
- switch (num) {
- case 0: { // a0.
- return kA0InUse[top_of_stack_state_];
- }
- case 1: { // a1.
- return kA1InUse[top_of_stack_state_];
- }
- case 2:
- case 3:
- case 4:
- case 5:
- case 6: { // a2 to a3, t0 to t2.
- ASSERT(num - kFirstAllocatedRegister < kNumberOfAllocatedRegisters);
- ASSERT(num >= kFirstAllocatedRegister);
- if ((register_allocation_map_ &
- (1 << (num - kFirstAllocatedRegister))) == 0) {
- return false;
- } else {
- return true;
- }
- }
- default: {
- ASSERT(num < kFirstAllocatedRegister ||
- num >= kFirstAllocatedRegister + kNumberOfAllocatedRegisters);
- return false;
- }
- }
- }
-
- // Add extra in-memory elements to the top of the frame to match an actual
- // frame (eg, the frame after an exception handler is pushed). No code is
- // emitted.
- void Adjust(int count);
-
- // Forget elements from the top of the frame to match an actual frame (eg,
- // the frame after a runtime call). No code is emitted except to bring the
- // frame to a spilled state.
- void Forget(int count);
-
-
- // Spill all values from the frame to memory.
- void SpillAll();
-
- void AssertIsSpilled() const {
- ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS);
- ASSERT(register_allocation_map_ == 0);
- }
-
- void AssertIsNotSpilled() {
- ASSERT(!SpilledScope::is_spilled());
- }
-
- // Spill all occurrences of a specific register from the frame.
- void Spill(Register reg) {
- UNIMPLEMENTED();
- }
-
- // Spill all occurrences of an arbitrary register if possible. Return the
- // register spilled or no_reg if it was not possible to free any register
- // (ie, they all have frame-external references). Unimplemented.
- Register SpillAnyRegister();
-
- // Make this virtual frame have a state identical to an expected virtual
- // frame. As a side effect, code may be emitted to make this frame match
- // the expected one.
- void MergeTo(const VirtualFrame* expected,
- Condition cond = al,
- Register r1 = no_reg,
- const Operand& r2 = Operand(no_reg));
-
- void MergeTo(VirtualFrame* expected,
- Condition cond = al,
- Register r1 = no_reg,
- const Operand& r2 = Operand(no_reg));
-
- // Checks whether this frame can be branched to by the other frame.
- bool IsCompatibleWith(const VirtualFrame* other) const {
- return (tos_known_smi_map_ & (~other->tos_known_smi_map_)) == 0;
- }
-
- inline void ForgetTypeInfo() {
- tos_known_smi_map_ = 0;
- }
-
- // Detach a frame from its code generator, perhaps temporarily. This
- // tells the register allocator that it is free to use frame-internal
- // registers. Used when the code generator's frame is switched from this
- // one to NULL by an unconditional jump.
- void DetachFromCodeGenerator() {
- }
-
- // (Re)attach a frame to its code generator. This informs the register
- // allocator that the frame-internal register references are active again.
- // Used when a code generator's frame is switched from NULL to this one by
- // binding a label.
- void AttachToCodeGenerator() {
- }
-
- // Emit code for the physical JS entry and exit frame sequences. After
- // calling Enter, the virtual frame is ready for use; and after calling
- // Exit it should not be used. Note that Enter does not allocate space in
- // the physical frame for storing frame-allocated locals.
- void Enter();
- void Exit();
-
- // Prepare for returning from the frame by elements in the virtual frame.
- // This avoids generating unnecessary merge code when jumping to the shared
- // return site. No spill code emitted. Value to return should be in v0.
- inline void PrepareForReturn();
-
- // Number of local variables after when we use a loop for allocating.
- static const int kLocalVarBound = 5;
-
- // Allocate and initialize the frame-allocated locals.
- void AllocateStackSlots();
-
- // The current top of the expression stack as an assembly operand.
- MemOperand Top() {
- AssertIsSpilled();
- return MemOperand(sp, 0);
- }
-
- // An element of the expression stack as an assembly operand.
- MemOperand ElementAt(int index) {
- int adjusted_index = index - kVirtualElements[top_of_stack_state_];
- ASSERT(adjusted_index >= 0);
- return MemOperand(sp, adjusted_index * kPointerSize);
- }
-
- bool KnownSmiAt(int index) {
- if (index >= kTOSKnownSmiMapSize) return false;
- return (tos_known_smi_map_ & (1 << index)) != 0;
- }
- // A frame-allocated local as an assembly operand.
- inline MemOperand LocalAt(int index);
-
- // Push the address of the receiver slot on the frame.
- void PushReceiverSlotAddress();
-
- // The function frame slot.
- MemOperand Function() { return MemOperand(fp, kFunctionOffset); }
-
- // The context frame slot.
- MemOperand Context() { return MemOperand(fp, kContextOffset); }
-
- // A parameter as an assembly operand.
- inline MemOperand ParameterAt(int index);
-
- // The receiver frame slot.
- inline MemOperand Receiver();
-
- // Push a try-catch or try-finally handler on top of the virtual frame.
- void PushTryHandler(HandlerType type);
-
- // Call stub given the number of arguments it expects on (and
- // removes from) the stack.
- inline void CallStub(CodeStub* stub, int arg_count);
-
- // Call JS function from top of the stack with arguments
- // taken from the stack.
- void CallJSFunction(int arg_count);
-
- // Call runtime given the number of arguments expected on (and
- // removed from) the stack.
- void CallRuntime(const Runtime::Function* f, int arg_count);
- void CallRuntime(Runtime::FunctionId id, int arg_count);
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- void DebugBreak();
-#endif
-
- // Invoke builtin given the number of arguments it expects on (and
- // removes from) the stack.
- void InvokeBuiltin(Builtins::JavaScript id,
- InvokeJSFlags flag,
- int arg_count);
-
- // Call load IC. Receiver is on the stack and is consumed. Result is returned
- // in v0.
- void CallLoadIC(Handle<String> name, RelocInfo::Mode mode);
-
- // Call store IC. If the load is contextual, value is found on top of the
- // frame. If not, value and receiver are on the frame. Both are consumed.
- // Result is returned in v0.
- void CallStoreIC(Handle<String> name, bool is_contextual);
-
- // Call keyed load IC. Key and receiver are on the stack. Both are consumed.
- // Result is returned in v0.
- void CallKeyedLoadIC();
-
- // Call keyed store IC. Value, key and receiver are on the stack. All three
- // are consumed. Result is returned in v0 (and a0).
- void CallKeyedStoreIC();
-
- // Call into an IC stub given the number of arguments it removes
- // from the stack. Register arguments to the IC stub are implicit,
- // and depend on the type of IC stub.
- void CallCodeObject(Handle<Code> ic,
- RelocInfo::Mode rmode,
- int dropped_args);
-
- // Drop a number of elements from the top of the expression stack. May
- // emit code to affect the physical frame. Does not clobber any registers
- // excepting possibly the stack pointer.
- void Drop(int count);
-
- // Drop one element.
- void Drop() { Drop(1); }
-
- // Pop an element from the top of the expression stack. Discards
- // the result.
- void Pop();
-
- // Pop an element from the top of the expression stack. The register
- // will be one normally used for the top of stack register allocation
- // so you can't hold on to it if you push on the stack.
- Register PopToRegister(Register but_not_to_this_one = no_reg);
-
- // Look at the top of the stack. The register returned is aliased and
- // must be copied to a scratch register before modification.
- Register Peek();
-
- // Look at the value beneath the top of the stack. The register returned is
- // aliased and must be copied to a scratch register before modification.
- Register Peek2();
-
- // Duplicate the top of stack.
- void Dup();
-
- // Duplicate the two elements on top of stack.
- void Dup2();
-
- // Flushes all registers, but it puts a copy of the top-of-stack in a0.
- void SpillAllButCopyTOSToA0();
-
- // Flushes all registers, but it puts a copy of the top-of-stack in a1.
- void SpillAllButCopyTOSToA1();
-
- // Flushes all registers, but it puts a copy of the top-of-stack in a1
- // and the next value on the stack in a0.
- void SpillAllButCopyTOSToA1A0();
-
- // Pop and save an element from the top of the expression stack and
- // emit a corresponding pop instruction.
- void EmitPop(Register reg);
- // Same but for multiple registers
- void EmitMultiPop(RegList regs);
- void EmitMultiPopReversed(RegList regs);
-
-
- // Takes the top two elements and puts them in a0 (top element) and a1
- // (second element).
- void PopToA1A0();
-
- // Takes the top element and puts it in a1.
- void PopToA1();
-
- // Takes the top element and puts it in a0.
- void PopToA0();
-
- // Push an element on top of the expression stack and emit a
- // corresponding push instruction.
- void EmitPush(Register reg, TypeInfo type_info = TypeInfo::Unknown());
- void EmitPush(Operand operand, TypeInfo type_info = TypeInfo::Unknown());
- void EmitPush(MemOperand operand, TypeInfo type_info = TypeInfo::Unknown());
- void EmitPushRoot(Heap::RootListIndex index);
-
- // Overwrite the nth thing on the stack. If the nth position is in a
- // register then this turns into a Move, otherwise an sw. Afterwards
- // you can still use the register even if it is a register that can be
- // used for TOS (a0 or a1).
- void SetElementAt(Register reg, int this_far_down);
-
- // Get a register which is free and which must be immediately used to
- // push on the top of the stack.
- Register GetTOSRegister();
-
- // Same but for multiple registers.
- void EmitMultiPush(RegList regs);
- void EmitMultiPushReversed(RegList regs);
-
- static Register scratch0() { return t4; }
- static Register scratch1() { return t5; }
- static Register scratch2() { return t6; }
-
- private:
- static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
- static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
- static const int kContextOffset = StandardFrameConstants::kContextOffset;
-
- static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize;
- static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots.
-
- // 5 states for the top of stack, which can be in memory or in a0 and a1.
- enum TopOfStack { NO_TOS_REGISTERS, A0_TOS, A1_TOS, A1_A0_TOS, A0_A1_TOS,
- TOS_STATES};
- static const int kMaxTOSRegisters = 2;
-
- static const bool kA0InUse[TOS_STATES];
- static const bool kA1InUse[TOS_STATES];
- static const int kVirtualElements[TOS_STATES];
- static const TopOfStack kStateAfterPop[TOS_STATES];
- static const TopOfStack kStateAfterPush[TOS_STATES];
- static const Register kTopRegister[TOS_STATES];
- static const Register kBottomRegister[TOS_STATES];
-
- // We allocate up to 5 locals in registers.
- static const int kNumberOfAllocatedRegisters = 5;
- // r2 to r6 are allocated to locals.
- static const int kFirstAllocatedRegister = 2;
-
- static const Register kAllocatedRegisters[kNumberOfAllocatedRegisters];
-
- static Register AllocatedRegister(int r) {
- ASSERT(r >= 0 && r < kNumberOfAllocatedRegisters);
- return kAllocatedRegisters[r];
- }
-
- // The number of elements on the stack frame.
- int element_count_;
- TopOfStack top_of_stack_state_:3;
- int register_allocation_map_:kNumberOfAllocatedRegisters;
- static const int kTOSKnownSmiMapSize = 4;
- unsigned tos_known_smi_map_:kTOSKnownSmiMapSize;
-
- // The index of the element that is at the processor's stack pointer
- // (the sp register). For now since everything is in memory it is given
- // by the number of elements on the not-very-virtual stack frame.
- int stack_pointer() { return element_count_ - 1; }
-
- // The number of frame-allocated locals and parameters respectively.
- inline int parameter_count() const;
- inline int local_count() const;
-
- // The index of the element that is at the processor's frame pointer
- // (the fp register). The parameters, receiver, function, and context
- // are below the frame pointer.
- inline int frame_pointer() const;
-
- // The index of the first parameter. The receiver lies below the first
- // parameter.
- int param0_index() { return 1; }
-
- // The index of the context slot in the frame. It is immediately
- // below the frame pointer.
- inline int context_index();
-
- // The index of the function slot in the frame. It is below the frame
- // pointer and context slot.
- inline int function_index();
-
- // The index of the first local. Between the frame pointer and the
- // locals lies the return address.
- inline int local0_index() const;
-
- // The index of the base of the expression stack.
- inline int expression_base_index() const;
-
- // Convert a frame index into a frame pointer relative offset into the
- // actual stack.
- inline int fp_relative(int index);
-
- // Spill all elements in registers. Spill the top spilled_args elements
- // on the frame. Sync all other frame elements.
- // Then drop dropped_args elements from the virtual frame, to match
- // the effect of an upcoming call that will drop them from the stack.
- void PrepareForCall(int spilled_args, int dropped_args);
-
- // If all top-of-stack registers are in use then the lowest one is pushed
- // onto the physical stack and made free.
- void EnsureOneFreeTOSRegister();
-
- // Emit instructions to get the top of stack state from where we are to where
- // we want to be.
- void MergeTOSTo(TopOfStack expected_state,
- Condition cond = al,
- Register r1 = no_reg,
- const Operand& r2 = Operand(no_reg));
-
- inline bool Equals(const VirtualFrame* other);
-
- inline void LowerHeight(int count) {
- element_count_ -= count;
- if (count >= kTOSKnownSmiMapSize) {
- tos_known_smi_map_ = 0;
- } else {
- tos_known_smi_map_ >>= count;
- }
- }
-
- inline void RaiseHeight(int count, unsigned known_smi_map = 0) {
- ASSERT(known_smi_map < (1u << count));
- element_count_ += count;
- if (count >= kTOSKnownSmiMapSize) {
- tos_known_smi_map_ = known_smi_map;
- } else {
- tos_known_smi_map_ = ((tos_known_smi_map_ << count) | known_smi_map);
- }
- }
- friend class JumpTarget;
-};
-
-
-} } // namespace v8::internal
-
-#endif // V8_MIPS_VIRTUAL_FRAME_MIPS_H_
-
diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js
index 99e98197..3a035351 100644
--- a/src/mirror-debugger.js
+++ b/src/mirror-debugger.js
@@ -174,11 +174,12 @@ PropertyType.Normal = 0;
PropertyType.Field = 1;
PropertyType.ConstantFunction = 2;
PropertyType.Callbacks = 3;
-PropertyType.Interceptor = 4;
-PropertyType.MapTransition = 5;
-PropertyType.ExternalArrayTransition = 6;
-PropertyType.ConstantTransition = 7;
-PropertyType.NullDescriptor = 8;
+PropertyType.Handler = 4;
+PropertyType.Interceptor = 5;
+PropertyType.MapTransition = 6;
+PropertyType.ExternalArrayTransition = 7;
+PropertyType.ConstantTransition = 8;
+PropertyType.NullDescriptor = 9;
// Different attributes for a property.
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index 6ecbc8c5..dd491166 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -25,6 +25,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+#include <bzlib.h>
+#endif
#include <signal.h>
#include <string>
#include <map>
@@ -95,11 +98,53 @@ typedef std::map<std::string, int*>::iterator CounterMapIterator;
static CounterMap counter_table_;
-class CppByteSink : public i::SnapshotByteSink {
+class Compressor {
+ public:
+ virtual ~Compressor() {}
+ virtual bool Compress(i::Vector<char> input) = 0;
+ virtual i::Vector<char>* output() = 0;
+};
+
+
+class PartialSnapshotSink : public i::SnapshotByteSink {
+ public:
+ PartialSnapshotSink() : data_(), raw_size_(-1) { }
+ virtual ~PartialSnapshotSink() { data_.Free(); }
+ virtual void Put(int byte, const char* description) {
+ data_.Add(byte);
+ }
+ virtual int Position() { return data_.length(); }
+ void Print(FILE* fp) {
+ int length = Position();
+ for (int j = 0; j < length; j++) {
+ if ((j & 0x1f) == 0x1f) {
+ fprintf(fp, "\n");
+ }
+ if (j != 0) {
+ fprintf(fp, ",");
+ }
+ fprintf(fp, "%d", at(j));
+ }
+ }
+ char at(int i) { return data_[i]; }
+ bool Compress(Compressor* compressor) {
+ ASSERT_EQ(-1, raw_size_);
+ raw_size_ = data_.length();
+ if (!compressor->Compress(data_.ToVector())) return false;
+ data_.Clear();
+ data_.AddAll(*compressor->output());
+ return true;
+ }
+ int raw_size() { return raw_size_; }
+ private:
+ i::List<char> data_;
+ int raw_size_;
+};
+
+
+class CppByteSink : public PartialSnapshotSink {
public:
- explicit CppByteSink(const char* snapshot_file)
- : bytes_written_(0),
- partial_sink_(this) {
+ explicit CppByteSink(const char* snapshot_file) {
fp_ = i::OS::FOpen(snapshot_file, "wb");
if (fp_ == NULL) {
i::PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
@@ -114,7 +159,18 @@ class CppByteSink : public i::SnapshotByteSink {
}
virtual ~CppByteSink() {
- fprintf(fp_, "const int Snapshot::size_ = %d;\n\n", bytes_written_);
+ fprintf(fp_, "const int Snapshot::size_ = %d;\n", Position());
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ fprintf(fp_, "const byte* Snapshot::raw_data_ = NULL;\n");
+ fprintf(fp_,
+ "const int Snapshot::raw_size_ = %d;\n\n",
+ raw_size());
+#else
+ fprintf(fp_,
+ "const byte* Snapshot::raw_data_ = Snapshot::data_;\n");
+ fprintf(fp_,
+ "const int Snapshot::raw_size_ = Snapshot::size_;\n\n");
+#endif
fprintf(fp_, "} } // namespace v8::internal\n");
fclose(fp_);
}
@@ -127,7 +183,6 @@ class CppByteSink : public i::SnapshotByteSink {
int map_space_used,
int cell_space_used,
int large_space_used) {
- fprintf(fp_, "};\n\n");
fprintf(fp_, "const int Snapshot::new_space_used_ = %d;\n", new_space_used);
fprintf(fp_,
"const int Snapshot::pointer_space_used_ = %d;\n",
@@ -151,57 +206,66 @@ class CppByteSink : public i::SnapshotByteSink {
int length = partial_sink_.Position();
fprintf(fp_, "};\n\n");
fprintf(fp_, "const int Snapshot::context_size_ = %d;\n", length);
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ fprintf(fp_,
+ "const int Snapshot::context_raw_size_ = %d;\n",
+ partial_sink_.raw_size());
+#else
+ fprintf(fp_,
+ "const int Snapshot::context_raw_size_ = "
+ "Snapshot::context_size_;\n");
+#endif
fprintf(fp_, "const byte Snapshot::context_data_[] = {\n");
- for (int j = 0; j < length; j++) {
- if ((j & 0x1f) == 0x1f) {
- fprintf(fp_, "\n");
- }
- char byte = partial_sink_.at(j);
- if (j != 0) {
- fprintf(fp_, ",");
- }
- fprintf(fp_, "%d", byte);
- }
+ partial_sink_.Print(fp_);
+ fprintf(fp_, "};\n\n");
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ fprintf(fp_, "const byte* Snapshot::context_raw_data_ = NULL;\n");
+#else
+ fprintf(fp_, "const byte* Snapshot::context_raw_data_ ="
+ " Snapshot::context_data_;\n");
+#endif
}
- virtual void Put(int byte, const char* description) {
- if (bytes_written_ != 0) {
- fprintf(fp_, ",");
- }
- fprintf(fp_, "%d", byte);
- bytes_written_++;
- if ((bytes_written_ & 0x1f) == 0) {
- fprintf(fp_, "\n");
- }
+ void WriteSnapshot() {
+ Print(fp_);
}
- virtual int Position() {
- return bytes_written_;
- }
+ PartialSnapshotSink* partial_sink() { return &partial_sink_; }
+
+ private:
+ FILE* fp_;
+ PartialSnapshotSink partial_sink_;
+};
- i::SnapshotByteSink* partial_sink() { return &partial_sink_; }
- class PartialSnapshotSink : public i::SnapshotByteSink {
- public:
- explicit PartialSnapshotSink(CppByteSink* parent)
- : parent_(parent),
- data_() { }
- virtual ~PartialSnapshotSink() { data_.Free(); }
- virtual void Put(int byte, const char* description) {
- data_.Add(byte);
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+class BZip2Compressor : public Compressor {
+ public:
+ BZip2Compressor() : output_(NULL) {}
+ virtual ~BZip2Compressor() {
+ delete output_;
+ }
+ virtual bool Compress(i::Vector<char> input) {
+ delete output_;
+ output_ = new i::ScopedVector<char>((input.length() * 101) / 100 + 1000);
+ unsigned int output_length_ = output_->length();
+ int result = BZ2_bzBuffToBuffCompress(output_->start(), &output_length_,
+ input.start(), input.length(),
+ 9, 1, 0);
+ if (result == BZ_OK) {
+ output_->Truncate(output_length_);
+ return true;
+ } else {
+ fprintf(stderr, "bzlib error code: %d\n", result);
+ return false;
}
- virtual int Position() { return data_.length(); }
- char at(int i) { return data_[i]; }
- private:
- CppByteSink* parent_;
- i::List<char> data_;
- };
+ }
+ virtual i::Vector<char>* output() { return output_; }
private:
- FILE* fp_;
- int bytes_written_;
- PartialSnapshotSink partial_sink_;
+ i::ScopedVector<char>* output_;
};
+#endif
int main(int argc, char** argv) {
@@ -242,6 +306,14 @@ int main(int argc, char** argv) {
ser.SerializeWeakReferences();
+#ifdef COMPRESS_STARTUP_DATA_BZ2
+ BZip2Compressor compressor;
+ if (!sink.Compress(&compressor))
+ return 1;
+ if (!sink.partial_sink()->Compress(&compressor))
+ return 1;
+#endif
+ sink.WriteSnapshot();
sink.WritePartialSnapshot();
sink.WriteSpaceUsed(
diff --git a/src/natives.h b/src/natives.h
index 1df94b08..92f0d90b 100644
--- a/src/natives.h
+++ b/src/natives.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -36,7 +36,7 @@ typedef bool (*NativeSourceCallback)(Vector<const char> name,
int index);
enum NativeType {
- CORE, D8, I18N
+ CORE, EXPERIMENTAL, D8, I18N
};
template <NativeType type>
@@ -57,6 +57,7 @@ class NativesCollection {
};
typedef NativesCollection<CORE> Natives;
+typedef NativesCollection<EXPERIMENTAL> ExperimentalNatives;
} } // namespace v8::internal
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index dd606dcd..76c520e8 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -116,6 +116,9 @@ void HeapObject::HeapObjectVerify() {
case EXTERNAL_FLOAT_ARRAY_TYPE:
ExternalFloatArray::cast(this)->ExternalFloatArrayVerify();
break;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ ExternalDoubleArray::cast(this)->ExternalDoubleArrayVerify();
+ break;
case CODE_TYPE:
Code::cast(this)->CodeVerify();
break;
@@ -152,8 +155,11 @@ void HeapObject::HeapObjectVerify() {
break;
case FILLER_TYPE:
break;
- case PROXY_TYPE:
- Proxy::cast(this)->ProxyVerify();
+ case JS_PROXY_TYPE:
+ JSProxy::cast(this)->JSProxyVerify();
+ break;
+ case FOREIGN_TYPE:
+ Foreign::cast(this)->ForeignVerify();
break;
case SHARED_FUNCTION_INFO_TYPE:
SharedFunctionInfo::cast(this)->SharedFunctionInfoVerify();
@@ -232,6 +238,11 @@ void ExternalFloatArray::ExternalFloatArrayVerify() {
}
+void ExternalDoubleArray::ExternalDoubleArrayVerify() {
+ ASSERT(IsExternalDoubleArray());
+}
+
+
void JSObject::JSObjectVerify() {
VerifyHeapPointer(properties());
VerifyHeapPointer(elements());
@@ -261,7 +272,7 @@ void Map::MapVerify() {
void Map::SharedMapVerify() {
MapVerify();
ASSERT(is_shared());
- ASSERT_EQ(GetHeap()->empty_descriptor_array(), instance_descriptors());
+ ASSERT(instance_descriptors()->IsEmpty());
ASSERT_EQ(0, pre_allocated_property_fields());
ASSERT_EQ(0, unused_property_fields());
ASSERT_EQ(StaticVisitorBase::GetVisitorId(instance_type(), instance_size()),
@@ -433,14 +444,22 @@ void JSRegExp::JSRegExpVerify() {
FixedArray* arr = FixedArray::cast(data());
Object* ascii_data = arr->get(JSRegExp::kIrregexpASCIICodeIndex);
- // TheHole : Not compiled yet.
+ // Smi : Not compiled yet (-1) or code prepared for flushing.
// JSObject: Compilation error.
// Code/ByteArray: Compiled code.
- ASSERT(ascii_data->IsTheHole() || ascii_data->IsJSObject() ||
- (is_native ? ascii_data->IsCode() : ascii_data->IsByteArray()));
+ ASSERT(ascii_data->IsSmi() ||
+ (is_native ? ascii_data->IsCode() : ascii_data->IsByteArray()));
Object* uc16_data = arr->get(JSRegExp::kIrregexpUC16CodeIndex);
- ASSERT(uc16_data->IsTheHole() || uc16_data->IsJSObject() ||
- (is_native ? uc16_data->IsCode() : uc16_data->IsByteArray()));
+ ASSERT(uc16_data->IsSmi() ||
+ (is_native ? uc16_data->IsCode() : uc16_data->IsByteArray()));
+
+ Object* ascii_saved = arr->get(JSRegExp::kIrregexpASCIICodeSavedIndex);
+ ASSERT(ascii_saved->IsSmi() || ascii_saved->IsString() ||
+ ascii_saved->IsCode());
+ Object* uc16_saved = arr->get(JSRegExp::kIrregexpUC16CodeSavedIndex);
+ ASSERT(uc16_saved->IsSmi() || uc16_saved->IsString() ||
+ uc16_saved->IsCode());
+
ASSERT(arr->get(JSRegExp::kIrregexpCaptureCountIndex)->IsSmi());
ASSERT(arr->get(JSRegExp::kIrregexpMaxRegisterCountIndex)->IsSmi());
break;
@@ -453,8 +472,13 @@ void JSRegExp::JSRegExpVerify() {
}
-void Proxy::ProxyVerify() {
- ASSERT(IsProxy());
+void JSProxy::JSProxyVerify() {
+ ASSERT(IsJSProxy());
+ VerifyPointer(handler());
+}
+
+void Foreign::ForeignVerify() {
+ ASSERT(IsForeign());
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 65aec5dc..aa5fc866 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -406,6 +406,13 @@ bool Object::IsExternalFloatArray() {
}
+bool Object::IsExternalDoubleArray() {
+ return Object::IsHeapObject() &&
+ HeapObject::cast(this)->map()->instance_type() ==
+ EXTERNAL_DOUBLE_ARRAY_TYPE;
+}
+
+
bool MaybeObject::IsFailure() {
return HAS_FAILURE_TAG(this);
}
@@ -577,9 +584,15 @@ bool Object::IsStringWrapper() {
}
-bool Object::IsProxy() {
+bool Object::IsJSProxy() {
return Object::IsHeapObject()
- && HeapObject::cast(this)->map()->instance_type() == PROXY_TYPE;
+ && HeapObject::cast(this)->map()->instance_type() == JS_PROXY_TYPE;
+}
+
+
+bool Object::IsForeign() {
+ return Object::IsHeapObject()
+ && HeapObject::cast(this)->map()->instance_type() == FOREIGN_TYPE;
}
@@ -1661,9 +1674,21 @@ Object** FixedArray::data_start() {
bool DescriptorArray::IsEmpty() {
- ASSERT(this->length() > kFirstIndex ||
+ ASSERT(this->IsSmi() ||
+ this->length() > kFirstIndex ||
this == HEAP->empty_descriptor_array());
- return length() <= kFirstIndex;
+ return this->IsSmi() || length() <= kFirstIndex;
+}
+
+
+int DescriptorArray::bit_field3_storage() {
+ Object* storage = READ_FIELD(this, kBitField3StorageOffset);
+ return Smi::cast(storage)->value();
+}
+
+void DescriptorArray::set_bit_field3_storage(int value) {
+ ASSERT(!IsEmpty());
+ WRITE_FIELD(this, kBitField3StorageOffset, Smi::FromInt(value));
}
@@ -1744,8 +1769,8 @@ Object* DescriptorArray::GetCallbacksObject(int descriptor_number) {
AccessorDescriptor* DescriptorArray::GetCallbacks(int descriptor_number) {
ASSERT(GetType(descriptor_number) == CALLBACKS);
- Proxy* p = Proxy::cast(GetCallbacksObject(descriptor_number));
- return reinterpret_cast<AccessorDescriptor*>(p->proxy());
+ Foreign* p = Foreign::cast(GetCallbacksObject(descriptor_number));
+ return reinterpret_cast<AccessorDescriptor*>(p->address());
}
@@ -1891,7 +1916,8 @@ CAST_ACCESSOR(JSBuiltinsObject)
CAST_ACCESSOR(Code)
CAST_ACCESSOR(JSArray)
CAST_ACCESSOR(JSRegExp)
-CAST_ACCESSOR(Proxy)
+CAST_ACCESSOR(JSProxy)
+CAST_ACCESSOR(Foreign)
CAST_ACCESSOR(ByteArray)
CAST_ACCESSOR(ExternalArray)
CAST_ACCESSOR(ExternalByteArray)
@@ -1901,6 +1927,7 @@ CAST_ACCESSOR(ExternalUnsignedShortArray)
CAST_ACCESSOR(ExternalIntArray)
CAST_ACCESSOR(ExternalUnsignedIntArray)
CAST_ACCESSOR(ExternalFloatArray)
+CAST_ACCESSOR(ExternalDoubleArray)
CAST_ACCESSOR(ExternalPixelArray)
CAST_ACCESSOR(Struct)
@@ -2315,6 +2342,20 @@ void ExternalFloatArray::set(int index, float value) {
}
+double ExternalDoubleArray::get(int index) {
+ ASSERT((index >= 0) && (index < this->length()));
+ double* ptr = static_cast<double*>(external_pointer());
+ return ptr[index];
+}
+
+
+void ExternalDoubleArray::set(int index, double value) {
+ ASSERT((index >= 0) && (index < this->length()));
+ double* ptr = static_cast<double*>(external_pointer());
+ ptr[index] = value;
+}
+
+
int Map::visitor_id() {
return READ_BYTE_FIELD(this, kVisitorIdOffset);
}
@@ -2499,14 +2540,14 @@ bool Map::attached_to_shared_function_info() {
void Map::set_is_shared(bool value) {
if (value) {
- set_bit_field2(bit_field2() | (1 << kIsShared));
+ set_bit_field3(bit_field3() | (1 << kIsShared));
} else {
- set_bit_field2(bit_field2() & ~(1 << kIsShared));
+ set_bit_field3(bit_field3() & ~(1 << kIsShared));
}
}
bool Map::is_shared() {
- return ((1 << kIsShared) & bit_field2()) != 0;
+ return ((1 << kIsShared) & bit_field3()) != 0;
}
@@ -2566,7 +2607,6 @@ Code::ExtraICState Code::extra_ic_state() {
PropertyType Code::type() {
- ASSERT(ic_state() == MONOMORPHIC);
return ExtractTypeFromFlags(flags());
}
@@ -2579,7 +2619,8 @@ int Code::arguments_count() {
int Code::major_key() {
ASSERT(kind() == STUB ||
- kind() == TYPE_RECORDING_BINARY_OP_IC ||
+ kind() == UNARY_OP_IC ||
+ kind() == BINARY_OP_IC ||
kind() == COMPARE_IC);
return READ_BYTE_FIELD(this, kStubMajorKeyOffset);
}
@@ -2587,7 +2628,8 @@ int Code::major_key() {
void Code::set_major_key(int major) {
ASSERT(kind() == STUB ||
- kind() == TYPE_RECORDING_BINARY_OP_IC ||
+ kind() == UNARY_OP_IC ||
+ kind() == BINARY_OP_IC ||
kind() == COMPARE_IC);
ASSERT(0 <= major && major < 256);
WRITE_BYTE_FIELD(this, kStubMajorKeyOffset, major);
@@ -2683,38 +2725,50 @@ void Code::set_check_type(CheckType value) {
ExternalArrayType Code::external_array_type() {
- ASSERT(is_external_array_load_stub() || is_external_array_store_stub());
+ ASSERT(is_keyed_load_stub() || is_keyed_store_stub());
byte type = READ_BYTE_FIELD(this, kExternalArrayTypeOffset);
return static_cast<ExternalArrayType>(type);
}
void Code::set_external_array_type(ExternalArrayType value) {
- ASSERT(is_external_array_load_stub() || is_external_array_store_stub());
+ ASSERT(is_keyed_load_stub() || is_keyed_store_stub());
WRITE_BYTE_FIELD(this, kExternalArrayTypeOffset, value);
}
-byte Code::type_recording_binary_op_type() {
- ASSERT(is_type_recording_binary_op_stub());
+byte Code::unary_op_type() {
+ ASSERT(is_unary_op_stub());
+ return READ_BYTE_FIELD(this, kUnaryOpTypeOffset);
+}
+
+
+void Code::set_unary_op_type(byte value) {
+ ASSERT(is_unary_op_stub());
+ WRITE_BYTE_FIELD(this, kUnaryOpTypeOffset, value);
+}
+
+
+byte Code::binary_op_type() {
+ ASSERT(is_binary_op_stub());
return READ_BYTE_FIELD(this, kBinaryOpTypeOffset);
}
-void Code::set_type_recording_binary_op_type(byte value) {
- ASSERT(is_type_recording_binary_op_stub());
+void Code::set_binary_op_type(byte value) {
+ ASSERT(is_binary_op_stub());
WRITE_BYTE_FIELD(this, kBinaryOpTypeOffset, value);
}
-byte Code::type_recording_binary_op_result_type() {
- ASSERT(is_type_recording_binary_op_stub());
+byte Code::binary_op_result_type() {
+ ASSERT(is_binary_op_stub());
return READ_BYTE_FIELD(this, kBinaryOpReturnTypeOffset);
}
-void Code::set_type_recording_binary_op_result_type(byte value) {
- ASSERT(is_type_recording_binary_op_stub());
+void Code::set_binary_op_result_type(byte value) {
+ ASSERT(is_binary_op_stub());
WRITE_BYTE_FIELD(this, kBinaryOpReturnTypeOffset, value);
}
@@ -2744,11 +2798,10 @@ Code::Flags Code::ComputeFlags(Kind kind,
PropertyType type,
int argc,
InlineCacheHolderFlag holder) {
- // Extra IC state is only allowed for monomorphic call IC stubs
- // or for store IC stubs.
+ // Extra IC state is only allowed for call IC stubs or for store IC
+ // stubs.
ASSERT(extra_ic_state == kNoExtraICState ||
- (kind == CALL_IC && (ic_state == MONOMORPHIC ||
- ic_state == MONOMORPHIC_PROTOTYPE_FAILURE)) ||
+ (kind == CALL_IC) ||
(kind == STORE_IC) ||
(kind == KEYED_STORE_IC));
// Compute the bit mask.
@@ -2926,8 +2979,82 @@ MaybeObject* Map::GetSlowElementsMap() {
}
-ACCESSORS(Map, instance_descriptors, DescriptorArray,
- kInstanceDescriptorsOffset)
+DescriptorArray* Map::instance_descriptors() {
+ Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
+ if (object->IsSmi()) {
+ return HEAP->empty_descriptor_array();
+ } else {
+ return DescriptorArray::cast(object);
+ }
+}
+
+
+void Map::init_instance_descriptors() {
+ WRITE_FIELD(this, kInstanceDescriptorsOrBitField3Offset, Smi::FromInt(0));
+}
+
+
+void Map::clear_instance_descriptors() {
+ Object* object = READ_FIELD(this,
+ kInstanceDescriptorsOrBitField3Offset);
+ if (!object->IsSmi()) {
+ WRITE_FIELD(
+ this,
+ kInstanceDescriptorsOrBitField3Offset,
+ Smi::FromInt(DescriptorArray::cast(object)->bit_field3_storage()));
+ }
+}
+
+
+void Map::set_instance_descriptors(DescriptorArray* value,
+ WriteBarrierMode mode) {
+ Object* object = READ_FIELD(this,
+ kInstanceDescriptorsOrBitField3Offset);
+ if (value == isolate()->heap()->empty_descriptor_array()) {
+ clear_instance_descriptors();
+ return;
+ } else {
+ if (object->IsSmi()) {
+ value->set_bit_field3_storage(Smi::cast(object)->value());
+ } else {
+ value->set_bit_field3_storage(
+ DescriptorArray::cast(object)->bit_field3_storage());
+ }
+ }
+ ASSERT(!is_shared());
+ WRITE_FIELD(this, kInstanceDescriptorsOrBitField3Offset, value);
+ CONDITIONAL_WRITE_BARRIER(GetHeap(),
+ this,
+ kInstanceDescriptorsOrBitField3Offset,
+ mode);
+}
+
+
+int Map::bit_field3() {
+ Object* object = READ_FIELD(this,
+ kInstanceDescriptorsOrBitField3Offset);
+ if (object->IsSmi()) {
+ return Smi::cast(object)->value();
+ } else {
+ return DescriptorArray::cast(object)->bit_field3_storage();
+ }
+}
+
+
+void Map::set_bit_field3(int value) {
+ ASSERT(Smi::IsValid(value));
+ Object* object = READ_FIELD(this,
+ kInstanceDescriptorsOrBitField3Offset);
+ if (object->IsSmi()) {
+ WRITE_FIELD(this,
+ kInstanceDescriptorsOrBitField3Offset,
+ Smi::FromInt(value));
+ } else {
+ DescriptorArray::cast(object)->set_bit_field3_storage(value);
+ }
+}
+
+
ACCESSORS(Map, code_cache, Object, kCodeCacheOffset)
ACCESSORS(Map, prototype_transitions, FixedArray, kPrototypeTransitionsOffset)
ACCESSORS(Map, constructor, Object, kConstructorOffset)
@@ -3003,7 +3130,7 @@ ACCESSORS(Script, line_offset, Smi, kLineOffsetOffset)
ACCESSORS(Script, column_offset, Smi, kColumnOffsetOffset)
ACCESSORS(Script, data, Object, kDataOffset)
ACCESSORS(Script, context_data, Object, kContextOffset)
-ACCESSORS(Script, wrapper, Proxy, kWrapperOffset)
+ACCESSORS(Script, wrapper, Foreign, kWrapperOffset)
ACCESSORS(Script, type, Smi, kTypeOffset)
ACCESSORS(Script, compilation_type, Smi, kCompilationTypeOffset)
ACCESSORS(Script, line_ends, Object, kLineEndsOffset)
@@ -3182,6 +3309,18 @@ void SharedFunctionInfo::set_strict_mode(bool value) {
}
+bool SharedFunctionInfo::es5_native() {
+ return BooleanBit::get(compiler_hints(), kES5Native);
+}
+
+
+void SharedFunctionInfo::set_es5_native(bool value) {
+ set_compiler_hints(BooleanBit::set(compiler_hints(),
+ kES5Native,
+ value));
+}
+
+
ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset)
ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset)
@@ -3473,13 +3612,16 @@ void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id,
}
-Address Proxy::proxy() {
- return AddressFrom<Address>(READ_INTPTR_FIELD(this, kProxyOffset));
+ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
+
+
+Address Foreign::address() {
+ return AddressFrom<Address>(READ_INTPTR_FIELD(this, kAddressOffset));
}
-void Proxy::set_proxy(Address value) {
- WRITE_INTPTR_FIELD(this, kProxyOffset, OffsetFrom(value));
+void Foreign::set_address(Address value) {
+ WRITE_INTPTR_FIELD(this, kAddressOffset, OffsetFrom(value));
}
@@ -3512,6 +3654,8 @@ JSMessageObject* JSMessageObject::cast(Object* obj) {
INT_ACCESSORS(Code, instruction_size, kInstructionSizeOffset)
ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset)
ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
+ACCESSORS(Code, next_code_flushing_candidate,
+ Object, kNextCodeFlushingCandidateOffset)
byte* Code::instruction_start() {
@@ -3575,6 +3719,12 @@ JSRegExp::Type JSRegExp::TypeTag() {
}
+JSRegExp::Type JSRegExp::TypeTagUnchecked() {
+ Smi* smi = Smi::cast(DataAtUnchecked(kTagIndex));
+ return static_cast<JSRegExp::Type>(smi->value());
+}
+
+
int JSRegExp::CaptureCount() {
switch (TypeTag()) {
case ATOM:
@@ -3610,6 +3760,13 @@ Object* JSRegExp::DataAt(int index) {
}
+Object* JSRegExp::DataAtUnchecked(int index) {
+ FixedArray* fa = reinterpret_cast<FixedArray*>(data());
+ int offset = FixedArray::kHeaderSize + index * kPointerSize;
+ return READ_FIELD(fa, offset);
+}
+
+
void JSRegExp::SetDataAt(int index, Object* value) {
ASSERT(TypeTag() != NOT_COMPILED);
ASSERT(index >= kDataIndex); // Only implementation data can be set this way.
@@ -3617,6 +3774,17 @@ void JSRegExp::SetDataAt(int index, Object* value) {
}
+void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) {
+ ASSERT(index >= kDataIndex); // Only implementation data can be set this way.
+ FixedArray* fa = reinterpret_cast<FixedArray*>(data());
+ if (value->IsSmi()) {
+ fa->set_unchecked(index, Smi::cast(value));
+ } else {
+ fa->set_unchecked(heap, index, value, SKIP_WRITE_BARRIER);
+ }
+}
+
+
JSObject::ElementsKind JSObject::GetElementsKind() {
if (map()->has_fast_elements()) {
ASSERT(elements()->map() == GetHeap()->fixed_array_map() ||
@@ -3645,14 +3813,18 @@ JSObject::ElementsKind JSObject::GetElementsKind() {
return EXTERNAL_INT_ELEMENTS;
case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
return EXTERNAL_UNSIGNED_INT_ELEMENTS;
+ case EXTERNAL_FLOAT_ARRAY_TYPE:
+ return EXTERNAL_FLOAT_ELEMENTS;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ return EXTERNAL_DOUBLE_ELEMENTS;
case EXTERNAL_PIXEL_ARRAY_TYPE:
return EXTERNAL_PIXEL_ELEMENTS;
default:
break;
}
}
- ASSERT(array->map()->instance_type() == EXTERNAL_FLOAT_ARRAY_TYPE);
- return EXTERNAL_FLOAT_ELEMENTS;
+ UNREACHABLE();
+ return DICTIONARY_ELEMENTS;
}
@@ -3693,6 +3865,8 @@ EXTERNAL_ELEMENTS_CHECK(UnsignedInt,
EXTERNAL_UNSIGNED_INT_ARRAY_TYPE)
EXTERNAL_ELEMENTS_CHECK(Float,
EXTERNAL_FLOAT_ARRAY_TYPE)
+EXTERNAL_ELEMENTS_CHECK(Double,
+ EXTERNAL_DOUBLE_ARRAY_TYPE)
EXTERNAL_ELEMENTS_CHECK(Pixel, EXTERNAL_PIXEL_ARRAY_TYPE)
@@ -4084,16 +4258,16 @@ int JSObject::BodyDescriptor::SizeOf(Map* map, HeapObject* object) {
}
-void Proxy::ProxyIterateBody(ObjectVisitor* v) {
+void Foreign::ForeignIterateBody(ObjectVisitor* v) {
v->VisitExternalReference(
- reinterpret_cast<Address *>(FIELD_ADDR(this, kProxyOffset)));
+ reinterpret_cast<Address *>(FIELD_ADDR(this, kAddressOffset)));
}
template<typename StaticVisitor>
-void Proxy::ProxyIterateBody() {
+void Foreign::ForeignIterateBody() {
StaticVisitor::VisitExternalReference(
- reinterpret_cast<Address *>(FIELD_ADDR(this, kProxyOffset)));
+ reinterpret_cast<Address *>(FIELD_ADDR(this, kAddressOffset)));
}
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index b7e2fdd8..60028c06 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -114,6 +114,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
case EXTERNAL_FLOAT_ARRAY_TYPE:
ExternalFloatArray::cast(this)->ExternalFloatArrayPrint(out);
break;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ ExternalDoubleArray::cast(this)->ExternalDoubleArrayPrint(out);
+ break;
case FILLER_TYPE:
PrintF(out, "filler");
break;
@@ -145,8 +148,11 @@ void HeapObject::HeapObjectPrint(FILE* out) {
case CODE_TYPE:
Code::cast(this)->CodePrint(out);
break;
- case PROXY_TYPE:
- Proxy::cast(this)->ProxyPrint(out);
+ case JS_PROXY_TYPE:
+ JSProxy::cast(this)->JSProxyPrint(out);
+ break;
+ case FOREIGN_TYPE:
+ Foreign::cast(this)->ForeignPrint(out);
break;
case SHARED_FUNCTION_INFO_TYPE:
SharedFunctionInfo::cast(this)->SharedFunctionInfoPrint(out);
@@ -217,6 +223,11 @@ void ExternalFloatArray::ExternalFloatArrayPrint(FILE* out) {
}
+void ExternalDoubleArray::ExternalDoubleArrayPrint(FILE* out) {
+ PrintF(out, "external double array");
+}
+
+
void JSObject::PrintProperties(FILE* out) {
if (HasFastProperties()) {
DescriptorArray* descs = map()->instance_descriptors();
@@ -330,6 +341,13 @@ void JSObject::PrintElements(FILE* out) {
}
break;
}
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ ExternalDoubleArray* p = ExternalDoubleArray::cast(elements());
+ for (int i = 0; i < p->length(); i++) {
+ PrintF(out, " %d: %f\n", i, p->get(i));
+ }
+ break;
+ }
case DICTIONARY_ELEMENTS:
elements()->Print(out);
break;
@@ -383,6 +401,7 @@ static const char* TypeToString(InstanceType type) {
case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
return "EXTERNAL_UNSIGNED_INT_ARRAY";
case EXTERNAL_FLOAT_ARRAY_TYPE: return "EXTERNAL_FLOAT_ARRAY";
+ case EXTERNAL_DOUBLE_ARRAY_TYPE: return "EXTERNAL_DOUBLE_ARRAY";
case FILLER_TYPE: return "FILLER";
case JS_OBJECT_TYPE: return "JS_OBJECT";
case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT";
@@ -392,19 +411,19 @@ static const char* TypeToString(InstanceType type) {
case JS_FUNCTION_TYPE: return "JS_FUNCTION";
case CODE_TYPE: return "CODE";
case JS_ARRAY_TYPE: return "JS_ARRAY";
+ case JS_PROXY_TYPE: return "JS_PROXY";
case JS_REGEXP_TYPE: return "JS_REGEXP";
case JS_VALUE_TYPE: return "JS_VALUE";
case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT";
case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY";
- case PROXY_TYPE: return "PROXY";
- case LAST_STRING_TYPE: return "LAST_STRING_TYPE";
+ case FOREIGN_TYPE: return "FOREIGN";
case JS_MESSAGE_OBJECT_TYPE: return "JS_MESSAGE_OBJECT_TYPE";
#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME;
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
+ default: return "UNKNOWN";
}
- return "UNKNOWN";
}
@@ -515,6 +534,15 @@ void String::StringPrint(FILE* out) {
}
+void JSProxy::JSProxyPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSProxy");
+ PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - handler = ");
+ handler()->Print(out);
+ PrintF(out, "\n");
+}
+
+
void JSFunction::JSFunctionPrint(FILE* out) {
HeapObject::PrintHeader(out, "Function");
PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
@@ -607,8 +635,8 @@ void Code::CodePrint(FILE* out) {
}
-void Proxy::ProxyPrint(FILE* out) {
- PrintF(out, "proxy to %p", proxy());
+void Foreign::ForeignPrint(FILE* out) {
+ PrintF(out, "foreign address : %p", address());
}
diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc
index 5a23658c..685e8add 100644
--- a/src/objects-visiting.cc
+++ b/src/objects-visiting.cc
@@ -85,13 +85,21 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_GLOBAL_PROPERTY_CELL_TYPE:
return kVisitPropertyCell;
+ case JS_REGEXP_TYPE:
+ return kVisitJSRegExp;
+
case SHARED_FUNCTION_INFO_TYPE:
return kVisitSharedFunctionInfo;
- case PROXY_TYPE:
+ case JS_PROXY_TYPE:
+ return GetVisitorIdForSize(kVisitStruct,
+ kVisitStructGeneric,
+ JSProxy::kSize);
+
+ case FOREIGN_TYPE:
return GetVisitorIdForSize(kVisitDataObject,
kVisitDataObjectGeneric,
- Proxy::kSize);
+ Foreign::kSize);
case FILLER_TYPE:
return kVisitDataObjectGeneric;
@@ -100,7 +108,6 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
- case JS_REGEXP_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE:
@@ -121,6 +128,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case EXTERNAL_INT_ARRAY_TYPE:
case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
case EXTERNAL_FLOAT_ARRAY_TYPE:
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
return GetVisitorIdForSize(kVisitDataObject,
kVisitDataObjectGeneric,
instance_size);
diff --git a/src/objects-visiting.h b/src/objects-visiting.h
index da955da6..dcbf2f8e 100644
--- a/src/objects-visiting.h
+++ b/src/objects-visiting.h
@@ -28,6 +28,8 @@
#ifndef V8_OBJECTS_VISITING_H_
#define V8_OBJECTS_VISITING_H_
+#include "allocation.h"
+
// This file provides base classes and auxiliary methods for defining
// static object visitors used during GC.
// Visiting HeapObject body with a normal ObjectVisitor requires performing
@@ -102,6 +104,7 @@ class StaticVisitorBase : public AllStatic {
kVisitPropertyCell,
kVisitSharedFunctionInfo,
kVisitJSFunction,
+ kVisitJSRegExp,
kVisitorIdCount,
kMinObjectSizeInWords = 2
@@ -295,6 +298,8 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
SharedFunctionInfo::BodyDescriptor,
int>::Visit);
+ table_.Register(kVisitJSRegExp, &VisitJSRegExp);
+
table_.Register(kVisitSeqAsciiString, &VisitSeqAsciiString);
table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString);
@@ -332,6 +337,10 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
SeqAsciiStringSize(map->instance_type());
}
+ static inline int VisitJSRegExp(Map* map, HeapObject* object) {
+ return JSObjectVisitor::Visit(map, object);
+ }
+
static inline int VisitSeqTwoByteString(Map* map, HeapObject* object) {
return SeqTwoByteString::cast(object)->
SeqTwoByteStringSize(map->instance_type());
diff --git a/src/objects.cc b/src/objects.cc
index fac83f19..b407c016 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -41,7 +41,6 @@
#include "macro-assembler.h"
#include "safepoint-table.h"
#include "scanner-base.h"
-#include "scopeinfo.h"
#include "string-stream.h"
#include "utils.h"
#include "vm-state-inl.h"
@@ -135,24 +134,22 @@ Object* Object::ToBoolean() {
void Object::Lookup(String* name, LookupResult* result) {
Object* holder = NULL;
if (IsSmi()) {
- Heap* heap = Isolate::Current()->heap();
- Context* global_context = heap->isolate()->context()->global_context();
+ Context* global_context = Isolate::Current()->context()->global_context();
holder = global_context->number_function()->instance_prototype();
} else {
HeapObject* heap_object = HeapObject::cast(this);
if (heap_object->IsJSObject()) {
return JSObject::cast(this)->Lookup(name, result);
}
- Heap* heap = heap_object->GetHeap();
+ Context* global_context = Isolate::Current()->context()->global_context();
if (heap_object->IsString()) {
- Context* global_context = heap->isolate()->context()->global_context();
holder = global_context->string_function()->instance_prototype();
} else if (heap_object->IsHeapNumber()) {
- Context* global_context = heap->isolate()->context()->global_context();
holder = global_context->number_function()->instance_prototype();
} else if (heap_object->IsBoolean()) {
- Context* global_context = heap->isolate()->context()->global_context();
holder = global_context->boolean_function()->instance_prototype();
+ } else if (heap_object->IsJSProxy()) {
+ return result->HandlerResult();
}
}
ASSERT(holder != NULL); // Cannot handle null or undefined.
@@ -177,11 +174,12 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
Object* holder) {
Isolate* isolate = name->GetIsolate();
// To accommodate both the old and the new api we switch on the
- // data structure used to store the callbacks. Eventually proxy
+ // data structure used to store the callbacks. Eventually foreign
// callbacks should be phased out.
- if (structure->IsProxy()) {
+ if (structure->IsForeign()) {
AccessorDescriptor* callback =
- reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
+ reinterpret_cast<AccessorDescriptor*>(
+ Foreign::cast(structure)->address());
MaybeObject* value = (callback->getter)(receiver, callback->data);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
return value;
@@ -228,6 +226,34 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
}
+MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw,
+ String* name_raw,
+ Object* handler_raw) {
+ Isolate* isolate = name_raw->GetIsolate();
+ HandleScope scope;
+ Handle<Object> receiver(receiver_raw);
+ Handle<Object> name(name_raw);
+ Handle<Object> handler(handler_raw);
+
+ // Extract trap function.
+ LookupResult lookup;
+ Handle<Object> trap(v8::internal::GetProperty(handler, "get", &lookup));
+ if (!lookup.IsFound()) {
+ // Get the derived `get' property.
+ trap = isolate->derived_get_trap();
+ }
+
+ // Call trap function.
+ Object** args[] = { receiver.location(), name.location() };
+ bool has_exception;
+ Handle<Object> result =
+ Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
+ if (has_exception) return Failure::Exception();
+
+ return *result;
+}
+
+
MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver,
JSFunction* getter) {
HandleScope scope;
@@ -495,30 +521,34 @@ MaybeObject* Object::GetProperty(Object* receiver,
Heap* heap = name->GetHeap();
// Traverse the prototype chain from the current object (this) to
- // the holder and check for access rights. This avoid traversing the
+ // the holder and check for access rights. This avoids traversing the
// objects more than once in case of interceptors, because the
// holder will always be the interceptor holder and the search may
// only continue with a current object just after the interceptor
// holder in the prototype chain.
- Object* last = result->IsProperty() ? result->holder() : heap->null_value();
- for (Object* current = this; true; current = current->GetPrototype()) {
- if (current->IsAccessCheckNeeded()) {
- // Check if we're allowed to read from the current object. Note
- // that even though we may not actually end up loading the named
- // property from the current object, we still check that we have
- // access to it.
- JSObject* checked = JSObject::cast(current);
- if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
- return checked->GetPropertyWithFailedAccessCheck(receiver,
- result,
- name,
- attributes);
- }
- }
- // Stop traversing the chain once we reach the last object in the
- // chain; either the holder of the result or null in case of an
- // absent property.
- if (current == last) break;
+ // Proxy handlers do not use the proxy's prototype, so we can skip this.
+ if (!result->IsHandler()) {
+ Object* last = result->IsProperty() ? result->holder() : heap->null_value();
+ ASSERT(this != this->GetPrototype());
+ for (Object* current = this; true; current = current->GetPrototype()) {
+ if (current->IsAccessCheckNeeded()) {
+ // Check if we're allowed to read from the current object. Note
+ // that even though we may not actually end up loading the named
+ // property from the current object, we still check that we have
+ // access to it.
+ JSObject* checked = JSObject::cast(current);
+ if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
+ return checked->GetPropertyWithFailedAccessCheck(receiver,
+ result,
+ name,
+ attributes);
+ }
+ }
+ // Stop traversing the chain once we reach the last object in the
+ // chain; either the holder of the result or null in case of an
+ // absent property.
+ if (current == last) break;
+ }
}
if (!result->IsProperty()) {
@@ -544,14 +574,22 @@ MaybeObject* Object::GetProperty(Object* receiver,
result->GetCallbackObject(),
name,
holder);
+ case HANDLER: {
+ JSProxy* proxy = JSProxy::cast(this);
+ return GetPropertyWithHandler(receiver, name, proxy->handler());
+ }
case INTERCEPTOR: {
JSObject* recvr = JSObject::cast(receiver);
return holder->GetPropertyWithInterceptor(recvr, name, attributes);
}
- default:
- UNREACHABLE();
- return NULL;
+ case MAP_TRANSITION:
+ case EXTERNAL_ARRAY_TRANSITION:
+ case CONSTANT_TRANSITION:
+ case NULL_DESCRIPTOR:
+ break;
}
+ UNREACHABLE();
+ return NULL;
}
@@ -576,6 +614,8 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
holder = global_context->number_function()->instance_prototype();
} else if (heap_object->IsBoolean()) {
holder = global_context->boolean_function()->instance_prototype();
+ } else if (heap_object->IsJSProxy()) {
+ return heap->undefined_value(); // For now...
} else {
// Undefined and null have no indexed properties.
ASSERT(heap_object->IsUndefined() || heap_object->IsNull());
@@ -596,9 +636,10 @@ Object* Object::GetPrototype() {
HeapObject* heap_object = HeapObject::cast(this);
- // The object is either a number, a string, a boolean, or a real JS object.
- if (heap_object->IsJSObject()) {
- return JSObject::cast(this)->map()->prototype();
+ // The object is either a number, a string, a boolean,
+ // a real JS object, or a Harmony proxy.
+ if (heap_object->IsJSObject() || heap_object->IsJSProxy()) {
+ return heap_object->map()->prototype();
}
Heap* heap = heap_object->GetHeap();
Context* context = heap->isolate()->context()->global_context();
@@ -1045,6 +1086,10 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
accumulator->Add("<ExternalFloatArray[%u]>",
ExternalFloatArray::cast(this)->length());
break;
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
+ accumulator->Add("<ExternalDoubleArray[%u]>",
+ ExternalDoubleArray::cast(this)->length());
+ break;
case SHARED_FUNCTION_INFO_TYPE:
accumulator->Add("<SharedFunctionInfo>");
break;
@@ -1082,8 +1127,8 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
accumulator->Put('>');
break;
- case PROXY_TYPE:
- accumulator->Add("<Proxy>");
+ case FOREIGN_TYPE:
+ accumulator->Add("<Foreign>");
break;
case JS_GLOBAL_PROPERTY_CELL_TYPE:
accumulator->Add("Cell for ");
@@ -1151,8 +1196,11 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
case ODDBALL_TYPE:
Oddball::BodyDescriptor::IterateBody(this, v);
break;
- case PROXY_TYPE:
- reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
+ case JS_PROXY_TYPE:
+ JSProxy::BodyDescriptor::IterateBody(this, v);
+ break;
+ case FOREIGN_TYPE:
+ reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
break;
case MAP_TYPE:
Map::BodyDescriptor::IterateBody(this, v);
@@ -1174,6 +1222,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
case EXTERNAL_INT_ARRAY_TYPE:
case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
case EXTERNAL_FLOAT_ARRAY_TYPE:
+ case EXTERNAL_DOUBLE_ARRAY_TYPE:
break;
case SHARED_FUNCTION_INFO_TYPE:
SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
@@ -1732,11 +1781,12 @@ MaybeObject* JSObject::SetPropertyWithCallback(Object* structure,
Handle<Object> value_handle(value, isolate);
// To accommodate both the old and the new api we switch on the
- // data structure used to store the callbacks. Eventually proxy
+ // data structure used to store the callbacks. Eventually foreign
// callbacks should be phased out.
- if (structure->IsProxy()) {
+ if (structure->IsForeign()) {
AccessorDescriptor* callback =
- reinterpret_cast<AccessorDescriptor*>(Proxy::cast(structure)->proxy());
+ reinterpret_cast<AccessorDescriptor*>(
+ Foreign::cast(structure)->address());
MaybeObject* obj = (callback->setter)(this, value, callback->data);
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
if (obj->IsFailure()) return obj;
@@ -2481,7 +2531,8 @@ bool NormalizedMapCache::CheckHit(Map* slow,
fast->inobject_properties()) &&
slow->instance_type() == fast->instance_type() &&
slow->bit_field() == fast->bit_field() &&
- (slow->bit_field2() & ~(1<<Map::kIsShared)) == fast->bit_field2();
+ slow->bit_field2() == fast->bit_field2() &&
+ (slow->bit_field3() & ~(1<<Map::kIsShared)) == fast->bit_field3();
}
@@ -2572,6 +2623,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
case INTERCEPTOR:
+ case EXTERNAL_ARRAY_TRANSITION:
break;
default:
UNREACHABLE();
@@ -2602,7 +2654,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode,
instance_size_delta);
set_map(new_map);
- new_map->set_instance_descriptors(current_heap->empty_descriptor_array());
+ new_map->clear_instance_descriptors();
set_properties(dictionary);
@@ -2844,6 +2896,7 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
// Pixel and external array elements cannot be deleted. Just
// silently ignore here.
break;
@@ -2858,8 +2911,9 @@ MaybeObject* JSObject::DeleteElement(uint32_t index, DeleteMode mode) {
// exception. dictionary->DeleteProperty will return false_value()
// if a non-configurable property is being deleted.
HandleScope scope;
+ Handle<Object> self(this);
Handle<Object> i = isolate->factory()->NewNumberFromUint(index);
- Handle<Object> args[2] = { i, Handle<Object>(this) };
+ Handle<Object> args[2] = { i, self };
return isolate->Throw(*isolate->factory()->NewTypeError(
"strict_delete_property", HandleVector(args, 2)));
}
@@ -2963,6 +3017,7 @@ bool JSObject::ReferencesObject(Object* obj) {
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
// Raw pixels and external arrays do not reference other
// objects.
break;
@@ -3050,6 +3105,17 @@ MaybeObject* JSObject::PreventExtensions() {
return JSObject::cast(proto)->PreventExtensions();
}
+ // It's not possible to seal objects with external array elements
+ if (HasExternalArrayElements()) {
+ HandleScope scope(isolate);
+ Handle<Object> object(this);
+ Handle<Object> error =
+ isolate->factory()->NewTypeError(
+ "cant_prevent_ext_external_array_elements",
+ HandleVector(&object, 1));
+ return isolate->Throw(*error);
+ }
+
// If there are fast elements we normalize.
if (HasFastElements()) {
Object* ok;
@@ -3234,6 +3300,7 @@ MaybeObject* JSObject::DefineGetterSetter(String* name,
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
// Ignore getters and setters on pixel and external array
// elements.
return heap->undefined_value();
@@ -3460,6 +3527,7 @@ MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS:
// Ignore getters and setters on pixel and external array
// elements.
return isolate->heap()->undefined_value();
@@ -3587,8 +3655,7 @@ MaybeObject* Map::CopyDropDescriptors() {
// pointing to the same transition which is bad because the garbage
// collector relies on being able to reverse pointers from transitions
// to maps. If properties need to be retained use CopyDropTransitions.
- Map::cast(result)->set_instance_descriptors(
- heap->empty_descriptor_array());
+ Map::cast(result)->clear_instance_descriptors();
// Please note instance_type and instance_size are set when allocated.
Map::cast(result)->set_inobject_properties(inobject_properties());
Map::cast(result)->set_unused_property_fields(unused_property_fields());
@@ -3610,6 +3677,7 @@ MaybeObject* Map::CopyDropDescriptors() {
}
Map::cast(result)->set_bit_field(bit_field());
Map::cast(result)->set_bit_field2(bit_field2());
+ Map::cast(result)->set_bit_field3(bit_field3());
Map::cast(result)->set_is_shared(false);
Map::cast(result)->ClearCodeCache(heap);
return result;
@@ -3638,6 +3706,7 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode,
Map::cast(result)->set_bit_field(bit_field());
Map::cast(result)->set_bit_field2(bit_field2());
+ Map::cast(result)->set_bit_field3(bit_field3());
Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP);
@@ -3716,7 +3785,7 @@ void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
Object** map_or_index_field = NULL;
while (current != meta_map) {
DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
- *RawField(current, Map::kInstanceDescriptorsOffset));
+ *RawField(current, Map::kInstanceDescriptorsOrBitField3Offset));
if (!d->IsEmpty()) {
FixedArray* contents = reinterpret_cast<FixedArray*>(
d->get(DescriptorArray::kContentArrayIndex));
@@ -3782,8 +3851,6 @@ void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
MaybeObject* CodeCache::Update(String* name, Code* code) {
- ASSERT(code->ic_state() == MONOMORPHIC);
-
// The number of monomorphic stubs for normal load/store/call IC's can grow to
// a large number and therefore they need to go into a hash table. They are
// used to load global properties from cells.
@@ -4218,6 +4285,7 @@ MaybeObject* DescriptorArray::Allocate(int number_of_descriptors) {
heap->AllocateFixedArray(number_of_descriptors << 1);
if (!maybe_array->ToObject(&array)) return maybe_array;
}
+ result->set(kBitField3StorageIndex, Smi::FromInt(0));
result->set(kContentArrayIndex, array);
result->set(kEnumerationIndexIndex,
Smi::FromInt(PropertyDetails::kInitialIndex));
@@ -4976,8 +5044,7 @@ int Relocatable::ArchiveSpacePerThread() {
// Archive statics that are thread local.
-char* Relocatable::ArchiveState(char* to) {
- Isolate* isolate = Isolate::Current();
+char* Relocatable::ArchiveState(Isolate* isolate, char* to) {
*reinterpret_cast<Relocatable**>(to) = isolate->relocatable_top();
isolate->set_relocatable_top(NULL);
return to + ArchiveSpacePerThread();
@@ -4985,8 +5052,7 @@ char* Relocatable::ArchiveState(char* to) {
// Restore statics that are thread local.
-char* Relocatable::RestoreState(char* from) {
- Isolate* isolate = Isolate::Current();
+char* Relocatable::RestoreState(Isolate* isolate, char* from) {
isolate->set_relocatable_top(*reinterpret_cast<Relocatable**>(from));
return from + ArchiveSpacePerThread();
}
@@ -5481,8 +5547,16 @@ bool String::IsEqualTo(Vector<const char> str) {
bool String::IsAsciiEqualTo(Vector<const char> str) {
int slen = length();
if (str.length() != slen) return false;
- for (int i = 0; i < slen; i++) {
- if (Get(i) != static_cast<uint16_t>(str[i])) return false;
+ if (this->IsSeqAsciiString()) {
+ SeqAsciiString* seq = SeqAsciiString::cast(this);
+ char* ch = seq->GetChars();
+ for (int i = 0; i < slen; i++, ch++) {
+ if (*ch != str[i]) return false;
+ }
+ } else {
+ for (int i = 0; i < slen; i++) {
+ if (Get(i) != static_cast<uint16_t>(str[i])) return false;
+ }
}
return true;
}
@@ -5676,8 +5750,8 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
// Live DescriptorArray objects will be marked, so we must use
// low-level accessors to get and modify their data.
DescriptorArray* d = reinterpret_cast<DescriptorArray*>(
- *RawField(this, Map::kInstanceDescriptorsOffset));
- if (d == heap->raw_unchecked_empty_descriptor_array()) return;
+ *RawField(this, Map::kInstanceDescriptorsOrBitField3Offset));
+ if (d->IsEmpty()) return;
Smi* NullDescriptorDetails =
PropertyDetails(NONE, NULL_DESCRIPTOR).AsSmi();
FixedArray* contents = reinterpret_cast<FixedArray*>(
@@ -6071,6 +6145,29 @@ void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
}
+void SharedFunctionInfo::DisableOptimization(JSFunction* function) {
+ // Disable optimization for the shared function info and mark the
+ // code as non-optimizable. The marker on the shared function info
+ // is there because we flush non-optimized code thereby loosing the
+ // non-optimizable information for the code. When the code is
+ // regenerated and set on the shared function info it is marked as
+ // non-optimizable if optimization is disabled for the shared
+ // function info.
+ set_optimization_disabled(true);
+ // Code should be the lazy compilation stub or else unoptimized. If the
+ // latter, disable optimization for the code too.
+ ASSERT(code()->kind() == Code::FUNCTION || code()->kind() == Code::BUILTIN);
+ if (code()->kind() == Code::FUNCTION) {
+ code()->set_optimizable(false);
+ }
+ if (FLAG_trace_opt) {
+ PrintF("[disabled optimization for: ");
+ function->PrintName();
+ PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
+ }
+}
+
+
bool SharedFunctionInfo::VerifyBailoutId(int id) {
// TODO(srdjan): debugging ARM crashes in hydrogen. OK to disable while
// we are always bailing out on ARM.
@@ -6528,13 +6625,12 @@ const char* Code::Kind2String(Kind kind) {
case BUILTIN: return "BUILTIN";
case LOAD_IC: return "LOAD_IC";
case KEYED_LOAD_IC: return "KEYED_LOAD_IC";
- case KEYED_EXTERNAL_ARRAY_LOAD_IC: return "KEYED_EXTERNAL_ARRAY_LOAD_IC";
case STORE_IC: return "STORE_IC";
case KEYED_STORE_IC: return "KEYED_STORE_IC";
- case KEYED_EXTERNAL_ARRAY_STORE_IC: return "KEYED_EXTERNAL_ARRAY_STORE_IC";
case CALL_IC: return "CALL_IC";
case KEYED_CALL_IC: return "KEYED_CALL_IC";
- case TYPE_RECORDING_BINARY_OP_IC: return "TYPE_RECORDING_BINARY_OP_IC";
+ case UNARY_OP_IC: return "UNARY_OP_IC";
+ case BINARY_OP_IC: return "BINARY_OP_IC";
case COMPARE_IC: return "COMPARE_IC";
}
UNREACHABLE();
@@ -6563,6 +6659,7 @@ const char* Code::PropertyType2String(PropertyType type) {
case FIELD: return "FIELD";
case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
case CALLBACKS: return "CALLBACKS";
+ case HANDLER: return "HANDLER";
case INTERCEPTOR: return "INTERCEPTOR";
case MAP_TRANSITION: return "MAP_TRANSITION";
case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION";
@@ -7047,7 +7144,8 @@ bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
ExternalArray* array = ExternalArray::cast(elements());
if (index < static_cast<uint32_t>(array->length())) {
return true;
@@ -7169,7 +7267,8 @@ JSObject::LocalElementType JSObject::HasLocalElement(uint32_t index) {
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
ExternalArray* array = ExternalArray::cast(elements());
if (index < static_cast<uint32_t>(array->length())) return FAST_ELEMENT;
break;
@@ -7228,7 +7327,8 @@ bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) {
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
ExternalArray* array = ExternalArray::cast(elements());
if (index < static_cast<uint32_t>(array->length())) {
return true;
@@ -7299,11 +7399,11 @@ MaybeObject* JSObject::GetElementWithCallback(Object* receiver,
uint32_t index,
Object* holder) {
Isolate* isolate = GetIsolate();
- ASSERT(!structure->IsProxy());
+ ASSERT(!structure->IsForeign());
// api style callbacks.
if (structure->IsAccessorInfo()) {
- AccessorInfo* data = AccessorInfo::cast(structure);
+ Handle<AccessorInfo> data(AccessorInfo::cast(structure));
Object* fun_obj = data->getter();
v8::AccessorGetter call_fun = v8::ToCData<v8::AccessorGetter>(fun_obj);
HandleScope scope(isolate);
@@ -7354,20 +7454,22 @@ MaybeObject* JSObject::SetElementWithCallback(Object* structure,
Handle<Object> value_handle(value, isolate);
// To accommodate both the old and the new api we switch on the
- // data structure used to store the callbacks. Eventually proxy
+ // data structure used to store the callbacks. Eventually foreign
// callbacks should be phased out.
- ASSERT(!structure->IsProxy());
+ ASSERT(!structure->IsForeign());
if (structure->IsAccessorInfo()) {
// api style callbacks
- AccessorInfo* data = AccessorInfo::cast(structure);
+ Handle<JSObject> self(this);
+ Handle<JSObject> holder_handle(JSObject::cast(holder));
+ Handle<AccessorInfo> data(AccessorInfo::cast(structure));
Object* call_obj = data->setter();
v8::AccessorSetter call_fun = v8::ToCData<v8::AccessorSetter>(call_obj);
if (call_fun == NULL) return value;
Handle<Object> number = isolate->factory()->NewNumberFromUint(index);
Handle<String> key(isolate->factory()->NumberToString(number));
- LOG(isolate, ApiNamedPropertyAccess("store", this, *key));
- CustomArguments args(isolate, data->data(), this, JSObject::cast(holder));
+ LOG(isolate, ApiNamedPropertyAccess("store", *self, *key));
+ CustomArguments args(isolate, data->data(), *self, *holder_handle);
v8::AccessorInfo info(args.end());
{
// Leaving JavaScript.
@@ -7549,6 +7651,10 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
ExternalFloatArray* array = ExternalFloatArray::cast(elements());
return array->SetValue(index, value);
}
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ ExternalDoubleArray* array = ExternalDoubleArray::cast(elements());
+ return array->SetValue(index, value);
+ }
case DICTIONARY_ELEMENTS: {
// Insert element in the dictionary.
FixedArray* elms = FixedArray::cast(elements());
@@ -7565,8 +7671,8 @@ MaybeObject* JSObject::SetElementWithoutInterceptor(uint32_t index,
// If put fails instrict mode, throw exception.
if (!dictionary->ValueAtPut(entry, value) &&
strict_mode == kStrictMode) {
- Handle<Object> number(isolate->factory()->NewNumberFromUint(index));
Handle<Object> holder(this);
+ Handle<Object> number(isolate->factory()->NewNumberFromUint(index));
Handle<Object> args[2] = { number, holder };
return isolate->Throw(
*isolate->factory()->NewTypeError("strict_read_only_property",
@@ -7690,7 +7796,8 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
MaybeObject* maybe_value = GetExternalElement(index);
Object* value;
if (!maybe_value->ToObject(&value)) return maybe_value;
@@ -7792,7 +7899,8 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
MaybeObject* maybe_value = GetExternalElement(index);
Object* value;
if (!maybe_value->ToObject(&value)) return maybe_value;
@@ -7895,6 +8003,14 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) {
}
break;
}
+ case EXTERNAL_DOUBLE_ELEMENTS: {
+ ExternalDoubleArray* array = ExternalDoubleArray::cast(elements());
+ if (index < static_cast<uint32_t>(array->length())) {
+ double value = array->get(index);
+ return GetHeap()->AllocateHeapNumber(value);
+ }
+ break;
+ }
case FAST_ELEMENTS:
case DICTIONARY_ELEMENTS:
UNREACHABLE();
@@ -7924,7 +8040,8 @@ bool JSObject::HasDenseElements() {
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
return true;
}
case DICTIONARY_ELEMENTS: {
@@ -8161,7 +8278,8 @@ bool JSObject::HasRealElementProperty(uint32_t index) {
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
ExternalArray* array = ExternalArray::cast(elements());
return index < static_cast<uint32_t>(array->length());
}
@@ -8342,8 +8460,7 @@ void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
}
ASSERT(storage->length() >= index);
} else {
- property_dictionary()->CopyKeysTo(storage,
- index);
+ property_dictionary()->CopyKeysTo(storage, index);
}
}
@@ -8403,7 +8520,8 @@ int JSObject::GetLocalElementKeys(FixedArray* storage,
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
- case EXTERNAL_FLOAT_ELEMENTS: {
+ case EXTERNAL_FLOAT_ELEMENTS:
+ case EXTERNAL_DOUBLE_ELEMENTS: {
int length = ExternalArray::cast(elements())->length();
while (counter < length) {
if (storage != NULL) {
@@ -8688,6 +8806,71 @@ class AsciiSymbolKey : public SequentialSymbolKey<char> {
};
+class SubStringAsciiSymbolKey : public HashTableKey {
+ public:
+ explicit SubStringAsciiSymbolKey(Handle<SeqAsciiString> string,
+ int from,
+ int length)
+ : string_(string), from_(from), length_(length) { }
+
+ uint32_t Hash() {
+ ASSERT(length_ >= 0);
+ ASSERT(from_ + length_ <= string_->length());
+ StringHasher hasher(length_);
+
+ // Very long strings have a trivial hash that doesn't inspect the
+ // string contents.
+ if (hasher.has_trivial_hash()) {
+ hash_field_ = hasher.GetHashField();
+ } else {
+ int i = 0;
+ // Do the iterative array index computation as long as there is a
+ // chance this is an array index.
+ while (i < length_ && hasher.is_array_index()) {
+ hasher.AddCharacter(static_cast<uc32>(
+ string_->SeqAsciiStringGet(i + from_)));
+ i++;
+ }
+
+ // Process the remaining characters without updating the array
+ // index.
+ while (i < length_) {
+ hasher.AddCharacterNoIndex(static_cast<uc32>(
+ string_->SeqAsciiStringGet(i + from_)));
+ i++;
+ }
+ hash_field_ = hasher.GetHashField();
+ }
+
+ uint32_t result = hash_field_ >> String::kHashShift;
+ ASSERT(result != 0); // Ensure that the hash value of 0 is never computed.
+ return result;
+ }
+
+
+ uint32_t HashForObject(Object* other) {
+ return String::cast(other)->Hash();
+ }
+
+ bool IsMatch(Object* string) {
+ Vector<const char> chars(string_->GetChars() + from_, length_);
+ return String::cast(string)->IsAsciiEqualTo(chars);
+ }
+
+ MaybeObject* AsObject() {
+ if (hash_field_ == 0) Hash();
+ Vector<const char> chars(string_->GetChars() + from_, length_);
+ return HEAP->AllocateAsciiSymbol(chars, hash_field_);
+ }
+
+ private:
+ Handle<SeqAsciiString> string_;
+ int from_;
+ int length_;
+ uint32_t hash_field_;
+};
+
+
class TwoByteSymbolKey : public SequentialSymbolKey<uc16> {
public:
explicit TwoByteSymbolKey(Vector<const uc16> str)
@@ -9318,6 +9501,26 @@ MaybeObject* ExternalFloatArray::SetValue(uint32_t index, Object* value) {
}
+MaybeObject* ExternalDoubleArray::SetValue(uint32_t index, Object* value) {
+ double double_value = 0;
+ Heap* heap = GetHeap();
+ if (index < static_cast<uint32_t>(length())) {
+ if (value->IsSmi()) {
+ int int_value = Smi::cast(value)->value();
+ double_value = static_cast<double>(int_value);
+ } else if (value->IsHeapNumber()) {
+ double_value = HeapNumber::cast(value)->value();
+ } else {
+ // Clamp undefined to zero (default). All other types have been
+ // converted to a number type further up in the call chain.
+ ASSERT(value->IsUndefined());
+ }
+ set(index, double_value);
+ }
+ return heap->AllocateHeapNumber(double_value);
+}
+
+
JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) {
ASSERT(!HasFastProperties());
Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
@@ -9462,6 +9665,15 @@ MaybeObject* SymbolTable::LookupAsciiSymbol(Vector<const char> str,
}
+MaybeObject* SymbolTable::LookupSubStringAsciiSymbol(Handle<SeqAsciiString> str,
+ int from,
+ int length,
+ Object** s) {
+ SubStringAsciiSymbolKey key(str, from, length);
+ return LookupKey(&key, s);
+}
+
+
MaybeObject* SymbolTable::LookupTwoByteSymbol(Vector<const uc16> str,
Object** s) {
TwoByteSymbolKey key(str);
@@ -9984,7 +10196,8 @@ void StringDictionary::CopyEnumKeysTo(FixedArray* storage,
template<typename Shape, typename Key>
void Dictionary<Shape, Key>::CopyKeysTo(
- FixedArray* storage, int index) {
+ FixedArray* storage,
+ int index) {
ASSERT(storage->length() >= NumberOfElementsFilterAttributes(
static_cast<PropertyAttributes>(NONE)));
int capacity = HashTable<Shape, Key>::Capacity();
diff --git a/src/objects.h b/src/objects.h
index 72daad94..332b2e47 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -28,7 +28,9 @@
#ifndef V8_OBJECTS_H_
#define V8_OBJECTS_H_
+#include "allocation.h"
#include "builtins.h"
+#include "list.h"
#include "smart-pointer.h"
#include "unicode-inl.h"
#if V8_TARGET_ARCH_ARM
@@ -89,7 +91,8 @@
// - Code
// - Map
// - Oddball
-// - Proxy
+// - JSProxy
+// - Foreign
// - SharedFunctionInfo
// - Struct
// - AccessorInfo
@@ -286,7 +289,8 @@ static const int kVariableSizeSentinel = 0;
V(JS_GLOBAL_PROPERTY_CELL_TYPE) \
\
V(HEAP_NUMBER_TYPE) \
- V(PROXY_TYPE) \
+ V(JS_PROXY_TYPE) \
+ V(FOREIGN_TYPE) \
V(BYTE_ARRAY_TYPE) \
/* Note: the order of these external array */ \
/* types is relied upon in */ \
@@ -484,7 +488,6 @@ const uint32_t kShortcutTypeTag = kConsStringTag;
enum InstanceType {
// String types.
- // FIRST_STRING_TYPE
SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag,
ASCII_SYMBOL_TYPE = kAsciiStringTag | kSymbolTag | kSeqStringTag,
CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag,
@@ -514,7 +517,8 @@ enum InstanceType {
// "Data", objects that cannot contain non-map-word pointers to heap
// objects.
HEAP_NUMBER_TYPE,
- PROXY_TYPE,
+ FOREIGN_TYPE,
+ JS_PROXY_TYPE,
BYTE_ARRAY_TYPE,
EXTERNAL_BYTE_ARRAY_TYPE, // FIRST_EXTERNAL_ARRAY_TYPE
EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE,
@@ -523,6 +527,7 @@ enum InstanceType {
EXTERNAL_INT_ARRAY_TYPE,
EXTERNAL_UNSIGNED_INT_ARRAY_TYPE,
EXTERNAL_FLOAT_ARRAY_TYPE,
+ EXTERNAL_DOUBLE_ARRAY_TYPE,
EXTERNAL_PIXEL_ARRAY_TYPE, // LAST_EXTERNAL_ARRAY_TYPE
FILLER_TYPE, // LAST_DATA_TYPE
@@ -566,8 +571,6 @@ enum InstanceType {
LAST_TYPE = JS_FUNCTION_TYPE,
INVALID_TYPE = FIRST_TYPE - 1,
FIRST_NONSTRING_TYPE = MAP_TYPE,
- FIRST_STRING_TYPE = FIRST_TYPE,
- LAST_STRING_TYPE = FIRST_NONSTRING_TYPE - 1,
// Boundaries for testing for an external array.
FIRST_EXTERNAL_ARRAY_TYPE = EXTERNAL_BYTE_ARRAY_TYPE,
LAST_EXTERNAL_ARRAY_TYPE = EXTERNAL_PIXEL_ARRAY_TYPE,
@@ -588,7 +591,7 @@ static const int kExternalArrayTypeCount = LAST_EXTERNAL_ARRAY_TYPE -
STATIC_CHECK(JS_OBJECT_TYPE == Internals::kJSObjectType);
STATIC_CHECK(FIRST_NONSTRING_TYPE == Internals::kFirstNonstringType);
-STATIC_CHECK(PROXY_TYPE == Internals::kProxyType);
+STATIC_CHECK(FOREIGN_TYPE == Internals::kForeignType);
enum CompareResult {
@@ -613,7 +616,6 @@ enum CompareResult {
class StringStream;
class ObjectVisitor;
-class Failure;
struct ValueInfo : public Malloced {
ValueInfo() : type(FIRST_TYPE), ptr(NULL), str(NULL), number(0) { }
@@ -627,6 +629,7 @@ struct ValueInfo : public Malloced {
// A template-ized version of the IsXXX functions.
template <class C> static inline bool Is(Object* obj);
+class Failure;
class MaybeObject BASE_EMBEDDED {
public:
@@ -703,6 +706,7 @@ class MaybeObject BASE_EMBEDDED {
V(ExternalIntArray) \
V(ExternalUnsignedIntArray) \
V(ExternalFloatArray) \
+ V(ExternalDoubleArray) \
V(ExternalPixelArray) \
V(ByteArray) \
V(JSObject) \
@@ -722,9 +726,10 @@ class MaybeObject BASE_EMBEDDED {
V(JSValue) \
V(JSMessageObject) \
V(StringWrapper) \
- V(Proxy) \
+ V(Foreign) \
V(Boolean) \
V(JSArray) \
+ V(JSProxy) \
V(JSRegExp) \
V(HashTable) \
V(Dictionary) \
@@ -809,6 +814,9 @@ class Object : public MaybeObject {
Object* structure,
String* name,
Object* holder);
+ MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(Object* receiver,
+ String* name,
+ Object* handler);
MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver,
JSFunction* getter);
@@ -1348,6 +1356,7 @@ class JSObject: public HeapObject {
EXTERNAL_INT_ELEMENTS,
EXTERNAL_UNSIGNED_INT_ELEMENTS,
EXTERNAL_FLOAT_ELEMENTS,
+ EXTERNAL_DOUBLE_ELEMENTS,
EXTERNAL_PIXEL_ELEMENTS
};
@@ -1389,6 +1398,7 @@ class JSObject: public HeapObject {
inline bool HasExternalIntElements();
inline bool HasExternalUnsignedIntElements();
inline bool HasExternalFloatElements();
+ inline bool HasExternalDoubleElements();
inline bool AllowsSetElementsLength();
inline NumberDictionary* element_dictionary(); // Gets slow elements.
// Requires: this->HasFastElements().
@@ -2029,16 +2039,22 @@ class FixedArray: public HeapObject {
// DescriptorArrays are fixed arrays used to hold instance descriptors.
// The format of the these objects is:
-// [0]: point to a fixed array with (value, detail) pairs.
-// [1]: next enumeration index (Smi), or pointer to small fixed array:
+// TODO(1399): It should be possible to make room for bit_field3 in the map
+// without overloading the instance descriptors field in the map
+// (and storing it in the DescriptorArray when the map has one).
+// [0]: storage for bit_field3 for Map owning this object (Smi)
+// [1]: point to a fixed array with (value, detail) pairs.
+// [2]: next enumeration index (Smi), or pointer to small fixed array:
// [0]: next enumeration index (Smi)
// [1]: pointer to fixed array with enum cache
-// [2]: first key
+// [3]: first key
// [length() - 1]: last key
//
class DescriptorArray: public FixedArray {
public:
- // Is this the singleton empty_descriptor_array?
+ // Returns true for both shared empty_descriptor_array and for smis, which the
+ // map uses to encode additional bit fields when the descriptor array is not
+ // yet used.
inline bool IsEmpty();
// Returns the number of descriptors in the array.
@@ -2075,6 +2091,12 @@ class DescriptorArray: public FixedArray {
return bridge->get(kEnumCacheBridgeCacheIndex);
}
+ // TODO(1399): It should be possible to make room for bit_field3 in the map
+ // without overloading the instance descriptors field in the map
+ // (and storing it in the DescriptorArray when the map has one).
+ inline int bit_field3_storage();
+ inline void set_bit_field3_storage(int value);
+
// Initialize or change the enum cache,
// using the supplied storage for the small "bridge".
void SetEnumCache(FixedArray* bridge_storage, FixedArray* new_cache);
@@ -2153,9 +2175,10 @@ class DescriptorArray: public FixedArray {
// Constant for denoting key was not found.
static const int kNotFound = -1;
- static const int kContentArrayIndex = 0;
- static const int kEnumerationIndexIndex = 1;
- static const int kFirstIndex = 2;
+ static const int kBitField3StorageIndex = 0;
+ static const int kContentArrayIndex = 1;
+ static const int kEnumerationIndexIndex = 2;
+ static const int kFirstIndex = 3;
// The length of the "bridge" to the enum cache.
static const int kEnumCacheBridgeLength = 2;
@@ -2163,7 +2186,8 @@ class DescriptorArray: public FixedArray {
static const int kEnumCacheBridgeCacheIndex = 1;
// Layout description.
- static const int kContentArrayOffset = FixedArray::kHeaderSize;
+ static const int kBitField3StorageOffset = FixedArray::kHeaderSize;
+ static const int kContentArrayOffset = kBitField3StorageOffset + kPointerSize;
static const int kEnumerationIndexOffset = kContentArrayOffset + kPointerSize;
static const int kFirstOffset = kEnumerationIndexOffset + kPointerSize;
@@ -2427,6 +2451,8 @@ class SymbolTableShape {
static const int kEntrySize = 1;
};
+class SeqAsciiString;
+
// SymbolTable.
//
// No special elements in the prefix and the element size is 1
@@ -2440,6 +2466,11 @@ class SymbolTable: public HashTable<SymbolTableShape, HashTableKey*> {
MUST_USE_RESULT MaybeObject* LookupSymbol(Vector<const char> str, Object** s);
MUST_USE_RESULT MaybeObject* LookupAsciiSymbol(Vector<const char> str,
Object** s);
+ MUST_USE_RESULT MaybeObject* LookupSubStringAsciiSymbol(
+ Handle<SeqAsciiString> str,
+ int from,
+ int length,
+ Object** s);
MUST_USE_RESULT MaybeObject* LookupTwoByteSymbol(Vector<const uc16> str,
Object** s);
MUST_USE_RESULT MaybeObject* LookupString(String* key, Object** s);
@@ -3105,6 +3136,34 @@ class ExternalFloatArray: public ExternalArray {
};
+class ExternalDoubleArray: public ExternalArray {
+ public:
+ // Setter and getter.
+ inline double get(int index);
+ inline void set(int index, double value);
+
+ // This accessor applies the correct conversion from Smi, HeapNumber
+ // and undefined.
+ MaybeObject* SetValue(uint32_t index, Object* value);
+
+ // Casting.
+ static inline ExternalDoubleArray* cast(Object* obj);
+
+#ifdef OBJECT_PRINT
+ inline void ExternalDoubleArrayPrint() {
+ ExternalDoubleArrayPrint(stdout);
+ }
+ void ExternalDoubleArrayPrint(FILE* out);
+#endif // OBJECT_PRINT
+#ifdef DEBUG
+ void ExternalDoubleArrayVerify();
+#endif // DEBUG
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalDoubleArray);
+};
+
+
// DeoptimizationInputData is a fixed array used to hold the deoptimization
// data for code generated by the Hydrogen/Lithium compiler. It also
// contains information about functions that were inlined. If N different
@@ -3243,13 +3302,12 @@ class Code: public HeapObject {
BUILTIN,
LOAD_IC,
KEYED_LOAD_IC,
- KEYED_EXTERNAL_ARRAY_LOAD_IC,
CALL_IC,
KEYED_CALL_IC,
STORE_IC,
KEYED_STORE_IC,
- KEYED_EXTERNAL_ARRAY_STORE_IC,
- TYPE_RECORDING_BINARY_OP_IC,
+ UNARY_OP_IC,
+ BINARY_OP_IC,
COMPARE_IC,
// No more than 16 kinds. The value currently encoded in four bits in
// Flags.
@@ -3291,6 +3349,12 @@ class Code: public HeapObject {
// [deoptimization_data]: Array containing data for deopt.
DECL_ACCESSORS(deoptimization_data, FixedArray)
+ // [code_flushing_candidate]: Field only used during garbage
+ // collection to hold code flushing candidates. The contents of this
+ // field does not have to be traced during garbage collection since
+ // it is only used by the garbage collector itself.
+ DECL_ACCESSORS(next_code_flushing_candidate, Object)
+
// Unchecked accessors to be used during GC.
inline ByteArray* unchecked_relocation_info();
inline FixedArray* unchecked_deoptimization_data();
@@ -3317,16 +3381,13 @@ class Code: public HeapObject {
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
inline bool is_call_stub() { return kind() == CALL_IC; }
inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
- inline bool is_type_recording_binary_op_stub() {
- return kind() == TYPE_RECORDING_BINARY_OP_IC;
- }
- inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; }
- inline bool is_external_array_load_stub() {
- return kind() == KEYED_EXTERNAL_ARRAY_LOAD_IC;
+ inline bool is_unary_op_stub() {
+ return kind() == UNARY_OP_IC;
}
- inline bool is_external_array_store_stub() {
- return kind() == KEYED_EXTERNAL_ARRAY_STORE_IC;
+ inline bool is_binary_op_stub() {
+ return kind() == BINARY_OP_IC;
}
+ inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; }
// [major_key]: For kind STUB or BINARY_OP_IC, the major key.
inline int major_key();
@@ -3374,11 +3435,15 @@ class Code: public HeapObject {
inline ExternalArrayType external_array_type();
inline void set_external_array_type(ExternalArrayType value);
+ // [type-recording unary op type]: For all UNARY_OP_IC.
+ inline byte unary_op_type();
+ inline void set_unary_op_type(byte value);
+
// [type-recording binary op type]: For all TYPE_RECORDING_BINARY_OP_IC.
- inline byte type_recording_binary_op_type();
- inline void set_type_recording_binary_op_type(byte value);
- inline byte type_recording_binary_op_result_type();
- inline void set_type_recording_binary_op_result_type(byte value);
+ inline byte binary_op_type();
+ inline void set_binary_op_type(byte value);
+ inline byte binary_op_result_type();
+ inline void set_binary_op_result_type(byte value);
// [compare state]: For kind compare IC stubs, tells what state the
// stub is in.
@@ -3504,9 +3569,12 @@ class Code: public HeapObject {
static const int kRelocationInfoOffset = kInstructionSizeOffset + kIntSize;
static const int kDeoptimizationDataOffset =
kRelocationInfoOffset + kPointerSize;
- static const int kFlagsOffset = kDeoptimizationDataOffset + kPointerSize;
- static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize;
+ static const int kNextCodeFlushingCandidateOffset =
+ kDeoptimizationDataOffset + kPointerSize;
+ static const int kFlagsOffset =
+ kNextCodeFlushingCandidateOffset + kPointerSize;
+ static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize;
static const int kKindSpecificFlagsSize = 2 * kIntSize;
static const int kHeaderPaddingStart = kKindSpecificFlagsOffset +
@@ -3525,6 +3593,7 @@ class Code: public HeapObject {
static const int kExternalArrayTypeOffset = kKindSpecificFlagsOffset;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
+ static const int kUnaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kHasDeoptimizationSupportOffset = kOptimizableOffset + 1;
@@ -3597,6 +3666,13 @@ class Map: public HeapObject {
inline byte bit_field2();
inline void set_bit_field2(byte value);
+ // Bit field 3.
+ // TODO(1399): It should be possible to make room for bit_field3 in the map
+ // without overloading the instance descriptors field (and storing it in the
+ // DescriptorArray when the map has one).
+ inline int bit_field3();
+ inline void set_bit_field3(int value);
+
// Tells whether the object in the prototype property will be used
// for instances created from this function. If the prototype
// property is set to a value that is not a JSObject, the prototype
@@ -3718,9 +3794,17 @@ class Map: public HeapObject {
inline JSFunction* unchecked_constructor();
+ // Should only be called by the code that initializes map to set initial valid
+ // value of the instance descriptor member.
+ inline void init_instance_descriptors();
+
// [instance descriptors]: describes the object.
DECL_ACCESSORS(instance_descriptors, DescriptorArray)
+ // Sets the instance descriptor array for the map to be an empty descriptor
+ // array.
+ inline void clear_instance_descriptors();
+
// [stub cache]: contains stubs compiled for this map.
DECL_ACCESSORS(code_cache, Object)
@@ -3846,9 +3930,19 @@ class Map: public HeapObject {
static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize;
static const int kPrototypeOffset = kInstanceAttributesOffset + kIntSize;
static const int kConstructorOffset = kPrototypeOffset + kPointerSize;
- static const int kInstanceDescriptorsOffset =
+ // Storage for instance descriptors is overloaded to also contain additional
+ // map flags when unused (bit_field3). When the map has instance descriptors,
+ // the flags are transferred to the instance descriptor array and accessed
+ // through an extra indirection.
+ // TODO(1399): It should be possible to make room for bit_field3 in the map
+ // without overloading the instance descriptors field, but the map is
+ // currently perfectly aligned to 32 bytes and extending it at all would
+ // double its size. After the increment GC work lands, this size restriction
+ // could be loosened and bit_field3 moved directly back in the map.
+ static const int kInstanceDescriptorsOrBitField3Offset =
kConstructorOffset + kPointerSize;
- static const int kCodeCacheOffset = kInstanceDescriptorsOffset + kPointerSize;
+ static const int kCodeCacheOffset =
+ kInstanceDescriptorsOrBitField3Offset + kPointerSize;
static const int kPrototypeTransitionsOffset =
kCodeCacheOffset + kPointerSize;
static const int kPadStart = kPrototypeTransitionsOffset + kPointerSize;
@@ -3895,8 +3989,10 @@ class Map: public HeapObject {
static const int kHasFastElements = 2;
static const int kStringWrapperSafeForDefaultValueOf = 3;
static const int kAttachedToSharedFunctionInfo = 4;
- static const int kIsShared = 5;
- static const int kHasExternalArrayElements = 6;
+ static const int kHasExternalArrayElements = 5;
+
+ // Bit positions for bit field 3
+ static const int kIsShared = 1;
// Layout of the default cache. It holds alternating name and code objects.
static const int kCodeCacheEntrySize = 2;
@@ -3913,7 +4009,7 @@ class Map: public HeapObject {
// An abstract superclass, a marker class really, for simple structure classes.
-// It doesn't carry much functionality but allows struct classes to me
+// It doesn't carry much functionality but allows struct classes to be
// identified in the type system.
class Struct: public HeapObject {
public:
@@ -3961,7 +4057,7 @@ class Script: public Struct {
DECL_ACCESSORS(context_data, Object)
// [wrapper]: the wrapper cache.
- DECL_ACCESSORS(wrapper, Proxy)
+ DECL_ACCESSORS(wrapper, Foreign)
// [type]: the script type.
DECL_ACCESSORS(type, Smi)
@@ -4307,6 +4403,13 @@ class SharedFunctionInfo: public HeapObject {
inline bool strict_mode();
inline void set_strict_mode(bool value);
+ // Indicates whether the function is a native ES5 function.
+ // These needs special threatment in .call and .apply since
+ // null passed as the receiver should not be translated to the
+ // global object.
+ inline bool es5_native();
+ inline void set_es5_native(bool value);
+
// Indicates whether or not the code in the shared function support
// deoptimization.
inline bool has_deoptimization_support();
@@ -4314,6 +4417,11 @@ class SharedFunctionInfo: public HeapObject {
// Enable deoptimization support through recompiled code.
void EnableDeoptimizationSupport(Code* recompiled);
+ // Disable (further) attempted optimization of all functions sharing this
+ // shared function info. The function is the one we actually tried to
+ // optimize.
+ void DisableOptimization(JSFunction* function);
+
// Lookup the bailout ID and ASSERT that it exists in the non-optimized
// code, returns whether it asserted (i.e., always true if assertions are
// disabled).
@@ -4487,6 +4595,7 @@ class SharedFunctionInfo: public HeapObject {
static const int kCodeAgeMask = 0x7;
static const int kOptimizationDisabled = 6;
static const int kStrictModeFunction = 7;
+ static const int kES5Native = 8;
private:
#if V8_HOST_ARCH_32_BIT
@@ -4500,18 +4609,27 @@ class SharedFunctionInfo: public HeapObject {
#endif
public:
- // Constants for optimizing codegen for strict mode function tests.
+ // Constants for optimizing codegen for strict mode function and
+ // es5 native tests.
// Allows to use byte-widgh instructions.
static const int kStrictModeBitWithinByte =
(kStrictModeFunction + kCompilerHintsSmiTagSize) % kBitsPerByte;
+ static const int kES5NativeBitWithinByte =
+ (kES5Native + kCompilerHintsSmiTagSize) % kBitsPerByte;
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
static const int kStrictModeByteOffset = kCompilerHintsOffset +
- (kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
+ (kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte;
+ static const int kES5NativeByteOffset = kCompilerHintsOffset +
+ (kES5Native + kCompilerHintsSmiTagSize) / kBitsPerByte;
#elif __BYTE_ORDER == __BIG_ENDIAN
static const int kStrictModeByteOffset = kCompilerHintsOffset +
- (kCompilerHintsSize - 1) -
- ((kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte);
+ (kCompilerHintsSize - 1) -
+ ((kStrictModeFunction + kCompilerHintsSmiTagSize) / kBitsPerByte);
+ static const int kES5NativeByteOffset = kCompilerHintsOffset +
+ (kCompilerHintsSize - 1) -
+ ((kES5Native + kCompilerHintsSmiTagSize) / kBitsPerByte);
#else
#error Unknown byte ordering
#endif
@@ -4685,7 +4803,7 @@ class JSFunction: public JSObject {
class JSGlobalProxy : public JSObject {
public:
- // [context]: the owner global context of this proxy object.
+ // [context]: the owner global context of this global proxy object.
// It is null value if this object is not used by any context.
DECL_ACCESSORS(context, Object)
@@ -4936,8 +5054,10 @@ class JSMessageObject: public JSObject {
// If it is an atom regexp
// - a reference to a literal string to search for
// If it is an irregexp regexp:
-// - a reference to code for ASCII inputs (bytecode or compiled).
-// - a reference to code for UC16 inputs (bytecode or compiled).
+// - a reference to code for ASCII inputs (bytecode or compiled), or a smi
+// used for tracking the last usage (used for code flushing).
+// - a reference to code for UC16 inputs (bytecode or compiled), or a smi
+// used for tracking the last usage (used for code flushing)..
// - max number of registers used by irregexp implementations.
// - number of capture registers (output values) of the regexp.
class JSRegExp: public JSObject {
@@ -4970,6 +5090,12 @@ class JSRegExp: public JSObject {
inline Object* DataAt(int index);
// Set implementation data after the object has been prepared.
inline void SetDataAt(int index, Object* value);
+
+ // Used during GC when flushing code or setting age.
+ inline Object* DataAtUnchecked(int index);
+ inline void SetDataAtUnchecked(int index, Object* value, Heap* heap);
+ inline Type TypeTagUnchecked();
+
static int code_index(bool is_ascii) {
if (is_ascii) {
return kIrregexpASCIICodeIndex;
@@ -4978,6 +5104,14 @@ class JSRegExp: public JSObject {
}
}
+ static int saved_code_index(bool is_ascii) {
+ if (is_ascii) {
+ return kIrregexpASCIICodeSavedIndex;
+ } else {
+ return kIrregexpUC16CodeSavedIndex;
+ }
+ }
+
static inline JSRegExp* cast(Object* obj);
// Dispatched behavior.
@@ -5008,11 +5142,19 @@ class JSRegExp: public JSObject {
// fails, this fields hold an exception object that should be
// thrown if the regexp is used again.
static const int kIrregexpUC16CodeIndex = kDataIndex + 1;
+
+ // Saved instance of Irregexp compiled code or bytecode for ASCII that
+ // is a potential candidate for flushing.
+ static const int kIrregexpASCIICodeSavedIndex = kDataIndex + 2;
+ // Saved instance of Irregexp compiled code or bytecode for UC16 that is
+ // a potential candidate for flushing.
+ static const int kIrregexpUC16CodeSavedIndex = kDataIndex + 3;
+
// Maximal number of registers used by either ASCII or UC16.
// Only used to check that there is enough stack space
- static const int kIrregexpMaxRegisterCountIndex = kDataIndex + 2;
+ static const int kIrregexpMaxRegisterCountIndex = kDataIndex + 4;
// Number of captures in the compiled regexp.
- static const int kIrregexpCaptureCountIndex = kDataIndex + 3;
+ static const int kIrregexpCaptureCountIndex = kDataIndex + 5;
static const int kIrregexpDataSize = kIrregexpCaptureCountIndex + 1;
@@ -5033,6 +5175,18 @@ class JSRegExp: public JSObject {
static const int kMultilineFieldIndex = 3;
static const int kLastIndexFieldIndex = 4;
static const int kInObjectFieldCount = 5;
+
+ // The uninitialized value for a regexp code object.
+ static const int kUninitializedValue = -1;
+
+ // The compilation error value for the regexp code object. The real error
+ // object is in the saved code field.
+ static const int kCompilationErrorValue = -2;
+
+ // When we store the sweep generation at which we moved the code from the
+ // code index to the saved code index we mask it of to be in the [0:255]
+ // range.
+ static const int kCodeAgeMask = 0xff;
};
@@ -5889,8 +6043,8 @@ class Relocatable BASE_EMBEDDED {
static void PostGarbageCollectionProcessing();
static int ArchiveSpacePerThread();
- static char* ArchiveState(char* to);
- static char* RestoreState(char* from);
+ static char* ArchiveState(Isolate* isolate, char* to);
+ static char* RestoreState(Isolate* isolate, char* from);
static void Iterate(ObjectVisitor* v);
static void Iterate(ObjectVisitor* v, Relocatable* top);
static char* Iterate(ObjectVisitor* v, char* t);
@@ -6044,44 +6198,77 @@ class JSGlobalPropertyCell: public HeapObject {
};
+// The JSProxy describes EcmaScript Harmony proxies
+class JSProxy: public HeapObject {
+ public:
+ // [handler]: The handler property.
+ DECL_ACCESSORS(handler, Object)
+
+ // Casting.
+ static inline JSProxy* cast(Object* obj);
-// Proxy describes objects pointing from JavaScript to C structures.
+ // Dispatched behavior.
+#ifdef OBJECT_PRINT
+ inline void JSProxyPrint() {
+ JSProxyPrint(stdout);
+ }
+ void JSProxyPrint(FILE* out);
+#endif
+#ifdef DEBUG
+ void JSProxyVerify();
+#endif
+
+ // Layout description.
+ static const int kHandlerOffset = HeapObject::kHeaderSize;
+ static const int kSize = kHandlerOffset + kPointerSize;
+
+ typedef FixedBodyDescriptor<kHandlerOffset,
+ kHandlerOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JSProxy);
+};
+
+
+
+// Foreign describes objects pointing from JavaScript to C structures.
// Since they cannot contain references to JS HeapObjects they can be
// placed in old_data_space.
-class Proxy: public HeapObject {
+class Foreign: public HeapObject {
public:
- // [proxy]: field containing the address.
- inline Address proxy();
- inline void set_proxy(Address value);
+ // [address]: field containing the address.
+ inline Address address();
+ inline void set_address(Address value);
// Casting.
- static inline Proxy* cast(Object* obj);
+ static inline Foreign* cast(Object* obj);
// Dispatched behavior.
- inline void ProxyIterateBody(ObjectVisitor* v);
+ inline void ForeignIterateBody(ObjectVisitor* v);
template<typename StaticVisitor>
- inline void ProxyIterateBody();
+ inline void ForeignIterateBody();
#ifdef OBJECT_PRINT
- inline void ProxyPrint() {
- ProxyPrint(stdout);
+ inline void ForeignPrint() {
+ ForeignPrint(stdout);
}
- void ProxyPrint(FILE* out);
+ void ForeignPrint(FILE* out);
#endif
#ifdef DEBUG
- void ProxyVerify();
+ void ForeignVerify();
#endif
// Layout description.
- static const int kProxyOffset = HeapObject::kHeaderSize;
- static const int kSize = kProxyOffset + kPointerSize;
+ static const int kAddressOffset = HeapObject::kHeaderSize;
+ static const int kSize = kAddressOffset + kPointerSize;
- STATIC_CHECK(kProxyOffset == Internals::kProxyProxyOffset);
+ STATIC_CHECK(kAddressOffset == Internals::kForeignAddressOffset);
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(Proxy);
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Foreign);
};
diff --git a/src/parser.cc b/src/parser.cc
index ce9b7c3d..7ad64407 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -28,7 +28,7 @@
#include "v8.h"
#include "api.h"
-#include "ast.h"
+#include "ast-inl.h"
#include "bootstrapper.h"
#include "codegen.h"
#include "compiler.h"
@@ -41,8 +41,6 @@
#include "scopeinfo.h"
#include "string-stream.h"
-#include "ast-inl.h"
-
namespace v8 {
namespace internal {
@@ -129,7 +127,7 @@ void RegExpBuilder::FlushText() {
void RegExpBuilder::AddCharacter(uc16 c) {
pending_empty_ = false;
if (characters_ == NULL) {
- characters_ = new ZoneList<uc16>(4);
+ characters_ = new(zone()) ZoneList<uc16>(4);
}
characters_->Add(c);
LAST(ADD_CHAR);
@@ -594,7 +592,7 @@ Parser::Parser(Handle<Script> script,
FunctionLiteral* Parser::ParseProgram(Handle<String> source,
bool in_global_context,
StrictModeFlag strict_mode) {
- CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
+ CompilationZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
HistogramTimerScope timer(isolate()->counters()->parse());
isolate()->counters()->total_parse_size()->Increment(source->length());
@@ -641,7 +639,7 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source,
if (strict_mode == kStrictMode) {
top_scope_->EnableStrictMode();
}
- ZoneList<Statement*>* body = new ZoneList<Statement*>(16);
+ ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16);
bool ok = true;
int beg_loc = scanner().location().beg_pos;
ParseSourceElements(body, Token::EOS, &ok);
@@ -676,7 +674,7 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source,
}
FunctionLiteral* Parser::ParseLazy(CompilationInfo* info) {
- CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
+ CompilationZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
HistogramTimerScope timer(isolate()->counters()->parse_lazy());
Handle<String> source(String::cast(script_->source()));
isolate()->counters()->total_parse_size()->Increment(source->length());
@@ -1051,9 +1049,10 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder {
if (names_ == NULL) {
ASSERT(assigned_arguments_ == NULL);
ASSERT(assigned_constants_ == NULL);
- names_ = new ZoneStringList(4);
- assigned_arguments_ = new ZoneList<int>(4);
- assigned_constants_ = new ZoneObjectList(4);
+ Zone* zone = isolate_->zone();
+ names_ = new(zone) ZoneStringList(4);
+ assigned_arguments_ = new(zone) ZoneList<int>(4);
+ assigned_constants_ = new(zone) ZoneObjectList(4);
}
}
@@ -1303,7 +1302,10 @@ VariableProxy* Parser::Declare(Handle<String> name,
// to the corresponding activation frame at runtime if necessary.
// For instance declarations inside an eval scope need to be added
// to the calling function context.
- if (top_scope_->is_function_scope()) {
+ // Similarly, strict mode eval scope does not leak variable declarations to
+ // the caller's scope so we declare all locals, too.
+ if (top_scope_->is_function_scope() ||
+ top_scope_->is_strict_mode_eval_scope()) {
// Declare the variable in the function scope.
var = top_scope_->LocalLookup(name);
if (var == NULL) {
@@ -1652,7 +1654,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
if (top_scope_->is_global_scope()) {
// Compute the arguments for the runtime call.
- ZoneList<Expression*>* arguments = new ZoneList<Expression*>(3);
+ ZoneList<Expression*>* arguments = new(zone()) ZoneList<Expression*>(3);
// We have at least 1 parameter.
arguments->Add(new(zone()) Literal(name));
CallRuntime* initialize;
@@ -1769,7 +1771,7 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels,
*ok = false;
return NULL;
}
- if (labels == NULL) labels = new ZoneStringList(4);
+ if (labels == NULL) labels = new(zone()) ZoneStringList(4);
labels->Add(label);
// Remove the "ghost" variable that turned out to be a label
// from the top scope. This way, we don't try to resolve it
@@ -1910,7 +1912,7 @@ Block* Parser::WithHelper(Expression* obj,
bool is_catch_block,
bool* ok) {
// Parse the statement and collect escaping labels.
- ZoneList<Label*>* target_list = new ZoneList<Label*>(0);
+ ZoneList<Label*>* target_list = new(zone()) ZoneList<Label*>(0);
TargetCollector collector(target_list);
Statement* stat;
{ Target target(&this->target_stack_, &collector);
@@ -1985,7 +1987,7 @@ CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) {
}
Expect(Token::COLON, CHECK_OK);
int pos = scanner().location().beg_pos;
- ZoneList<Statement*>* statements = new ZoneList<Statement*>(5);
+ ZoneList<Statement*>* statements = new(zone()) ZoneList<Statement*>(5);
while (peek() != Token::CASE &&
peek() != Token::DEFAULT &&
peek() != Token::RBRACE) {
@@ -2011,7 +2013,7 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels,
Expect(Token::RPAREN, CHECK_OK);
bool default_seen = false;
- ZoneList<CaseClause*>* cases = new ZoneList<CaseClause*>(4);
+ ZoneList<CaseClause*>* cases = new(zone()) ZoneList<CaseClause*>(4);
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK);
@@ -2056,7 +2058,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
Expect(Token::TRY, CHECK_OK);
- ZoneList<Label*>* target_list = new ZoneList<Label*>(0);
+ ZoneList<Label*>* target_list = new(zone()) ZoneList<Label*>(0);
TargetCollector collector(target_list);
Block* try_block;
@@ -2079,7 +2081,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
// then we will need to collect jump targets from the catch block. Since
// we don't know yet if there will be a finally block, we always collect
// the jump targets.
- ZoneList<Label*>* catch_target_list = new ZoneList<Label*>(0);
+ ZoneList<Label*>* catch_target_list = new(zone()) ZoneList<Label*>(0);
TargetCollector catch_collector(catch_target_list);
bool has_catch = false;
if (tok == Token::CATCH) {
@@ -2490,7 +2492,7 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) {
x = NewCompareNode(cmp, x, y, position);
if (cmp != op) {
// The comparison was negated - add a NOT.
- x = new(zone()) UnaryOperation(Token::NOT, x);
+ x = new(zone()) UnaryOperation(Token::NOT, x, position);
}
} else {
@@ -2540,6 +2542,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
Token::Value op = peek();
if (Token::IsUnaryOp(op)) {
op = Next();
+ int position = scanner().location().beg_pos;
Expression* expression = ParseUnaryExpression(CHECK_OK);
// Compute some expressions involving only number literals.
@@ -2567,7 +2570,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
}
}
- return new(zone()) UnaryOperation(op, expression);
+ return new(zone()) UnaryOperation(op, expression, position);
} else if (Token::IsCountOp(op)) {
op = Next();
@@ -2724,7 +2727,9 @@ Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) {
if (!stack->is_empty()) {
int last = stack->pop();
- result = new(zone()) CallNew(result, new ZoneList<Expression*>(0), last);
+ result = new(zone()) CallNew(result,
+ new(zone()) ZoneList<Expression*>(0),
+ last);
}
return result;
}
@@ -2991,7 +2996,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
- ZoneList<Expression*>* values = new ZoneList<Expression*>(4);
+ ZoneList<Expression*>* values = new(zone()) ZoneList<Expression*>(4);
Expect(Token::LBRACK, CHECK_OK);
while (peek() != Token::RBRACK) {
Expression* elem;
@@ -3333,7 +3338,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
// )*[','] '}'
ZoneList<ObjectLiteral::Property*>* properties =
- new ZoneList<ObjectLiteral::Property*>(4);
+ new(zone()) ZoneList<ObjectLiteral::Property*>(4);
int number_of_boilerplate_properties = 0;
bool has_function = false;
@@ -3495,7 +3500,7 @@ ZoneList<Expression*>* Parser::ParseArguments(bool* ok) {
// Arguments ::
// '(' (AssignmentExpression)*[','] ')'
- ZoneList<Expression*>* result = new ZoneList<Expression*>(4);
+ ZoneList<Expression*>* result = new(zone()) ZoneList<Expression*>(4);
Expect(Token::LPAREN, CHECK_OK);
bool done = (peek() == Token::RPAREN);
while (!done) {
@@ -3538,7 +3543,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
int num_parameters = 0;
Scope* scope = NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
- ZoneList<Statement*>* body = new ZoneList<Statement*>(8);
+ ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8);
int materialized_literal_count;
int expected_property_count;
int start_pos;
@@ -3553,9 +3558,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
// '(' (Identifier)*[','] ')'
Expect(Token::LPAREN, CHECK_OK);
start_pos = scanner().location().beg_pos;
- Scanner::Location name_loc = Scanner::NoLocation();
- Scanner::Location dupe_loc = Scanner::NoLocation();
- Scanner::Location reserved_loc = Scanner::NoLocation();
+ Scanner::Location name_loc = Scanner::Location::invalid();
+ Scanner::Location dupe_loc = Scanner::Location::invalid();
+ Scanner::Location reserved_loc = Scanner::Location::invalid();
bool done = (peek() == Token::RPAREN);
while (!done) {
@@ -3876,12 +3881,14 @@ void Parser::CheckStrictModeLValue(Expression* expression,
}
-// Checks whether octal literal last seen is between beg_pos and end_pos.
-// If so, reports an error.
+// Checks whether an octal literal was last seen between beg_pos and end_pos.
+// If so, reports an error. Only called for strict mode.
void Parser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
- int octal = scanner().octal_position();
- if (beg_pos <= octal && octal <= end_pos) {
- ReportMessageAt(Scanner::Location(octal, octal + 1), "strict_octal_literal",
+ Scanner::Location octal = scanner().octal_position();
+ if (octal.IsValid() &&
+ beg_pos <= octal.beg_pos &&
+ octal.end_pos <= end_pos) {
+ ReportMessageAt(octal, "strict_octal_literal",
Vector<const char*>::empty());
scanner().clear_octal_position();
*ok = false;
@@ -4009,7 +4016,7 @@ Expression* Parser::NewThrowError(Handle<String> constructor,
Handle<JSArray> array = isolate()->factory()->NewJSArrayWithElements(elements,
TENURED);
- ZoneList<Expression*>* args = new ZoneList<Expression*>(2);
+ ZoneList<Expression*>* args = new(zone()) ZoneList<Expression*>(2);
args->Add(new(zone()) Literal(type));
args->Add(new(zone()) Literal(array));
return new(zone()) Throw(new(zone()) CallRuntime(constructor, NULL, args),
@@ -4017,186 +4024,6 @@ Expression* Parser::NewThrowError(Handle<String> constructor,
}
// ----------------------------------------------------------------------------
-// JSON
-
-Handle<Object> JsonParser::ParseJson(Handle<String> script,
- UC16CharacterStream* source) {
- scanner_.Initialize(source);
- stack_overflow_ = false;
- Handle<Object> result = ParseJsonValue();
- if (result.is_null() || scanner_.Next() != Token::EOS) {
- if (stack_overflow_) {
- // Scanner failed.
- isolate()->StackOverflow();
- } else {
- // Parse failed. Scanner's current token is the unexpected token.
- Token::Value token = scanner_.current_token();
-
- const char* message;
- const char* name_opt = NULL;
-
- switch (token) {
- case Token::EOS:
- message = "unexpected_eos";
- break;
- case Token::NUMBER:
- message = "unexpected_token_number";
- break;
- case Token::STRING:
- message = "unexpected_token_string";
- break;
- case Token::IDENTIFIER:
- case Token::FUTURE_RESERVED_WORD:
- message = "unexpected_token_identifier";
- break;
- default:
- message = "unexpected_token";
- name_opt = Token::String(token);
- ASSERT(name_opt != NULL);
- break;
- }
-
- Scanner::Location source_location = scanner_.location();
- Factory* factory = isolate()->factory();
- MessageLocation location(factory->NewScript(script),
- source_location.beg_pos,
- source_location.end_pos);
- Handle<JSArray> array;
- if (name_opt == NULL) {
- array = factory->NewJSArray(0);
- } else {
- Handle<String> name = factory->NewStringFromUtf8(CStrVector(name_opt));
- Handle<FixedArray> element = factory->NewFixedArray(1);
- element->set(0, *name);
- array = factory->NewJSArrayWithElements(element);
- }
- Handle<Object> result = factory->NewSyntaxError(message, array);
- isolate()->Throw(*result, &location);
- return Handle<Object>::null();
- }
- }
- return result;
-}
-
-
-Handle<String> JsonParser::GetString() {
- int literal_length = scanner_.literal_length();
- if (literal_length == 0) {
- return isolate()->factory()->empty_string();
- }
- if (scanner_.is_literal_ascii()) {
- return isolate()->factory()->NewStringFromAscii(
- scanner_.literal_ascii_string());
- } else {
- return isolate()->factory()->NewStringFromTwoByte(
- scanner_.literal_uc16_string());
- }
-}
-
-
-// Parse any JSON value.
-Handle<Object> JsonParser::ParseJsonValue() {
- Token::Value token = scanner_.Next();
- switch (token) {
- case Token::STRING:
- return GetString();
- case Token::NUMBER:
- return isolate()->factory()->NewNumber(scanner_.number());
- case Token::FALSE_LITERAL:
- return isolate()->factory()->false_value();
- case Token::TRUE_LITERAL:
- return isolate()->factory()->true_value();
- case Token::NULL_LITERAL:
- return isolate()->factory()->null_value();
- case Token::LBRACE:
- return ParseJsonObject();
- case Token::LBRACK:
- return ParseJsonArray();
- default:
- return ReportUnexpectedToken();
- }
-}
-
-
-// Parse a JSON object. Scanner must be right after '{' token.
-Handle<Object> JsonParser::ParseJsonObject() {
- Handle<JSFunction> object_constructor(
- isolate()->global_context()->object_function());
- Handle<JSObject> json_object =
- isolate()->factory()->NewJSObject(object_constructor);
- if (scanner_.peek() == Token::RBRACE) {
- scanner_.Next();
- } else {
- if (StackLimitCheck(isolate()).HasOverflowed()) {
- stack_overflow_ = true;
- return Handle<Object>::null();
- }
- do {
- if (scanner_.Next() != Token::STRING) {
- return ReportUnexpectedToken();
- }
- Handle<String> key = GetString();
- if (scanner_.Next() != Token::COLON) {
- return ReportUnexpectedToken();
- }
- Handle<Object> value = ParseJsonValue();
- if (value.is_null()) return Handle<Object>::null();
- uint32_t index;
- if (key->AsArrayIndex(&index)) {
- SetOwnElement(json_object, index, value, kNonStrictMode);
- } else if (key->Equals(isolate()->heap()->Proto_symbol())) {
- // We can't remove the __proto__ accessor since it's hardcoded
- // in several places. Instead go along and add the value as
- // the prototype of the created object if possible.
- SetPrototype(json_object, value);
- } else {
- SetLocalPropertyIgnoreAttributes(json_object, key, value, NONE);
- }
- } while (scanner_.Next() == Token::COMMA);
- if (scanner_.current_token() != Token::RBRACE) {
- return ReportUnexpectedToken();
- }
- }
- return json_object;
-}
-
-
-// Parse a JSON array. Scanner must be right after '[' token.
-Handle<Object> JsonParser::ParseJsonArray() {
- ZoneScope zone_scope(DELETE_ON_EXIT);
- ZoneList<Handle<Object> > elements(4);
-
- Token::Value token = scanner_.peek();
- if (token == Token::RBRACK) {
- scanner_.Next();
- } else {
- if (StackLimitCheck(isolate()).HasOverflowed()) {
- stack_overflow_ = true;
- return Handle<Object>::null();
- }
- do {
- Handle<Object> element = ParseJsonValue();
- if (element.is_null()) return Handle<Object>::null();
- elements.Add(element);
- token = scanner_.Next();
- } while (token == Token::COMMA);
- if (token != Token::RBRACK) {
- return ReportUnexpectedToken();
- }
- }
-
- // Allocate a fixed array with all the elements.
- Handle<FixedArray> fast_elements =
- isolate()->factory()->NewFixedArray(elements.length());
-
- for (int i = 0, n = elements.length(); i < n; i++) {
- fast_elements->set(i, *elements[i]);
- }
-
- return isolate()->factory()->NewJSArrayWithElements(fast_elements);
-}
-
-// ----------------------------------------------------------------------------
// Regular expressions
@@ -4383,7 +4210,8 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '.': {
Advance();
// everything except \x0a, \x0d, \u2028 and \u2029
- ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2);
+ ZoneList<CharacterRange>* ranges =
+ new(zone()) ZoneList<CharacterRange>(2);
CharacterRange::AddClassEscape('.', ranges);
RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
builder->AddAtom(atom);
@@ -4410,7 +4238,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
Advance(2);
} else {
if (captures_ == NULL) {
- captures_ = new ZoneList<RegExpCapture*>(2);
+ captures_ = new(zone()) ZoneList<RegExpCapture*>(2);
}
if (captures_started() >= kMaxCaptures) {
ReportError(CStrVector("Too many captures") CHECK_FAILED);
@@ -4453,7 +4281,8 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case 'd': case 'D': case 's': case 'S': case 'w': case 'W': {
uc32 c = Next();
Advance(2);
- ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2);
+ ZoneList<CharacterRange>* ranges =
+ new(zone()) ZoneList<CharacterRange>(2);
CharacterRange::AddClassEscape(c, ranges);
RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
builder->AddAtom(atom);
@@ -4949,7 +4778,7 @@ RegExpTree* RegExpParser::ParseCharacterClass() {
is_negated = true;
Advance();
}
- ZoneList<CharacterRange>* ranges = new ZoneList<CharacterRange>(2);
+ ZoneList<CharacterRange>* ranges = new(zone()) ZoneList<CharacterRange>(2);
while (has_more() && current() != ']') {
uc16 char_class = kNoCharClass;
CharacterRange first = ParseClassAtom(&char_class CHECK_FAILED);
diff --git a/src/parser.h b/src/parser.h
index 64f13030..a7132ce2 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -32,6 +32,7 @@
#include "ast.h"
#include "scanner.h"
#include "scopes.h"
+#include "preparse-data-format.h"
#include "preparse-data.h"
namespace v8 {
@@ -759,66 +760,6 @@ class CompileTimeValue: public AllStatic {
DISALLOW_IMPLICIT_CONSTRUCTORS(CompileTimeValue);
};
-
-// ----------------------------------------------------------------------------
-// JSON PARSING
-
-// JSON is a subset of JavaScript, as specified in, e.g., the ECMAScript 5
-// specification section 15.12.1 (and appendix A.8).
-// The grammar is given section 15.12.1.2 (and appendix A.8.2).
-class JsonParser BASE_EMBEDDED {
- public:
- // Parse JSON input as a single JSON value.
- // Returns null handle and sets exception if parsing failed.
- static Handle<Object> Parse(Handle<String> source) {
- if (source->IsExternalTwoByteString()) {
- ExternalTwoByteStringUC16CharacterStream stream(
- Handle<ExternalTwoByteString>::cast(source), 0, source->length());
- return JsonParser().ParseJson(source, &stream);
- } else {
- GenericStringUC16CharacterStream stream(source, 0, source->length());
- return JsonParser().ParseJson(source, &stream);
- }
- }
-
- private:
- JsonParser()
- : isolate_(Isolate::Current()),
- scanner_(isolate_->unicode_cache()) { }
- ~JsonParser() { }
-
- Isolate* isolate() { return isolate_; }
-
- // Parse a string containing a single JSON value.
- Handle<Object> ParseJson(Handle<String> script, UC16CharacterStream* source);
- // Parse a single JSON value from input (grammar production JSONValue).
- // A JSON value is either a (double-quoted) string literal, a number literal,
- // one of "true", "false", or "null", or an object or array literal.
- Handle<Object> ParseJsonValue();
- // Parse a JSON object literal (grammar production JSONObject).
- // An object literal is a squiggly-braced and comma separated sequence
- // (possibly empty) of key/value pairs, where the key is a JSON string
- // literal, the value is a JSON value, and the two are separated by a colon.
- // A JSON array dosn't allow numbers and identifiers as keys, like a
- // JavaScript array.
- Handle<Object> ParseJsonObject();
- // Parses a JSON array literal (grammar production JSONArray). An array
- // literal is a square-bracketed and comma separated sequence (possibly empty)
- // of JSON values.
- // A JSON array doesn't allow leaving out values from the sequence, nor does
- // it allow a terminal comma, like a JavaScript array does.
- Handle<Object> ParseJsonArray();
-
- // Mark that a parsing error has happened at the current token, and
- // return a null handle. Primarily for readability.
- Handle<Object> ReportUnexpectedToken() { return Handle<Object>::null(); }
- // Converts the currently parsed literal to a JavaScript String.
- Handle<String> GetString();
-
- Isolate* isolate_;
- JsonScanner scanner_;
- bool stack_overflow_;
-};
} } // namespace v8::internal
#endif // V8_PARSER_H_
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index 8b83f2bd..99264d2b 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -52,6 +52,7 @@
#undef MAP_TYPE
#include "v8.h"
+#include "v8threads.h"
#include "platform.h"
#include "vm-state-inl.h"
@@ -397,31 +398,6 @@ class Thread::PlatformData : public Malloced {
};
-ThreadHandle::ThreadHandle(Kind kind) {
- data_ = new PlatformData(kind);
-}
-
-
-void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
- data_->Initialize(kind);
-}
-
-
-ThreadHandle::~ThreadHandle() {
- delete data_;
-}
-
-
-bool ThreadHandle::IsSelf() const {
- return pthread_equal(data_->thread_, pthread_self());
-}
-
-
-bool ThreadHandle::IsValid() const {
- return data_->thread_ != kNoThread;
-}
-
-
Thread::Thread(Isolate* isolate, const Options& options)
: data_(new PlatformData),
isolate_(isolate),
@@ -448,8 +424,8 @@ static void* ThreadEntry(void* arg) {
// This is also initialized by the first argument to pthread_create() but we
// don't know which thread will run first (the original thread or the new
// one) so we initialize it here too.
- thread_->data_->thread_ = pthread_self();
- ASSERT(thread->IsValid());
+ thread->data()->thread_ = pthread_self();
+ ASSERT(thread->data()->thread_ != kNoThread);
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate());
thread->Run();
return NULL;
@@ -470,13 +446,13 @@ void Thread::Start() {
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
- pthread_create(&thread_handle_data()->thread_, attr_ptr, ThreadEntry, this);
- ASSERT(IsValid());
+ pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
+ ASSERT(data_->thread_ != kNoThread);
}
void Thread::Join() {
- pthread_join(thread_handle_data()->thread_, NULL);
+ pthread_join(data_->thread_, NULL);
}
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index c60658fb..d4d772ca 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -87,18 +87,30 @@ void OS::Setup() {
uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
srandom(static_cast<unsigned int>(seed));
limit_mutex = CreateMutex();
+
+#ifdef __arm__
+ // When running on ARM hardware check that the EABI used by V8 and
+ // by the C code is the same.
+ bool hard_float = OS::ArmUsingHardFloat();
+ if (hard_float) {
+#if !USE_EABI_HARDFLOAT
+ PrintF("ERROR: Binary compiled with -mfloat-abi=hard but without "
+ "-DUSE_EABI_HARDFLOAT\n");
+ exit(1);
+#endif
+ } else {
+#if USE_EABI_HARDFLOAT
+ PrintF("ERROR: Binary not compiled with -mfloat-abi=hard but with "
+ "-DUSE_EABI_HARDFLOAT\n");
+ exit(1);
+#endif
+ }
+#endif
}
uint64_t OS::CpuFeaturesImpliedByPlatform() {
-#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
- // Here gcc is telling us that we are on an ARM and gcc is assuming
- // that we have VFP3 instructions. If gcc can assume it then so can
- // we. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
- return 1u << VFP3 | 1u << ARMv7;
-#elif CAN_USE_ARMV7_INSTRUCTIONS
- return 1u << ARMv7;
-#elif(defined(__mips_hard_float) && __mips_hard_float != 0)
+#if(defined(__mips_hard_float) && __mips_hard_float != 0)
// Here gcc is telling us that we are on an MIPS and gcc is assuming that we
// have FPU instructions. If gcc can assume it then so can we.
return 1u << FPU;
@@ -142,6 +154,7 @@ static bool CPUInfoContainsString(const char * search_string) {
return false;
}
+
bool OS::ArmCpuHasFeature(CpuFeature feature) {
const char* search_string = NULL;
// Simple detection of VFP at runtime for Linux.
@@ -177,6 +190,50 @@ bool OS::ArmCpuHasFeature(CpuFeature feature) {
return false;
}
+
+
+// Simple helper function to detect whether the C code is compiled with
+// option -mfloat-abi=hard. The register d0 is loaded with 1.0 and the register
+// pair r0, r1 is loaded with 0.0. If -mfloat-abi=hard is pased to GCC then
+// calling this will return 1.0 and otherwise 0.0.
+static void ArmUsingHardFloatHelper() {
+ asm("mov r0, #0");
+#if defined(__VFP_FP__) && !defined(__SOFTFP__)
+ // Load 0x3ff00000 into r1 using instructions available in both ARM
+ // and Thumb mode.
+ asm("mov r1, #3");
+ asm("mov r2, #255");
+ asm("lsl r1, r1, #8");
+ asm("orr r1, r1, r2");
+ asm("lsl r1, r1, #20");
+ // For vmov d0, r0, r1 use ARM mode.
+#ifdef __thumb__
+ asm volatile(
+ "@ Enter ARM Mode \n\t"
+ " adr r3, 1f \n\t"
+ " bx r3 \n\t"
+ " .ALIGN 4 \n\t"
+ " .ARM \n"
+ "1: vmov d0, r0, r1 \n\t"
+ "@ Enter THUMB Mode\n\t"
+ " adr r3, 2f+1 \n\t"
+ " bx r3 \n\t"
+ " .THUMB \n"
+ "2: \n\t");
+#else
+ asm("vmov d0, r0, r1");
+#endif // __thumb__
+#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
+ asm("mov r1, #0");
+}
+
+
+bool OS::ArmUsingHardFloat() {
+ // Cast helper function from returning void to returning double.
+ typedef double (*F)();
+ F f = FUNCTION_CAST<F>(FUNCTION_ADDR(ArmUsingHardFloatHelper));
+ return f() == 1.0;
+}
#endif // def __arm__
diff --git a/src/platform-nullos.cc b/src/platform-nullos.cc
index aacad149..295482d0 100644
--- a/src/platform-nullos.cc
+++ b/src/platform-nullos.cc
@@ -186,6 +186,11 @@ bool OS::ArmCpuHasFeature(CpuFeature feature) {
}
+bool OS::ArmUsingHardFloat() {
+ UNIMPLEMENTED();
+}
+
+
bool OS::IsOutsideAllocatedSpace(void* address) {
UNIMPLEMENTED();
return false;
diff --git a/src/platform-win32.cc b/src/platform-win32.cc
index 8673f047..390d3d99 100644
--- a/src/platform-win32.cc
+++ b/src/platform-win32.cc
@@ -407,13 +407,11 @@ void Time::TzSet() {
}
// Make standard and DST timezone names.
- OS::SNPrintF(Vector<char>(std_tz_name_, kTzNameSize),
- "%S",
- tzinfo_.StandardName);
+ WideCharToMultiByte(CP_UTF8, 0, tzinfo_.StandardName, -1,
+ std_tz_name_, kTzNameSize, NULL, NULL);
std_tz_name_[kTzNameSize - 1] = '\0';
- OS::SNPrintF(Vector<char>(dst_tz_name_, kTzNameSize),
- "%S",
- tzinfo_.DaylightName);
+ WideCharToMultiByte(CP_UTF8, 0, tzinfo_.DaylightName, -1,
+ dst_tz_name_, kTzNameSize, NULL, NULL);
dst_tz_name_[kTzNameSize - 1] = '\0';
// If OS returned empty string or resource id (like "@tzres.dll,-211")
diff --git a/src/platform.h b/src/platform.h
index fc417ef3..725008a7 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -294,6 +294,10 @@ class OS {
// Support runtime detection of VFP3 on ARM CPUs.
static bool ArmCpuHasFeature(CpuFeature feature);
+ // Support runtime detection of whether the hard float option of the
+ // EABI is used.
+ static bool ArmUsingHardFloat();
+
// Support runtime detection of FPU on MIPS CPUs.
static bool MipsCpuHasFeature(CpuFeature feature);
diff --git a/src/preparse-data-format.h b/src/preparse-data-format.h
new file mode 100644
index 00000000..64c4f181
--- /dev/null
+++ b/src/preparse-data-format.h
@@ -0,0 +1,62 @@
+// Copyright 2010 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_PREPARSE_DATA_FORMAT_H_
+#define V8_PREPARSE_DATA_FORMAT_H_
+
+namespace v8 {
+namespace internal {
+
+// Generic and general data used by preparse data recorders and readers.
+
+struct PreparseDataConstants {
+ public:
+ // Layout and constants of the preparse data exchange format.
+ static const unsigned kMagicNumber = 0xBadDead;
+ static const unsigned kCurrentVersion = 6;
+
+ static const int kMagicOffset = 0;
+ static const int kVersionOffset = 1;
+ static const int kHasErrorOffset = 2;
+ static const int kFunctionsSizeOffset = 3;
+ static const int kSymbolCountOffset = 4;
+ static const int kSizeOffset = 5;
+ static const int kHeaderSize = 6;
+
+ // If encoding a message, the following positions are fixed.
+ static const int kMessageStartPos = 0;
+ static const int kMessageEndPos = 1;
+ static const int kMessageArgCountPos = 2;
+ static const int kMessageTextPos = 3;
+
+ static const unsigned char kNumberTerminator = 0x80u;
+};
+
+
+} } // namespace v8::internal.
+
+#endif // V8_PREPARSE_DATA_FORMAT_H_
diff --git a/src/preparse-data.cc b/src/preparse-data.cc
index 92a0338a..98c343e7 100644
--- a/src/preparse-data.cc
+++ b/src/preparse-data.cc
@@ -26,15 +26,13 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/v8stdint.h"
-#include "globals.h"
-#include "checks.h"
-#include "allocation.h"
-#include "utils.h"
-#include "list-inl.h"
-#include "hashmap.h"
+#include "preparse-data-format.h"
#include "preparse-data.h"
+#include "checks.h"
+#include "globals.h"
+#include "hashmap.h"
namespace v8 {
namespace internal {
@@ -76,7 +74,7 @@ void FunctionLoggingParserRecorder::LogMessage(int start_pos,
function_store_.Add((arg_opt == NULL) ? 0 : 1);
STATIC_ASSERT(PreparseDataConstants::kMessageTextPos == 3);
WriteString(CStrVector(message));
- if (arg_opt) WriteString(CStrVector(arg_opt));
+ if (arg_opt != NULL) WriteString(CStrVector(arg_opt));
is_recording_ = false;
}
diff --git a/src/preparse-data.h b/src/preparse-data.h
index bb5707b6..551d2e84 100644
--- a/src/preparse-data.h
+++ b/src/preparse-data.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,40 +25,16 @@
// (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_PREPARSER_DATA_H_
-#define V8_PREPARSER_DATA_H_
+#ifndef V8_PREPARSE_DATA_H_
+#define V8_PREPARSE_DATA_H_
+#include "allocation.h"
#include "hashmap.h"
+#include "utils-inl.h"
namespace v8 {
namespace internal {
-// Generic and general data used by preparse data recorders and readers.
-
-class PreparseDataConstants : public AllStatic {
- public:
- // Layout and constants of the preparse data exchange format.
- static const unsigned kMagicNumber = 0xBadDead;
- static const unsigned kCurrentVersion = 6;
-
- static const int kMagicOffset = 0;
- static const int kVersionOffset = 1;
- static const int kHasErrorOffset = 2;
- static const int kFunctionsSizeOffset = 3;
- static const int kSymbolCountOffset = 4;
- static const int kSizeOffset = 5;
- static const int kHeaderSize = 6;
-
- // If encoding a message, the following positions are fixed.
- static const int kMessageStartPos = 0;
- static const int kMessageEndPos = 1;
- static const int kMessageArgCountPos = 2;
- static const int kMessageTextPos = 3;
-
- static const byte kNumberTerminator = 0x80u;
-};
-
-
// ----------------------------------------------------------------------------
// ParserRecorder - Logging of preparser data.
@@ -246,4 +222,4 @@ class CompleteParserRecorder: public FunctionLoggingParserRecorder {
} } // namespace v8::internal.
-#endif // V8_PREPARSER_DATA_H_
+#endif // V8_PREPARSE_DATA_H_
diff --git a/src/preparser-api.cc b/src/preparser-api.cc
index 9646eb6f..a0d13edb 100644
--- a/src/preparser-api.cc
+++ b/src/preparser-api.cc
@@ -33,6 +33,7 @@
#include "utils.h"
#include "list.h"
#include "scanner-base.h"
+#include "preparse-data-format.h"
#include "preparse-data.h"
#include "preparser.h"
diff --git a/src/preparser.cc b/src/preparser.cc
index fec1567c..86db379d 100644
--- a/src/preparser.cc
+++ b/src/preparser.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -34,6 +34,7 @@
#include "list.h"
#include "scanner-base.h"
+#include "preparse-data-format.h"
#include "preparse-data.h"
#include "preparser.h"
@@ -55,13 +56,6 @@ namespace preparser {
namespace i = ::v8::internal;
-#define CHECK_OK ok); \
- if (!*ok) return -1; \
- ((void)0
-#define DUMMY ) // to make indentation work
-#undef DUMMY
-
-
void PreParser::ReportUnexpectedToken(i::Token::Value token) {
// We don't report stack overflows here, to avoid increasing the
// stack depth even further. Instead we report it after parsing is
@@ -94,18 +88,53 @@ void PreParser::ReportUnexpectedToken(i::Token::Value token) {
}
+// Checks whether octal literal last seen is between beg_pos and end_pos.
+// If so, reports an error.
+void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) {
+ i::Scanner::Location octal = scanner_->octal_position();
+ if (beg_pos <= octal.beg_pos && octal.end_pos <= end_pos) {
+ ReportMessageAt(octal.beg_pos, octal.end_pos, "strict_octal_literal", NULL);
+ scanner_->clear_octal_position();
+ *ok = false;
+ }
+}
+
+
+#define CHECK_OK ok); \
+ if (!*ok) return kUnknownSourceElements; \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
bool* ok) {
// SourceElements ::
// (Statement)* <end_token>
+ bool allow_directive_prologue = true;
while (peek() != end_token) {
- ParseStatement(CHECK_OK);
+ Statement statement = ParseStatement(CHECK_OK);
+ if (allow_directive_prologue) {
+ if (statement.IsUseStrictLiteral()) {
+ set_strict_mode();
+ } else if (!statement.IsStringLiteral()) {
+ allow_directive_prologue = false;
+ }
+ }
}
return kUnknownSourceElements;
}
+#undef CHECK_OK
+#define CHECK_OK ok); \
+ if (!*ok) return Statement::Default(); \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
PreParser::Statement PreParser::ParseStatement(bool* ok) {
// Statement ::
// Block
@@ -142,10 +171,10 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) {
case i::Token::SEMICOLON:
Next();
- return kUnknownStatement;
+ return Statement::Default();
case i::Token::IF:
- return ParseIfStatement(ok);
+ return ParseIfStatement(ok);
case i::Token::DO:
return ParseDoWhileStatement(ok);
@@ -196,9 +225,24 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
Expect(i::Token::FUNCTION, CHECK_OK);
- ParseIdentifier(CHECK_OK);
- ParseFunctionLiteral(CHECK_OK);
- return kUnknownStatement;
+
+ Identifier identifier = ParseIdentifier(CHECK_OK);
+ i::Scanner::Location location = scanner_->location();
+
+ Expression function_value = ParseFunctionLiteral(CHECK_OK);
+
+ if (function_value.IsStrictFunction() &&
+ !identifier.IsValidStrictVariable()) {
+ // Strict mode violation, using either reserved word or eval/arguments
+ // as name of strict function.
+ const char* type = "strict_function_name";
+ if (identifier.IsFutureReserved()) {
+ type = "strict_reserved_word";
+ }
+ ReportMessageAt(location.beg_pos, location.end_pos, type, NULL);
+ *ok = false;
+ }
+ return Statement::FunctionDeclaration();
}
@@ -221,7 +265,7 @@ PreParser::Statement PreParser::ParseNativeDeclaration(bool* ok) {
}
Expect(i::Token::RPAREN, CHECK_OK);
Expect(i::Token::SEMICOLON, CHECK_OK);
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -234,10 +278,18 @@ PreParser::Statement PreParser::ParseBlock(bool* ok) {
//
Expect(i::Token::LBRACE, CHECK_OK);
while (peek() != i::Token::RBRACE) {
- ParseStatement(CHECK_OK);
+ i::Scanner::Location start_location = scanner_->peek_location();
+ Statement statement = ParseStatement(CHECK_OK);
+ i::Scanner::Location end_location = scanner_->location();
+ if (strict_mode() && statement.IsFunctionDeclaration()) {
+ ReportMessageAt(start_location.beg_pos, end_location.end_pos,
+ "strict_function", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
}
- Expect(i::Token::RBRACE, CHECK_OK);
- return kUnknownStatement;
+ Expect(i::Token::RBRACE, ok);
+ return Statement::Default();
}
@@ -265,10 +317,17 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
if (peek() == i::Token::VAR) {
Consume(i::Token::VAR);
} else if (peek() == i::Token::CONST) {
+ if (strict_mode()) {
+ i::Scanner::Location location = scanner_->peek_location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "strict_const", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
Consume(i::Token::CONST);
} else {
*ok = false;
- return 0;
+ return Statement::Default();
}
// The scope of a variable/const declared anywhere inside a function
@@ -277,7 +336,14 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
do {
// Parse variable name.
if (nvars > 0) Consume(i::Token::COMMA);
- ParseIdentifier(CHECK_OK);
+ Identifier identifier = ParseIdentifier(CHECK_OK);
+ if (strict_mode() && !identifier.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_var_name",
+ identifier,
+ ok);
+ return Statement::Default();
+ }
nvars++;
if (peek() == i::Token::ASSIGN) {
Expect(i::Token::ASSIGN, CHECK_OK);
@@ -286,24 +352,33 @@ PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
} while (peek() == i::Token::COMMA);
if (num_decl != NULL) *num_decl = nvars;
- return kUnknownStatement;
+ return Statement::Default();
}
-PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(
- bool* ok) {
+PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
// ExpressionStatement | LabelledStatement ::
// Expression ';'
// Identifier ':' Statement
Expression expr = ParseExpression(true, CHECK_OK);
- if (peek() == i::Token::COLON && expr == kIdentifierExpression) {
- Consume(i::Token::COLON);
- return ParseStatement(ok);
+ if (peek() == i::Token::COLON && expr.IsRawIdentifier()) {
+ if (!strict_mode() || !expr.AsIdentifier().IsFutureReserved()) {
+ Consume(i::Token::COLON);
+ i::Scanner::Location start_location = scanner_->peek_location();
+ Statement statement = ParseStatement(CHECK_OK);
+ if (strict_mode() && statement.IsFunctionDeclaration()) {
+ i::Scanner::Location end_location = scanner_->location();
+ ReportMessageAt(start_location.beg_pos, end_location.end_pos,
+ "strict_function", NULL);
+ *ok = false;
+ }
+ return Statement::Default();
+ }
}
// Parsed expression statement.
ExpectSemicolon(CHECK_OK);
- return kUnknownStatement;
+ return Statement::ExpressionStatement(expr);
}
@@ -320,7 +395,7 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
Next();
ParseStatement(CHECK_OK);
}
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -337,7 +412,7 @@ PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
ParseIdentifier(CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -354,7 +429,7 @@ PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
ParseIdentifier(CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -380,7 +455,7 @@ PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
ParseExpression(true, CHECK_OK);
}
ExpectSemicolon(CHECK_OK);
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -388,6 +463,13 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
// WithStatement ::
// 'with' '(' Expression ')' Statement
Expect(i::Token::WITH, CHECK_OK);
+ if (strict_mode()) {
+ i::Scanner::Location location = scanner_->location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "strict_mode_with", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
Expect(i::Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(i::Token::RPAREN, CHECK_OK);
@@ -395,7 +477,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
scope_->EnterWith();
ParseStatement(CHECK_OK);
scope_->LeaveWith();
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -419,13 +501,20 @@ PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) {
Expect(i::Token::DEFAULT, CHECK_OK);
Expect(i::Token::COLON, CHECK_OK);
} else {
- ParseStatement(CHECK_OK);
+ i::Scanner::Location start_location = scanner_->peek_location();
+ Statement statement = ParseStatement(CHECK_OK);
+ if (strict_mode() && statement.IsFunctionDeclaration()) {
+ i::Scanner::Location end_location = scanner_->location();
+ ReportMessageAt(start_location.beg_pos, end_location.end_pos,
+ "strict_function", NULL);
+ *ok = false;
+ return Statement::Default();
+ }
}
token = peek();
}
- Expect(i::Token::RBRACE, CHECK_OK);
-
- return kUnknownStatement;
+ Expect(i::Token::RBRACE, ok);
+ return Statement::Default();
}
@@ -438,8 +527,8 @@ PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
Expect(i::Token::WHILE, CHECK_OK);
Expect(i::Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
- Expect(i::Token::RPAREN, CHECK_OK);
- return kUnknownStatement;
+ Expect(i::Token::RPAREN, ok);
+ return Statement::Default();
}
@@ -451,8 +540,8 @@ PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
Expect(i::Token::LPAREN, CHECK_OK);
ParseExpression(true, CHECK_OK);
Expect(i::Token::RPAREN, CHECK_OK);
- ParseStatement(CHECK_OK);
- return kUnknownStatement;
+ ParseStatement(ok);
+ return Statement::Default();
}
@@ -472,7 +561,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
Expect(i::Token::RPAREN, CHECK_OK);
ParseStatement(CHECK_OK);
- return kUnknownStatement;
+ return Statement::Default();
}
} else {
ParseExpression(false, CHECK_OK);
@@ -482,7 +571,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
Expect(i::Token::RPAREN, CHECK_OK);
ParseStatement(CHECK_OK);
- return kUnknownStatement;
+ return Statement::Default();
}
}
}
@@ -500,8 +589,8 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
}
Expect(i::Token::RPAREN, CHECK_OK);
- ParseStatement(CHECK_OK);
- return kUnknownStatement;
+ ParseStatement(ok);
+ return Statement::Default();
}
@@ -515,12 +604,11 @@ PreParser::Statement PreParser::ParseThrowStatement(bool* ok) {
ReportMessageAt(pos.beg_pos, pos.end_pos,
"newline_after_throw", NULL);
*ok = false;
- return kUnknownStatement;
+ return Statement::Default();
}
ParseExpression(true, CHECK_OK);
- ExpectSemicolon(CHECK_OK);
-
- return kUnknownStatement;
+ ExpectSemicolon(ok);
+ return Statement::Default();
}
@@ -547,12 +635,19 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
if (peek() == i::Token::CATCH) {
Consume(i::Token::CATCH);
Expect(i::Token::LPAREN, CHECK_OK);
- ParseIdentifier(CHECK_OK);
+ Identifier id = ParseIdentifier(CHECK_OK);
+ if (strict_mode() && !id.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_catch_variable",
+ id,
+ ok);
+ return Statement::Default();
+ }
Expect(i::Token::RPAREN, CHECK_OK);
scope_->EnterWith();
ParseBlock(ok);
scope_->LeaveWith();
- if (!*ok) return kUnknownStatement;
+ if (!*ok) Statement::Default();
catch_or_finally_seen = true;
}
if (peek() == i::Token::FINALLY) {
@@ -563,7 +658,7 @@ PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
if (!catch_or_finally_seen) {
*ok = false;
}
- return kUnknownStatement;
+ return Statement::Default();
}
@@ -575,11 +670,19 @@ PreParser::Statement PreParser::ParseDebuggerStatement(bool* ok) {
// 'debugger' ';'
Expect(i::Token::DEBUGGER, CHECK_OK);
- ExpectSemicolon(CHECK_OK);
- return kUnknownStatement;
+ ExpectSemicolon(ok);
+ return Statement::Default();
}
+#undef CHECK_OK
+#define CHECK_OK ok); \
+ if (!*ok) return Expression::Default(); \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
// Precedence = 1
PreParser::Expression PreParser::ParseExpression(bool accept_IN, bool* ok) {
// Expression ::
@@ -590,7 +693,7 @@ PreParser::Expression PreParser::ParseExpression(bool accept_IN, bool* ok) {
while (peek() == i::Token::COMMA) {
Expect(i::Token::COMMA, CHECK_OK);
ParseAssignmentExpression(accept_IN, CHECK_OK);
- result = kUnknownExpression;
+ result = Expression::Default();
}
return result;
}
@@ -603,6 +706,7 @@ PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN,
// ConditionalExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
+ i::Scanner::Location before = scanner_->peek_location();
Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK);
if (!i::Token::IsAssignmentOp(peek())) {
@@ -610,14 +714,23 @@ PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN,
return expression;
}
+ if (strict_mode() && expression.IsIdentifier() &&
+ expression.AsIdentifier().IsEvalOrArguments()) {
+ i::Scanner::Location after = scanner_->location();
+ ReportMessageAt(before.beg_pos, after.end_pos,
+ "strict_lhs_assignment", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
+
i::Token::Value op = Next(); // Get assignment operator.
ParseAssignmentExpression(accept_IN, CHECK_OK);
- if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) {
+ if ((op == i::Token::ASSIGN) && expression.IsThisProperty()) {
scope_->AddProperty();
}
- return kUnknownExpression;
+ return Expression::Default();
}
@@ -638,7 +751,7 @@ PreParser::Expression PreParser::ParseConditionalExpression(bool accept_IN,
ParseAssignmentExpression(true, CHECK_OK);
Expect(i::Token::COLON, CHECK_OK);
ParseAssignmentExpression(accept_IN, CHECK_OK);
- return kUnknownExpression;
+ return Expression::Default();
}
@@ -660,7 +773,7 @@ PreParser::Expression PreParser::ParseBinaryExpression(int prec,
while (Precedence(peek(), accept_IN) == prec1) {
Next();
ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK);
- result = kUnknownExpression;
+ result = Expression::Default();
}
}
return result;
@@ -681,10 +794,22 @@ PreParser::Expression PreParser::ParseUnaryExpression(bool* ok) {
// '!' UnaryExpression
i::Token::Value op = peek();
- if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) {
+ if (i::Token::IsUnaryOp(op)) {
op = Next();
ParseUnaryExpression(ok);
- return kUnknownExpression;
+ return Expression::Default();
+ } else if (i::Token::IsCountOp(op)) {
+ op = Next();
+ i::Scanner::Location before = scanner_->peek_location();
+ Expression expression = ParseUnaryExpression(CHECK_OK);
+ if (strict_mode() && expression.IsIdentifier() &&
+ expression.AsIdentifier().IsEvalOrArguments()) {
+ i::Scanner::Location after = scanner_->location();
+ ReportMessageAt(before.beg_pos, after.end_pos,
+ "strict_lhs_prefix", NULL);
+ *ok = false;
+ }
+ return Expression::Default();
} else {
return ParsePostfixExpression(ok);
}
@@ -695,11 +820,20 @@ PreParser::Expression PreParser::ParsePostfixExpression(bool* ok) {
// PostfixExpression ::
// LeftHandSideExpression ('++' | '--')?
+ i::Scanner::Location before = scanner_->peek_location();
Expression expression = ParseLeftHandSideExpression(CHECK_OK);
if (!scanner_->has_line_terminator_before_next() &&
i::Token::IsCountOp(peek())) {
+ if (strict_mode() && expression.IsIdentifier() &&
+ expression.AsIdentifier().IsEvalOrArguments()) {
+ i::Scanner::Location after = scanner_->location();
+ ReportMessageAt(before.beg_pos, after.end_pos,
+ "strict_lhs_postfix", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
Next();
- return kUnknownExpression;
+ return Expression::Default();
}
return expression;
}
@@ -709,7 +843,7 @@ PreParser::Expression PreParser::ParseLeftHandSideExpression(bool* ok) {
// LeftHandSideExpression ::
// (NewExpression | MemberExpression) ...
- Expression result;
+ Expression result = Expression::Default();
if (peek() == i::Token::NEW) {
result = ParseNewExpression(CHECK_OK);
} else {
@@ -722,27 +856,27 @@ PreParser::Expression PreParser::ParseLeftHandSideExpression(bool* ok) {
Consume(i::Token::LBRACK);
ParseExpression(true, CHECK_OK);
Expect(i::Token::RBRACK, CHECK_OK);
- if (result == kThisExpression) {
- result = kThisPropertyExpression;
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
} else {
- result = kUnknownExpression;
+ result = Expression::Default();
}
break;
}
case i::Token::LPAREN: {
ParseArguments(CHECK_OK);
- result = kUnknownExpression;
+ result = Expression::Default();
break;
}
case i::Token::PERIOD: {
Consume(i::Token::PERIOD);
ParseIdentifierName(CHECK_OK);
- if (result == kThisExpression) {
- result = kThisPropertyExpression;
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
} else {
- result = kUnknownExpression;
+ result = Expression::Default();
}
break;
}
@@ -788,13 +922,21 @@ PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
// ('[' Expression ']' | '.' Identifier | Arguments)*
// Parse the initial primary or function expression.
- Expression result = kUnknownExpression;
+ Expression result = Expression::Default();
if (peek() == i::Token::FUNCTION) {
Consume(i::Token::FUNCTION);
+ Identifier identifier = Identifier::Default();
if (peek_any_identifier()) {
- ParseIdentifier(CHECK_OK);
+ identifier = ParseIdentifier(CHECK_OK);
}
result = ParseFunctionLiteral(CHECK_OK);
+ if (result.IsStrictFunction() && !identifier.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_function_name",
+ identifier,
+ ok);
+ return Expression::Default();
+ }
} else {
result = ParsePrimaryExpression(CHECK_OK);
}
@@ -805,20 +947,20 @@ PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
Consume(i::Token::LBRACK);
ParseExpression(true, CHECK_OK);
Expect(i::Token::RBRACK, CHECK_OK);
- if (result == kThisExpression) {
- result = kThisPropertyExpression;
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
} else {
- result = kUnknownExpression;
+ result = Expression::Default();
}
break;
}
case i::Token::PERIOD: {
Consume(i::Token::PERIOD);
ParseIdentifierName(CHECK_OK);
- if (result == kThisExpression) {
- result = kThisPropertyExpression;
+ if (result.IsThis()) {
+ result = Expression::ThisProperty();
} else {
- result = kUnknownExpression;
+ result = Expression::Default();
}
break;
}
@@ -827,7 +969,7 @@ PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
// Consume one of the new prefixes (already parsed).
ParseArguments(CHECK_OK);
new_count--;
- result = kUnknownExpression;
+ result = Expression::Default();
break;
}
default:
@@ -851,18 +993,27 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
// RegExpLiteral
// '(' Expression ')'
- Expression result = kUnknownExpression;
+ Expression result = Expression::Default();
switch (peek()) {
case i::Token::THIS: {
Next();
- result = kThisExpression;
+ result = Expression::This();
break;
}
- case i::Token::IDENTIFIER:
- case i::Token::FUTURE_RESERVED_WORD: {
- ParseIdentifier(CHECK_OK);
- result = kIdentifierExpression;
+ case i::Token::FUTURE_RESERVED_WORD:
+ if (strict_mode()) {
+ Next();
+ i::Scanner::Location location = scanner_->location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "strict_reserved_word", NULL);
+ *ok = false;
+ return Expression::Default();
+ }
+ // FALLTHROUGH
+ case i::Token::IDENTIFIER: {
+ Identifier id = ParseIdentifier(CHECK_OK);
+ result = Expression::FromIdentifier(id);
break;
}
@@ -900,7 +1051,7 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
parenthesized_function_ = (peek() == i::Token::FUNCTION);
result = ParseExpression(true, CHECK_OK);
Expect(i::Token::RPAREN, CHECK_OK);
- if (result == kIdentifierExpression) result = kUnknownExpression;
+ result = result.Parenthesize();
break;
case i::Token::MOD:
@@ -910,7 +1061,7 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
default: {
Next();
*ok = false;
- return kUnknownExpression;
+ return Expression::Default();
}
}
@@ -933,7 +1084,7 @@ PreParser::Expression PreParser::ParseArrayLiteral(bool* ok) {
Expect(i::Token::RBRACK, CHECK_OK);
scope_->NextMaterializedLiteralIndex();
- return kUnknownExpression;
+ return Expression::Default();
}
@@ -962,7 +1113,7 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
name != i::Token::STRING &&
!is_keyword) {
*ok = false;
- return kUnknownExpression;
+ return Expression::Default();
}
if (!is_keyword) {
LogSymbol();
@@ -988,7 +1139,7 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
} else {
// Unexpected token.
*ok = false;
- return kUnknownExpression;
+ return Expression::Default();
}
}
@@ -1001,7 +1152,7 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
Expect(i::Token::RBRACE, CHECK_OK);
scope_->NextMaterializedLiteralIndex();
- return kUnknownExpression;
+ return Expression::Default();
}
@@ -1013,7 +1164,7 @@ PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal,
ReportMessageAt(location.beg_pos, location.end_pos,
"unterminated_regexp", NULL);
*ok = false;
- return kUnknownExpression;
+ return Expression::Default();
}
scope_->NextMaterializedLiteralIndex();
@@ -1024,10 +1175,10 @@ PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal,
ReportMessageAt(location.beg_pos, location.end_pos,
"invalid_regexp_flags", NULL);
*ok = false;
- return kUnknownExpression;
+ return Expression::Default();
}
Next();
- return kUnknownExpression;
+ return Expression::Default();
}
@@ -1035,16 +1186,21 @@ PreParser::Arguments PreParser::ParseArguments(bool* ok) {
// Arguments ::
// '(' (AssignmentExpression)*[','] ')'
- Expect(i::Token::LPAREN, CHECK_OK);
+ Expect(i::Token::LPAREN, ok);
+ if (!*ok) return -1;
bool done = (peek() == i::Token::RPAREN);
int argc = 0;
while (!done) {
- ParseAssignmentExpression(true, CHECK_OK);
+ ParseAssignmentExpression(true, ok);
+ if (!*ok) return -1;
argc++;
done = (peek() == i::Token::RPAREN);
- if (!done) Expect(i::Token::COMMA, CHECK_OK);
+ if (!done) {
+ Expect(i::Token::COMMA, ok);
+ if (!*ok) return -1;
+ }
}
- Expect(i::Token::RPAREN, CHECK_OK);
+ Expect(i::Token::RPAREN, ok);
return argc;
}
@@ -1057,13 +1213,19 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
ScopeType outer_scope_type = scope_->type();
bool inside_with = scope_->IsInsideWith();
Scope function_scope(&scope_, kFunctionScope);
-
// FormalParameterList ::
// '(' (Identifier)*[','] ')'
Expect(i::Token::LPAREN, CHECK_OK);
+ int start_position = scanner_->location().beg_pos;
bool done = (peek() == i::Token::RPAREN);
while (!done) {
- ParseIdentifier(CHECK_OK);
+ Identifier id = ParseIdentifier(CHECK_OK);
+ if (!id.IsValidStrictVariable()) {
+ StrictModeIdentifierViolation(scanner_->location(),
+ "strict_param_name",
+ id,
+ CHECK_OK);
+ }
done = (peek() == i::Token::RPAREN);
if (!done) {
Expect(i::Token::COMMA, CHECK_OK);
@@ -1086,7 +1248,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
log_->PauseRecording();
ParseSourceElements(i::Token::RBRACE, ok);
log_->ResumeRecording();
- if (!*ok) return kUnknownExpression;
+ if (!*ok) Expression::Default();
Expect(i::Token::RBRACE, CHECK_OK);
@@ -1099,7 +1261,15 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
ParseSourceElements(i::Token::RBRACE, CHECK_OK);
Expect(i::Token::RBRACE, CHECK_OK);
}
- return kUnknownExpression;
+
+ if (strict_mode()) {
+ int end_position = scanner_->location().end_pos;
+ CheckOctalLiteral(start_position, end_position, CHECK_OK);
+ CheckDelayedStrictModeViolation(start_position, end_position, CHECK_OK);
+ return Expression::StrictFunction();
+ }
+
+ return Expression::Default();
}
@@ -1109,11 +1279,13 @@ PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
Expect(i::Token::MOD, CHECK_OK);
ParseIdentifier(CHECK_OK);
- ParseArguments(CHECK_OK);
+ ParseArguments(ok);
- return kUnknownExpression;
+ return Expression::Default();
}
+#undef CHECK_OK
+
void PreParser::ExpectSemicolon(bool* ok) {
// Check for automatic semicolon insertion according to
@@ -1142,27 +1314,98 @@ void PreParser::LogSymbol() {
}
-PreParser::Identifier PreParser::GetIdentifierSymbol() {
+PreParser::Expression PreParser::GetStringSymbol() {
+ const int kUseStrictLength = 10;
+ const char* kUseStrictChars = "use strict";
LogSymbol();
- return kUnknownIdentifier;
+ if (scanner_->is_literal_ascii() &&
+ scanner_->literal_length() == kUseStrictLength &&
+ !scanner_->literal_contains_escapes() &&
+ !strncmp(scanner_->literal_ascii_string().start(), kUseStrictChars,
+ kUseStrictLength)) {
+ return Expression::UseStrictStringLiteral();
+ }
+ return Expression::StringLiteral();
}
-PreParser::Expression PreParser::GetStringSymbol() {
+PreParser::Identifier PreParser::GetIdentifierSymbol() {
LogSymbol();
- return kUnknownExpression;
+ if (scanner_->current_token() == i::Token::FUTURE_RESERVED_WORD) {
+ return Identifier::FutureReserved();
+ }
+ if (scanner_->is_literal_ascii()) {
+ // Detect strict-mode poison words.
+ if (scanner_->literal_length() == 4 &&
+ !strncmp(scanner_->literal_ascii_string().start(), "eval", 4)) {
+ return Identifier::Eval();
+ }
+ if (scanner_->literal_length() == 9 &&
+ !strncmp(scanner_->literal_ascii_string().start(), "arguments", 9)) {
+ return Identifier::Arguments();
+ }
+ }
+ return Identifier::Default();
}
PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
if (!Check(i::Token::FUTURE_RESERVED_WORD)) {
Expect(i::Token::IDENTIFIER, ok);
+ if (!*ok) return Identifier::Default();
}
- if (!*ok) return kUnknownIdentifier;
return GetIdentifierSymbol();
}
+void PreParser::SetStrictModeViolation(i::Scanner::Location location,
+ const char* type,
+ bool* ok) {
+ if (strict_mode()) {
+ ReportMessageAt(location.beg_pos, location.end_pos, type, NULL);
+ *ok = false;
+ return;
+ }
+ // Delay report in case this later turns out to be strict code
+ // (i.e., for function names and parameters prior to a "use strict"
+ // directive).
+ strict_mode_violation_location_ = location;
+ strict_mode_violation_type_ = type;
+}
+
+
+void PreParser::CheckDelayedStrictModeViolation(int beg_pos,
+ int end_pos,
+ bool* ok) {
+ i::Scanner::Location location = strict_mode_violation_location_;
+ if (location.IsValid() &&
+ location.beg_pos > beg_pos && location.end_pos < end_pos) {
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ strict_mode_violation_type_, NULL);
+ *ok = false;
+ }
+ strict_mode_violation_location_ = i::Scanner::Location::invalid();
+}
+
+
+void PreParser::StrictModeIdentifierViolation(i::Scanner::Location location,
+ const char* eval_args_type,
+ Identifier identifier,
+ bool* ok) {
+ const char* type = eval_args_type;
+ if (identifier.IsFutureReserved()) {
+ type = "strict_reserved_word";
+ }
+ if (strict_mode()) {
+ ReportMessageAt(location.beg_pos, location.end_pos, type, NULL);
+ *ok = false;
+ return;
+ }
+ strict_mode_violation_location_ = location;
+ strict_mode_violation_type_ = type;
+}
+
+
PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
i::Token::Value next = Next();
if (i::Token::IsKeyword(next)) {
@@ -1170,24 +1413,28 @@ PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
const char* keyword = i::Token::String(next);
log_->LogAsciiSymbol(pos, i::Vector<const char>(keyword,
i::StrLength(keyword)));
- return kUnknownExpression;
+ return Identifier::Default();
}
if (next == i::Token::IDENTIFIER ||
next == i::Token::FUTURE_RESERVED_WORD) {
return GetIdentifierSymbol();
}
*ok = false;
- return kUnknownIdentifier;
+ return Identifier::Default();
}
+#undef CHECK_OK
+
// This function reads an identifier and determines whether or not it
// is 'get' or 'set'.
PreParser::Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get,
bool* is_set,
bool* ok) {
- PreParser::Identifier result = ParseIdentifier(CHECK_OK);
- if (scanner_->is_literal_ascii() && scanner_->literal_length() == 3) {
+ Identifier result = ParseIdentifier(ok);
+ if (!*ok) return Identifier::Default();
+ if (scanner_->is_literal_ascii() &&
+ scanner_->literal_length() == 3) {
const char* token = scanner_->literal_ascii_string().start();
*is_get = strncmp(token, "get", 3) == 0;
*is_set = !*is_get && strncmp(token, "set", 3) == 0;
@@ -1200,6 +1447,4 @@ bool PreParser::peek_any_identifier() {
return next == i::Token::IDENTIFIER ||
next == i::Token::FUTURE_RESERVED_WORD;
}
-
-#undef CHECK_OK
} } // v8::preparser
diff --git a/src/preparser.h b/src/preparser.h
index b7fa6c73..2efd53ef 100644
--- a/src/preparser.h
+++ b/src/preparser.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -33,7 +33,7 @@ namespace preparser {
// Preparsing checks a JavaScript program and emits preparse-data that helps
// a later parsing to be faster.
-// See preparse-data.h for the data.
+// See preparse-data-format.h for the data format.
// The PreParser checks that the syntax follows the grammar for JavaScript,
// and collects some information about the program along the way.
@@ -67,41 +67,226 @@ class PreParser {
}
private:
+ // These types form an algebra over syntactic categories that is just
+ // rich enough to let us recognize and propagate the constructs that
+ // are either being counted in the preparser data, or is important
+ // to throw the correct syntax error exceptions.
+
enum ScopeType {
kTopLevelScope,
kFunctionScope
};
- // Types that allow us to recognize simple this-property assignments.
- // A simple this-property assignment is a statement on the form
- // "this.propertyName = {primitive constant or function parameter name);"
- // where propertyName isn't "__proto__".
- // The result is only relevant if the function body contains only
- // simple this-property assignments.
+ class Expression;
- enum StatementType {
- kUnknownStatement
+ class Identifier {
+ public:
+ static Identifier Default() {
+ return Identifier(kUnknownIdentifier);
+ }
+ static Identifier Eval() {
+ return Identifier(kEvalIdentifier);
+ }
+ static Identifier Arguments() {
+ return Identifier(kArgumentsIdentifier);
+ }
+ static Identifier FutureReserved() {
+ return Identifier(kFutureReservedIdentifier);
+ }
+ bool IsEval() { return type_ == kEvalIdentifier; }
+ bool IsArguments() { return type_ == kArgumentsIdentifier; }
+ bool IsEvalOrArguments() { return type_ >= kEvalIdentifier; }
+ bool IsFutureReserved() { return type_ == kFutureReservedIdentifier; }
+ bool IsValidStrictVariable() { return type_ == kUnknownIdentifier; }
+ private:
+ enum Type {
+ kUnknownIdentifier,
+ kFutureReservedIdentifier,
+ kEvalIdentifier,
+ kArgumentsIdentifier
+ };
+ explicit Identifier(Type type) : type_(type) { }
+ Type type_;
+
+ friend class Expression;
};
- enum ExpressionType {
- kUnknownExpression,
- kIdentifierExpression, // Used to detect labels.
- kThisExpression,
- kThisPropertyExpression
+ // Bits 0 and 1 are used to identify the type of expression:
+ // If bit 0 is set, it's an identifier.
+ // if bit 1 is set, it's a string literal.
+ // If neither is set, it's no particular type, and both set isn't
+ // use yet.
+ // Bit 2 is used to mark the expression as being parenthesized,
+ // so "(foo)" isn't recognized as a pure identifier (and possible label).
+ class Expression {
+ public:
+ static Expression Default() {
+ return Expression(kUnknownExpression);
+ }
+
+ static Expression FromIdentifier(Identifier id) {
+ return Expression(kIdentifierFlag | (id.type_ << kIdentifierShift));
+ }
+
+ static Expression StringLiteral() {
+ return Expression(kUnknownStringLiteral);
+ }
+
+ static Expression UseStrictStringLiteral() {
+ return Expression(kUseStrictString);
+ }
+
+ static Expression This() {
+ return Expression(kThisExpression);
+ }
+
+ static Expression ThisProperty() {
+ return Expression(kThisPropertyExpression);
+ }
+
+ static Expression StrictFunction() {
+ return Expression(kStrictFunctionExpression);
+ }
+
+ bool IsIdentifier() {
+ return (code_ & kIdentifierFlag) != 0;
+ }
+
+ // Only works corretly if it is actually an identifier expression.
+ PreParser::Identifier AsIdentifier() {
+ return PreParser::Identifier(
+ static_cast<PreParser::Identifier::Type>(code_ >> kIdentifierShift));
+ }
+
+ bool IsParenthesized() {
+ // If bit 0 or 1 is set, we interpret bit 2 as meaning parenthesized.
+ return (code_ & 7) > 4;
+ }
+
+ bool IsRawIdentifier() {
+ return !IsParenthesized() && IsIdentifier();
+ }
+
+ bool IsStringLiteral() { return (code_ & kStringLiteralFlag) != 0; }
+
+ bool IsRawStringLiteral() {
+ return !IsParenthesized() && IsStringLiteral();
+ }
+
+ bool IsUseStrictLiteral() {
+ return (code_ & kStringLiteralMask) == kUseStrictString;
+ }
+
+ bool IsThis() {
+ return code_ == kThisExpression;
+ }
+
+ bool IsThisProperty() {
+ return code_ == kThisPropertyExpression;
+ }
+
+ bool IsStrictFunction() {
+ return code_ == kStrictFunctionExpression;
+ }
+
+ Expression Parenthesize() {
+ int type = code_ & 3;
+ if (type != 0) {
+ // Identifiers and string literals can be parenthesized.
+ // They no longer work as labels or directive prologues,
+ // but are still recognized in other contexts.
+ return Expression(code_ | kParentesizedExpressionFlag);
+ }
+ // For other types of expressions, it's not important to remember
+ // the parentheses.
+ return *this;
+ }
+
+ private:
+ // First two/three bits are used as flags.
+ // Bit 0 and 1 represent identifiers or strings literals, and are
+ // mutually exclusive, but can both be absent.
+ // If bit 0 or 1 are set, bit 2 marks that the expression has
+ // been wrapped in parentheses (a string literal can no longer
+ // be a directive prologue, and an identifier can no longer be
+ // a label.
+ enum {
+ kUnknownExpression = 0,
+ // Identifiers
+ kIdentifierFlag = 1, // Used to detect labels.
+ kIdentifierShift = 3,
+
+ kStringLiteralFlag = 2, // Used to detect directive prologue.
+ kUnknownStringLiteral = kStringLiteralFlag,
+ kUseStrictString = kStringLiteralFlag | 8,
+ kStringLiteralMask = kUseStrictString,
+
+ kParentesizedExpressionFlag = 4, // Only if identifier or string literal.
+
+ // Below here applies if neither identifier nor string literal.
+ kThisExpression = 4,
+ kThisPropertyExpression = 8,
+ kStrictFunctionExpression = 12
+ };
+
+ explicit Expression(int expression_code) : code_(expression_code) { }
+
+ int code_;
};
- enum IdentifierType {
- kUnknownIdentifier
+ class Statement {
+ public:
+ static Statement Default() {
+ return Statement(kUnknownStatement);
+ }
+
+ static Statement FunctionDeclaration() {
+ return Statement(kFunctionDeclaration);
+ }
+
+ // Creates expression statement from expression.
+ // Preserves being an unparenthesized string literal, possibly
+ // "use strict".
+ static Statement ExpressionStatement(Expression expression) {
+ if (!expression.IsParenthesized()) {
+ if (expression.IsUseStrictLiteral()) {
+ return Statement(kUseStrictExpressionStatement);
+ }
+ if (expression.IsStringLiteral()) {
+ return Statement(kStringLiteralExpressionStatement);
+ }
+ }
+ return Default();
+ }
+
+ bool IsStringLiteral() {
+ return code_ != kUnknownStatement;
+ }
+
+ bool IsUseStrictLiteral() {
+ return code_ == kUseStrictExpressionStatement;
+ }
+
+ bool IsFunctionDeclaration() {
+ return code_ == kFunctionDeclaration;
+ }
+
+ private:
+ enum Type {
+ kUnknownStatement,
+ kStringLiteralExpressionStatement,
+ kUseStrictExpressionStatement,
+ kFunctionDeclaration
+ };
+
+ explicit Statement(Type code) : code_(code) {}
+ Type code_;
};
- enum SourceElementTypes {
+ enum SourceElements {
kUnknownSourceElements
};
- typedef int SourceElements;
- typedef int Expression;
- typedef int Statement;
- typedef int Identifier;
typedef int Arguments;
class Scope {
@@ -112,7 +297,8 @@ class PreParser {
type_(type),
materialized_literal_count_(0),
expected_properties_(0),
- with_nesting_count_(0) {
+ with_nesting_count_(0),
+ strict_((prev_ != NULL) && prev_->is_strict()) {
*variable = this;
}
~Scope() { *variable_ = prev_; }
@@ -122,6 +308,8 @@ class PreParser {
int expected_properties() { return expected_properties_; }
int materialized_literal_count() { return materialized_literal_count_; }
bool IsInsideWith() { return with_nesting_count_ != 0; }
+ bool is_strict() { return strict_; }
+ void set_strict() { strict_ = true; }
void EnterWith() { with_nesting_count_++; }
void LeaveWith() { with_nesting_count_--; }
@@ -132,6 +320,7 @@ class PreParser {
int materialized_literal_count_;
int expected_properties_;
int with_nesting_count_;
+ bool strict_;
};
// Private constructor only used in PreParseProgram.
@@ -143,6 +332,8 @@ class PreParser {
log_(log),
scope_(NULL),
stack_limit_(stack_limit),
+ strict_mode_violation_location_(i::Scanner::Location::invalid()),
+ strict_mode_violation_type_(NULL),
stack_overflow_(false),
allow_lazy_(true),
parenthesized_function_(false) { }
@@ -152,10 +343,13 @@ class PreParser {
PreParseResult PreParse() {
Scope top_scope(&scope_, kTopLevelScope);
bool ok = true;
+ int start_position = scanner_->peek_location().beg_pos;
ParseSourceElements(i::Token::EOS, &ok);
if (stack_overflow_) return kPreParseStackOverflow;
if (!ok) {
ReportUnexpectedToken(scanner_->current_token());
+ } else if (scope_->is_strict()) {
+ CheckOctalLiteral(start_position, scanner_->location().end_pos, &ok);
}
return kPreParseSuccess;
}
@@ -169,6 +363,8 @@ class PreParser {
log_->LogMessage(start_pos, end_pos, type, name_opt);
}
+ void CheckOctalLiteral(int beg_pos, int end_pos, bool* ok);
+
// All ParseXXX functions take as the last argument an *ok parameter
// which is set to false if parsing failed; it is unchanged otherwise.
// By making the 'exception handling' explicit, we are forced to check
@@ -245,6 +441,12 @@ class PreParser {
bool peek_any_identifier();
+ void set_strict_mode() {
+ scope_->set_strict();
+ }
+
+ bool strict_mode() { return scope_->is_strict(); }
+
void Consume(i::Token::Value token) { Next(); }
void Expect(i::Token::Value token, bool* ok) {
@@ -265,10 +467,23 @@ class PreParser {
static int Precedence(i::Token::Value tok, bool accept_IN);
+ void SetStrictModeViolation(i::Scanner::Location,
+ const char* type,
+ bool *ok);
+
+ void CheckDelayedStrictModeViolation(int beg_pos, int end_pos, bool* ok);
+
+ void StrictModeIdentifierViolation(i::Scanner::Location,
+ const char* eval_args_type,
+ Identifier identifier,
+ bool* ok);
+
i::JavaScriptScanner* scanner_;
i::ParserRecorder* log_;
Scope* scope_;
uintptr_t stack_limit_;
+ i::Scanner::Location strict_mode_violation_location_;
+ const char* strict_mode_violation_type_;
bool stack_overflow_;
bool allow_lazy_;
bool parenthesized_function_;
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index c777ab45..60288a96 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -370,7 +370,10 @@ void PrettyPrinter::VisitCallRuntime(CallRuntime* node) {
void PrettyPrinter::VisitUnaryOperation(UnaryOperation* node) {
- Print("(%s", Token::String(node->op()));
+ Token::Value op = node->op();
+ bool needsSpace =
+ op == Token::DELETE || op == Token::TYPEOF || op == Token::VOID;
+ Print("(%s%s", Token::String(op), needsSpace ? " " : "");
Visit(node->expression());
Print(")");
}
@@ -388,7 +391,7 @@ void PrettyPrinter::VisitCountOperation(CountOperation* node) {
void PrettyPrinter::VisitBinaryOperation(BinaryOperation* node) {
Print("(");
Visit(node->left());
- Print("%s", Token::String(node->op()));
+ Print(" %s ", Token::String(node->op()));
Visit(node->right());
Print(")");
}
@@ -397,7 +400,7 @@ void PrettyPrinter::VisitBinaryOperation(BinaryOperation* node) {
void PrettyPrinter::VisitCompareOperation(CompareOperation* node) {
Print("(");
Visit(node->left());
- Print("%s", Token::String(node->op()));
+ Print(" %s ", Token::String(node->op()));
Visit(node->right());
Print(")");
}
diff --git a/src/prettyprinter.h b/src/prettyprinter.h
index 451b17ea..080081dd 100644
--- a/src/prettyprinter.h
+++ b/src/prettyprinter.h
@@ -28,6 +28,7 @@
#ifndef V8_PRETTYPRINTER_H_
#define V8_PRETTYPRINTER_H_
+#include "allocation.h"
#include "ast.h"
namespace v8 {
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 4cf62e25..c954c4fb 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,14 +28,15 @@
#ifdef ENABLE_LOGGING_AND_PROFILING
#include "v8.h"
+
+#include "profile-generator-inl.h"
+
#include "global-handles.h"
#include "heap-profiler.h"
#include "scopeinfo.h"
#include "unicode.h"
#include "zone-inl.h"
-#include "profile-generator-inl.h"
-
namespace v8 {
namespace internal {
@@ -1735,7 +1736,7 @@ const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
switch (object->map()->instance_type()) {
case MAP_TYPE: return "system / Map";
case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell";
- case PROXY_TYPE: return "system / Proxy";
+ case FOREIGN_TYPE: return "system / Foreign";
case ODDBALL_TYPE: return "system / Oddball";
#define MAKE_STRUCT_CASE(NAME, Name, name) \
case NAME##_TYPE: return "system / "#Name;
@@ -1864,9 +1865,11 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
SetInternalReference(obj, entry,
"constructor", map->constructor(),
Map::kConstructorOffset);
- SetInternalReference(obj, entry,
- "descriptors", map->instance_descriptors(),
- Map::kInstanceDescriptorsOffset);
+ if (!map->instance_descriptors()->IsEmpty()) {
+ SetInternalReference(obj, entry,
+ "descriptors", map->instance_descriptors(),
+ Map::kInstanceDescriptorsOrBitField3Offset);
+ }
SetInternalReference(obj, entry,
"code_cache", map->code_cache(),
Map::kCodeCacheOffset);
@@ -1904,7 +1907,7 @@ void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj,
HandleScope hs;
JSFunction* func = JSFunction::cast(js_obj);
Context* context = func->context();
- ZoneScope zscope(DELETE_ON_EXIT);
+ ZoneScope zscope(Isolate::Current(), DELETE_ON_EXIT);
SerializedScopeInfo* serialized_scope_info =
context->closure()->shared()->scope_info();
ScopeInfo<ZoneListAllocationPolicy> zone_scope_info(serialized_scope_info);
diff --git a/src/profile-generator.h b/src/profile-generator.h
index bbc9efc7..5b789ac3 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -30,6 +30,7 @@
#ifdef ENABLE_LOGGING_AND_PROFILING
+#include "allocation.h"
#include "hashmap.h"
#include "../include/v8-profiler.h"
diff --git a/src/property.cc b/src/property.cc
index c35fb834..dd232093 100644
--- a/src/property.cc
+++ b/src/property.cc
@@ -74,6 +74,9 @@ void LookupResult::Print(FILE* out) {
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
break;
+ case HANDLER:
+ PrintF(out, " -type = lookup proxy\n");
+ break;
case INTERCEPTOR:
PrintF(out, " -type = lookup interceptor\n");
break;
diff --git a/src/property.h b/src/property.h
index ee95ca21..87f9ea3d 100644
--- a/src/property.h
+++ b/src/property.h
@@ -28,6 +28,8 @@
#ifndef V8_PROPERTY_H_
#define V8_PROPERTY_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
@@ -155,24 +157,15 @@ class ConstantFunctionDescriptor: public Descriptor {
class CallbacksDescriptor: public Descriptor {
public:
CallbacksDescriptor(String* key,
- Object* proxy,
+ Object* foreign,
PropertyAttributes attributes,
int index = 0)
- : Descriptor(key, proxy, attributes, CALLBACKS, index) {}
+ : Descriptor(key, foreign, attributes, CALLBACKS, index) {}
};
class LookupResult BASE_EMBEDDED {
public:
- // Where did we find the result;
- enum {
- NOT_FOUND,
- DESCRIPTOR_TYPE,
- DICTIONARY_TYPE,
- INTERCEPTOR_TYPE,
- CONSTANT_TYPE
- } lookup_type_;
-
LookupResult()
: lookup_type_(NOT_FOUND),
cacheable_(true),
@@ -209,6 +202,12 @@ class LookupResult BASE_EMBEDDED {
number_ = entry;
}
+ void HandlerResult() {
+ lookup_type_ = HANDLER_TYPE;
+ holder_ = NULL;
+ details_ = PropertyDetails(NONE, HANDLER);
+ }
+
void InterceptorResult(JSObject* holder) {
lookup_type_ = INTERCEPTOR_TYPE;
holder_ = holder;
@@ -243,6 +242,7 @@ class LookupResult BASE_EMBEDDED {
bool IsDontEnum() { return details_.IsDontEnum(); }
bool IsDeleted() { return details_.IsDeleted(); }
bool IsFound() { return lookup_type_ != NOT_FOUND; }
+ bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
// Is the result is a property excluding transitions and the null
// descriptor?
@@ -343,6 +343,16 @@ class LookupResult BASE_EMBEDDED {
}
private:
+ // Where did we find the result;
+ enum {
+ NOT_FOUND,
+ DESCRIPTOR_TYPE,
+ DICTIONARY_TYPE,
+ HANDLER_TYPE,
+ INTERCEPTOR_TYPE,
+ CONSTANT_TYPE
+ } lookup_type_;
+
JSObject* holder_;
int number_;
bool cacheable_;
diff --git a/src/mips/jump-target-mips.cc b/src/proxy.js
index bd6d60ba..c11852b0 100644
--- a/src/mips/jump-target-mips.cc
+++ b/src/proxy.js
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,56 +25,59 @@
// (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 "v8.h"
-
-#if defined(V8_TARGET_ARCH_MIPS)
-
-#include "codegen-inl.h"
-#include "jump-target-inl.h"
-#include "register-allocator-inl.h"
-#include "virtual-frame-inl.h"
-
-namespace v8 {
-namespace internal {
-
-// -------------------------------------------------------------------------
-// JumpTarget implementation.
-
-#define __ ACCESS_MASM(cgen()->masm())
-
-// BRANCH_ARGS_CHECK checks that conditional jump arguments are correct.
-#define BRANCH_ARGS_CHECK(cond, rs, rt) ASSERT( \
- (cond == cc_always && rs.is(zero_reg) && rt.rm().is(zero_reg)) || \
- (cond != cc_always && (!rs.is(zero_reg) || !rt.rm().is(zero_reg))))
-
-
-void JumpTarget::DoJump() {
- UNIMPLEMENTED_MIPS();
-}
-
-// Original prototype for mips, needs arch-indep change. Leave out for now.
-// void JumpTarget::DoBranch(Condition cc, Hint ignored,
-// Register src1, const Operand& src2) {
-void JumpTarget::DoBranch(Condition cc, Hint ignored) {
- UNIMPLEMENTED_MIPS();
-}
-
-
-void JumpTarget::Call() {
- UNIMPLEMENTED_MIPS();
+global.Proxy = new $Object();
+
+var $Proxy = global.Proxy
+
+var fundamentalTraps = [
+ "getOwnPropertyDescriptor",
+ "getPropertyDescriptor",
+ "getOwnPropertyNames",
+ "getPropertyNames",
+ "defineProperty",
+ "delete",
+ "fix",
+]
+
+var derivedTraps = [
+ "has",
+ "hasOwn",
+ "get",
+ "set",
+ "enumerate",
+ "keys",
+]
+
+var functionTraps = [
+ "callTrap",
+ "constructTrap",
+]
+
+$Proxy.createFunction = function(handler, callTrap, constructTrap) {
+ handler.callTrap = callTrap
+ handler.constructTrap = constructTrap
+ $Proxy.create(handler)
}
-
-void JumpTarget::DoBind() {
- UNIMPLEMENTED_MIPS();
+$Proxy.create = function(handler, proto) {
+ if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
+ return %CreateJSProxy(handler, proto)
}
-#undef __
-#undef BRANCH_ARGS_CHECK
-} } // namespace v8::internal
+////////////////////////////////////////////////////////////////////////////////
+// Builtins
+////////////////////////////////////////////////////////////////////////////////
-#endif // V8_TARGET_ARCH_MIPS
+function DerivedGetTrap(receiver, name) {
+ var desc = this.getPropertyDescriptor(name)
+ if (IS_UNDEFINED(desc)) { return desc; }
+ if ('value' in desc) {
+ return desc.value
+ } else {
+ if (IS_UNDEFINED(desc.get)) { return desc.get; }
+ return desc.get.call(receiver) // The proposal says so...
+ }
+}
diff --git a/src/regexp.js b/src/regexp.js
index f68dee61..7b851a35 100644
--- a/src/regexp.js
+++ b/src/regexp.js
@@ -235,7 +235,7 @@ function RegExpTest(string) {
// Conversion is required by the ES5 specification (RegExp.prototype.exec
// algorithm, step 5) even if the value is discarded for non-global RegExps.
var i = TO_INTEGER(lastIndex);
-
+
if (this.global) {
if (i < 0 || i > string.length) {
this.lastIndex = 0;
@@ -250,11 +250,11 @@ function RegExpTest(string) {
}
lastMatchInfoOverride = null;
this.lastIndex = lastMatchInfo[CAPTURE1];
- return true;
+ return true;
} else {
// Non-global regexp.
- // Remove irrelevant preceeding '.*' in a non-global test regexp.
- // The expression checks whether this.source starts with '.*' and
+ // Remove irrelevant preceeding '.*' in a non-global test regexp.
+ // The expression checks whether this.source starts with '.*' and
// that the third char is not a '?'.
if (%_StringCharCodeAt(this.source, 0) == 46 && // '.'
%_StringCharCodeAt(this.source, 1) == 42 && // '*'
@@ -262,14 +262,14 @@ function RegExpTest(string) {
if (!%_ObjectEquals(regexp_key, this)) {
regexp_key = this;
regexp_val = new $RegExp(SubString(this.source, 2, this.source.length),
- (!this.ignoreCase
+ (!this.ignoreCase
? !this.multiline ? "" : "m"
: !this.multiline ? "i" : "im"));
}
if (%_RegExpExec(regexp_val, string, 0, lastMatchInfo) === null) {
return false;
}
- }
+ }
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
// matchIndices is either null or the lastMatchInfo array.
var matchIndices = %_RegExpExec(this, string, 0, lastMatchInfo);
diff --git a/src/runtime-profiler.cc b/src/runtime-profiler.cc
index 97f03416..ce9a3082 100644
--- a/src/runtime-profiler.cc
+++ b/src/runtime-profiler.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -153,6 +153,7 @@ void RuntimeProfiler::Optimize(JSFunction* function, bool eager, int delay) {
if (FLAG_trace_opt) {
PrintF("[marking (%s) ", eager ? "eagerly" : "lazily");
function->PrintName();
+ PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address()));
PrintF(" for recompilation");
if (delay > 0) {
PrintF(" (delayed %0.3f ms)", static_cast<double>(delay) / 1000);
@@ -170,7 +171,7 @@ void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) {
// Debug::has_break_points().
ASSERT(function->IsMarkedForLazyRecompilation());
if (!FLAG_use_osr ||
- isolate_->debug()->has_break_points() ||
+ isolate_->DebuggerHasBreakPoints() ||
function->IsBuiltin()) {
return;
}
diff --git a/src/runtime.cc b/src/runtime.cc
index 855bd41c..77b3f660 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -42,17 +42,19 @@
#include "execution.h"
#include "global-handles.h"
#include "jsregexp.h"
+#include "json-parser.h"
#include "liveedit.h"
#include "liveobjectlist-inl.h"
#include "parser.h"
#include "platform.h"
-#include "runtime.h"
#include "runtime-profiler.h"
+#include "runtime.h"
#include "scopeinfo.h"
#include "smart-pointer.h"
+#include "string-search.h"
#include "stub-cache.h"
#include "v8threads.h"
-#include "string-search.h"
+#include "vm-state-inl.h"
namespace v8 {
namespace internal {
@@ -587,10 +589,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
+ ASSERT(args.length() == 2);
+ Object* handler = args[0];
+ Object* prototype = args[1];
+ Object* used_prototype =
+ (prototype->IsJSObject() || prototype->IsJSProxy()) ? prototype
+ : isolate->heap()->null_value();
+ return isolate->heap()->AllocateJSProxy(handler, used_prototype);
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCatchExtensionObject) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, key, args[0]);
Object* value = args[1];
+ ASSERT(!value->IsFailure());
// Create a catch context extension object.
JSFunction* constructor =
isolate->context()->global_context()->
@@ -2600,7 +2614,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString(
int capture_count = regexp_handle->CaptureCount();
// CompiledReplacement uses zone allocation.
- CompilationZoneScope zone(DELETE_ON_EXIT);
+ CompilationZoneScope zone(isolate, DELETE_ON_EXIT);
CompiledReplacement compiled_replacement;
compiled_replacement.Compile(replacement_handle,
capture_count,
@@ -3114,7 +3128,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringMatch) {
}
int length = subject->length();
- CompilationZoneScope zone_space(DELETE_ON_EXIT);
+ CompilationZoneScope zone_space(isolate, DELETE_ON_EXIT);
ZoneList<int> offsets(8);
do {
int start;
@@ -3871,6 +3885,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
if (proto->IsNull()) return *obj_value;
js_object = Handle<JSObject>::cast(proto);
}
+
+ // Don't allow element properties to be redefined on objects with external
+ // array elements.
+ if (js_object->HasExternalArrayElements()) {
+ Handle<Object> args[2] = { js_object, name };
+ Handle<Object> error =
+ isolate->factory()->NewTypeError("redef_external_array_element",
+ HandleVector(args, 2));
+ return isolate->Throw(*error);
+ }
+
NormalizeElements(js_object);
Handle<NumberDictionary> dictionary(js_object->element_dictionary());
// Make sure that we never go back to fast case.
@@ -4106,6 +4131,23 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetProperty) {
}
+// Set the ES5 native flag on the function.
+// This is used to decide if we should transform null and undefined
+// into the global object when doing call and apply.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetES5Flag) {
+ NoHandleAllocation ha;
+ RUNTIME_ASSERT(args.length() == 1);
+
+ Handle<Object> object = args.at<Object>(0);
+
+ if (object->IsJSFunction()) {
+ JSFunction* func = JSFunction::cast(*object);
+ func->shared()->set_es5_native(true);
+ }
+ return isolate->heap()->undefined_value();
+}
+
+
// Set a local property, even if it is READ_ONLY. If the property does not
// exist, it will be added with attributes NONE.
RUNTIME_FUNCTION(MaybeObject*, Runtime_IgnoreAttributesAndSetProperty) {
@@ -4165,25 +4207,33 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_HasLocalProperty) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, key, args[1]);
+ uint32_t index;
+ const bool key_is_array_index = key->AsArrayIndex(&index);
+
Object* obj = args[0];
// Only JS objects can have properties.
if (obj->IsJSObject()) {
JSObject* object = JSObject::cast(obj);
- // Fast case - no interceptors.
+ // Fast case: either the key is a real named property or it is not
+ // an array index and there are no interceptors or hidden
+ // prototypes.
if (object->HasRealNamedProperty(key)) return isolate->heap()->true_value();
- // Slow case. Either it's not there or we have an interceptor. We should
- // have handles for this kind of deal.
+ Map* map = object->map();
+ if (!key_is_array_index &&
+ !map->has_named_interceptor() &&
+ !HeapObject::cast(map->prototype())->map()->is_hidden_prototype()) {
+ return isolate->heap()->false_value();
+ }
+ // Slow case.
HandleScope scope(isolate);
return HasLocalPropertyImplementation(isolate,
Handle<JSObject>(object),
Handle<String>(key));
- } else if (obj->IsString()) {
+ } else if (obj->IsString() && key_is_array_index) {
// Well, there is one exception: Handle [] on strings.
- uint32_t index;
- if (key->AsArrayIndex(&index)) {
- String* string = String::cast(obj);
- if (index < static_cast<uint32_t>(string->length()))
- return isolate->heap()->true_value();
+ String* string = String::cast(obj);
+ if (index < static_cast<uint32_t>(string->length())) {
+ return isolate->heap()->true_value();
}
}
return isolate->heap()->false_value();
@@ -5564,7 +5614,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
static const int kMaxInitialListCapacity = 16;
- ZoneScope scope(DELETE_ON_EXIT);
+ ZoneScope scope(isolate, DELETE_ON_EXIT);
// Find (up to limit) indices of separator and end-of-string in subject
int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
@@ -6160,6 +6210,135 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderJoin) {
return answer;
}
+template <typename Char>
+static void JoinSparseArrayWithSeparator(FixedArray* elements,
+ int elements_length,
+ uint32_t array_length,
+ String* separator,
+ Vector<Char> buffer) {
+ int previous_separator_position = 0;
+ int separator_length = separator->length();
+ int cursor = 0;
+ for (int i = 0; i < elements_length; i += 2) {
+ int position = NumberToInt32(elements->get(i));
+ String* string = String::cast(elements->get(i + 1));
+ int string_length = string->length();
+ if (string->length() > 0) {
+ while (previous_separator_position < position) {
+ String::WriteToFlat<Char>(separator, &buffer[cursor],
+ 0, separator_length);
+ cursor += separator_length;
+ previous_separator_position++;
+ }
+ String::WriteToFlat<Char>(string, &buffer[cursor],
+ 0, string_length);
+ cursor += string->length();
+ }
+ }
+ if (separator_length > 0) {
+ // Array length must be representable as a signed 32-bit number,
+ // otherwise the total string length would have been too large.
+ ASSERT(array_length <= 0x7fffffff); // Is int32_t.
+ int last_array_index = static_cast<int>(array_length - 1);
+ while (previous_separator_position < last_array_index) {
+ String::WriteToFlat<Char>(separator, &buffer[cursor],
+ 0, separator_length);
+ cursor += separator_length;
+ previous_separator_position++;
+ }
+ }
+ ASSERT(cursor <= buffer.length());
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 3);
+ CONVERT_CHECKED(JSArray, elements_array, args[0]);
+ RUNTIME_ASSERT(elements_array->HasFastElements());
+ CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]);
+ CONVERT_CHECKED(String, separator, args[2]);
+ // elements_array is fast-mode JSarray of alternating positions
+ // (increasing order) and strings.
+ // array_length is length of original array (used to add separators);
+ // separator is string to put between elements. Assumed to be non-empty.
+
+ // Find total length of join result.
+ int string_length = 0;
+ bool is_ascii = true;
+ int max_string_length = SeqAsciiString::kMaxLength;
+ bool overflow = false;
+ CONVERT_NUMBER_CHECKED(int, elements_length,
+ Int32, elements_array->length());
+ RUNTIME_ASSERT((elements_length & 1) == 0); // Even length.
+ FixedArray* elements = FixedArray::cast(elements_array->elements());
+ for (int i = 0; i < elements_length; i += 2) {
+ RUNTIME_ASSERT(elements->get(i)->IsNumber());
+ CONVERT_CHECKED(String, string, elements->get(i + 1));
+ int length = string->length();
+ if (is_ascii && !string->IsAsciiRepresentation()) {
+ is_ascii = false;
+ max_string_length = SeqTwoByteString::kMaxLength;
+ }
+ if (length > max_string_length ||
+ max_string_length - length < string_length) {
+ overflow = true;
+ break;
+ }
+ string_length += length;
+ }
+ int separator_length = separator->length();
+ if (!overflow && separator_length > 0) {
+ if (array_length <= 0x7fffffffu) {
+ int separator_count = static_cast<int>(array_length) - 1;
+ int remaining_length = max_string_length - string_length;
+ if ((remaining_length / separator_length) >= separator_count) {
+ string_length += separator_length * (array_length - 1);
+ } else {
+ // Not room for the separators within the maximal string length.
+ overflow = true;
+ }
+ } else {
+ // Nonempty separator and at least 2^31-1 separators necessary
+ // means that the string is too large to create.
+ STATIC_ASSERT(String::kMaxLength < 0x7fffffff);
+ overflow = true;
+ }
+ }
+ if (overflow) {
+ // Throw OutOfMemory exception for creating too large a string.
+ V8::FatalProcessOutOfMemory("Array join result too large.");
+ }
+
+ if (is_ascii) {
+ MaybeObject* result_allocation =
+ isolate->heap()->AllocateRawAsciiString(string_length);
+ if (result_allocation->IsFailure()) return result_allocation;
+ SeqAsciiString* result_string =
+ SeqAsciiString::cast(result_allocation->ToObjectUnchecked());
+ JoinSparseArrayWithSeparator<char>(elements,
+ elements_length,
+ array_length,
+ separator,
+ Vector<char>(result_string->GetChars(),
+ string_length));
+ return result_string;
+ } else {
+ MaybeObject* result_allocation =
+ isolate->heap()->AllocateRawTwoByteString(string_length);
+ if (result_allocation->IsFailure()) return result_allocation;
+ SeqTwoByteString* result_string =
+ SeqTwoByteString::cast(result_allocation->ToObjectUnchecked());
+ JoinSparseArrayWithSeparator<uc16>(elements,
+ elements_length,
+ array_length,
+ separator,
+ Vector<uc16>(result_string->GetChars(),
+ string_length));
+ return result_string;
+ }
+}
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberOr) {
NoHandleAllocation ha;
@@ -6598,9 +6777,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RoundNumber) {
int exponent = number->get_exponent();
int sign = number->get_sign();
- // We compare with kSmiValueSize - 3 because (2^30 - 0.1) has exponent 29 and
- // should be rounded to 2^30, which is not smi.
- if (!sign && exponent <= kSmiValueSize - 3) {
+ if (exponent < -1) {
+ // Number in range ]-0.5..0.5[. These always round to +/-zero.
+ if (sign) return isolate->heap()->minus_zero_value();
+ return Smi::FromInt(0);
+ }
+
+ // We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and
+ // should be rounded to 2^30, which is not smi (for 31-bit smis, similar
+ // agument holds for 32-bit smis).
+ if (!sign && exponent < kSmiValueSize - 2) {
return Smi::FromInt(static_cast<int>(value + 0.5));
}
@@ -7292,13 +7478,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) {
// If the function is not optimizable or debugger is active continue using the
// code from the full compiler.
if (!function->shared()->code()->optimizable() ||
- isolate->debug()->has_break_points()) {
+ isolate->DebuggerHasBreakPoints()) {
if (FLAG_trace_opt) {
PrintF("[failed to optimize ");
function->PrintName();
PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
function->shared()->code()->optimizable() ? "T" : "F",
- isolate->debug()->has_break_points() ? "T" : "F");
+ isolate->DebuggerHasBreakPoints() ? "T" : "F");
}
function->ReplaceCode(function->shared()->code());
return function->code();
@@ -7418,6 +7604,29 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ if (!V8::UseCrankshaft()) {
+ return Smi::FromInt(4); // 4 == "never".
+ }
+ if (FLAG_always_opt) {
+ return Smi::FromInt(3); // 3 == "always".
+ }
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ return function->IsOptimized() ? Smi::FromInt(1) // 1 == "yes".
+ : Smi::FromInt(2); // 2 == "no".
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationCount) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSFunction, function, 0);
+ return Smi::FromInt(function->shared()->opt_count());
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -7689,8 +7898,8 @@ static inline MaybeObject* Unhole(Heap* heap,
}
-static JSObject* ComputeReceiverForNonGlobal(Isolate* isolate,
- JSObject* holder) {
+static Object* ComputeReceiverForNonGlobal(Isolate* isolate,
+ JSObject* holder) {
ASSERT(!holder->IsGlobalObject());
Context* top = isolate->context();
// Get the context extension function.
@@ -7702,10 +7911,11 @@ static JSObject* ComputeReceiverForNonGlobal(Isolate* isolate,
// explicitly via a with-statement.
Object* constructor = holder->map()->constructor();
if (constructor != context_extension_function) return holder;
- // Fall back to using the global object as the receiver if the
- // property turns out to be a local variable allocated in a context
- // extension object - introduced via eval.
- return top->global()->global_receiver();
+ // Fall back to using the global object as the implicit receiver if
+ // the property turns out to be a local variable allocated in a
+ // context extension object - introduced via eval. Implicit global
+ // receivers are indicated with the hole value.
+ return isolate->heap()->the_hole_value();
}
@@ -7733,30 +7943,38 @@ static ObjectPair LoadContextSlotHelper(Arguments args,
// If the "property" we were looking for is a local variable or an
// argument in a context, the receiver is the global object; see
// ECMA-262, 3rd., 10.1.6 and 10.2.3.
- JSObject* receiver =
- isolate->context()->global()->global_receiver();
+ //
+ // Use the hole as the receiver to signal that the receiver is
+ // implicit and that the global receiver should be used.
+ Handle<Object> receiver = isolate->factory()->the_hole_value();
MaybeObject* value = (holder->IsContext())
? Context::cast(*holder)->get(index)
: JSObject::cast(*holder)->GetElement(index);
- return MakePair(Unhole(isolate->heap(), value, attributes), receiver);
+ return MakePair(Unhole(isolate->heap(), value, attributes), *receiver);
}
// If the holder is found, we read the property from it.
if (!holder.is_null() && holder->IsJSObject()) {
ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
JSObject* object = JSObject::cast(*holder);
- JSObject* receiver;
+ Object* receiver;
if (object->IsGlobalObject()) {
receiver = GlobalObject::cast(object)->global_receiver();
} else if (context->is_exception_holder(*holder)) {
- receiver = isolate->context()->global()->global_receiver();
+ // Use the hole as the receiver to signal that the receiver is
+ // implicit and that the global receiver should be used.
+ receiver = isolate->heap()->the_hole_value();
} else {
receiver = ComputeReceiverForNonGlobal(isolate, object);
}
+
+ // GetProperty below can cause GC.
+ Handle<Object> receiver_handle(receiver);
+
// No need to unhole the value here. This is taken care of by the
// GetProperty function.
MaybeObject* value = object->GetProperty(*name);
- return MakePair(value, receiver);
+ return MakePair(value, *receiver_handle);
}
if (throw_error) {
@@ -7766,7 +7984,7 @@ static ObjectPair LoadContextSlotHelper(Arguments args,
HandleVector(&name, 1));
return MakePair(isolate->Throw(*reference_error), NULL);
} else {
- // The property doesn't exist - return undefined
+ // The property doesn't exist - return undefined.
return MakePair(isolate->heap()->undefined_value(),
isolate->heap()->undefined_value());
}
@@ -8150,13 +8368,41 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ParseJson) {
}
+bool CodeGenerationFromStringsAllowed(Isolate* isolate,
+ Handle<Context> context) {
+ if (context->allow_code_gen_from_strings()->IsFalse()) {
+ // Check with callback if set.
+ AllowCodeGenerationFromStringsCallback callback =
+ isolate->allow_code_gen_callback();
+ if (callback == NULL) {
+ // No callback set and code generation disallowed.
+ return false;
+ } else {
+ // Callback set. Let it decide if code generation is allowed.
+ VMState state(isolate, EXTERNAL);
+ return callback(v8::Utils::ToLocal(context));
+ }
+ }
+ return true;
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) {
HandleScope scope(isolate);
ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
- // Compile source string in the global context.
+ // Extract global context.
Handle<Context> context(isolate->context()->global_context());
+
+ // Check if global context allows code generation from
+ // strings. Throw an exception if it doesn't.
+ if (!CodeGenerationFromStringsAllowed(isolate, context)) {
+ return isolate->Throw(*isolate->factory()->NewError(
+ "code_gen_from_strings", HandleVector<Object>(NULL, 0)));
+ }
+
+ // Compile source string in the global context.
Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
context,
true,
@@ -8174,17 +8420,28 @@ static ObjectPair CompileGlobalEval(Isolate* isolate,
Handle<String> source,
Handle<Object> receiver,
StrictModeFlag strict_mode) {
+ Handle<Context> context = Handle<Context>(isolate->context());
+ Handle<Context> global_context = Handle<Context>(context->global_context());
+
+ // Check if global context allows code generation from
+ // strings. Throw an exception if it doesn't.
+ if (!CodeGenerationFromStringsAllowed(isolate, global_context)) {
+ isolate->Throw(*isolate->factory()->NewError(
+ "code_gen_from_strings", HandleVector<Object>(NULL, 0)));
+ return MakePair(Failure::Exception(), NULL);
+ }
+
// Deal with a normal eval call with a string argument. Compile it
// and return the compiled function bound in the local context.
Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
source,
Handle<Context>(isolate->context()),
- isolate->context()->IsGlobalContext(),
+ context->IsGlobalContext(),
strict_mode);
if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
Handle<JSFunction> compiled =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
- shared, Handle<Context>(isolate->context()), NOT_TENURED);
+ shared, context, NOT_TENURED);
return MakePair(*compiled, *receiver);
}
@@ -8238,12 +8495,8 @@ RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) {
if (!context->IsGlobalContext()) {
// 'eval' is not bound in the global context. Just call the function
// with the given arguments. This is not necessarily the global eval.
- if (receiver->IsContext()) {
- context = Handle<Context>::cast(receiver);
- receiver = Handle<Object>(context->get(index), isolate);
- } else if (receiver->IsJSContextExtensionObject()) {
- receiver = Handle<JSObject>(
- isolate->context()->global()->global_receiver(), isolate);
+ if (receiver->IsContext() || receiver->IsJSContextExtensionObject()) {
+ receiver = isolate->factory()->the_hole_value();
}
return MakePair(*callee, *receiver);
}
@@ -8252,8 +8505,7 @@ RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) {
// Compare it to the builtin 'GlobalEval' function to make sure.
if (*callee != isolate->global_context()->global_eval_fun() ||
!args[1]->IsString()) {
- return MakePair(*callee,
- isolate->context()->global()->global_receiver());
+ return MakePair(*callee, isolate->heap()->the_hole_value());
}
ASSERT(args[3]->IsSmi());
@@ -8275,8 +8527,7 @@ RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEvalNoLookup) {
// Compare it to the builtin 'GlobalEval' function to make sure.
if (*callee != isolate->global_context()->global_eval_fun() ||
!args[1]->IsString()) {
- return MakePair(*callee,
- isolate->context()->global()->global_receiver());
+ return MakePair(*callee, isolate->heap()->the_hole_value());
}
ASSERT(args[3]->IsSmi());
@@ -8597,43 +8848,48 @@ static void CollectElementIndices(Handle<JSObject> object,
int dense_elements_length;
switch (kind) {
case JSObject::EXTERNAL_PIXEL_ELEMENTS: {
- dense_elements_length =
- ExternalPixelArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalPixelArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_BYTE_ELEMENTS: {
- dense_elements_length =
- ExternalByteArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalByteArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
- dense_elements_length =
- ExternalUnsignedByteArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalUnsignedByteArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_SHORT_ELEMENTS: {
- dense_elements_length =
- ExternalShortArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalShortArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
- dense_elements_length =
- ExternalUnsignedShortArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalUnsignedShortArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_INT_ELEMENTS: {
- dense_elements_length =
- ExternalIntArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalIntArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
- dense_elements_length =
- ExternalUnsignedIntArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalUnsignedIntArray::cast(object->elements())->length();
break;
}
case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
- dense_elements_length =
- ExternalFloatArray::cast(object->elements())->length();
+ dense_elements_length =
+ ExternalFloatArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_DOUBLE_ELEMENTS: {
+ dense_elements_length =
+ ExternalDoubleArray::cast(object->elements())->length();
break;
}
default:
@@ -8767,6 +9023,11 @@ static bool IterateElements(Isolate* isolate,
isolate, receiver, false, false, visitor);
break;
}
+ case JSObject::EXTERNAL_DOUBLE_ELEMENTS: {
+ IterateExternalArrayElements<ExternalDoubleArray, double>(
+ isolate, receiver, false, false, visitor);
+ break;
+ }
default:
UNREACHABLE();
break;
@@ -9110,7 +9371,7 @@ static MaybeObject* DebugLookupResultValue(Heap* heap,
return result->GetConstantFunction();
case CALLBACKS: {
Object* structure = result->GetCallbackObject();
- if (structure->IsProxy() || structure->IsAccessorInfo()) {
+ if (structure->IsForeign() || structure->IsAccessorInfo()) {
MaybeObject* maybe_value = receiver->GetPropertyWithCallback(
receiver, structure, name, result->holder());
if (!maybe_value->ToObject(&value)) {
@@ -10479,7 +10740,7 @@ static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
// Recursively copy the with contexts.
Handle<Context> previous(context_chain->previous());
Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
- Handle<Context> context = CopyWithContextChain(function_context, previous);
+ Handle<Context> context = CopyWithContextChain(previous, function_context);
return context->GetIsolate()->factory()->NewWithContext(
context, extension, context_chain->IsCatchContext());
}
diff --git a/src/runtime.h b/src/runtime.h
index bf1ba68b..d3223d1a 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -28,6 +28,7 @@
#ifndef V8_RUNTIME_H_
#define V8_RUNTIME_H_
+#include "allocation.h"
#include "zone.h"
namespace v8 {
@@ -86,9 +87,12 @@ namespace internal {
F(NotifyOSR, 0, 1) \
F(DeoptimizeFunction, 1, 1) \
F(OptimizeFunctionOnNextCall, 1, 1) \
+ F(GetOptimizationStatus, 1, 1) \
+ F(GetOptimizationCount, 1, 1) \
F(CompileForOnStackReplacement, 1, 1) \
F(SetNewFunctionAttributes, 1, 1) \
F(AllocateInNewSpace, 1, 1) \
+ F(SetES5Flag, 1, 1) \
\
/* Array join support */ \
F(PushIfAbsent, 2, 1) \
@@ -132,6 +136,7 @@ namespace internal {
F(StringAdd, 2, 1) \
F(StringBuilderConcat, 3, 1) \
F(StringBuilderJoin, 3, 1) \
+ F(SparseJoinWithSeparator, 3, 1) \
\
/* Bit operations */ \
F(NumberOr, 2, 1) \
@@ -270,6 +275,9 @@ namespace internal {
F(CreateArrayLiteral, 3, 1) \
F(CreateArrayLiteralShallow, 3, 1) \
\
+ /* Harmony proxies */ \
+ F(CreateJSProxy, 2, 1) \
+ \
/* Catch context extension objects */ \
F(CreateCatchExtensionObject, 2, 1) \
\
diff --git a/src/runtime.js b/src/runtime.js
index 66d839be..77b97aed 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -49,41 +49,47 @@ const $Function = global.Function;
const $Boolean = global.Boolean;
const $NaN = 0/0;
-
-// ECMA-262, section 11.9.1, page 55.
+// ECMA-262 Section 11.9.3.
function EQUALS(y) {
if (IS_STRING(this) && IS_STRING(y)) return %StringEquals(this, y);
var x = this;
- // NOTE: We use iteration instead of recursion, because it is
- // difficult to call EQUALS with the correct setting of 'this' in
- // an efficient way.
while (true) {
if (IS_NUMBER(x)) {
- if (y == null) return 1; // not equal
- return %NumberEquals(x, %ToNumber(y));
+ while (true) {
+ if (IS_NUMBER(y)) return %NumberEquals(x, y);
+ if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal
+ if (!IS_SPEC_OBJECT(y)) {
+ // String or boolean.
+ return %NumberEquals(x, %ToNumber(y));
+ }
+ y = %ToPrimitive(y, NO_HINT);
+ }
} else if (IS_STRING(x)) {
- if (IS_STRING(y)) return %StringEquals(x, y);
+ while (true) {
+ if (IS_STRING(y)) return %StringEquals(x, y);
+ if (IS_NUMBER(y)) return %NumberEquals(%ToNumber(x), y);
+ if (IS_BOOLEAN(y)) return %NumberEquals(%ToNumber(x), %ToNumber(y));
+ if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal
+ y = %ToPrimitive(y, NO_HINT);
+ }
+ } else if (IS_BOOLEAN(x)) {
+ if (IS_BOOLEAN(y)) return %_ObjectEquals(x, y) ? 0 : 1;
+ if (IS_NULL_OR_UNDEFINED(y)) return 1;
if (IS_NUMBER(y)) return %NumberEquals(%ToNumber(x), y);
- if (IS_BOOLEAN(y)) return %NumberEquals(%ToNumber(x), %ToNumber(y));
- if (y == null) return 1; // not equal
+ if (IS_STRING(y)) return %NumberEquals(%ToNumber(x), %ToNumber(y));
+ // y is object.
+ x = %ToNumber(x);
y = %ToPrimitive(y, NO_HINT);
- } else if (IS_BOOLEAN(x)) {
- if (IS_BOOLEAN(y)) {
- return %_ObjectEquals(x, y) ? 0 : 1;
- }
- if (y == null) return 1; // not equal
- return %NumberEquals(%ToNumber(x), %ToNumber(y));
- } else if (x == null) {
- // NOTE: This checks for both null and undefined.
- return (y == null) ? 0 : 1;
+ } else if (IS_NULL_OR_UNDEFINED(x)) {
+ return IS_NULL_OR_UNDEFINED(y) ? 0 : 1;
} else {
- // x is not a number, boolean, null or undefined.
- if (y == null) return 1; // not equal
+ // x is an object.
if (IS_SPEC_OBJECT(y)) {
return %_ObjectEquals(x, y) ? 0 : 1;
}
-
+ if (IS_NULL_OR_UNDEFINED(y)) return 1; // not equal
+ if (IS_BOOLEAN(y)) y = %ToNumber(y);
x = %ToPrimitive(x, NO_HINT);
}
}
@@ -638,6 +644,6 @@ function DefaultString(x) {
// NOTE: Setting the prototype for Array must take place as early as
// possible due to code generation for array literals. When
// generating code for a array literal a boilerplate array is created
-// that is cloned when running the code. It is essiential that the
+// that is cloned when running the code. It is essential that the
// boilerplate gets the right prototype.
%FunctionSetPrototype($Array, new $Array(0));
diff --git a/src/safepoint-table.h b/src/safepoint-table.h
index 084a0b4f..de537f98 100644
--- a/src/safepoint-table.h
+++ b/src/safepoint-table.h
@@ -28,6 +28,7 @@
#ifndef V8_SAFEPOINT_TABLE_H_
#define V8_SAFEPOINT_TABLE_H_
+#include "allocation.h"
#include "heap.h"
#include "v8memory.h"
#include "zone.h"
diff --git a/src/scanner-base.cc b/src/scanner-base.cc
index 9715ca99..e15ef416 100644
--- a/src/scanner-base.cc
+++ b/src/scanner-base.cc
@@ -38,8 +38,7 @@ namespace internal {
// Scanner
Scanner::Scanner(UnicodeCache* unicode_cache)
- : unicode_cache_(unicode_cache),
- octal_pos_(kNoOctalLocation) { }
+ : unicode_cache_(unicode_cache) { }
uc32 Scanner::ScanHexEscape(uc32 c, int length) {
@@ -70,34 +69,12 @@ uc32 Scanner::ScanHexEscape(uc32 c, int length) {
}
-// Octal escapes of the forms '\0xx' and '\xxx' are not a part of
-// ECMA-262. Other JS VMs support them.
-uc32 Scanner::ScanOctalEscape(uc32 c, int length) {
- uc32 x = c - '0';
- int i = 0;
- for (; i < length; i++) {
- int d = c0_ - '0';
- if (d < 0 || d > 7) break;
- int nx = x * 8 + d;
- if (nx >= 256) break;
- x = nx;
- Advance();
- }
- // Anything excelt '\0' is an octal escape sequence, illegal in strict mode.
- // Remember the position of octal escape sequences so that better error
- // can be reported later (in strict mode).
- if (c != '0' || i > 0) {
- octal_pos_ = source_pos() - i - 1; // Already advanced
- }
- return x;
-}
-
// ----------------------------------------------------------------------------
// JavaScriptScanner
JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants)
- : Scanner(scanner_contants) { }
+ : Scanner(scanner_contants), octal_pos_(Location::invalid()) { }
Token::Value JavaScriptScanner::Next() {
@@ -518,6 +495,31 @@ void JavaScriptScanner::ScanEscape() {
}
+// Octal escapes of the forms '\0xx' and '\xxx' are not a part of
+// ECMA-262. Other JS VMs support them.
+uc32 JavaScriptScanner::ScanOctalEscape(uc32 c, int length) {
+ uc32 x = c - '0';
+ int i = 0;
+ for (; i < length; i++) {
+ int d = c0_ - '0';
+ if (d < 0 || d > 7) break;
+ int nx = x * 8 + d;
+ if (nx >= 256) break;
+ x = nx;
+ Advance();
+ }
+ // Anything except '\0' is an octal escape sequence, illegal in strict mode.
+ // Remember the position of octal escape sequences so that an error
+ // can be reported later (in strict mode).
+ // We don't report the error immediately, because the octal escape can
+ // occur before the "use strict" directive.
+ if (c != '0' || i > 0) {
+ octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1);
+ }
+ return x;
+}
+
+
Token::Value JavaScriptScanner::ScanString() {
uc32 quote = c0_;
Advance(); // consume quote
@@ -562,6 +564,7 @@ Token::Value JavaScriptScanner::ScanNumber(bool seen_period) {
} else {
// if the first character is '0' we must check for octals and hex
if (c0_ == '0') {
+ int start_pos = source_pos(); // For reporting octal positions.
AddLiteralCharAdvance();
// either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number
@@ -586,7 +589,7 @@ Token::Value JavaScriptScanner::ScanNumber(bool seen_period) {
}
if (c0_ < '0' || '7' < c0_) {
// Octal literal finished.
- octal_pos_ = next_.location.beg_pos;
+ octal_pos_ = Location(start_pos, source_pos());
break;
}
AddLiteralCharAdvance();
@@ -729,6 +732,9 @@ bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) {
// worrying whether the following characters are part of the escape
// or not, since any '/', '\\' or '[' is guaranteed to not be part
// of the escape sequence.
+
+ // TODO(896): At some point, parse RegExps more throughly to capture
+ // octal esacpes in strict mode.
} else { // Unescaped character.
if (c0_ == '[') in_character_class = true;
if (c0_ == ']') in_character_class = false;
diff --git a/src/scanner-base.h b/src/scanner-base.h
index 60b97d22..02566dde 100644
--- a/src/scanner-base.h
+++ b/src/scanner-base.h
@@ -30,14 +30,13 @@
#ifndef V8_SCANNER_BASE_H_
#define V8_SCANNER_BASE_H_
-#include "globals.h"
-#include "checks.h"
#include "allocation.h"
+#include "char-predicates.h"
+#include "checks.h"
+#include "globals.h"
#include "token.h"
#include "unicode-inl.h"
-#include "char-predicates.h"
#include "utils.h"
-#include "list-inl.h"
namespace v8 {
namespace internal {
@@ -287,23 +286,17 @@ class Scanner {
return beg_pos >= 0 && end_pos >= beg_pos;
}
+ static Location invalid() { return Location(-1, -1); }
+
int beg_pos;
int end_pos;
};
- static Location NoLocation() {
- return Location(-1, -1);
- }
-
// Returns the location information for the current token
// (the token returned by Next()).
Location location() const { return current_.location; }
Location peek_location() const { return next_.location; }
- // Returns the location of the last seen octal literal
- int octal_position() const { return octal_pos_; }
- void clear_octal_position() { octal_pos_ = -1; }
-
// Returns the literal string, if any, for the current token (the
// token returned by Next()). The string is 0-terminated and in
// UTF-8 format; they may contain 0-characters. Literal strings are
@@ -327,6 +320,16 @@ class Scanner {
return current_.literal_chars->length();
}
+ bool literal_contains_escapes() const {
+ Location location = current_.location;
+ int source_length = (location.end_pos - location.beg_pos);
+ if (current_.token == Token::STRING) {
+ // Subtract delimiters.
+ source_length -= 2;
+ }
+ return current_.literal_chars->length() != source_length;
+ }
+
// Returns the literal string for the next token (the token that
// would be returned if Next() were called).
bool is_next_literal_ascii() {
@@ -418,9 +421,6 @@ class Scanner {
uc32 ScanHexEscape(uc32 c, int length);
- // Scans octal escape sequence. Also accepts "\0" decimal escape sequence.
- uc32 ScanOctalEscape(uc32 c, int length);
-
// Return the current source position.
int source_pos() {
return source_->pos() - kCharacterLookaheadBufferSize;
@@ -438,9 +438,6 @@ class Scanner {
// Input stream. Must be initialized to an UC16CharacterStream.
UC16CharacterStream* source_;
- // Start position of the octal literal last scanned.
- int octal_pos_;
-
// One Unicode character look-ahead; c0_ < 0 at the end of the input.
uc32 c0_;
};
@@ -493,6 +490,13 @@ class JavaScriptScanner : public Scanner {
// Used for checking if a property name is an identifier.
static bool IsIdentifier(unibrow::CharacterStream* buffer);
+ // Scans octal escape sequence. Also accepts "\0" decimal escape sequence.
+ uc32 ScanOctalEscape(uc32 c, int length);
+
+ // Returns the location of the last seen octal literal
+ Location octal_position() const { return octal_pos_; }
+ void clear_octal_position() { octal_pos_ = Location::invalid(); }
+
// Seek forward to the given position. This operation does not
// work in general, for instance when there are pushed back
// characters, but works for seeking forward until simple delimiter
@@ -522,6 +526,9 @@ class JavaScriptScanner : public Scanner {
// If the escape sequence cannot be decoded the result is kBadChar.
uc32 ScanIdentifierUnicodeEscape();
+ // Start position of the octal literal last scanned.
+ Location octal_pos_;
+
bool has_line_terminator_before_next_;
};
diff --git a/src/scanner.cc b/src/scanner.cc
index 666818e3..21a0c2d9 100755
--- a/src/scanner.cc
+++ b/src/scanner.cc
@@ -342,244 +342,4 @@ void V8JavaScriptScanner::Initialize(UC16CharacterStream* source) {
}
-// ----------------------------------------------------------------------------
-// JsonScanner
-
-JsonScanner::JsonScanner(UnicodeCache* unicode_cache)
- : Scanner(unicode_cache) { }
-
-
-void JsonScanner::Initialize(UC16CharacterStream* source) {
- source_ = source;
- Init();
- // Skip initial whitespace.
- SkipJsonWhiteSpace();
- // Preload first token as look-ahead.
- ScanJson();
-}
-
-
-Token::Value JsonScanner::Next() {
- // BUG 1215673: Find a thread safe way to set a stack limit in
- // pre-parse mode. Otherwise, we cannot safely pre-parse from other
- // threads.
- current_ = next_;
- // Check for stack-overflow before returning any tokens.
- ScanJson();
- return current_.token;
-}
-
-
-bool JsonScanner::SkipJsonWhiteSpace() {
- int start_position = source_pos();
- // JSON WhiteSpace is tab, carrige-return, newline and space.
- while (c0_ == ' ' || c0_ == '\n' || c0_ == '\r' || c0_ == '\t') {
- Advance();
- }
- return source_pos() != start_position;
-}
-
-
-void JsonScanner::ScanJson() {
- next_.literal_chars = NULL;
- Token::Value token;
- do {
- // Remember the position of the next token
- next_.location.beg_pos = source_pos();
- switch (c0_) {
- case '\t':
- case '\r':
- case '\n':
- case ' ':
- Advance();
- token = Token::WHITESPACE;
- break;
- case '{':
- Advance();
- token = Token::LBRACE;
- break;
- case '}':
- Advance();
- token = Token::RBRACE;
- break;
- case '[':
- Advance();
- token = Token::LBRACK;
- break;
- case ']':
- Advance();
- token = Token::RBRACK;
- break;
- case ':':
- Advance();
- token = Token::COLON;
- break;
- case ',':
- Advance();
- token = Token::COMMA;
- break;
- case '"':
- token = ScanJsonString();
- break;
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- token = ScanJsonNumber();
- break;
- case 't':
- token = ScanJsonIdentifier("true", Token::TRUE_LITERAL);
- break;
- case 'f':
- token = ScanJsonIdentifier("false", Token::FALSE_LITERAL);
- break;
- case 'n':
- token = ScanJsonIdentifier("null", Token::NULL_LITERAL);
- break;
- default:
- if (c0_ < 0) {
- Advance();
- token = Token::EOS;
- } else {
- Advance();
- token = Select(Token::ILLEGAL);
- }
- }
- } while (token == Token::WHITESPACE);
-
- next_.location.end_pos = source_pos();
- next_.token = token;
-}
-
-
-Token::Value JsonScanner::ScanJsonString() {
- ASSERT_EQ('"', c0_);
- Advance();
- LiteralScope literal(this);
- while (c0_ != '"') {
- // Check for control character (0x00-0x1f) or unterminated string (<0).
- if (c0_ < 0x20) return Token::ILLEGAL;
- if (c0_ != '\\') {
- AddLiteralCharAdvance();
- } else {
- Advance();
- switch (c0_) {
- case '"':
- case '\\':
- case '/':
- AddLiteralChar(c0_);
- break;
- case 'b':
- AddLiteralChar('\x08');
- break;
- case 'f':
- AddLiteralChar('\x0c');
- break;
- case 'n':
- AddLiteralChar('\x0a');
- break;
- case 'r':
- AddLiteralChar('\x0d');
- break;
- case 't':
- AddLiteralChar('\x09');
- break;
- case 'u': {
- uc32 value = 0;
- for (int i = 0; i < 4; i++) {
- Advance();
- int digit = HexValue(c0_);
- if (digit < 0) {
- return Token::ILLEGAL;
- }
- value = value * 16 + digit;
- }
- AddLiteralChar(value);
- break;
- }
- default:
- return Token::ILLEGAL;
- }
- Advance();
- }
- }
- literal.Complete();
- Advance();
- return Token::STRING;
-}
-
-
-Token::Value JsonScanner::ScanJsonNumber() {
- LiteralScope literal(this);
- bool negative = false;
-
- if (c0_ == '-') {
- AddLiteralCharAdvance();
- negative = true;
- }
- if (c0_ == '0') {
- AddLiteralCharAdvance();
- // Prefix zero is only allowed if it's the only digit before
- // a decimal point or exponent.
- if ('0' <= c0_ && c0_ <= '9') return Token::ILLEGAL;
- } else {
- int i = 0;
- int digits = 0;
- if (c0_ < '1' || c0_ > '9') return Token::ILLEGAL;
- do {
- i = i * 10 + c0_ - '0';
- digits++;
- AddLiteralCharAdvance();
- } while (c0_ >= '0' && c0_ <= '9');
- if (c0_ != '.' && c0_ != 'e' && c0_ != 'E' && digits < 10) {
- number_ = (negative ? -i : i);
- return Token::NUMBER;
- }
- }
- if (c0_ == '.') {
- AddLiteralCharAdvance();
- if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL;
- do {
- AddLiteralCharAdvance();
- } while (c0_ >= '0' && c0_ <= '9');
- }
- if (AsciiAlphaToLower(c0_) == 'e') {
- AddLiteralCharAdvance();
- if (c0_ == '-' || c0_ == '+') AddLiteralCharAdvance();
- if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL;
- do {
- AddLiteralCharAdvance();
- } while (c0_ >= '0' && c0_ <= '9');
- }
- literal.Complete();
- ASSERT_NOT_NULL(next_.literal_chars);
- number_ = StringToDouble(unicode_cache_,
- next_.literal_chars->ascii_literal(),
- NO_FLAGS, // Hex, octal or trailing junk.
- OS::nan_value());
- return Token::NUMBER;
-}
-
-
-Token::Value JsonScanner::ScanJsonIdentifier(const char* text,
- Token::Value token) {
- LiteralScope literal(this);
- while (*text != '\0') {
- if (c0_ != *text) return Token::ILLEGAL;
- Advance();
- text++;
- }
- if (unicode_cache_->IsIdentifierPart(c0_)) return Token::ILLEGAL;
- literal.Complete();
- return token;
-}
-
-
} } // namespace v8::internal
diff --git a/src/scanner.h b/src/scanner.h
index 871c69ba..804fac83 100644
--- a/src/scanner.h
+++ b/src/scanner.h
@@ -141,56 +141,6 @@ class V8JavaScriptScanner : public JavaScriptScanner {
};
-class JsonScanner : public Scanner {
- public:
- explicit JsonScanner(UnicodeCache* unicode_cache);
-
- void Initialize(UC16CharacterStream* source);
-
- // Returns the next token.
- Token::Value Next();
-
- // Returns the value of a number token.
- double number() {
- return number_;
- }
-
-
- protected:
- // Skip past JSON whitespace (only space, tab, newline and carrige-return).
- bool SkipJsonWhiteSpace();
-
- // Scan a single JSON token. The JSON lexical grammar is specified in the
- // ECMAScript 5 standard, section 15.12.1.1.
- // Recognizes all of the single-character tokens directly, or calls a function
- // to scan a number, string or identifier literal.
- // The only allowed whitespace characters between tokens are tab,
- // carriage-return, newline and space.
- void ScanJson();
-
- // A JSON number (production JSONNumber) is a subset of the valid JavaScript
- // decimal number literals.
- // It includes an optional minus sign, must have at least one
- // digit before and after a decimal point, may not have prefixed zeros (unless
- // the integer part is zero), and may include an exponent part (e.g., "e-10").
- // Hexadecimal and octal numbers are not allowed.
- Token::Value ScanJsonNumber();
-
- // A JSON string (production JSONString) is subset of valid JavaScript string
- // literals. The string must only be double-quoted (not single-quoted), and
- // the only allowed backslash-escapes are ", /, \, b, f, n, r, t and
- // four-digit hex escapes (uXXXX). Any other use of backslashes is invalid.
- Token::Value ScanJsonString();
-
- // Used to recognizes one of the literals "true", "false", or "null". These
- // are the only valid JSON identifiers (productions JSONBooleanLiteral,
- // JSONNullLiteral).
- Token::Value ScanJsonIdentifier(const char* text, Token::Value token);
-
- // Holds the value of a scanned number token.
- double number_;
-};
-
} } // namespace v8::internal
#endif // V8_SCANNER_H_
diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc
index 58e2ad28..ccc2cc82 100644
--- a/src/scopeinfo.cc
+++ b/src/scopeinfo.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -52,6 +52,7 @@ template<class Allocator>
ScopeInfo<Allocator>::ScopeInfo(Scope* scope)
: function_name_(FACTORY->empty_symbol()),
calls_eval_(scope->calls_eval()),
+ is_strict_mode_(scope->is_strict_mode()),
parameters_(scope->num_parameters()),
stack_slots_(scope->num_stack_slots()),
context_slots_(scope->num_heap_slots()),
@@ -248,6 +249,7 @@ ScopeInfo<Allocator>::ScopeInfo(SerializedScopeInfo* data)
Object** p = p0;
p = ReadSymbol(p, &function_name_);
p = ReadBool(p, &calls_eval_);
+ p = ReadBool(p, &is_strict_mode_);
p = ReadList<Allocator>(p, &context_slots_, &context_modes_);
p = ReadList<Allocator>(p, &parameters_);
p = ReadList<Allocator>(p, &stack_slots_);
@@ -301,8 +303,8 @@ static Object** WriteList(Object** p,
template<class Allocator>
Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() {
- // function name, calls eval, length for 3 tables:
- const int extra_slots = 1 + 1 + 3;
+ // function name, calls eval, is_strict_mode, length for 3 tables:
+ const int extra_slots = 1 + 1 + 1 + 3;
int length = extra_slots +
context_slots_.length() * 2 +
parameters_.length() +
@@ -316,6 +318,7 @@ Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() {
Object** p = p0;
p = WriteSymbol(p, function_name_);
p = WriteBool(p, calls_eval_);
+ p = WriteBool(p, is_strict_mode_);
p = WriteList(p, &context_slots_, &context_modes_);
p = WriteList(p, &parameters_);
p = WriteList(p, &stack_slots_);
@@ -363,7 +366,8 @@ SerializedScopeInfo* SerializedScopeInfo::Empty() {
Object** SerializedScopeInfo::ContextEntriesAddr() {
ASSERT(length() > 0);
- return data_start() + 2; // +2 for function name and calls eval.
+ // +3 for function name, calls eval, strict mode.
+ return data_start() + 3;
}
@@ -392,7 +396,18 @@ bool SerializedScopeInfo::CallsEval() {
p = ReadBool(p, &calls_eval);
return calls_eval;
}
- return true;
+ return false;
+}
+
+
+bool SerializedScopeInfo::IsStrictMode() {
+ if (length() > 0) {
+ Object** p = data_start() + 2; // +2 for function name, calls eval.
+ bool strict_mode;
+ p = ReadBool(p, &strict_mode);
+ return strict_mode;
+ }
+ return false;
}
diff --git a/src/scopeinfo.h b/src/scopeinfo.h
index 2552af22..ff720135 100644
--- a/src/scopeinfo.h
+++ b/src/scopeinfo.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,6 +28,7 @@
#ifndef V8_SCOPEINFO_H_
#define V8_SCOPEINFO_H_
+#include "allocation.h"
#include "variables.h"
#include "zone-inl.h"
@@ -92,6 +93,7 @@ class ScopeInfo BASE_EMBEDDED {
private:
Handle<String> function_name_;
bool calls_eval_;
+ bool is_strict_mode_;
List<Handle<String>, Allocator > parameters_;
List<Handle<String>, Allocator > stack_slots_;
List<Handle<String>, Allocator > context_slots_;
@@ -112,6 +114,9 @@ class SerializedScopeInfo : public FixedArray {
// Does this scope call eval?
bool CallsEval();
+ // Is this scope a strict mode scope?
+ bool IsStrictMode();
+
// Does this scope have an arguments shadow?
bool HasArgumentsShadow() {
return StackSlotIndex(GetHeap()->arguments_shadow_symbol()) >= 0;
diff --git a/src/scopes.cc b/src/scopes.cc
index 8df93c5d..61024426 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -199,6 +199,7 @@ void Scope::SetDefaults(Type type,
// Inherit the strict mode from the parent scope.
strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_;
outer_scope_calls_eval_ = false;
+ outer_scope_calls_non_strict_eval_ = false;
inner_scope_calls_eval_ = false;
outer_scope_is_eval_scope_ = false;
force_eager_compilation_ = false;
@@ -489,8 +490,17 @@ void Scope::AllocateVariables(Handle<Context> context) {
// and assume they may invoke eval themselves. Eventually we could capture
// this information in the ScopeInfo and then use it here (by traversing
// the call chain stack, at compile time).
+
bool eval_scope = is_eval_scope();
- PropagateScopeInfo(eval_scope, eval_scope);
+ bool outer_scope_calls_eval = false;
+ bool outer_scope_calls_non_strict_eval = false;
+ if (!is_global_scope()) {
+ context->ComputeEvalScopeInfo(&outer_scope_calls_eval,
+ &outer_scope_calls_non_strict_eval);
+ }
+ PropagateScopeInfo(outer_scope_calls_eval,
+ outer_scope_calls_non_strict_eval,
+ eval_scope);
// 2) Resolve variables.
Scope* global_scope = NULL;
@@ -622,10 +632,14 @@ void Scope::Print(int n) {
if (HasTrivialOuterContext()) {
Indent(n1, "// scope has trivial outer context\n");
}
+ if (is_strict_mode()) Indent(n1, "// strict mode scope\n");
if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n");
if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n");
if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n");
if (outer_scope_calls_eval_) Indent(n1, "// outer scope calls 'eval'\n");
+ if (outer_scope_calls_non_strict_eval_) {
+ Indent(n1, "// outer scope calls 'eval' in non-strict context\n");
+ }
if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n");
if (outer_scope_is_eval_scope_) {
Indent(n1, "// outer scope is 'eval' scope\n");
@@ -852,20 +866,30 @@ void Scope::ResolveVariablesRecursively(Scope* global_scope,
bool Scope::PropagateScopeInfo(bool outer_scope_calls_eval,
+ bool outer_scope_calls_non_strict_eval,
bool outer_scope_is_eval_scope) {
if (outer_scope_calls_eval) {
outer_scope_calls_eval_ = true;
}
+ if (outer_scope_calls_non_strict_eval) {
+ outer_scope_calls_non_strict_eval_ = true;
+ }
+
if (outer_scope_is_eval_scope) {
outer_scope_is_eval_scope_ = true;
}
bool calls_eval = scope_calls_eval_ || outer_scope_calls_eval_;
bool is_eval = is_eval_scope() || outer_scope_is_eval_scope_;
+ bool calls_non_strict_eval =
+ (scope_calls_eval_ && !is_strict_mode()) ||
+ outer_scope_calls_non_strict_eval_;
for (int i = 0; i < inner_scopes_.length(); i++) {
Scope* inner_scope = inner_scopes_[i];
- if (inner_scope->PropagateScopeInfo(calls_eval, is_eval)) {
+ if (inner_scope->PropagateScopeInfo(calls_eval,
+ calls_non_strict_eval,
+ is_eval)) {
inner_scope_calls_eval_ = true;
}
if (inner_scope->force_eager_compilation_) {
diff --git a/src/scopes.h b/src/scopes.h
index a0e56a47..faa6fd97 100644
--- a/src/scopes.h
+++ b/src/scopes.h
@@ -218,10 +218,16 @@ class Scope: public ZoneObject {
bool is_function_scope() const { return type_ == FUNCTION_SCOPE; }
bool is_global_scope() const { return type_ == GLOBAL_SCOPE; }
bool is_strict_mode() const { return strict_mode_; }
+ bool is_strict_mode_eval_scope() const {
+ return is_eval_scope() && is_strict_mode();
+ }
// Information about which scopes calls eval.
bool calls_eval() const { return scope_calls_eval_; }
bool outer_scope_calls_eval() const { return outer_scope_calls_eval_; }
+ bool outer_scope_calls_non_strict_eval() const {
+ return outer_scope_calls_non_strict_eval_;
+ }
// Is this scope inside a with statement.
bool inside_with() const { return scope_inside_with_; }
@@ -379,6 +385,7 @@ class Scope: public ZoneObject {
// Computed via PropagateScopeInfo.
bool outer_scope_calls_eval_;
+ bool outer_scope_calls_non_strict_eval_;
bool inner_scope_calls_eval_;
bool outer_scope_is_eval_scope_;
bool force_eager_compilation_;
@@ -410,6 +417,7 @@ class Scope: public ZoneObject {
// Scope analysis.
bool PropagateScopeInfo(bool outer_scope_calls_eval,
+ bool outer_scope_calls_non_strict_eval,
bool outer_scope_is_eval_scope);
bool HasTrivialContext() const;
diff --git a/src/serialize.cc b/src/serialize.cc
index 12e9613e..a64fba30 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -29,6 +29,7 @@
#include "accessors.h"
#include "api.h"
+#include "bootstrapper.h"
#include "execution.h"
#include "global-handles.h"
#include "ic-inl.h"
@@ -38,7 +39,6 @@
#include "serialize.h"
#include "stub-cache.h"
#include "v8threads.h"
-#include "bootstrapper.h"
namespace v8 {
namespace internal {
diff --git a/src/serialize.h b/src/serialize.h
index 07c0a255..d83722d0 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -148,7 +148,7 @@ class SnapshotByteSource {
// This only works for objects in the first page of a space. Don't use this for
// things in newspace since it bypasses the write barrier.
-RLYSTC const int k64 = (sizeof(uintptr_t) - 4) / 4;
+static const int k64 = (sizeof(uintptr_t) - 4) / 4;
#define COMMON_REFERENCE_PATTERNS(f) \
f(kNumberOfSpaces, 2, (11 - k64)) \
@@ -181,8 +181,8 @@ RLYSTC const int k64 = (sizeof(uintptr_t) - 4) / 4;
// both.
class SerializerDeserializer: public ObjectVisitor {
public:
- RLYSTC void Iterate(ObjectVisitor* visitor);
- RLYSTC void SetSnapshotCacheSize(int size);
+ static void Iterate(ObjectVisitor* visitor);
+ static void SetSnapshotCacheSize(int size);
protected:
// Where the pointed-to object can be found:
@@ -220,34 +220,34 @@ class SerializerDeserializer: public ObjectVisitor {
// Misc.
// Raw data to be copied from the snapshot.
- RLYSTC const int kRawData = 0x30;
+ static const int kRawData = 0x30;
// Some common raw lengths: 0x31-0x3f
// A tag emitted at strategic points in the snapshot to delineate sections.
// If the deserializer does not find these at the expected moments then it
// is an indication that the snapshot and the VM do not fit together.
// Examine the build process for architecture, version or configuration
// mismatches.
- RLYSTC const int kSynchronize = 0x70;
+ static const int kSynchronize = 0x70;
// Used for the source code of the natives, which is in the executable, but
// is referred to from external strings in the snapshot.
- RLYSTC const int kNativesStringResource = 0x71;
- RLYSTC const int kNewPage = 0x72;
+ static const int kNativesStringResource = 0x71;
+ static const int kNewPage = 0x72;
// 0x73-0x7f Free.
// 0xb0-0xbf Free.
// 0xf0-0xff Free.
- RLYSTC const int kLargeData = LAST_SPACE;
- RLYSTC const int kLargeCode = kLargeData + 1;
- RLYSTC const int kLargeFixedArray = kLargeCode + 1;
- RLYSTC const int kNumberOfSpaces = kLargeFixedArray + 1;
- RLYSTC const int kAnyOldSpace = -1;
+ static const int kLargeData = LAST_SPACE;
+ static const int kLargeCode = kLargeData + 1;
+ static const int kLargeFixedArray = kLargeCode + 1;
+ static const int kNumberOfSpaces = kLargeFixedArray + 1;
+ static const int kAnyOldSpace = -1;
// A bitmask for getting the space out of an instruction.
- RLYSTC const int kSpaceMask = 15;
+ static const int kSpaceMask = 15;
- RLYSTC inline bool SpaceIsLarge(int space) { return space >= kLargeData; }
- RLYSTC inline bool SpaceIsPaged(int space) {
+ static inline bool SpaceIsLarge(int space) { return space >= kLargeData; }
+ static inline bool SpaceIsPaged(int space) {
return space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE;
}
};
@@ -380,19 +380,19 @@ class SerializationAddressMapper {
}
private:
- RLYSTC bool SerializationMatchFun(void* key1, void* key2) {
+ static bool SerializationMatchFun(void* key1, void* key2) {
return key1 == key2;
}
- RLYSTC uint32_t Hash(HeapObject* obj) {
+ static uint32_t Hash(HeapObject* obj) {
return static_cast<int32_t>(reinterpret_cast<intptr_t>(obj->address()));
}
- RLYSTC void* Key(HeapObject* obj) {
+ static void* Key(HeapObject* obj) {
return reinterpret_cast<void*>(obj->address());
}
- RLYSTC void* Value(int v) {
+ static void* Value(int v) {
return reinterpret_cast<void*>(v);
}
@@ -403,7 +403,7 @@ class SerializationAddressMapper {
// There can be only one serializer per V8 process.
-STATIC_CLASS Serializer : public SerializerDeserializer {
+class Serializer : public SerializerDeserializer {
public:
explicit Serializer(SnapshotByteSink* sink);
~Serializer();
@@ -415,25 +415,25 @@ STATIC_CLASS Serializer : public SerializerDeserializer {
return fullness_[space];
}
- RLYSTC void Enable() {
+ static void Enable() {
if (!serialization_enabled_) {
ASSERT(!too_late_to_enable_now_);
}
serialization_enabled_ = true;
}
- RLYSTC void Disable() { serialization_enabled_ = false; }
+ static void Disable() { serialization_enabled_ = false; }
// Call this when you have made use of the fact that there is no serialization
// going on.
- RLYSTC void TooLateToEnableNow() { too_late_to_enable_now_ = true; }
- RLYSTC bool enabled() { return serialization_enabled_; }
+ static void TooLateToEnableNow() { too_late_to_enable_now_ = true; }
+ static bool enabled() { return serialization_enabled_; }
SerializationAddressMapper* address_mapper() { return &address_mapper_; }
#ifdef DEBUG
virtual void Synchronize(const char* tag);
#endif
protected:
- RLYSTC const int kInvalidRootIndex = -1;
+ static const int kInvalidRootIndex = -1;
virtual int RootIndex(HeapObject* heap_object) = 0;
virtual bool ShouldBeInThePartialSnapshotCache(HeapObject* o) = 0;
@@ -488,11 +488,11 @@ STATIC_CLASS Serializer : public SerializerDeserializer {
// object space it may return kLargeCode or kLargeFixedArray in order
// to indicate to the deserializer what kind of large object allocation
// to make.
- RLYSTC int SpaceOfObject(HeapObject* object);
+ static int SpaceOfObject(HeapObject* object);
// This just returns the space of the object. It will return LO_SPACE
// for all large objects since you can't check the type of the object
// once the map has been used for the serialization address.
- RLYSTC int SpaceOfAlreadySerializedObject(HeapObject* object);
+ static int SpaceOfAlreadySerializedObject(HeapObject* object);
int Allocate(int space, int size, bool* new_page_started);
int EncodeExternalReference(Address addr) {
return external_reference_encoder_->Encode(addr);
@@ -506,9 +506,9 @@ STATIC_CLASS Serializer : public SerializerDeserializer {
SnapshotByteSink* sink_;
int current_root_index_;
ExternalReferenceEncoder* external_reference_encoder_;
- RLYSTC bool serialization_enabled_;
+ static bool serialization_enabled_;
// Did we already make use of the fact that serialization was not enabled?
- RLYSTC bool too_late_to_enable_now_;
+ static bool too_late_to_enable_now_;
int large_object_total_;
SerializationAddressMapper address_mapper_;
diff --git a/src/snapshot-common.cc b/src/snapshot-common.cc
index 7f828958..ef89a5ef 100644
--- a/src/snapshot-common.cc
+++ b/src/snapshot-common.cc
@@ -53,7 +53,7 @@ bool Snapshot::Initialize(const char* snapshot_file) {
DeleteArray(str);
return true;
} else if (size_ > 0) {
- Deserialize(data_, size_);
+ Deserialize(raw_data_, raw_size_);
return true;
}
return false;
@@ -71,7 +71,8 @@ Handle<Context> Snapshot::NewContextFromSnapshot() {
map_space_used_,
cell_space_used_,
large_space_used_);
- SnapshotByteSource source(context_data_, context_size_);
+ SnapshotByteSource source(context_raw_data_,
+ context_raw_size_);
Deserializer deserializer(&source);
Object* root;
deserializer.DeserializePartial(&root);
diff --git a/src/snapshot-empty.cc b/src/snapshot-empty.cc
index cb26eb8c..0b35720c 100644
--- a/src/snapshot-empty.cc
+++ b/src/snapshot-empty.cc
@@ -35,9 +35,13 @@ namespace v8 {
namespace internal {
const byte Snapshot::data_[] = { 0 };
+const byte* Snapshot::raw_data_ = NULL;
const int Snapshot::size_ = 0;
+const int Snapshot::raw_size_ = 0;
const byte Snapshot::context_data_[] = { 0 };
+const byte* Snapshot::context_raw_data_ = NULL;
const int Snapshot::context_size_ = 0;
+const int Snapshot::context_raw_size_ = 0;
const int Snapshot::new_space_used_ = 0;
const int Snapshot::pointer_space_used_ = 0;
diff --git a/src/snapshot.h b/src/snapshot.h
index bedd186e..4f01a2d6 100644
--- a/src/snapshot.h
+++ b/src/snapshot.h
@@ -33,7 +33,7 @@
namespace v8 {
namespace internal {
-STATIC_CLASS Snapshot {
+class Snapshot {
public:
// Initialize the VM from the given snapshot file. If snapshot_file is
// NULL, use the internal snapshot instead. Returns false if no snapshot
@@ -50,9 +50,25 @@ STATIC_CLASS Snapshot {
// successfully.
static bool WriteToFile(const char* snapshot_file);
+ static const byte* data() { return data_; }
+ static int size() { return size_; }
+ static int raw_size() { return raw_size_; }
+ static void set_raw_data(const byte* raw_data) {
+ raw_data_ = raw_data;
+ }
+ static const byte* context_data() { return context_data_; }
+ static int context_size() { return context_size_; }
+ static int context_raw_size() { return context_raw_size_; }
+ static void set_context_raw_data(
+ const byte* context_raw_data) {
+ context_raw_data_ = context_raw_data;
+ }
+
private:
static const byte data_[];
+ static const byte* raw_data_;
static const byte context_data_[];
+ static const byte* context_raw_data_;
static const int new_space_used_;
static const int pointer_space_used_;
static const int data_space_used_;
@@ -61,7 +77,9 @@ STATIC_CLASS Snapshot {
static const int cell_space_used_;
static const int large_space_used_;
static const int size_;
+ static const int raw_size_;
static const int context_size_;
+ static const int context_raw_size_;
static bool Deserialize(const byte* content, int len);
diff --git a/src/spaces-inl.h b/src/spaces-inl.h
index 070f9705..f5f66545 100644
--- a/src/spaces-inl.h
+++ b/src/spaces-inl.h
@@ -155,7 +155,8 @@ uint32_t Page::GetRegionMaskForAddress(Address addr) {
uint32_t Page::GetRegionMaskForSpan(Address start, int length_in_bytes) {
uint32_t result = 0;
- if (length_in_bytes >= kPageSize) {
+ static const intptr_t kRegionMask = (1 << kRegionSizeLog2) - 1;
+ if (length_in_bytes + (OffsetFrom(start) & kRegionMask) >= kPageSize) {
result = kAllRegionsDirtyMarks;
} else if (length_in_bytes > 0) {
int start_region = GetRegionNumberForAddress(start);
diff --git a/src/spaces.cc b/src/spaces.cc
index 3db9306c..b494d24a 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -148,12 +148,12 @@ PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) {
// CodeRange
-CodeRange::CodeRange(Isolate* isolate)
- : isolate_(isolate),
- code_range_(NULL),
+CodeRange::CodeRange()
+ : code_range_(NULL),
free_list_(0),
allocation_list_(0),
- current_allocation_block_index_(0) {
+ current_allocation_block_index_(0),
+ isolate_(NULL) {
}
@@ -279,9 +279,8 @@ void CodeRange::TearDown() {
const int kEstimatedNumberOfChunks = 270;
-MemoryAllocator::MemoryAllocator(Isolate* isolate)
- : isolate_(isolate),
- capacity_(0),
+MemoryAllocator::MemoryAllocator()
+ : capacity_(0),
capacity_executable_(0),
size_(0),
size_executable_(0),
@@ -289,7 +288,8 @@ MemoryAllocator::MemoryAllocator(Isolate* isolate)
chunks_(kEstimatedNumberOfChunks),
free_chunk_ids_(kEstimatedNumberOfChunks),
max_nof_chunks_(0),
- top_(0) {
+ top_(0),
+ isolate_(NULL) {
}
@@ -1564,13 +1564,12 @@ static void ReportCodeKindStatistics() {
CASE(BUILTIN);
CASE(LOAD_IC);
CASE(KEYED_LOAD_IC);
- CASE(KEYED_EXTERNAL_ARRAY_LOAD_IC);
CASE(STORE_IC);
CASE(KEYED_STORE_IC);
- CASE(KEYED_EXTERNAL_ARRAY_STORE_IC);
CASE(CALL_IC);
CASE(KEYED_CALL_IC);
- CASE(TYPE_RECORDING_BINARY_OP_IC);
+ CASE(UNARY_OP_IC);
+ CASE(BINARY_OP_IC);
CASE(COMPARE_IC);
}
}
diff --git a/src/spaces.h b/src/spaces.h
index f323f85e..4024387c 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,7 +28,8 @@
#ifndef V8_SPACES_H_
#define V8_SPACES_H_
-#include "list-inl.h"
+#include "allocation.h"
+#include "list.h"
#include "log.h"
namespace v8 {
@@ -413,8 +414,6 @@ class Space : public Malloced {
// manages a range of virtual memory.
class CodeRange {
public:
- explicit CodeRange(Isolate* isolate);
-
// Reserves a range of virtual memory, but does not commit any of it.
// Can only be called once, at heap initialization time.
// Returns false on failure.
@@ -424,9 +423,9 @@ class CodeRange {
// manage it.
void TearDown();
- bool exists() { return this != NULL && code_range_ != NULL; }
+ bool exists() { return code_range_ != NULL; }
bool contains(Address address) {
- if (this == NULL || code_range_ == NULL) return false;
+ if (code_range_ == NULL) return false;
Address start = static_cast<Address>(code_range_->address());
return start <= address && address < start + code_range_->size();
}
@@ -439,7 +438,7 @@ class CodeRange {
void FreeRawMemory(void* buf, size_t length);
private:
- Isolate* isolate_;
+ CodeRange();
// The reserved range of virtual memory that all code objects are put in.
VirtualMemory* code_range_;
@@ -473,6 +472,10 @@ class CodeRange {
static int CompareFreeBlockAddress(const FreeBlock* left,
const FreeBlock* right);
+ friend class Isolate;
+
+ Isolate* isolate_;
+
DISALLOW_COPY_AND_ASSIGN(CodeRange);
};
@@ -503,8 +506,6 @@ class CodeRange {
class MemoryAllocator {
public:
- explicit MemoryAllocator(Isolate* isolate);
-
// Initializes its internal bookkeeping structures.
// Max capacity of the total space and executable memory limit.
bool Setup(intptr_t max_capacity, intptr_t capacity_executable);
@@ -675,11 +676,11 @@ class MemoryAllocator {
#endif
private:
+ MemoryAllocator();
+
static const int kChunkSize = kPagesPerChunk * Page::kPageSize;
static const int kChunkSizeLog2 = kPagesPerChunkLog2 + kPageSizeBits;
- Isolate* isolate_;
-
// Maximum space size in bytes.
intptr_t capacity_;
// Maximum subset of capacity_ that can be executable
@@ -772,6 +773,10 @@ class MemoryAllocator {
Page* prev,
Page** last_page_in_use);
+ friend class Isolate;
+
+ Isolate* isolate_;
+
DISALLOW_COPY_AND_ASSIGN(MemoryAllocator);
};
diff --git a/src/splay-tree.h b/src/splay-tree.h
index c2652760..0cb9ea84 100644
--- a/src/splay-tree.h
+++ b/src/splay-tree.h
@@ -28,6 +28,8 @@
#ifndef V8_SPLAY_TREE_H_
#define V8_SPLAY_TREE_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/string.js b/src/string.js
index d8d402c4..f24862cd 100644
--- a/src/string.js
+++ b/src/string.js
@@ -62,6 +62,10 @@ function StringValueOf() {
// ECMA-262, section 15.5.4.4
function StringCharAt(pos) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.charAt"]);
+ }
var result = %_StringCharAt(this, pos);
if (%_IsSmi(result)) {
result = %_StringCharAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
@@ -72,6 +76,10 @@ function StringCharAt(pos) {
// ECMA-262 section 15.5.4.5
function StringCharCodeAt(pos) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.charCodeAt"]);
+ }
var result = %_StringCharCodeAt(this, pos);
if (!%_IsSmi(result)) {
result = %_StringCharCodeAt(TO_STRING_INLINE(this), TO_INTEGER(pos));
@@ -82,6 +90,9 @@ function StringCharCodeAt(pos) {
// ECMA-262, section 15.5.4.6
function StringConcat() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
+ }
var len = %_ArgumentsLength();
var this_as_string = TO_STRING_INLINE(this);
if (len === 1) {
@@ -102,6 +113,10 @@ function StringConcat() {
// ECMA-262 section 15.5.4.7
function StringIndexOf(pattern /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.indexOf"]);
+ }
var subject = TO_STRING_INLINE(this);
pattern = TO_STRING_INLINE(pattern);
var index = 0;
@@ -117,6 +132,10 @@ function StringIndexOf(pattern /* position */) { // length == 1
// ECMA-262 section 15.5.4.8
function StringLastIndexOf(pat /* position */) { // length == 1
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.lastIndexOf"]);
+ }
var sub = TO_STRING_INLINE(this);
var subLength = sub.length;
var pat = TO_STRING_INLINE(pat);
@@ -146,6 +165,10 @@ function StringLastIndexOf(pat /* position */) { // length == 1
// This function is implementation specific. For now, we do not
// do anything locale specific.
function StringLocaleCompare(other) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.localeCompare"]);
+ }
if (%_ArgumentsLength() === 0) return 0;
return %StringLocaleCompare(TO_STRING_INLINE(this),
TO_STRING_INLINE(other));
@@ -154,6 +177,10 @@ function StringLocaleCompare(other) {
// ECMA-262 section 15.5.4.10
function StringMatch(regexp) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.match"]);
+ }
var subject = TO_STRING_INLINE(this);
if (IS_REGEXP(regexp)) {
if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0);
@@ -187,6 +214,10 @@ var reusableMatchInfo = [2, "", "", -1, -1];
// ECMA-262, section 15.5.4.11
function StringReplace(search, replace) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.replace"]);
+ }
var subject = TO_STRING_INLINE(this);
// Delegate to one of the regular expression variants if necessary.
@@ -243,7 +274,7 @@ function StringReplace(search, replace) {
// the result.
function ExpandReplacement(string, subject, matchInfo, builder) {
var length = string.length;
- var builder_elements = builder.elements;
+ var builder_elements = builder.elements;
var next = %StringIndexOf(string, '$', 0);
if (next < 0) {
if (length > 0) builder_elements.push(string);
@@ -467,6 +498,10 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
// ECMA-262 section 15.5.4.12
function StringSearch(re) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.search"]);
+ }
var regexp;
if (IS_STRING(re)) {
regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
@@ -485,6 +520,10 @@ function StringSearch(re) {
// ECMA-262 section 15.5.4.13
function StringSlice(start, end) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.slice"]);
+ }
var s = TO_STRING_INLINE(this);
var s_len = s.length;
var start_i = TO_INTEGER(start);
@@ -520,6 +559,10 @@ function StringSlice(start, end) {
// ECMA-262 section 15.5.4.14
function StringSplit(separator, limit) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.split"]);
+ }
var subject = TO_STRING_INLINE(this);
limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
if (limit === 0) return [];
@@ -613,6 +656,10 @@ function StringSplit(separator, limit) {
// ECMA-262 section 15.5.4.15
function StringSubstring(start, end) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.subString"]);
+ }
var s = TO_STRING_INLINE(this);
var s_len = s.length;
@@ -646,6 +693,10 @@ function StringSubstring(start, end) {
// This is not a part of ECMA-262.
function StringSubstr(start, n) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.substr"]);
+ }
var s = TO_STRING_INLINE(this);
var len;
@@ -686,37 +737,65 @@ function StringSubstr(start, n) {
// ECMA-262, 15.5.4.16
function StringToLowerCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toLowerCase"]);
+ }
return %StringToLowerCase(TO_STRING_INLINE(this));
}
// ECMA-262, 15.5.4.17
function StringToLocaleLowerCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toLocaleLowerCase"]);
+ }
return %StringToLowerCase(TO_STRING_INLINE(this));
}
// ECMA-262, 15.5.4.18
function StringToUpperCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toUpperCase"]);
+ }
return %StringToUpperCase(TO_STRING_INLINE(this));
}
// ECMA-262, 15.5.4.19
function StringToLocaleUpperCase() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.toLocaleUpperCase"]);
+ }
return %StringToUpperCase(TO_STRING_INLINE(this));
}
// ES5, 15.5.4.20
function StringTrim() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.trim"]);
+ }
return %StringTrim(TO_STRING_INLINE(this), true, true);
}
function StringTrimLeft() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.trimLeft"]);
+ }
return %StringTrim(TO_STRING_INLINE(this), true, false);
}
function StringTrimRight() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["String.prototype.trimRight"]);
+ }
return %StringTrim(TO_STRING_INLINE(this), false, true);
}
@@ -830,6 +909,8 @@ function ReplaceResultBuilder(str) {
this.special_string = str;
}
+ReplaceResultBuilder.prototype.__proto__ = null;
+
ReplaceResultBuilder.prototype.add = function(str) {
str = TO_STRING_INLINE(str);
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index 0c6a7f74..8c6d84c6 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -457,34 +457,6 @@ MaybeObject* StubCache::ComputeKeyedLoadFunctionPrototype(
}
-MaybeObject* StubCache::ComputeKeyedLoadSpecialized(JSObject* receiver) {
- // Using NORMAL as the PropertyType for array element loads is a misuse. The
- // generated stub always accesses fast elements, not slow-mode fields, but
- // some property type is required for the stub lookup. Note that overloading
- // the NORMAL PropertyType is only safe as long as no stubs are generated for
- // other keyed field loads. This is guaranteed to be the case since all field
- // keyed loads that are not array elements go through a generic builtin stub.
- Code::Flags flags =
- Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, NORMAL);
- String* name = heap()->KeyedLoadSpecialized_symbol();
- Object* code = receiver->map()->FindInCodeCache(name, flags);
- if (code->IsUndefined()) {
- KeyedLoadStubCompiler compiler;
- { MaybeObject* maybe_code = compiler.CompileLoadSpecialized(receiver);
- if (!maybe_code->ToObject(&code)) return maybe_code;
- }
- PROFILE(isolate_,
- CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), 0));
- Object* result;
- { MaybeObject* maybe_result =
- receiver->UpdateMapCodeCache(name, Code::cast(code));
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
- }
- return code;
-}
-
-
MaybeObject* StubCache::ComputeStoreField(String* name,
JSObject* receiver,
int field_index,
@@ -513,30 +485,6 @@ MaybeObject* StubCache::ComputeStoreField(String* name,
}
-MaybeObject* StubCache::ComputeKeyedStoreSpecialized(
- JSObject* receiver,
- StrictModeFlag strict_mode) {
- Code::Flags flags =
- Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, NORMAL, strict_mode);
- String* name = heap()->KeyedStoreSpecialized_symbol();
- Object* code = receiver->map()->FindInCodeCache(name, flags);
- if (code->IsUndefined()) {
- KeyedStoreStubCompiler compiler(strict_mode);
- { MaybeObject* maybe_code = compiler.CompileStoreSpecialized(receiver);
- if (!maybe_code->ToObject(&code)) return maybe_code;
- }
- PROFILE(isolate_,
- CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), 0));
- Object* result;
- { MaybeObject* maybe_result =
- receiver->UpdateMapCodeCache(name, Code::cast(code));
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
- }
- return code;
-}
-
-
namespace {
ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) {
@@ -555,6 +503,8 @@ ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) {
return kExternalUnsignedIntArray;
case JSObject::EXTERNAL_FLOAT_ELEMENTS:
return kExternalFloatArray;
+ case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
+ return kExternalDoubleArray;
case JSObject::EXTERNAL_PIXEL_ELEMENTS:
return kExternalPixelArray;
default:
@@ -563,56 +513,6 @@ ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) {
}
}
-String* ExternalArrayTypeToStubName(Heap* heap,
- ExternalArrayType array_type,
- bool is_store) {
- if (is_store) {
- switch (array_type) {
- case kExternalByteArray:
- return heap->KeyedStoreExternalByteArray_symbol();
- case kExternalUnsignedByteArray:
- return heap->KeyedStoreExternalUnsignedByteArray_symbol();
- case kExternalShortArray:
- return heap->KeyedStoreExternalShortArray_symbol();
- case kExternalUnsignedShortArray:
- return heap->KeyedStoreExternalUnsignedShortArray_symbol();
- case kExternalIntArray:
- return heap->KeyedStoreExternalIntArray_symbol();
- case kExternalUnsignedIntArray:
- return heap->KeyedStoreExternalUnsignedIntArray_symbol();
- case kExternalFloatArray:
- return heap->KeyedStoreExternalFloatArray_symbol();
- case kExternalPixelArray:
- return heap->KeyedStoreExternalPixelArray_symbol();
- default:
- UNREACHABLE();
- return NULL;
- }
- } else {
- switch (array_type) {
- case kExternalByteArray:
- return heap->KeyedLoadExternalByteArray_symbol();
- case kExternalUnsignedByteArray:
- return heap->KeyedLoadExternalUnsignedByteArray_symbol();
- case kExternalShortArray:
- return heap->KeyedLoadExternalShortArray_symbol();
- case kExternalUnsignedShortArray:
- return heap->KeyedLoadExternalUnsignedShortArray_symbol();
- case kExternalIntArray:
- return heap->KeyedLoadExternalIntArray_symbol();
- case kExternalUnsignedIntArray:
- return heap->KeyedLoadExternalUnsignedIntArray_symbol();
- case kExternalFloatArray:
- return heap->KeyedLoadExternalFloatArray_symbol();
- case kExternalPixelArray:
- return heap->KeyedLoadExternalPixelArray_symbol();
- default:
- UNREACHABLE();
- return NULL;
- }
- }
-}
-
} // anonymous namespace
@@ -622,37 +522,88 @@ MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray(
StrictModeFlag strict_mode) {
Code::Flags flags =
Code::ComputeMonomorphicFlags(
- is_store ? Code::KEYED_EXTERNAL_ARRAY_STORE_IC :
- Code::KEYED_EXTERNAL_ARRAY_LOAD_IC,
+ is_store ? Code::KEYED_STORE_IC :
+ Code::KEYED_LOAD_IC,
NORMAL,
strict_mode);
ExternalArrayType array_type =
ElementsKindToExternalArrayType(receiver->GetElementsKind());
- String* name = ExternalArrayTypeToStubName(heap(), array_type, is_store);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
- if (code->IsUndefined()) {
- ExternalArrayStubCompiler compiler;
- { MaybeObject* maybe_code =
- is_store ?
- compiler.CompileKeyedStoreStub(receiver, array_type, flags) :
- compiler.CompileKeyedLoadStub(receiver, array_type, flags);
- if (!maybe_code->ToObject(&code)) return maybe_code;
- }
- Code::cast(code)->set_external_array_type(array_type);
- if (is_store) {
- PROFILE(isolate_,
- CodeCreateEvent(Logger::KEYED_EXTERNAL_ARRAY_STORE_IC_TAG,
- Code::cast(code), 0));
- } else {
- PROFILE(isolate_,
- CodeCreateEvent(Logger::KEYED_EXTERNAL_ARRAY_LOAD_IC_TAG,
- Code::cast(code), 0));
- }
- Object* result;
- { MaybeObject* maybe_result =
- receiver->UpdateMapCodeCache(name, Code::cast(code));
- if (!maybe_result->ToObject(&result)) return maybe_result;
- }
+ String* name = is_store
+ ? isolate()->heap()->KeyedStoreSpecializedMonomorphic_symbol()
+ : isolate()->heap()->KeyedLoadSpecializedMonomorphic_symbol();
+ Object* maybe_code = receiver->map()->FindInCodeCache(name, flags);
+ if (!maybe_code->IsUndefined()) return Code::cast(maybe_code);
+
+ MaybeObject* maybe_new_code = NULL;
+ if (is_store) {
+ ExternalArrayStoreStubCompiler compiler(strict_mode);
+ maybe_new_code = compiler.CompileStore(receiver, array_type);
+ } else {
+ ExternalArrayLoadStubCompiler compiler(strict_mode);
+ maybe_new_code = compiler.CompileLoad(receiver, array_type);
+ }
+ Code* code;
+ if (!maybe_new_code->To(&code)) return maybe_new_code;
+ code->set_external_array_type(array_type);
+ if (is_store) {
+ PROFILE(isolate_,
+ CodeCreateEvent(Logger::KEYED_EXTERNAL_ARRAY_STORE_IC_TAG,
+ Code::cast(code), 0));
+ } else {
+ PROFILE(isolate_,
+ CodeCreateEvent(Logger::KEYED_EXTERNAL_ARRAY_LOAD_IC_TAG,
+ Code::cast(code), 0));
+ }
+ ASSERT(code->IsCode());
+ Object* result;
+ { MaybeObject* maybe_result =
+ receiver->UpdateMapCodeCache(name, Code::cast(code));
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ return code;
+}
+
+
+MaybeObject* StubCache::ComputeKeyedLoadOrStoreFastElement(
+ JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode) {
+ Code::Flags flags =
+ Code::ComputeMonomorphicFlags(
+ is_store ? Code::KEYED_STORE_IC :
+ Code::KEYED_LOAD_IC,
+ NORMAL,
+ strict_mode);
+ String* name = is_store
+ ? isolate()->heap()->KeyedStoreSpecializedMonomorphic_symbol()
+ : isolate()->heap()->KeyedLoadSpecializedMonomorphic_symbol();
+ Object* maybe_code = receiver->map()->FindInCodeCache(name, flags);
+ if (!maybe_code->IsUndefined()) return Code::cast(maybe_code);
+
+ MaybeObject* maybe_new_code = NULL;
+ if (is_store) {
+ KeyedStoreStubCompiler compiler(strict_mode);
+ maybe_new_code = compiler.CompileStoreFastElement(receiver->map());
+ } else {
+ KeyedLoadStubCompiler compiler;
+ maybe_new_code = compiler.CompileLoadFastElement(receiver->map());
+ }
+ Code* code;
+ if (!maybe_new_code->To(&code)) return maybe_new_code;
+ if (is_store) {
+ PROFILE(isolate_,
+ CodeCreateEvent(Logger::KEYED_STORE_IC_TAG,
+ Code::cast(code), 0));
+ } else {
+ PROFILE(isolate_,
+ CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG,
+ Code::cast(code), 0));
+ }
+ ASSERT(code->IsCode());
+ Object* result;
+ { MaybeObject* maybe_result =
+ receiver->UpdateMapCodeCache(name, Code::cast(code));
+ if (!maybe_result->ToObject(&result)) return maybe_result;
}
return code;
}
@@ -838,6 +789,7 @@ MaybeObject* StubCache::ComputeCallConstant(int argc,
MaybeObject* StubCache::ComputeCallField(int argc,
InLoopFlag in_loop,
Code::Kind kind,
+ Code::ExtraICState extra_ic_state,
String* name,
Object* object,
JSObject* holder,
@@ -856,14 +808,14 @@ MaybeObject* StubCache::ComputeCallField(int argc,
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
FIELD,
- Code::kNoExtraICState,
+ extra_ic_state,
cache_holder,
in_loop,
argc);
Object* code = map_holder->map()->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
CallStubCompiler compiler(
- argc, in_loop, kind, Code::kNoExtraICState, cache_holder);
+ argc, in_loop, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallField(JSObject::cast(object),
holder,
@@ -886,11 +838,13 @@ MaybeObject* StubCache::ComputeCallField(int argc,
}
-MaybeObject* StubCache::ComputeCallInterceptor(int argc,
- Code::Kind kind,
- String* name,
- Object* object,
- JSObject* holder) {
+MaybeObject* StubCache::ComputeCallInterceptor(
+ int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state,
+ String* name,
+ Object* object,
+ JSObject* holder) {
// Compute the check type and the map.
InlineCacheHolderFlag cache_holder =
IC::GetCodeCacheForObject(object, holder);
@@ -905,14 +859,14 @@ MaybeObject* StubCache::ComputeCallInterceptor(int argc,
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
INTERCEPTOR,
- Code::kNoExtraICState,
+ extra_ic_state,
cache_holder,
NOT_IN_LOOP,
argc);
Object* code = map_holder->map()->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
CallStubCompiler compiler(
- argc, NOT_IN_LOOP, kind, Code::kNoExtraICState, cache_holder);
+ argc, NOT_IN_LOOP, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallInterceptor(JSObject::cast(object), holder, name);
if (!maybe_code->ToObject(&code)) return maybe_code;
@@ -935,10 +889,12 @@ MaybeObject* StubCache::ComputeCallInterceptor(int argc,
MaybeObject* StubCache::ComputeCallNormal(int argc,
InLoopFlag in_loop,
Code::Kind kind,
+ Code::ExtraICState extra_ic_state,
String* name,
JSObject* receiver) {
Object* code;
- { MaybeObject* maybe_code = ComputeCallNormal(argc, in_loop, kind);
+ { MaybeObject* maybe_code =
+ ComputeCallNormal(argc, in_loop, kind, extra_ic_state);
if (!maybe_code->ToObject(&code)) return maybe_code;
}
return code;
@@ -948,6 +904,7 @@ MaybeObject* StubCache::ComputeCallNormal(int argc,
MaybeObject* StubCache::ComputeCallGlobal(int argc,
InLoopFlag in_loop,
Code::Kind kind,
+ Code::ExtraICState extra_ic_state,
String* name,
JSObject* receiver,
GlobalObject* holder,
@@ -958,7 +915,7 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc,
JSObject* map_holder = IC::GetCodeCacheHolder(receiver, cache_holder);
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
NORMAL,
- Code::kNoExtraICState,
+ extra_ic_state,
cache_holder,
in_loop,
argc);
@@ -970,7 +927,7 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc,
// caches.
if (!function->is_compiled()) return Failure::InternalError();
CallStubCompiler compiler(
- argc, in_loop, kind, Code::kNoExtraICState, cache_holder);
+ argc, in_loop, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallGlobal(receiver, holder, cell, function, name);
if (!maybe_code->ToObject(&code)) return maybe_code;
@@ -1040,11 +997,15 @@ static MaybeObject* FillCache(Isolate* isolate, MaybeObject* maybe_code) {
Code* StubCache::FindCallInitialize(int argc,
InLoopFlag in_loop,
+ RelocInfo::Mode mode,
Code::Kind kind) {
+ Code::ExtraICState extra_state =
+ CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) |
+ CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT);
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
UNINITIALIZED,
- Code::kNoExtraICState,
+ extra_state,
NORMAL,
argc);
Object* result = ProbeCache(isolate(), flags)->ToObjectUnchecked();
@@ -1057,11 +1018,15 @@ Code* StubCache::FindCallInitialize(int argc,
MaybeObject* StubCache::ComputeCallInitialize(int argc,
InLoopFlag in_loop,
+ RelocInfo::Mode mode,
Code::Kind kind) {
+ Code::ExtraICState extra_state =
+ CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) |
+ CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT);
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
UNINITIALIZED,
- Code::kNoExtraICState,
+ extra_state,
NORMAL,
argc);
Object* probe;
@@ -1074,17 +1039,20 @@ MaybeObject* StubCache::ComputeCallInitialize(int argc,
}
-Handle<Code> StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
+Handle<Code> StubCache::ComputeCallInitialize(int argc,
+ InLoopFlag in_loop,
+ RelocInfo::Mode mode) {
if (in_loop == IN_LOOP) {
// Force the creation of the corresponding stub outside loops,
// because it may be used when clearing the ICs later - it is
// possible for a series of IC transitions to lose the in-loop
// information, and the IC clearing code can't generate a stub
// that it needs so we need to ensure it is generated already.
- ComputeCallInitialize(argc, NOT_IN_LOOP);
+ ComputeCallInitialize(argc, NOT_IN_LOOP, mode);
}
CALL_HEAP_FUNCTION(isolate_,
- ComputeCallInitialize(argc, in_loop, Code::CALL_IC), Code);
+ ComputeCallInitialize(argc, in_loop, mode, Code::CALL_IC),
+ Code);
}
@@ -1100,17 +1068,23 @@ Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc,
}
CALL_HEAP_FUNCTION(
isolate_,
- ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC), Code);
+ ComputeCallInitialize(argc,
+ in_loop,
+ RelocInfo::CODE_TARGET,
+ Code::KEYED_CALL_IC),
+ Code);
}
-MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc,
- InLoopFlag in_loop,
- Code::Kind kind) {
+MaybeObject* StubCache::ComputeCallPreMonomorphic(
+ int argc,
+ InLoopFlag in_loop,
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
PREMONOMORPHIC,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc);
Object* probe;
@@ -1125,11 +1099,12 @@ MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc,
MaybeObject* StubCache::ComputeCallNormal(int argc,
InLoopFlag in_loop,
- Code::Kind kind) {
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
MONOMORPHIC,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc);
Object* probe;
@@ -1142,13 +1117,15 @@ MaybeObject* StubCache::ComputeCallNormal(int argc,
}
-MaybeObject* StubCache::ComputeCallMegamorphic(int argc,
- InLoopFlag in_loop,
- Code::Kind kind) {
+MaybeObject* StubCache::ComputeCallMegamorphic(
+ int argc,
+ InLoopFlag in_loop,
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
MEGAMORPHIC,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc);
Object* probe;
@@ -1161,13 +1138,15 @@ MaybeObject* StubCache::ComputeCallMegamorphic(int argc,
}
-MaybeObject* StubCache::ComputeCallMiss(int argc, Code::Kind kind) {
+MaybeObject* StubCache::ComputeCallMiss(int argc,
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
// MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs
// and monomorphic stubs are not mixed up together in the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
MONOMORPHIC_PROTOTYPE_FAILURE,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc,
OWN_MAP);
@@ -1182,7 +1161,11 @@ MaybeObject* StubCache::ComputeCallMiss(int argc, Code::Kind kind) {
#ifdef ENABLE_DEBUGGER_SUPPORT
-MaybeObject* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) {
+MaybeObject* StubCache::ComputeCallDebugBreak(
+ int argc,
+ Code::Kind kind) {
+ // Extra IC state is irrelevant for debug break ICs. They jump to
+ // the actual call ic to carry out the work.
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
DEBUG_BREAK,
@@ -1199,8 +1182,11 @@ MaybeObject* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) {
}
-MaybeObject* StubCache::ComputeCallDebugPrepareStepIn(int argc,
- Code::Kind kind) {
+MaybeObject* StubCache::ComputeCallDebugPrepareStepIn(
+ int argc,
+ Code::Kind kind) {
+ // Extra IC state is irrelevant for debug break ICs. They jump to
+ // the actual call ic to carry out the work.
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
DEBUG_PREPARE_STEP_IN,
@@ -1484,8 +1470,9 @@ MaybeObject* StubCompiler::CompileCallInitialize(Code::Flags flags) {
HandleScope scope(isolate());
int argc = Code::ExtractArgumentsCountFromFlags(flags);
Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags);
if (kind == Code::CALL_IC) {
- CallIC::GenerateInitialize(masm(), argc);
+ CallIC::GenerateInitialize(masm(), argc, extra_ic_state);
} else {
KeyedCallIC::GenerateInitialize(masm(), argc);
}
@@ -1511,8 +1498,9 @@ MaybeObject* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) {
// The code of the PreMonomorphic stub is the same as the code
// of the Initialized stub. They just differ on the code object flags.
Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags);
if (kind == Code::CALL_IC) {
- CallIC::GenerateInitialize(masm(), argc);
+ CallIC::GenerateInitialize(masm(), argc, extra_ic_state);
} else {
KeyedCallIC::GenerateInitialize(masm(), argc);
}
@@ -1537,6 +1525,9 @@ MaybeObject* StubCompiler::CompileCallNormal(Code::Flags flags) {
int argc = Code::ExtractArgumentsCountFromFlags(flags);
Code::Kind kind = Code::ExtractKindFromFlags(flags);
if (kind == Code::CALL_IC) {
+ // Call normal is always with a explict receiver.
+ ASSERT(!CallIC::Contextual::decode(
+ Code::ExtractExtraICStateFromFlags(flags)));
CallIC::GenerateNormal(masm(), argc);
} else {
KeyedCallIC::GenerateNormal(masm(), argc);
@@ -1560,8 +1551,9 @@ MaybeObject* StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
HandleScope scope(isolate());
int argc = Code::ExtractArgumentsCountFromFlags(flags);
Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags);
if (kind == Code::CALL_IC) {
- CallIC::GenerateMegamorphic(masm(), argc);
+ CallIC::GenerateMegamorphic(masm(), argc, extra_ic_state);
} else {
KeyedCallIC::GenerateMegamorphic(masm(), argc);
}
@@ -1585,8 +1577,9 @@ MaybeObject* StubCompiler::CompileCallMiss(Code::Flags flags) {
HandleScope scope(isolate());
int argc = Code::ExtractArgumentsCountFromFlags(flags);
Code::Kind kind = Code::ExtractKindFromFlags(flags);
+ Code::ExtraICState extra_ic_state = Code::ExtractExtraICStateFromFlags(flags);
if (kind == Code::CALL_IC) {
- CallIC::GenerateMiss(masm(), argc);
+ CallIC::GenerateMiss(masm(), argc, extra_ic_state);
} else {
KeyedCallIC::GenerateMiss(masm(), argc);
}
@@ -1632,7 +1625,8 @@ MaybeObject* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) {
int argc = Code::ExtractArgumentsCountFromFlags(flags);
Code::Kind kind = Code::ExtractKindFromFlags(flags);
if (kind == Code::CALL_IC) {
- CallIC::GenerateMiss(masm(), argc);
+ // For the debugger extra ic state is irrelevant.
+ CallIC::GenerateMiss(masm(), argc, Code::kNoExtraICState);
} else {
KeyedCallIC::GenerateMiss(masm(), argc);
}
@@ -1711,8 +1705,11 @@ MaybeObject* LoadStubCompiler::GetCode(PropertyType type, String* name) {
}
-MaybeObject* KeyedLoadStubCompiler::GetCode(PropertyType type, String* name) {
- Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, type);
+MaybeObject* KeyedLoadStubCompiler::GetCode(PropertyType type,
+ String* name,
+ InlineCacheState state) {
+ Code::Flags flags = Code::ComputeFlags(
+ Code::KEYED_LOAD_IC, NOT_IN_LOOP, state, Code::kNoExtraICState, type);
MaybeObject* result = GetCodeWithFlags(flags, name);
if (!result->IsFailure()) {
PROFILE(isolate(),
@@ -1744,9 +1741,11 @@ MaybeObject* StoreStubCompiler::GetCode(PropertyType type, String* name) {
}
-MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) {
- Code::Flags flags = Code::ComputeMonomorphicFlags(
- Code::KEYED_STORE_IC, type, strict_mode_);
+MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type,
+ String* name,
+ InlineCacheState state) {
+ Code::Flags flags = Code::ComputeFlags(
+ Code::KEYED_STORE_IC, NOT_IN_LOOP, state, strict_mode_, type);
MaybeObject* result = GetCodeWithFlags(flags, name);
if (!result->IsFailure()) {
PROFILE(isolate(),
@@ -1924,15 +1923,36 @@ void CallOptimization::AnalyzePossibleApiFunction(JSFunction* function) {
}
-MaybeObject* ExternalArrayStubCompiler::GetCode(Code::Flags flags) {
+MaybeObject* ExternalArrayLoadStubCompiler::GetCode() {
Object* result;
- { MaybeObject* maybe_result = GetCodeWithFlags(flags, "ExternalArrayStub");
+ Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC,
+ NORMAL,
+ strict_mode_);
+ { MaybeObject* maybe_result = GetCodeWithFlags(flags,
+ "ExternalArrayLoadStub");
+ if (!maybe_result->ToObject(&result)) return maybe_result;
+ }
+ Code* code = Code::cast(result);
+ USE(code);
+ PROFILE(isolate(),
+ CodeCreateEvent(Logger::STUB_TAG, code, "ExternalArrayLoadStub"));
+ return result;
+}
+
+
+MaybeObject* ExternalArrayStoreStubCompiler::GetCode() {
+ Object* result;
+ Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC,
+ NORMAL,
+ strict_mode_);
+ { MaybeObject* maybe_result = GetCodeWithFlags(flags,
+ "ExternalArrayStoreStub");
if (!maybe_result->ToObject(&result)) return maybe_result;
}
Code* code = Code::cast(result);
USE(code);
PROFILE(isolate(),
- CodeCreateEvent(Logger::STUB_TAG, code, "ExternalArrayStub"));
+ CodeCreateEvent(Logger::STUB_TAG, code, "ExternalArrayStoreStub"));
return result;
}
diff --git a/src/stub-cache.h b/src/stub-cache.h
index c5dcf36d..a1243c22 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -28,8 +28,10 @@
#ifndef V8_STUB_CACHE_H_
#define V8_STUB_CACHE_H_
+#include "allocation.h"
#include "arguments.h"
#include "macro-assembler.h"
+#include "objects.h"
#include "zone-inl.h"
namespace v8 {
@@ -143,9 +145,6 @@ class StubCache {
String* name,
JSFunction* receiver);
- MUST_USE_RESULT MaybeObject* ComputeKeyedLoadSpecialized(
- JSObject* receiver);
-
// ---
MUST_USE_RESULT MaybeObject* ComputeStoreField(
@@ -184,25 +183,26 @@ class StubCache {
Map* transition,
StrictModeFlag strict_mode);
- MUST_USE_RESULT MaybeObject* ComputeKeyedStoreSpecialized(
+ MUST_USE_RESULT MaybeObject* ComputeKeyedLoadOrStoreExternalArray(
JSObject* receiver,
+ bool is_store,
StrictModeFlag strict_mode);
-
- MUST_USE_RESULT MaybeObject* ComputeKeyedLoadOrStoreExternalArray(
+ MUST_USE_RESULT MaybeObject* ComputeKeyedLoadOrStoreFastElement(
JSObject* receiver,
bool is_store,
StrictModeFlag strict_mode);
-
// ---
- MUST_USE_RESULT MaybeObject* ComputeCallField(int argc,
- InLoopFlag in_loop,
- Code::Kind,
- String* name,
- Object* object,
- JSObject* holder,
- int index);
+ MUST_USE_RESULT MaybeObject* ComputeCallField(
+ int argc,
+ InLoopFlag in_loop,
+ Code::Kind,
+ Code::ExtraICState extra_ic_state,
+ String* name,
+ Object* object,
+ JSObject* holder,
+ int index);
MUST_USE_RESULT MaybeObject* ComputeCallConstant(
int argc,
@@ -214,22 +214,27 @@ class StubCache {
JSObject* holder,
JSFunction* function);
- MUST_USE_RESULT MaybeObject* ComputeCallNormal(int argc,
- InLoopFlag in_loop,
- Code::Kind,
- String* name,
- JSObject* receiver);
+ MUST_USE_RESULT MaybeObject* ComputeCallNormal(
+ int argc,
+ InLoopFlag in_loop,
+ Code::Kind,
+ Code::ExtraICState extra_ic_state,
+ String* name,
+ JSObject* receiver);
- MUST_USE_RESULT MaybeObject* ComputeCallInterceptor(int argc,
- Code::Kind,
- String* name,
- Object* object,
- JSObject* holder);
+ MUST_USE_RESULT MaybeObject* ComputeCallInterceptor(
+ int argc,
+ Code::Kind,
+ Code::ExtraICState extra_ic_state,
+ String* name,
+ Object* object,
+ JSObject* holder);
MUST_USE_RESULT MaybeObject* ComputeCallGlobal(
int argc,
InLoopFlag in_loop,
Code::Kind,
+ Code::ExtraICState extra_ic_state,
String* name,
JSObject* receiver,
GlobalObject* holder,
@@ -240,30 +245,39 @@ class StubCache {
MUST_USE_RESULT MaybeObject* ComputeCallInitialize(int argc,
InLoopFlag in_loop,
+ RelocInfo::Mode mode,
Code::Kind kind);
- Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
+ Handle<Code> ComputeCallInitialize(int argc,
+ InLoopFlag in_loop,
+ RelocInfo::Mode mode);
Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
MUST_USE_RESULT MaybeObject* ComputeCallPreMonomorphic(
int argc,
InLoopFlag in_loop,
- Code::Kind kind);
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state);
MUST_USE_RESULT MaybeObject* ComputeCallNormal(int argc,
InLoopFlag in_loop,
- Code::Kind kind);
+ Code::Kind kind,
+ Code::ExtraICState state);
MUST_USE_RESULT MaybeObject* ComputeCallMegamorphic(int argc,
InLoopFlag in_loop,
- Code::Kind kind);
+ Code::Kind kind,
+ Code::ExtraICState state);
- MUST_USE_RESULT MaybeObject* ComputeCallMiss(int argc, Code::Kind kind);
+ MUST_USE_RESULT MaybeObject* ComputeCallMiss(int argc,
+ Code::Kind kind,
+ Code::ExtraICState state);
// Finds the Code object stored in the Heap::non_monomorphic_cache().
MUST_USE_RESULT Code* FindCallInitialize(int argc,
InLoopFlag in_loop,
+ RelocInfo::Mode mode,
Code::Kind kind);
#ifdef ENABLE_DEBUGGER_SUPPORT
@@ -336,7 +350,7 @@ class StubCache {
Entry secondary_[kSecondaryTableSize];
// Computes the hashed offsets for primary and secondary caches.
- RLYSTC int PrimaryOffset(String* name, Code::Flags flags, Map* map) {
+ static int PrimaryOffset(String* name, Code::Flags flags, Map* map) {
// This works well because the heap object tag size and the hash
// shift are equal. Shifting down the length field to get the
// hash code would effectively throw away two bits of the hash
@@ -359,7 +373,7 @@ class StubCache {
return key & ((kPrimaryTableSize - 1) << kHeapObjectTagSize);
}
- RLYSTC int SecondaryOffset(String* name, Code::Flags flags, int seed) {
+ static int SecondaryOffset(String* name, Code::Flags flags, int seed) {
// Use the seed from the primary cache in the secondary cache.
uint32_t string_low32bits =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name));
@@ -376,7 +390,7 @@ class StubCache {
// ends in String::kHashShift 0s. Then we shift it so it is a multiple
// of sizeof(Entry). This makes it easier to avoid making mistakes
// in the hashed offset computations.
- RLYSTC Entry* entry(Entry* table, int offset) {
+ static Entry* entry(Entry* table, int offset) {
const int shift_amount = kPointerSizeLog2 + 1 - String::kHashShift;
return reinterpret_cast<Entry*>(
reinterpret_cast<Address>(table) + (offset << shift_amount));
@@ -468,7 +482,10 @@ class StubCompiler BASE_EMBEDDED {
Register scratch,
Label* miss_label);
- static void GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind);
+ static void GenerateLoadMiss(MacroAssembler* masm,
+ Code::Kind kind);
+
+ static void GenerateKeyedLoadMissForceGeneric(MacroAssembler* masm);
// Generates code that verifies that the property holder has not changed
// (checking maps of objects in the prototype chain for fast and global
@@ -633,10 +650,21 @@ class KeyedLoadStubCompiler: public StubCompiler {
MUST_USE_RESULT MaybeObject* CompileLoadStringLength(String* name);
MUST_USE_RESULT MaybeObject* CompileLoadFunctionPrototype(String* name);
- MUST_USE_RESULT MaybeObject* CompileLoadSpecialized(JSObject* receiver);
+ MUST_USE_RESULT MaybeObject* CompileLoadFastElement(Map* receiver_map);
+
+ MUST_USE_RESULT MaybeObject* CompileLoadMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics);
+
+ static void GenerateLoadExternalArray(MacroAssembler* masm,
+ ExternalArrayType array_type);
+
+ static void GenerateLoadFastElement(MacroAssembler* masm);
private:
- MaybeObject* GetCode(PropertyType type, String* name);
+ MaybeObject* GetCode(PropertyType type,
+ String* name,
+ InlineCacheState state = MONOMORPHIC);
};
@@ -677,10 +705,22 @@ class KeyedStoreStubCompiler: public StubCompiler {
Map* transition,
String* name);
- MUST_USE_RESULT MaybeObject* CompileStoreSpecialized(JSObject* receiver);
+ MUST_USE_RESULT MaybeObject* CompileStoreFastElement(Map* receiver_map);
+
+ MUST_USE_RESULT MaybeObject* CompileStoreMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics);
+
+ static void GenerateStoreFastElement(MacroAssembler* masm,
+ bool is_js_array);
+
+ static void GenerateStoreExternalArray(MacroAssembler* masm,
+ ExternalArrayType array_type);
private:
- MaybeObject* GetCode(PropertyType type, String* name);
+ MaybeObject* GetCode(PropertyType type,
+ String* name,
+ InlineCacheState state = MONOMORPHIC);
StrictModeFlag strict_mode_;
};
@@ -708,23 +748,30 @@ class CallStubCompiler: public StubCompiler {
Code::ExtraICState extra_ic_state,
InlineCacheHolderFlag cache_holder);
- MUST_USE_RESULT MaybeObject* CompileCallField(JSObject* object,
- JSObject* holder,
- int index,
- String* name);
- MUST_USE_RESULT MaybeObject* CompileCallConstant(Object* object,
- JSObject* holder,
- JSFunction* function,
- String* name,
- CheckType check);
- MUST_USE_RESULT MaybeObject* CompileCallInterceptor(JSObject* object,
- JSObject* holder,
- String* name);
- MUST_USE_RESULT MaybeObject* CompileCallGlobal(JSObject* object,
- GlobalObject* holder,
- JSGlobalPropertyCell* cell,
- JSFunction* function,
- String* name);
+ MUST_USE_RESULT MaybeObject* CompileCallField(
+ JSObject* object,
+ JSObject* holder,
+ int index,
+ String* name);
+
+ MUST_USE_RESULT MaybeObject* CompileCallConstant(
+ Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check);
+
+ MUST_USE_RESULT MaybeObject* CompileCallInterceptor(
+ JSObject* object,
+ JSObject* holder,
+ String* name);
+
+ MUST_USE_RESULT MaybeObject* CompileCallGlobal(
+ JSObject* object,
+ GlobalObject* holder,
+ JSGlobalPropertyCell* cell,
+ JSFunction* function,
+ String* name);
static bool HasCustomCallGenerator(JSFunction* function);
@@ -847,20 +894,36 @@ class CallOptimization BASE_EMBEDDED {
CallHandlerInfo* api_call_info_;
};
-class ExternalArrayStubCompiler: public StubCompiler {
+class ExternalArrayLoadStubCompiler: public StubCompiler {
public:
- explicit ExternalArrayStubCompiler() {}
+ explicit ExternalArrayLoadStubCompiler(StrictModeFlag strict_mode)
+ : strict_mode_(strict_mode) { }
+
+ MUST_USE_RESULT MaybeObject* CompileLoad(
+ JSObject* receiver, ExternalArrayType array_type);
+
+ private:
+ MaybeObject* GetCode();
+
+ StrictModeFlag strict_mode_;
+};
+
- MUST_USE_RESULT MaybeObject* CompileKeyedLoadStub(
- JSObject* receiver, ExternalArrayType array_type, Code::Flags flags);
+class ExternalArrayStoreStubCompiler: public StubCompiler {
+ public:
+ explicit ExternalArrayStoreStubCompiler(StrictModeFlag strict_mode)
+ : strict_mode_(strict_mode) {}
- MUST_USE_RESULT MaybeObject* CompileKeyedStoreStub(
- JSObject* receiver, ExternalArrayType array_type, Code::Flags flags);
+ MUST_USE_RESULT MaybeObject* CompileStore(
+ JSObject* receiver, ExternalArrayType array_type);
private:
- MaybeObject* GetCode(Code::Flags flags);
+ MaybeObject* GetCode();
+
+ StrictModeFlag strict_mode_;
};
+
} } // namespace v8::internal
#endif // V8_STUB_CACHE_H_
diff --git a/src/top.cc b/src/top.cc
deleted file mode 100644
index b9207c89..00000000
--- a/src/top.cc
+++ /dev/null
@@ -1,983 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "v8.h"
-
-#include "api.h"
-#include "bootstrapper.h"
-#include "compiler.h"
-#include "debug.h"
-#include "execution.h"
-#include "messages.h"
-#include "platform.h"
-#include "simulator.h"
-#include "string-stream.h"
-#include "vm-state-inl.h"
-
-
-// TODO(isolates): move to isolate.cc. This stuff is kept here to
-// simplify merging.
-
-namespace v8 {
-namespace internal {
-
-ThreadLocalTop::ThreadLocalTop() {
- InitializeInternal();
- // This flag may be set using v8::V8::IgnoreOutOfMemoryException()
- // before an isolate is initialized. The initialize methods below do
- // not touch it to preserve its value.
- ignore_out_of_memory_ = false;
-}
-
-
-void ThreadLocalTop::InitializeInternal() {
- c_entry_fp_ = 0;
- handler_ = 0;
-#ifdef USE_SIMULATOR
- simulator_ = NULL;
-#endif
-#ifdef ENABLE_LOGGING_AND_PROFILING
- js_entry_sp_ = NULL;
- external_callback_ = NULL;
-#endif
-#ifdef ENABLE_VMSTATE_TRACKING
- current_vm_state_ = EXTERNAL;
-#endif
- try_catch_handler_address_ = NULL;
- context_ = NULL;
- thread_id_ = ThreadId::Invalid();
- external_caught_exception_ = false;
- failed_access_check_callback_ = NULL;
- save_context_ = NULL;
- catcher_ = NULL;
-}
-
-
-void ThreadLocalTop::Initialize() {
- InitializeInternal();
-#ifdef USE_SIMULATOR
-#ifdef V8_TARGET_ARCH_ARM
- simulator_ = Simulator::current(Isolate::Current());
-#elif V8_TARGET_ARCH_MIPS
- simulator_ = Simulator::current(Isolate::Current());
-#endif
-#endif
- thread_id_ = ThreadId::Current();
-}
-
-
-v8::TryCatch* ThreadLocalTop::TryCatchHandler() {
- return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address());
-}
-
-
-Address Isolate::get_address_from_id(Isolate::AddressId id) {
- return isolate_addresses_[id];
-}
-
-
-char* Isolate::Iterate(ObjectVisitor* v, char* thread_storage) {
- ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(thread_storage);
- Iterate(v, thread);
- return thread_storage + sizeof(ThreadLocalTop);
-}
-
-
-void Isolate::IterateThread(ThreadVisitor* v) {
- v->VisitThread(this, thread_local_top());
-}
-
-
-void Isolate::IterateThread(ThreadVisitor* v, char* t) {
- ThreadLocalTop* thread = reinterpret_cast<ThreadLocalTop*>(t);
- v->VisitThread(this, thread);
-}
-
-
-void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
- // Visit the roots from the top for a given thread.
- Object* pending;
- // The pending exception can sometimes be a failure. We can't show
- // that to the GC, which only understands objects.
- if (thread->pending_exception_->ToObject(&pending)) {
- v->VisitPointer(&pending);
- thread->pending_exception_ = pending; // In case GC updated it.
- }
- v->VisitPointer(&(thread->pending_message_obj_));
- v->VisitPointer(BitCast<Object**>(&(thread->pending_message_script_)));
- v->VisitPointer(BitCast<Object**>(&(thread->context_)));
- Object* scheduled;
- if (thread->scheduled_exception_->ToObject(&scheduled)) {
- v->VisitPointer(&scheduled);
- thread->scheduled_exception_ = scheduled;
- }
-
- for (v8::TryCatch* block = thread->TryCatchHandler();
- block != NULL;
- block = TRY_CATCH_FROM_ADDRESS(block->next_)) {
- v->VisitPointer(BitCast<Object**>(&(block->exception_)));
- v->VisitPointer(BitCast<Object**>(&(block->message_)));
- }
-
- // Iterate over pointers on native execution stack.
- for (StackFrameIterator it(this, thread); !it.done(); it.Advance()) {
- it.frame()->Iterate(v);
- }
-}
-
-
-void Isolate::Iterate(ObjectVisitor* v) {
- ThreadLocalTop* current_t = thread_local_top();
- Iterate(v, current_t);
-}
-
-
-void Isolate::RegisterTryCatchHandler(v8::TryCatch* that) {
- // The ARM simulator has a separate JS stack. We therefore register
- // the C++ try catch handler with the simulator and get back an
- // address that can be used for comparisons with addresses into the
- // JS stack. When running without the simulator, the address
- // returned will be the address of the C++ try catch handler itself.
- Address address = reinterpret_cast<Address>(
- SimulatorStack::RegisterCTryCatch(reinterpret_cast<uintptr_t>(that)));
- thread_local_top()->set_try_catch_handler_address(address);
-}
-
-
-void Isolate::UnregisterTryCatchHandler(v8::TryCatch* that) {
- ASSERT(thread_local_top()->TryCatchHandler() == that);
- thread_local_top()->set_try_catch_handler_address(
- reinterpret_cast<Address>(that->next_));
- thread_local_top()->catcher_ = NULL;
- SimulatorStack::UnregisterCTryCatch();
-}
-
-
-Handle<String> Isolate::StackTraceString() {
- if (stack_trace_nesting_level_ == 0) {
- stack_trace_nesting_level_++;
- HeapStringAllocator allocator;
- StringStream::ClearMentionedObjectCache();
- StringStream accumulator(&allocator);
- incomplete_message_ = &accumulator;
- PrintStack(&accumulator);
- Handle<String> stack_trace = accumulator.ToString();
- incomplete_message_ = NULL;
- stack_trace_nesting_level_ = 0;
- return stack_trace;
- } else if (stack_trace_nesting_level_ == 1) {
- stack_trace_nesting_level_++;
- OS::PrintError(
- "\n\nAttempt to print stack while printing stack (double fault)\n");
- OS::PrintError(
- "If you are lucky you may find a partial stack dump on stdout.\n\n");
- incomplete_message_->OutputToStdOut();
- return factory()->empty_symbol();
- } else {
- OS::Abort();
- // Unreachable
- return factory()->empty_symbol();
- }
-}
-
-
-Handle<JSArray> Isolate::CaptureCurrentStackTrace(
- int frame_limit, StackTrace::StackTraceOptions options) {
- // Ensure no negative values.
- int limit = Max(frame_limit, 0);
- Handle<JSArray> stack_trace = factory()->NewJSArray(frame_limit);
-
- Handle<String> column_key = factory()->LookupAsciiSymbol("column");
- Handle<String> line_key = factory()->LookupAsciiSymbol("lineNumber");
- Handle<String> script_key = factory()->LookupAsciiSymbol("scriptName");
- Handle<String> name_or_source_url_key =
- factory()->LookupAsciiSymbol("nameOrSourceURL");
- Handle<String> script_name_or_source_url_key =
- factory()->LookupAsciiSymbol("scriptNameOrSourceURL");
- Handle<String> function_key = factory()->LookupAsciiSymbol("functionName");
- Handle<String> eval_key = factory()->LookupAsciiSymbol("isEval");
- Handle<String> constructor_key =
- factory()->LookupAsciiSymbol("isConstructor");
-
- StackTraceFrameIterator it(this);
- int frames_seen = 0;
- while (!it.done() && (frames_seen < limit)) {
- JavaScriptFrame* frame = it.frame();
- // Set initial size to the maximum inlining level + 1 for the outermost
- // function.
- List<FrameSummary> frames(Compiler::kMaxInliningLevels + 1);
- frame->Summarize(&frames);
- for (int i = frames.length() - 1; i >= 0 && frames_seen < limit; i--) {
- // Create a JSObject to hold the information for the StackFrame.
- Handle<JSObject> stackFrame = factory()->NewJSObject(object_function());
-
- Handle<JSFunction> fun = frames[i].function();
- Handle<Script> script(Script::cast(fun->shared()->script()));
-
- if (options & StackTrace::kLineNumber) {
- int script_line_offset = script->line_offset()->value();
- int position = frames[i].code()->SourcePosition(frames[i].pc());
- int line_number = GetScriptLineNumber(script, position);
- // line_number is already shifted by the script_line_offset.
- int relative_line_number = line_number - script_line_offset;
- if (options & StackTrace::kColumnOffset && relative_line_number >= 0) {
- Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
- int start = (relative_line_number == 0) ? 0 :
- Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
- int column_offset = position - start;
- if (relative_line_number == 0) {
- // For the case where the code is on the same line as the script
- // tag.
- column_offset += script->column_offset()->value();
- }
- SetLocalPropertyNoThrow(stackFrame, column_key,
- Handle<Smi>(Smi::FromInt(column_offset + 1)));
- }
- SetLocalPropertyNoThrow(stackFrame, line_key,
- Handle<Smi>(Smi::FromInt(line_number + 1)));
- }
-
- if (options & StackTrace::kScriptName) {
- Handle<Object> script_name(script->name(), this);
- SetLocalPropertyNoThrow(stackFrame, script_key, script_name);
- }
-
- if (options & StackTrace::kScriptNameOrSourceURL) {
- Handle<Object> script_name(script->name(), this);
- Handle<JSValue> script_wrapper = GetScriptWrapper(script);
- Handle<Object> property = GetProperty(script_wrapper,
- name_or_source_url_key);
- ASSERT(property->IsJSFunction());
- Handle<JSFunction> method = Handle<JSFunction>::cast(property);
- bool caught_exception;
- Handle<Object> result = Execution::TryCall(method, script_wrapper, 0,
- NULL, &caught_exception);
- if (caught_exception) {
- result = factory()->undefined_value();
- }
- SetLocalPropertyNoThrow(stackFrame, script_name_or_source_url_key,
- result);
- }
-
- if (options & StackTrace::kFunctionName) {
- Handle<Object> fun_name(fun->shared()->name(), this);
- if (fun_name->ToBoolean()->IsFalse()) {
- fun_name = Handle<Object>(fun->shared()->inferred_name(), this);
- }
- SetLocalPropertyNoThrow(stackFrame, function_key, fun_name);
- }
-
- if (options & StackTrace::kIsEval) {
- int type = Smi::cast(script->compilation_type())->value();
- Handle<Object> is_eval = (type == Script::COMPILATION_TYPE_EVAL) ?
- factory()->true_value() : factory()->false_value();
- SetLocalPropertyNoThrow(stackFrame, eval_key, is_eval);
- }
-
- if (options & StackTrace::kIsConstructor) {
- Handle<Object> is_constructor = (frames[i].is_constructor()) ?
- factory()->true_value() : factory()->false_value();
- SetLocalPropertyNoThrow(stackFrame, constructor_key, is_constructor);
- }
-
- FixedArray::cast(stack_trace->elements())->set(frames_seen, *stackFrame);
- frames_seen++;
- }
- it.Advance();
- }
-
- stack_trace->set_length(Smi::FromInt(frames_seen));
- return stack_trace;
-}
-
-
-void Isolate::PrintStack() {
- if (stack_trace_nesting_level_ == 0) {
- stack_trace_nesting_level_++;
-
- StringAllocator* allocator;
- if (preallocated_message_space_ == NULL) {
- allocator = new HeapStringAllocator();
- } else {
- allocator = preallocated_message_space_;
- }
-
- StringStream::ClearMentionedObjectCache();
- StringStream accumulator(allocator);
- incomplete_message_ = &accumulator;
- PrintStack(&accumulator);
- accumulator.OutputToStdOut();
- InitializeLoggingAndCounters();
- accumulator.Log();
- incomplete_message_ = NULL;
- stack_trace_nesting_level_ = 0;
- if (preallocated_message_space_ == NULL) {
- // Remove the HeapStringAllocator created above.
- delete allocator;
- }
- } else if (stack_trace_nesting_level_ == 1) {
- stack_trace_nesting_level_++;
- OS::PrintError(
- "\n\nAttempt to print stack while printing stack (double fault)\n");
- OS::PrintError(
- "If you are lucky you may find a partial stack dump on stdout.\n\n");
- incomplete_message_->OutputToStdOut();
- }
-}
-
-
-static void PrintFrames(StringStream* accumulator,
- StackFrame::PrintMode mode) {
- StackFrameIterator it;
- for (int i = 0; !it.done(); it.Advance()) {
- it.frame()->Print(accumulator, mode, i++);
- }
-}
-
-
-void Isolate::PrintStack(StringStream* accumulator) {
- if (!IsInitialized()) {
- accumulator->Add(
- "\n==== Stack trace is not available ==========================\n\n");
- accumulator->Add(
- "\n==== Isolate for the thread is not initialized =============\n\n");
- return;
- }
- // The MentionedObjectCache is not GC-proof at the moment.
- AssertNoAllocation nogc;
- ASSERT(StringStream::IsMentionedObjectCacheClear());
-
- // Avoid printing anything if there are no frames.
- if (c_entry_fp(thread_local_top()) == 0) return;
-
- accumulator->Add(
- "\n==== Stack trace ============================================\n\n");
- PrintFrames(accumulator, StackFrame::OVERVIEW);
-
- accumulator->Add(
- "\n==== Details ================================================\n\n");
- PrintFrames(accumulator, StackFrame::DETAILS);
-
- accumulator->PrintMentionedObjectCache();
- accumulator->Add("=====================\n\n");
-}
-
-
-void Isolate::SetFailedAccessCheckCallback(
- v8::FailedAccessCheckCallback callback) {
- thread_local_top()->failed_access_check_callback_ = callback;
-}
-
-
-void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) {
- if (!thread_local_top()->failed_access_check_callback_) return;
-
- ASSERT(receiver->IsAccessCheckNeeded());
- ASSERT(context());
-
- // Get the data object from access check info.
- JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
- if (!constructor->shared()->IsApiFunction()) return;
- Object* data_obj =
- constructor->shared()->get_api_func_data()->access_check_info();
- if (data_obj == heap_.undefined_value()) return;
-
- HandleScope scope;
- Handle<JSObject> receiver_handle(receiver);
- Handle<Object> data(AccessCheckInfo::cast(data_obj)->data());
- thread_local_top()->failed_access_check_callback_(
- v8::Utils::ToLocal(receiver_handle),
- type,
- v8::Utils::ToLocal(data));
-}
-
-
-enum MayAccessDecision {
- YES, NO, UNKNOWN
-};
-
-
-static MayAccessDecision MayAccessPreCheck(Isolate* isolate,
- JSObject* receiver,
- v8::AccessType type) {
- // During bootstrapping, callback functions are not enabled yet.
- if (isolate->bootstrapper()->IsActive()) return YES;
-
- if (receiver->IsJSGlobalProxy()) {
- Object* receiver_context = JSGlobalProxy::cast(receiver)->context();
- if (!receiver_context->IsContext()) return NO;
-
- // Get the global context of current top context.
- // avoid using Isolate::global_context() because it uses Handle.
- Context* global_context = isolate->context()->global()->global_context();
- if (receiver_context == global_context) return YES;
-
- if (Context::cast(receiver_context)->security_token() ==
- global_context->security_token())
- return YES;
- }
-
- return UNKNOWN;
-}
-
-
-bool Isolate::MayNamedAccess(JSObject* receiver, Object* key,
- v8::AccessType type) {
- ASSERT(receiver->IsAccessCheckNeeded());
-
- // The callers of this method are not expecting a GC.
- AssertNoAllocation no_gc;
-
- // Skip checks for hidden properties access. Note, we do not
- // require existence of a context in this case.
- if (key == heap_.hidden_symbol()) return true;
-
- // Check for compatibility between the security tokens in the
- // current lexical context and the accessed object.
- ASSERT(context());
-
- MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
- if (decision != UNKNOWN) return decision == YES;
-
- // Get named access check callback
- JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
- if (!constructor->shared()->IsApiFunction()) return false;
-
- Object* data_obj =
- constructor->shared()->get_api_func_data()->access_check_info();
- if (data_obj == heap_.undefined_value()) return false;
-
- Object* fun_obj = AccessCheckInfo::cast(data_obj)->named_callback();
- v8::NamedSecurityCallback callback =
- v8::ToCData<v8::NamedSecurityCallback>(fun_obj);
-
- if (!callback) return false;
-
- HandleScope scope(this);
- Handle<JSObject> receiver_handle(receiver, this);
- Handle<Object> key_handle(key, this);
- Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
- LOG(this, ApiNamedSecurityCheck(key));
- bool result = false;
- {
- // Leaving JavaScript.
- VMState state(this, EXTERNAL);
- result = callback(v8::Utils::ToLocal(receiver_handle),
- v8::Utils::ToLocal(key_handle),
- type,
- v8::Utils::ToLocal(data));
- }
- return result;
-}
-
-
-bool Isolate::MayIndexedAccess(JSObject* receiver,
- uint32_t index,
- v8::AccessType type) {
- ASSERT(receiver->IsAccessCheckNeeded());
- // Check for compatibility between the security tokens in the
- // current lexical context and the accessed object.
- ASSERT(context());
-
- MayAccessDecision decision = MayAccessPreCheck(this, receiver, type);
- if (decision != UNKNOWN) return decision == YES;
-
- // Get indexed access check callback
- JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
- if (!constructor->shared()->IsApiFunction()) return false;
-
- Object* data_obj =
- constructor->shared()->get_api_func_data()->access_check_info();
- if (data_obj == heap_.undefined_value()) return false;
-
- Object* fun_obj = AccessCheckInfo::cast(data_obj)->indexed_callback();
- v8::IndexedSecurityCallback callback =
- v8::ToCData<v8::IndexedSecurityCallback>(fun_obj);
-
- if (!callback) return false;
-
- HandleScope scope(this);
- Handle<JSObject> receiver_handle(receiver, this);
- Handle<Object> data(AccessCheckInfo::cast(data_obj)->data(), this);
- LOG(this, ApiIndexedSecurityCheck(index));
- bool result = false;
- {
- // Leaving JavaScript.
- VMState state(this, EXTERNAL);
- result = callback(v8::Utils::ToLocal(receiver_handle),
- index,
- type,
- v8::Utils::ToLocal(data));
- }
- return result;
-}
-
-
-const char* const Isolate::kStackOverflowMessage =
- "Uncaught RangeError: Maximum call stack size exceeded";
-
-
-Failure* Isolate::StackOverflow() {
- HandleScope scope;
- Handle<String> key = factory()->stack_overflow_symbol();
- Handle<JSObject> boilerplate =
- Handle<JSObject>::cast(GetProperty(js_builtins_object(), key));
- Handle<Object> exception = Copy(boilerplate);
- // TODO(1240995): To avoid having to call JavaScript code to compute
- // the message for stack overflow exceptions which is very likely to
- // double fault with another stack overflow exception, we use a
- // precomputed message.
- DoThrow(*exception, NULL);
- return Failure::Exception();
-}
-
-
-Failure* Isolate::TerminateExecution() {
- DoThrow(heap_.termination_exception(), NULL);
- return Failure::Exception();
-}
-
-
-Failure* Isolate::Throw(Object* exception, MessageLocation* location) {
- DoThrow(exception, location);
- return Failure::Exception();
-}
-
-
-Failure* Isolate::ReThrow(MaybeObject* exception, MessageLocation* location) {
- bool can_be_caught_externally = false;
- ShouldReportException(&can_be_caught_externally,
- is_catchable_by_javascript(exception));
- thread_local_top()->catcher_ = can_be_caught_externally ?
- try_catch_handler() : NULL;
-
- // Set the exception being re-thrown.
- set_pending_exception(exception);
- if (exception->IsFailure()) return exception->ToFailureUnchecked();
- return Failure::Exception();
-}
-
-
-Failure* Isolate::ThrowIllegalOperation() {
- return Throw(heap_.illegal_access_symbol());
-}
-
-
-void Isolate::ScheduleThrow(Object* exception) {
- // When scheduling a throw we first throw the exception to get the
- // error reporting if it is uncaught before rescheduling it.
- Throw(exception);
- thread_local_top()->scheduled_exception_ = pending_exception();
- thread_local_top()->external_caught_exception_ = false;
- clear_pending_exception();
-}
-
-
-Failure* Isolate::PromoteScheduledException() {
- MaybeObject* thrown = scheduled_exception();
- clear_scheduled_exception();
- // Re-throw the exception to avoid getting repeated error reporting.
- return ReThrow(thrown);
-}
-
-
-void Isolate::PrintCurrentStackTrace(FILE* out) {
- StackTraceFrameIterator it(this);
- while (!it.done()) {
- HandleScope scope;
- // Find code position if recorded in relocation info.
- JavaScriptFrame* frame = it.frame();
- int pos = frame->LookupCode()->SourcePosition(frame->pc());
- Handle<Object> pos_obj(Smi::FromInt(pos));
- // Fetch function and receiver.
- Handle<JSFunction> fun(JSFunction::cast(frame->function()));
- Handle<Object> recv(frame->receiver());
- // Advance to the next JavaScript frame and determine if the
- // current frame is the top-level frame.
- it.Advance();
- Handle<Object> is_top_level = it.done()
- ? factory()->true_value()
- : factory()->false_value();
- // Generate and print stack trace line.
- Handle<String> line =
- Execution::GetStackTraceLine(recv, fun, pos_obj, is_top_level);
- if (line->length() > 0) {
- line->PrintOn(out);
- fprintf(out, "\n");
- }
- }
-}
-
-
-void Isolate::ComputeLocation(MessageLocation* target) {
- *target = MessageLocation(Handle<Script>(heap_.empty_script()), -1, -1);
- StackTraceFrameIterator it(this);
- if (!it.done()) {
- JavaScriptFrame* frame = it.frame();
- JSFunction* fun = JSFunction::cast(frame->function());
- Object* script = fun->shared()->script();
- if (script->IsScript() &&
- !(Script::cast(script)->source()->IsUndefined())) {
- int pos = frame->LookupCode()->SourcePosition(frame->pc());
- // Compute the location from the function and the reloc info.
- Handle<Script> casted_script(Script::cast(script));
- *target = MessageLocation(casted_script, pos, pos + 1);
- }
- }
-}
-
-
-bool Isolate::ShouldReportException(bool* can_be_caught_externally,
- bool catchable_by_javascript) {
- // Find the top-most try-catch handler.
- StackHandler* handler =
- StackHandler::FromAddress(Isolate::handler(thread_local_top()));
- while (handler != NULL && !handler->is_try_catch()) {
- handler = handler->next();
- }
-
- // Get the address of the external handler so we can compare the address to
- // determine which one is closer to the top of the stack.
- Address external_handler_address =
- thread_local_top()->try_catch_handler_address();
-
- // The exception has been externally caught if and only if there is
- // an external handler which is on top of the top-most try-catch
- // handler.
- *can_be_caught_externally = external_handler_address != NULL &&
- (handler == NULL || handler->address() > external_handler_address ||
- !catchable_by_javascript);
-
- if (*can_be_caught_externally) {
- // Only report the exception if the external handler is verbose.
- return try_catch_handler()->is_verbose_;
- } else {
- // Report the exception if it isn't caught by JavaScript code.
- return handler == NULL;
- }
-}
-
-
-void Isolate::DoThrow(MaybeObject* exception, MessageLocation* location) {
- ASSERT(!has_pending_exception());
-
- HandleScope scope;
- Object* exception_object = Smi::FromInt(0);
- bool is_object = exception->ToObject(&exception_object);
- Handle<Object> exception_handle(exception_object);
-
- // Determine reporting and whether the exception is caught externally.
- bool catchable_by_javascript = is_catchable_by_javascript(exception);
- // Only real objects can be caught by JS.
- ASSERT(!catchable_by_javascript || is_object);
- bool can_be_caught_externally = false;
- bool should_report_exception =
- ShouldReportException(&can_be_caught_externally, catchable_by_javascript);
- bool report_exception = catchable_by_javascript && should_report_exception;
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Notify debugger of exception.
- if (catchable_by_javascript) {
- debugger_->OnException(exception_handle, report_exception);
- }
-#endif
-
- // Generate the message.
- Handle<Object> message_obj;
- MessageLocation potential_computed_location;
- bool try_catch_needs_message =
- can_be_caught_externally &&
- try_catch_handler()->capture_message_;
- if (report_exception || try_catch_needs_message) {
- if (location == NULL) {
- // If no location was specified we use a computed one instead
- ComputeLocation(&potential_computed_location);
- location = &potential_computed_location;
- }
- if (!bootstrapper()->IsActive()) {
- // It's not safe to try to make message objects or collect stack
- // traces while the bootstrapper is active since the infrastructure
- // may not have been properly initialized.
- Handle<String> stack_trace;
- if (FLAG_trace_exception) stack_trace = StackTraceString();
- Handle<JSArray> stack_trace_object;
- if (report_exception && capture_stack_trace_for_uncaught_exceptions_) {
- stack_trace_object = CaptureCurrentStackTrace(
- stack_trace_for_uncaught_exceptions_frame_limit_,
- stack_trace_for_uncaught_exceptions_options_);
- }
- ASSERT(is_object); // Can't use the handle unless there's a real object.
- message_obj = MessageHandler::MakeMessageObject("uncaught_exception",
- location, HandleVector<Object>(&exception_handle, 1), stack_trace,
- stack_trace_object);
- }
- }
-
- // Save the message for reporting if the the exception remains uncaught.
- thread_local_top()->has_pending_message_ = report_exception;
- if (!message_obj.is_null()) {
- thread_local_top()->pending_message_obj_ = *message_obj;
- if (location != NULL) {
- thread_local_top()->pending_message_script_ = *location->script();
- thread_local_top()->pending_message_start_pos_ = location->start_pos();
- thread_local_top()->pending_message_end_pos_ = location->end_pos();
- }
- }
-
- // Do not forget to clean catcher_ if currently thrown exception cannot
- // be caught. If necessary, ReThrow will update the catcher.
- thread_local_top()->catcher_ = can_be_caught_externally ?
- try_catch_handler() : NULL;
-
- // NOTE: Notifying the debugger or generating the message
- // may have caused new exceptions. For now, we just ignore
- // that and set the pending exception to the original one.
- if (is_object) {
- set_pending_exception(*exception_handle);
- } else {
- // Failures are not on the heap so they neither need nor work with handles.
- ASSERT(exception_handle->IsFailure());
- set_pending_exception(exception);
- }
-}
-
-
-bool Isolate::IsExternallyCaught() {
- ASSERT(has_pending_exception());
-
- if ((thread_local_top()->catcher_ == NULL) ||
- (try_catch_handler() != thread_local_top()->catcher_)) {
- // When throwing the exception, we found no v8::TryCatch
- // which should care about this exception.
- return false;
- }
-
- if (!is_catchable_by_javascript(pending_exception())) {
- return true;
- }
-
- // Get the address of the external handler so we can compare the address to
- // determine which one is closer to the top of the stack.
- Address external_handler_address =
- thread_local_top()->try_catch_handler_address();
- ASSERT(external_handler_address != NULL);
-
- // The exception has been externally caught if and only if there is
- // an external handler which is on top of the top-most try-finally
- // handler.
- // There should be no try-catch blocks as they would prohibit us from
- // finding external catcher in the first place (see catcher_ check above).
- //
- // Note, that finally clause would rethrow an exception unless it's
- // aborted by jumps in control flow like return, break, etc. and we'll
- // have another chances to set proper v8::TryCatch.
- StackHandler* handler =
- StackHandler::FromAddress(Isolate::handler(thread_local_top()));
- while (handler != NULL && handler->address() < external_handler_address) {
- ASSERT(!handler->is_try_catch());
- if (handler->is_try_finally()) return false;
-
- handler = handler->next();
- }
-
- return true;
-}
-
-
-void Isolate::ReportPendingMessages() {
- ASSERT(has_pending_exception());
- PropagatePendingExceptionToExternalTryCatch();
-
- // If the pending exception is OutOfMemoryException set out_of_memory in
- // the global context. Note: We have to mark the global context here
- // since the GenerateThrowOutOfMemory stub cannot make a RuntimeCall to
- // set it.
- HandleScope scope;
- if (thread_local_top_.pending_exception_ == Failure::OutOfMemoryException()) {
- context()->mark_out_of_memory();
- } else if (thread_local_top_.pending_exception_ ==
- heap()->termination_exception()) {
- // Do nothing: if needed, the exception has been already propagated to
- // v8::TryCatch.
- } else {
- if (thread_local_top_.has_pending_message_) {
- thread_local_top_.has_pending_message_ = false;
- if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
- HandleScope scope;
- Handle<Object> message_obj(thread_local_top_.pending_message_obj_);
- if (thread_local_top_.pending_message_script_ != NULL) {
- Handle<Script> script(thread_local_top_.pending_message_script_);
- int start_pos = thread_local_top_.pending_message_start_pos_;
- int end_pos = thread_local_top_.pending_message_end_pos_;
- MessageLocation location(script, start_pos, end_pos);
- MessageHandler::ReportMessage(this, &location, message_obj);
- } else {
- MessageHandler::ReportMessage(this, NULL, message_obj);
- }
- }
- }
- }
- clear_pending_message();
-}
-
-
-void Isolate::TraceException(bool flag) {
- FLAG_trace_exception = flag; // TODO(isolates): This is an unfortunate use.
-}
-
-
-bool Isolate::OptionalRescheduleException(bool is_bottom_call) {
- ASSERT(has_pending_exception());
- PropagatePendingExceptionToExternalTryCatch();
-
- // Allways reschedule out of memory exceptions.
- if (!is_out_of_memory()) {
- bool is_termination_exception =
- pending_exception() == heap_.termination_exception();
-
- // Do not reschedule the exception if this is the bottom call.
- bool clear_exception = is_bottom_call;
-
- if (is_termination_exception) {
- if (is_bottom_call) {
- thread_local_top()->external_caught_exception_ = false;
- clear_pending_exception();
- return false;
- }
- } else if (thread_local_top()->external_caught_exception_) {
- // If the exception is externally caught, clear it if there are no
- // JavaScript frames on the way to the C++ frame that has the
- // external handler.
- ASSERT(thread_local_top()->try_catch_handler_address() != NULL);
- Address external_handler_address =
- thread_local_top()->try_catch_handler_address();
- JavaScriptFrameIterator it;
- if (it.done() || (it.frame()->sp() > external_handler_address)) {
- clear_exception = true;
- }
- }
-
- // Clear the exception if needed.
- if (clear_exception) {
- thread_local_top()->external_caught_exception_ = false;
- clear_pending_exception();
- return false;
- }
- }
-
- // Reschedule the exception.
- thread_local_top()->scheduled_exception_ = pending_exception();
- clear_pending_exception();
- return true;
-}
-
-
-void Isolate::SetCaptureStackTraceForUncaughtExceptions(
- bool capture,
- int frame_limit,
- StackTrace::StackTraceOptions options) {
- capture_stack_trace_for_uncaught_exceptions_ = capture;
- stack_trace_for_uncaught_exceptions_frame_limit_ = frame_limit;
- stack_trace_for_uncaught_exceptions_options_ = options;
-}
-
-
-bool Isolate::is_out_of_memory() {
- if (has_pending_exception()) {
- MaybeObject* e = pending_exception();
- if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
- return true;
- }
- }
- if (has_scheduled_exception()) {
- MaybeObject* e = scheduled_exception();
- if (e->IsFailure() && Failure::cast(e)->IsOutOfMemoryException()) {
- return true;
- }
- }
- return false;
-}
-
-
-Handle<Context> Isolate::global_context() {
- GlobalObject* global = thread_local_top()->context_->global();
- return Handle<Context>(global->global_context());
-}
-
-
-Handle<Context> Isolate::GetCallingGlobalContext() {
- JavaScriptFrameIterator it;
-#ifdef ENABLE_DEBUGGER_SUPPORT
- if (debug_->InDebugger()) {
- while (!it.done()) {
- JavaScriptFrame* frame = it.frame();
- Context* context = Context::cast(frame->context());
- if (context->global_context() == *debug_->debug_context()) {
- it.Advance();
- } else {
- break;
- }
- }
- }
-#endif // ENABLE_DEBUGGER_SUPPORT
- if (it.done()) return Handle<Context>::null();
- JavaScriptFrame* frame = it.frame();
- Context* context = Context::cast(frame->context());
- return Handle<Context>(context->global_context());
-}
-
-
-char* Isolate::ArchiveThread(char* to) {
- if (RuntimeProfiler::IsEnabled() && current_vm_state() == JS) {
- RuntimeProfiler::IsolateExitedJS(this);
- }
- memcpy(to, reinterpret_cast<char*>(thread_local_top()),
- sizeof(ThreadLocalTop));
- InitializeThreadLocal();
- return to + sizeof(ThreadLocalTop);
-}
-
-
-char* Isolate::RestoreThread(char* from) {
- memcpy(reinterpret_cast<char*>(thread_local_top()), from,
- sizeof(ThreadLocalTop));
- // This might be just paranoia, but it seems to be needed in case a
- // thread_local_top_ is restored on a separate OS thread.
-#ifdef USE_SIMULATOR
-#ifdef V8_TARGET_ARCH_ARM
- thread_local_top()->simulator_ = Simulator::current(this);
-#elif V8_TARGET_ARCH_MIPS
- thread_local_top()->simulator_ = Simulator::current(this);
-#endif
-#endif
- if (RuntimeProfiler::IsEnabled() && current_vm_state() == JS) {
- RuntimeProfiler::IsolateEnteredJS(this);
- }
- return from + sizeof(ThreadLocalTop);
-}
-
-} } // namespace v8::internal
diff --git a/src/type-info.cc b/src/type-info.cc
index 4069c83d..5f794bde 100644
--- a/src/type-info.cc
+++ b/src/type-info.cc
@@ -58,9 +58,6 @@ TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) {
}
-STATIC_ASSERT(DEFAULT_STRING_STUB == Code::kNoExtraICState);
-
-
TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
Handle<Context> global_context) {
global_context_ = global_context;
@@ -69,8 +66,8 @@ TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
}
-Handle<Object> TypeFeedbackOracle::GetInfo(int pos) {
- int entry = dictionary_->FindEntry(pos);
+Handle<Object> TypeFeedbackOracle::GetInfo(unsigned ast_id) {
+ int entry = dictionary_->FindEntry(ast_id);
return entry != NumberDictionary::kNotFound
? Handle<Object>(dictionary_->ValueAt(entry))
: Isolate::Current()->factory()->undefined_value();
@@ -78,11 +75,12 @@ Handle<Object> TypeFeedbackOracle::GetInfo(int pos) {
bool TypeFeedbackOracle::LoadIsMonomorphic(Property* expr) {
- Handle<Object> map_or_code(GetInfo(expr->position()));
+ Handle<Object> map_or_code(GetInfo(expr->id()));
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
- Handle<Code> code(Code::cast(*map_or_code));
- return code->kind() == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC &&
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ return code->is_keyed_load_stub() &&
+ code->ic_state() == MONOMORPHIC &&
code->FindFirstMap() != NULL;
}
return false;
@@ -90,80 +88,83 @@ bool TypeFeedbackOracle::LoadIsMonomorphic(Property* expr) {
bool TypeFeedbackOracle::StoreIsMonomorphic(Expression* expr) {
- Handle<Object> map_or_code(GetInfo(expr->position()));
+ Handle<Object> map_or_code(GetInfo(expr->id()));
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
- Handle<Code> code(Code::cast(*map_or_code));
- return code->kind() == Code::KEYED_EXTERNAL_ARRAY_STORE_IC &&
- code->FindFirstMap() != NULL;
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ return code->is_keyed_store_stub() &&
+ code->ic_state() == MONOMORPHIC;
}
return false;
}
bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
- Handle<Object> value = GetInfo(expr->position());
+ Handle<Object> value = GetInfo(expr->id());
return value->IsMap() || value->IsSmi();
}
Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
ASSERT(LoadIsMonomorphic(expr));
- Handle<Object> map_or_code(
- Handle<HeapObject>::cast(GetInfo(expr->position())));
+ Handle<Object> map_or_code(GetInfo(expr->id()));
if (map_or_code->IsCode()) {
- Handle<Code> code(Code::cast(*map_or_code));
- return Handle<Map>(code->FindFirstMap());
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ Map* first_map = code->FindFirstMap();
+ ASSERT(first_map != NULL);
+ return Handle<Map>(first_map);
}
- return Handle<Map>(Map::cast(*map_or_code));
+ return Handle<Map>::cast(map_or_code);
}
Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) {
ASSERT(StoreIsMonomorphic(expr));
- Handle<HeapObject> map_or_code(
- Handle<HeapObject>::cast(GetInfo(expr->position())));
+ Handle<Object> map_or_code(GetInfo(expr->id()));
if (map_or_code->IsCode()) {
- Handle<Code> code(Code::cast(*map_or_code));
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
return Handle<Map>(code->FindFirstMap());
}
- return Handle<Map>(Map::cast(*map_or_code));
+ return Handle<Map>::cast(map_or_code);
}
ZoneMapList* TypeFeedbackOracle::LoadReceiverTypes(Property* expr,
Handle<String> name) {
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL);
- return CollectReceiverTypes(expr->position(), name, flags);
+ return CollectReceiverTypes(expr->id(), name, flags);
}
ZoneMapList* TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr,
Handle<String> name) {
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL);
- return CollectReceiverTypes(expr->position(), name, flags);
+ return CollectReceiverTypes(expr->id(), name, flags);
}
ZoneMapList* TypeFeedbackOracle::CallReceiverTypes(Call* expr,
- Handle<String> name) {
+ Handle<String> name,
+ CallKind call_kind) {
int arity = expr->arguments()->length();
- // Note: these flags won't let us get maps from stubs with
- // non-default extra ic state in the megamorphic case. In the more
- // important monomorphic case the map is obtained directly, so it's
- // not a problem until we decide to emit more polymorphic code.
+
+ // Note: Currently we do not take string extra ic data into account
+ // here.
+ Code::ExtraICState extra_ic_state =
+ CallIC::Contextual::encode(call_kind == CALL_AS_FUNCTION);
+
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
NORMAL,
- Code::kNoExtraICState,
+ extra_ic_state,
OWN_MAP,
NOT_IN_LOOP,
arity);
- return CollectReceiverTypes(expr->position(), name, flags);
+ return CollectReceiverTypes(expr->id(), name, flags);
}
CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
- Handle<Object> value = GetInfo(expr->position());
+ Handle<Object> value = GetInfo(expr->id());
if (!value->IsSmi()) return RECEIVER_MAP_CHECK;
CheckType check = static_cast<CheckType>(Smi::cast(*value)->value());
ASSERT(check != RECEIVER_MAP_CHECK);
@@ -172,14 +173,14 @@ CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
ExternalArrayType TypeFeedbackOracle::GetKeyedLoadExternalArrayType(
Property* expr) {
- Handle<Object> stub = GetInfo(expr->position());
+ Handle<Object> stub = GetInfo(expr->id());
ASSERT(stub->IsCode());
return Code::cast(*stub)->external_array_type();
}
ExternalArrayType TypeFeedbackOracle::GetKeyedStoreExternalArrayType(
Expression* expr) {
- Handle<Object> stub = GetInfo(expr->position());
+ Handle<Object> stub = GetInfo(expr->id());
ASSERT(stub->IsCode());
return Code::cast(*stub)->external_array_type();
}
@@ -207,13 +208,13 @@ Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
- return *GetInfo(expr->position()) ==
+ return *GetInfo(expr->id()) ==
Isolate::Current()->builtins()->builtin(id);
}
TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
- Handle<Object> object = GetInfo(expr->position());
+ Handle<Object> object = GetInfo(expr->id());
TypeInfo unknown = TypeInfo::Unknown();
if (!object->IsCode()) return unknown;
Handle<Code> code = Handle<Code>::cast(object);
@@ -229,6 +230,9 @@ TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
return TypeInfo::Smi();
case CompareIC::HEAP_NUMBERS:
return TypeInfo::Number();
+ case CompareIC::SYMBOLS:
+ case CompareIC::STRINGS:
+ return TypeInfo::String();
case CompareIC::OBJECTS:
// TODO(kasperl): We really need a type for JS objects here.
return TypeInfo::NonPrimitive();
@@ -239,44 +243,75 @@ TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
}
+bool TypeFeedbackOracle::IsSymbolCompare(CompareOperation* expr) {
+ Handle<Object> object = GetInfo(expr->id());
+ if (!object->IsCode()) return false;
+ Handle<Code> code = Handle<Code>::cast(object);
+ if (!code->is_compare_ic_stub()) return false;
+ CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
+ return state == CompareIC::SYMBOLS;
+}
+
+
+TypeInfo TypeFeedbackOracle::UnaryType(UnaryOperation* expr) {
+ Handle<Object> object = GetInfo(expr->id());
+ TypeInfo unknown = TypeInfo::Unknown();
+ if (!object->IsCode()) return unknown;
+ Handle<Code> code = Handle<Code>::cast(object);
+ ASSERT(code->is_unary_op_stub());
+ UnaryOpIC::TypeInfo type = static_cast<UnaryOpIC::TypeInfo>(
+ code->unary_op_type());
+ switch (type) {
+ case UnaryOpIC::SMI:
+ return TypeInfo::Smi();
+ case UnaryOpIC::HEAP_NUMBER:
+ return TypeInfo::Double();
+ default:
+ return unknown;
+ }
+}
+
+
TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
- Handle<Object> object = GetInfo(expr->position());
+ Handle<Object> object = GetInfo(expr->id());
TypeInfo unknown = TypeInfo::Unknown();
if (!object->IsCode()) return unknown;
Handle<Code> code = Handle<Code>::cast(object);
- if (code->is_type_recording_binary_op_stub()) {
- TRBinaryOpIC::TypeInfo type = static_cast<TRBinaryOpIC::TypeInfo>(
- code->type_recording_binary_op_type());
- TRBinaryOpIC::TypeInfo result_type = static_cast<TRBinaryOpIC::TypeInfo>(
- code->type_recording_binary_op_result_type());
+ if (code->is_binary_op_stub()) {
+ BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>(
+ code->binary_op_type());
+ BinaryOpIC::TypeInfo result_type = static_cast<BinaryOpIC::TypeInfo>(
+ code->binary_op_result_type());
switch (type) {
- case TRBinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::UNINITIALIZED:
// Uninitialized means never executed.
// TODO(fschneider): Introduce a separate value for never-executed ICs
return unknown;
- case TRBinaryOpIC::SMI:
+ case BinaryOpIC::SMI:
switch (result_type) {
- case TRBinaryOpIC::UNINITIALIZED:
- case TRBinaryOpIC::SMI:
+ case BinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::SMI:
return TypeInfo::Smi();
- case TRBinaryOpIC::INT32:
+ case BinaryOpIC::INT32:
return TypeInfo::Integer32();
- case TRBinaryOpIC::HEAP_NUMBER:
+ case BinaryOpIC::HEAP_NUMBER:
return TypeInfo::Double();
default:
return unknown;
}
- case TRBinaryOpIC::INT32:
+ case BinaryOpIC::INT32:
if (expr->op() == Token::DIV ||
- result_type == TRBinaryOpIC::HEAP_NUMBER) {
+ result_type == BinaryOpIC::HEAP_NUMBER) {
return TypeInfo::Double();
}
return TypeInfo::Integer32();
- case TRBinaryOpIC::HEAP_NUMBER:
+ case BinaryOpIC::HEAP_NUMBER:
return TypeInfo::Double();
- case TRBinaryOpIC::STRING:
- case TRBinaryOpIC::GENERIC:
+ case BinaryOpIC::BOTH_STRING:
+ return TypeInfo::String();
+ case BinaryOpIC::STRING:
+ case BinaryOpIC::GENERIC:
return unknown;
default:
return unknown;
@@ -287,7 +322,7 @@ TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
- Handle<Object> object = GetInfo(clause->position());
+ Handle<Object> object = GetInfo(clause->CompareId());
TypeInfo unknown = TypeInfo::Unknown();
if (!object->IsCode()) return unknown;
Handle<Code> code = Handle<Code>::cast(object);
@@ -313,11 +348,40 @@ TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
}
-ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(int position,
+TypeInfo TypeFeedbackOracle::IncrementType(CountOperation* expr) {
+ Handle<Object> object = GetInfo(expr->CountId());
+ TypeInfo unknown = TypeInfo::Unknown();
+ if (!object->IsCode()) return unknown;
+ Handle<Code> code = Handle<Code>::cast(object);
+ if (!code->is_binary_op_stub()) return unknown;
+
+ BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>(
+ code->binary_op_type());
+ switch (type) {
+ case BinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::SMI:
+ return TypeInfo::Smi();
+ case BinaryOpIC::INT32:
+ return TypeInfo::Integer32();
+ case BinaryOpIC::HEAP_NUMBER:
+ return TypeInfo::Double();
+ case BinaryOpIC::BOTH_STRING:
+ case BinaryOpIC::STRING:
+ case BinaryOpIC::GENERIC:
+ return unknown;
+ default:
+ return unknown;
+ }
+ UNREACHABLE();
+ return unknown;
+}
+
+
+ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id,
Handle<String> name,
Code::Flags flags) {
Isolate* isolate = Isolate::Current();
- Handle<Object> object = GetInfo(position);
+ Handle<Object> object = GetInfo(ast_id);
if (object->IsUndefined() || object->IsSmi()) return NULL;
if (*object == isolate->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) {
@@ -340,8 +404,9 @@ ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(int position,
}
-void TypeFeedbackOracle::SetInfo(int position, Object* target) {
- MaybeObject* maybe_result = dictionary_->AtNumberPut(position, target);
+void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) {
+ ASSERT(dictionary_->FindEntry(ast_id) == NumberDictionary::kNotFound);
+ MaybeObject* maybe_result = dictionary_->AtNumberPut(ast_id, target);
USE(maybe_result);
#ifdef DEBUG
Object* result;
@@ -358,53 +423,48 @@ void TypeFeedbackOracle::PopulateMap(Handle<Code> code) {
const int kInitialCapacity = 16;
List<int> code_positions(kInitialCapacity);
- List<int> source_positions(kInitialCapacity);
- CollectPositions(*code, &code_positions, &source_positions);
+ List<unsigned> ast_ids(kInitialCapacity);
+ CollectIds(*code, &code_positions, &ast_ids);
ASSERT(dictionary_.is_null()); // Only initialize once.
dictionary_ = isolate->factory()->NewNumberDictionary(
code_positions.length());
- int length = code_positions.length();
- ASSERT(source_positions.length() == length);
+ const int length = code_positions.length();
+ ASSERT(ast_ids.length() == length);
for (int i = 0; i < length; i++) {
AssertNoAllocation no_allocation;
RelocInfo info(code->instruction_start() + code_positions[i],
RelocInfo::CODE_TARGET, 0);
Code* target = Code::GetCodeFromTargetAddress(info.target_address());
- int position = source_positions[i];
+ unsigned id = ast_ids[i];
InlineCacheState state = target->ic_state();
Code::Kind kind = target->kind();
- if (kind == Code::TYPE_RECORDING_BINARY_OP_IC ||
+ if (kind == Code::BINARY_OP_IC ||
+ kind == Code::UNARY_OP_IC ||
kind == Code::COMPARE_IC) {
- // TODO(kasperl): Avoid having multiple ICs with the same
- // position by making sure that we have position information
- // recorded for all binary ICs.
- int entry = dictionary_->FindEntry(position);
- if (entry == NumberDictionary::kNotFound) {
- SetInfo(position, target);
- }
+ SetInfo(id, target);
} else if (state == MONOMORPHIC) {
- if (kind == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC ||
- kind == Code::KEYED_EXTERNAL_ARRAY_STORE_IC) {
- SetInfo(position, target);
- } else if (target->kind() != Code::CALL_IC ||
- target->check_type() == RECEIVER_MAP_CHECK) {
+ if (kind == Code::KEYED_LOAD_IC ||
+ kind == Code::KEYED_STORE_IC) {
+ SetInfo(id, target);
+ } else if (kind != Code::CALL_IC ||
+ target->check_type() == RECEIVER_MAP_CHECK) {
Map* map = target->FindFirstMap();
if (map == NULL) {
- SetInfo(position, target);
+ SetInfo(id, target);
} else {
- SetInfo(position, map);
+ SetInfo(id, map);
}
} else {
ASSERT(target->kind() == Code::CALL_IC);
CheckType check = target->check_type();
ASSERT(check != RECEIVER_MAP_CHECK);
- SetInfo(position, Smi::FromInt(check));
+ SetInfo(id, Smi::FromInt(check));
}
} else if (state == MEGAMORPHIC) {
- SetInfo(position, target);
+ SetInfo(id, target);
}
}
// Allocate handle in the parent scope.
@@ -412,41 +472,34 @@ void TypeFeedbackOracle::PopulateMap(Handle<Code> code) {
}
-void TypeFeedbackOracle::CollectPositions(Code* code,
- List<int>* code_positions,
- List<int>* source_positions) {
+void TypeFeedbackOracle::CollectIds(Code* code,
+ List<int>* code_positions,
+ List<unsigned>* ast_ids) {
AssertNoAllocation no_allocation;
- int position = 0;
- // Because the ICs we use for global variables access in the full
- // code generator do not have any meaningful positions, we avoid
- // collecting those by filtering out contextual code targets.
- int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
- RelocInfo::kPositionMask;
+ int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
for (RelocIterator it(code, mask); !it.done(); it.next()) {
RelocInfo* info = it.rinfo();
- RelocInfo::Mode mode = info->rmode();
- if (RelocInfo::IsCodeTarget(mode)) {
- Code* target = Code::GetCodeFromTargetAddress(info->target_address());
- if (target->is_inline_cache_stub()) {
- InlineCacheState state = target->ic_state();
- Code::Kind kind = target->kind();
- if (kind == Code::TYPE_RECORDING_BINARY_OP_IC) {
- if (target->type_recording_binary_op_type() ==
- TRBinaryOpIC::GENERIC) {
- continue;
- }
- } else if (kind == Code::COMPARE_IC) {
- if (target->compare_state() == CompareIC::GENERIC) continue;
- } else {
- if (state != MONOMORPHIC && state != MEGAMORPHIC) continue;
+ ASSERT(RelocInfo::IsCodeTarget(info->rmode()));
+ Code* target = Code::GetCodeFromTargetAddress(info->target_address());
+ if (target->is_inline_cache_stub()) {
+ InlineCacheState state = target->ic_state();
+ Code::Kind kind = target->kind();
+ if (kind == Code::BINARY_OP_IC) {
+ if (target->binary_op_type() ==
+ BinaryOpIC::GENERIC) {
+ continue;
}
- code_positions->Add(
- static_cast<int>(info->pc() - code->instruction_start()));
- source_positions->Add(position);
+ } else if (kind == Code::COMPARE_IC) {
+ if (target->compare_state() == CompareIC::GENERIC) continue;
+ } else {
+ if (state != MONOMORPHIC && state != MEGAMORPHIC) continue;
}
- } else {
- ASSERT(RelocInfo::IsPosition(mode));
- position = static_cast<int>(info->data());
+ code_positions->Add(
+ static_cast<int>(info->pc() - code->instruction_start()));
+ ASSERT(ast_ids->length() == 0 ||
+ (*ast_ids)[ast_ids->length()-1] !=
+ static_cast<unsigned>(info->data()));
+ ast_ids->Add(static_cast<unsigned>(info->data()));
}
}
}
diff --git a/src/type-info.h b/src/type-info.h
index f6e67291..828e3c72 100644
--- a/src/type-info.h
+++ b/src/type-info.h
@@ -28,6 +28,7 @@
#ifndef V8_TYPE_INFO_H_
#define V8_TYPE_INFO_H_
+#include "allocation.h"
#include "globals.h"
#include "zone.h"
#include "zone-inl.h"
@@ -36,18 +37,18 @@ namespace v8 {
namespace internal {
// Unknown
-// | |
-// | \--------------|
-// Primitive Non-primitive
-// | \--------| |
-// Number String |
-// / | | |
-// Double Integer32 | /
-// | | / /
-// | Smi / /
-// | | / /
-// | | / /
-// Uninitialized.--/
+// | \____________
+// | |
+// Primitive Non-primitive
+// | \_______ |
+// | | |
+// Number String |
+// / \ | |
+// Double Integer32 | /
+// | | / /
+// | Smi / /
+// | | / __/
+// Uninitialized.
class TypeInfo {
public:
@@ -71,32 +72,6 @@ class TypeInfo {
// We haven't started collecting info yet.
static TypeInfo Uninitialized() { return TypeInfo(kUninitialized); }
- // Return compact representation. Very sensitive to enum values below!
- // Compacting drops information about primitive types and strings types.
- // We use the compact representation when we only care about number types.
- int ThreeBitRepresentation() {
- ASSERT(type_ != kUninitialized);
- int answer = type_ & 0xf;
- answer = answer > 6 ? answer - 2 : answer;
- ASSERT(answer >= 0);
- ASSERT(answer <= 7);
- return answer;
- }
-
- // Decode compact representation. Very sensitive to enum values below!
- static TypeInfo ExpandedRepresentation(int three_bit_representation) {
- Type t = static_cast<Type>(three_bit_representation > 4 ?
- three_bit_representation + 2 :
- three_bit_representation);
- t = (t == kUnknown) ? t : static_cast<Type>(t | kPrimitive);
- ASSERT(t == kUnknown ||
- t == kNumber ||
- t == kInteger32 ||
- t == kSmi ||
- t == kDouble);
- return TypeInfo(t);
- }
-
int ToInt() {
return type_;
}
@@ -227,9 +202,11 @@ enum StringStubFeedback {
// Forward declarations.
class Assignment;
+class UnaryOperation;
class BinaryOperation;
class Call;
class CompareOperation;
+class CountOperation;
class CompilationInfo;
class Property;
class CaseClause;
@@ -247,7 +224,9 @@ class TypeFeedbackOracle BASE_EMBEDDED {
ZoneMapList* LoadReceiverTypes(Property* expr, Handle<String> name);
ZoneMapList* StoreReceiverTypes(Assignment* expr, Handle<String> name);
- ZoneMapList* CallReceiverTypes(Call* expr, Handle<String> name);
+ ZoneMapList* CallReceiverTypes(Call* expr,
+ Handle<String> name,
+ CallKind call_kind);
ExternalArrayType GetKeyedLoadExternalArrayType(Property* expr);
ExternalArrayType GetKeyedStoreExternalArrayType(Expression* expr);
@@ -258,26 +237,29 @@ class TypeFeedbackOracle BASE_EMBEDDED {
bool LoadIsBuiltin(Property* expr, Builtins::Name id);
// Get type information for arithmetic operations and compares.
+ TypeInfo UnaryType(UnaryOperation* expr);
TypeInfo BinaryType(BinaryOperation* expr);
TypeInfo CompareType(CompareOperation* expr);
+ bool IsSymbolCompare(CompareOperation* expr);
TypeInfo SwitchType(CaseClause* clause);
+ TypeInfo IncrementType(CountOperation* expr);
private:
- ZoneMapList* CollectReceiverTypes(int position,
+ ZoneMapList* CollectReceiverTypes(unsigned ast_id,
Handle<String> name,
Code::Flags flags);
- void SetInfo(int position, Object* target);
+ void SetInfo(unsigned ast_id, Object* target);
void PopulateMap(Handle<Code> code);
- void CollectPositions(Code* code,
- List<int>* code_positions,
- List<int>* source_positions);
+ void CollectIds(Code* code,
+ List<int>* code_positions,
+ List<unsigned>* ast_ids);
// Returns an element from the backing store. Returns undefined if
// there is no information.
- Handle<Object> GetInfo(int pos);
+ Handle<Object> GetInfo(unsigned ast_id);
Handle<Context> global_context_;
Handle<NumberDictionary> dictionary_;
diff --git a/src/unbound-queue.h b/src/unbound-queue.h
index 443d5ce6..59a426b7 100644
--- a/src/unbound-queue.h
+++ b/src/unbound-queue.h
@@ -28,6 +28,8 @@
#ifndef V8_UNBOUND_QUEUE_
#define V8_UNBOUND_QUEUE_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
diff --git a/src/uri.js b/src/uri.js
index e94b3fe5..72ca6f15 100644
--- a/src/uri.js
+++ b/src/uri.js
@@ -166,7 +166,10 @@ function URIDecodeOctets(octets, result, index) {
// ECMA-262, section 15.1.3
function Encode(uri, unescape) {
var uriLength = uri.length;
- var result = new $Array(uriLength);
+ // We are going to pass result to %StringFromCharCodeArray
+ // which does not expect any getters/setters installed
+ // on the incoming array.
+ var result = new InternalArray(uriLength);
var index = 0;
for (var k = 0; k < uriLength; k++) {
var cc1 = uri.charCodeAt(k);
@@ -192,7 +195,10 @@ function Encode(uri, unescape) {
// ECMA-262, section 15.1.3
function Decode(uri, reserved) {
var uriLength = uri.length;
- var result = new $Array(uriLength);
+ // We are going to pass result to %StringFromCharCodeArray
+ // which does not expect any getters/setters installed
+ // on the incoming array.
+ var result = new InternalArray(uriLength);
var index = 0;
for (var k = 0; k < uriLength; k++) {
var ch = uri.charAt(k);
diff --git a/src/mips/virtual-frame-mips-inl.h b/src/utils-inl.h
index f0d2fab0..76a3c104 100644
--- a/src/mips/virtual-frame-mips-inl.h
+++ b/src/utils-inl.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,34 +25,24 @@
// (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_VIRTUAL_FRAME_MIPS_INL_H_
-#define V8_VIRTUAL_FRAME_MIPS_INL_H_
+#ifndef V8_UTILS_INL_H_
+#define V8_UTILS_INL_H_
-#include "assembler-mips.h"
-#include "virtual-frame-mips.h"
+#include "list-inl.h"
namespace v8 {
namespace internal {
-
-MemOperand VirtualFrame::ParameterAt(int index) {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
-}
-
-
-// The receiver frame slot.
-MemOperand VirtualFrame::Receiver() {
- UNIMPLEMENTED_MIPS();
- return MemOperand(zero_reg, 0);
+template<typename T, int growth_factor, int max_growth>
+void Collector<T, growth_factor, max_growth>::Reset() {
+ for (int i = chunks_.length() - 1; i >= 0; i--) {
+ chunks_.at(i).Dispose();
+ }
+ chunks_.Rewind(0);
+ index_ = 0;
+ size_ = 0;
}
-
-void VirtualFrame::Forget(int count) {
- UNIMPLEMENTED_MIPS();
-}
-
-
} } // namespace v8::internal
-#endif // V8_VIRTUAL_FRAME_MIPS_INL_H_
+#endif // V8_UTILS_INL_H_
diff --git a/src/utils.h b/src/utils.h
index b89f2849..da7a1d97 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -222,6 +222,11 @@ class BitField {
return static_cast<uint32_t>(value) << shift;
}
+ // Returns a uint32_t with the bit field value updated.
+ static uint32_t update(uint32_t previous, T value) {
+ return (previous & ~mask()) | encode(value);
+ }
+
// Extracts the bit field from the value.
static T decode(uint32_t value) {
return static_cast<T>((value & mask()) >> shift);
@@ -251,6 +256,12 @@ static inline uint32_t ComputeIntegerHash(uint32_t key) {
}
+static inline uint32_t ComputePointerHash(void* ptr) {
+ return ComputeIntegerHash(
+ static_cast<uint32_t>(reinterpret_cast<intptr_t>(ptr)));
+}
+
+
// ----------------------------------------------------------------------------
// Miscellaneous
@@ -576,14 +587,7 @@ class Collector {
}
// Resets the collector to be empty.
- virtual void Reset() {
- for (int i = chunks_.length() - 1; i >= 0; i--) {
- chunks_.at(i).Dispose();
- }
- chunks_.Rewind(0);
- index_ = 0;
- size_ = 0;
- }
+ virtual void Reset();
// Total number of elements added to collector so far.
inline int size() { return size_; }
diff --git a/src/v8-counters.h b/src/v8-counters.h
index 5e765b27..e3b16e92 100644
--- a/src/v8-counters.h
+++ b/src/v8-counters.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -134,6 +134,7 @@ namespace internal {
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
SC(keyed_load_generic_lookup_cache, V8.KeyedLoadGenericLookupCache) \
SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
+ SC(keyed_load_polymorphic_stubs, V8.KeyedLoadPolymorphicStubs) \
SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
/* How is the generic keyed-call stub used? */ \
SC(keyed_call_generic_smi_fast, V8.KeyedCallGenericSmiFast) \
@@ -179,6 +180,8 @@ namespace internal {
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
+ SC(keyed_store_polymorphic_stubs, V8.KeyedStorePolymorphicStubs) \
+ SC(keyed_store_external_array_slow, V8.KeyedStoreExternalArraySlow) \
SC(store_normal_miss, V8.StoreNormalMiss) \
SC(store_normal_hit, V8.StoreNormalHit) \
SC(cow_arrays_created_stub, V8.COWArraysCreatedStub) \
diff --git a/src/v8.h b/src/v8.h
index 776fa9c2..9d985213 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -66,6 +66,7 @@
#include "log-inl.h"
#include "cpu-profiler-inl.h"
#include "handles-inl.h"
+#include "isolate-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/v8dll-main.cc b/src/v8dll-main.cc
index 3d4b3a37..49d86895 100644
--- a/src/v8dll-main.cc
+++ b/src/v8dll-main.cc
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -25,10 +25,14 @@
// (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 <windows.h>
-
+// The GYP based build ends up defining USING_V8_SHARED when compiling this
+// file.
+#undef USING_V8_SHARED
#include "../include/v8.h"
+#ifdef WIN32
+#include <windows.h> // NOLINT
+
extern "C" {
BOOL WINAPI DllMain(HANDLE hinstDLL,
DWORD dwReason,
@@ -37,3 +41,4 @@ BOOL WINAPI DllMain(HANDLE hinstDLL,
return TRUE;
}
}
+#endif
diff --git a/src/v8globals.h b/src/v8globals.h
index 2a01dfd1..a23ca194 100644
--- a/src/v8globals.h
+++ b/src/v8globals.h
@@ -1,4 +1,4 @@
-// Copyright 2010 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -154,7 +154,7 @@ class Object;
class MaybeObject;
class OldSpace;
class Property;
-class Proxy;
+class Foreign;
class RegExpNode;
struct RegExpCompileData;
class RegExpTree;
@@ -185,6 +185,8 @@ class Mutex;
typedef bool (*WeakSlotCallback)(Object** pointer);
+typedef bool (*WeakSlotCallbackWithHeap)(Heap* heap, Object** pointer);
+
// -----------------------------------------------------------------------------
// Miscellaneous
@@ -218,7 +220,12 @@ enum GarbageCollector { SCAVENGER, MARK_COMPACTOR };
enum Executability { NOT_EXECUTABLE, EXECUTABLE };
-enum VisitMode { VISIT_ALL, VISIT_ALL_IN_SCAVENGE, VISIT_ONLY_STRONG };
+enum VisitMode {
+ VISIT_ALL,
+ VISIT_ALL_IN_SCAVENGE,
+ VISIT_ALL_IN_SWEEP_NEWSPACE,
+ VISIT_ONLY_STRONG
+};
// Flag indicating whether code is built into the VM (one of the natives files).
enum NativesFlag { NOT_NATIVES_CODE, NATIVES_CODE };
@@ -303,7 +310,9 @@ enum InLoopFlag {
enum CallFunctionFlags {
NO_CALL_FUNCTION_FLAGS = 0,
- RECEIVER_MIGHT_BE_VALUE = 1 << 0 // Receiver might not be a JSObject.
+ // Receiver might implicitly be the global objects. If it is, the
+ // hole is passed to the call function stub.
+ RECEIVER_MIGHT_BE_IMPLICIT = 1 << 0
};
@@ -322,11 +331,12 @@ enum PropertyType {
FIELD = 1, // only in fast mode
CONSTANT_FUNCTION = 2, // only in fast mode
CALLBACKS = 3,
- INTERCEPTOR = 4, // only in lookup results, not in descriptors.
- MAP_TRANSITION = 5, // only in fast mode
- EXTERNAL_ARRAY_TRANSITION = 6,
- CONSTANT_TRANSITION = 7, // only in fast mode
- NULL_DESCRIPTOR = 8, // only in fast mode
+ HANDLER = 4, // only in lookup results, not in descriptors
+ INTERCEPTOR = 5, // only in lookup results, not in descriptors
+ MAP_TRANSITION = 6, // only in fast mode
+ EXTERNAL_ARRAY_TRANSITION = 7,
+ CONSTANT_TRANSITION = 8, // only in fast mode
+ NULL_DESCRIPTOR = 9, // only in fast mode
// All properties before MAP_TRANSITION are real.
FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION,
// There are no IC stubs for NULL_DESCRIPTORS. Therefore,
@@ -481,6 +491,22 @@ enum StrictModeFlag {
kInvalidStrictFlag
};
+
+// Used to specify if a macro instruction must perform a smi check on tagged
+// values.
+enum SmiCheckType {
+ DONT_DO_SMI_CHECK = 0,
+ DO_SMI_CHECK
+};
+
+
+// Used to specify whether a receiver is implicitly or explicitly
+// provided to a call.
+enum CallKind {
+ CALL_AS_METHOD = 0,
+ CALL_AS_FUNCTION
+};
+
} } // namespace v8::internal
#endif // V8_V8GLOBALS_H_
diff --git a/src/v8natives.js b/src/v8natives.js
index 429cea5e..700fe588 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -56,6 +56,7 @@ function InstallFunctions(object, attributes, functions) {
%FunctionSetName(f, key);
%FunctionRemovePrototype(f);
%SetProperty(object, key, f, attributes);
+ %SetES5Flag(f);
}
%ToFastProperties(object);
}
@@ -196,12 +197,20 @@ $Object.prototype.constructor = $Object;
// ECMA-262 - 15.2.4.2
function ObjectToString() {
+ if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ return '[object Undefined]';
+ }
+ if (IS_NULL(this)) return '[object Null]';
return "[object " + %_ClassOf(ToObject(this)) + "]";
}
// ECMA-262 - 15.2.4.3
function ObjectToLocaleString() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Object.prototype.toLocaleString"]);
+ }
return this.toString();
}
@@ -214,12 +223,16 @@ function ObjectValueOf() {
// ECMA-262 - 15.2.4.5
function ObjectHasOwnProperty(V) {
- return %HasLocalProperty(ToObject(this), ToString(V));
+ return %HasLocalProperty(TO_OBJECT_INLINE(this), TO_STRING_INLINE(V));
}
// ECMA-262 - 15.2.4.6
function ObjectIsPrototypeOf(V) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Object.prototype.isPrototypeOf"]);
+ }
if (!IS_SPEC_OBJECT(V)) return false;
return %IsInPrototypeChain(this, V);
}
@@ -313,18 +326,18 @@ function IsInconsistentDescriptor(desc) {
// ES5 8.10.4
function FromPropertyDescriptor(desc) {
if (IS_UNDEFINED(desc)) return desc;
- var obj = new $Object();
+
if (IsDataDescriptor(desc)) {
- obj.value = desc.getValue();
- obj.writable = desc.isWritable();
- }
- if (IsAccessorDescriptor(desc)) {
- obj.get = desc.getGet();
- obj.set = desc.getSet();
+ return { value: desc.getValue(),
+ writable: desc.isWritable(),
+ enumerable: desc.isEnumerable(),
+ configurable: desc.isConfigurable() };
}
- obj.enumerable = desc.isEnumerable();
- obj.configurable = desc.isConfigurable();
- return obj;
+ // Must be an AccessorDescriptor then. We never return a generic descriptor.
+ return { get: desc.getGet(),
+ set: desc.getSet(),
+ enumerable: desc.isEnumerable(),
+ configurable: desc.isConfigurable() };
}
// ES5 8.10.5.
@@ -391,6 +404,7 @@ function PropertyDescriptor() {
}
PropertyDescriptor.prototype.__proto__ = null;
+
PropertyDescriptor.prototype.toString = function() {
return "[object PropertyDescriptor]";
};
@@ -1050,6 +1064,10 @@ function NumberToString(radix) {
// ECMA-262 section 15.7.4.3
function NumberToLocaleString() {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Number.prototype.toLocaleString"]);
+ }
return this.toString();
}
@@ -1070,6 +1088,10 @@ function NumberToFixed(fractionDigits) {
if (f < 0 || f > 20) {
throw new $RangeError("toFixed() digits argument must be between 0 and 20");
}
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Number.prototype.toFixed"]);
+ }
var x = ToNumber(this);
return %NumberToFixed(x, f);
}
@@ -1084,6 +1106,10 @@ function NumberToExponential(fractionDigits) {
throw new $RangeError("toExponential() argument must be between 0 and 20");
}
}
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Number.prototype.toExponential"]);
+ }
var x = ToNumber(this);
return %NumberToExponential(x, f);
}
@@ -1091,6 +1117,10 @@ function NumberToExponential(fractionDigits) {
// ECMA-262 section 15.7.4.7
function NumberToPrecision(precision) {
+ if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
+ throw MakeTypeError("called_on_null_or_undefined",
+ ["Number.prototype.toPrecision"]);
+ }
if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
var p = TO_INTEGER(precision);
if (p < 1 || p > 21) {
diff --git a/src/v8threads.cc b/src/v8threads.cc
index 4b033fcf..26169b50 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -43,90 +43,94 @@ bool Locker::active_ = false;
// Constructor for the Locker object. Once the Locker is constructed the
-// current thread will be guaranteed to have the big V8 lock.
-Locker::Locker() : has_lock_(false), top_level_(true) {
- // TODO(isolates): When Locker has Isolate parameter and it is provided, grab
- // that one instead of using the current one.
- // We pull default isolate for Locker constructor w/o p[arameter.
- // A thread should not enter an isolate before acquiring a lock,
- // in cases which mandate using Lockers.
- // So getting a lock is the first thing threads do in a scenario where
- // multple threads share an isolate. Hence, we need to access
- // 'locking isolate' before we can actually enter into default isolate.
- internal::Isolate* isolate = internal::Isolate::GetDefaultIsolateForLocking();
- ASSERT(isolate != NULL);
-
+// current thread will be guaranteed to have the lock for a given isolate.
+Locker::Locker(v8::Isolate* isolate)
+ : has_lock_(false),
+ top_level_(false),
+ isolate_(reinterpret_cast<i::Isolate*>(isolate)) {
+ if (isolate_ == NULL) {
+ isolate_ = i::Isolate::GetDefaultIsolateForLocking();
+ }
// Record that the Locker has been used at least once.
active_ = true;
// Get the big lock if necessary.
- if (!isolate->thread_manager()->IsLockedByCurrentThread()) {
- isolate->thread_manager()->Lock();
+ if (!isolate_->thread_manager()->IsLockedByCurrentThread()) {
+ isolate_->thread_manager()->Lock();
has_lock_ = true;
- if (isolate->IsDefaultIsolate()) {
- // This only enters if not yet entered.
- internal::Isolate::EnterDefaultIsolate();
- }
-
- ASSERT(internal::Thread::HasThreadLocal(
- internal::Isolate::thread_id_key()));
-
// Make sure that V8 is initialized. Archiving of threads interferes
// with deserialization by adding additional root pointers, so we must
// initialize here, before anyone can call ~Locker() or Unlocker().
- if (!isolate->IsInitialized()) {
+ if (!isolate_->IsInitialized()) {
+ isolate_->Enter();
V8::Initialize();
+ isolate_->Exit();
}
+
// This may be a locker within an unlocker in which case we have to
// get the saved state for this thread and restore it.
- if (isolate->thread_manager()->RestoreThread()) {
+ if (isolate_->thread_manager()->RestoreThread()) {
top_level_ = false;
} else {
- internal::ExecutionAccess access(isolate);
- isolate->stack_guard()->ClearThread(access);
- isolate->stack_guard()->InitThread(access);
+ internal::ExecutionAccess access(isolate_);
+ isolate_->stack_guard()->ClearThread(access);
+ isolate_->stack_guard()->InitThread(access);
+ }
+ if (isolate_->IsDefaultIsolate()) {
+ // This only enters if not yet entered.
+ internal::Isolate::EnterDefaultIsolate();
}
}
- ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
+ ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
}
-bool Locker::IsLocked() {
- return internal::Isolate::Current()->thread_manager()->
- IsLockedByCurrentThread();
+bool Locker::IsLocked(v8::Isolate* isolate) {
+ i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
+ if (internal_isolate == NULL) {
+ internal_isolate = i::Isolate::GetDefaultIsolateForLocking();
+ }
+ return internal_isolate->thread_manager()->IsLockedByCurrentThread();
}
Locker::~Locker() {
- // TODO(isolate): this should use a field storing the isolate it
- // locked instead.
- internal::Isolate* isolate = internal::Isolate::Current();
- ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
+ ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
if (has_lock_) {
+ if (isolate_->IsDefaultIsolate()) {
+ isolate_->Exit();
+ }
if (top_level_) {
- isolate->thread_manager()->FreeThreadResources();
+ isolate_->thread_manager()->FreeThreadResources();
} else {
- isolate->thread_manager()->ArchiveThread();
+ isolate_->thread_manager()->ArchiveThread();
}
- isolate->thread_manager()->Unlock();
+ isolate_->thread_manager()->Unlock();
}
}
-Unlocker::Unlocker() {
- internal::Isolate* isolate = internal::Isolate::Current();
- ASSERT(isolate->thread_manager()->IsLockedByCurrentThread());
- isolate->thread_manager()->ArchiveThread();
- isolate->thread_manager()->Unlock();
+Unlocker::Unlocker(v8::Isolate* isolate)
+ : isolate_(reinterpret_cast<i::Isolate*>(isolate)) {
+ if (isolate_ == NULL) {
+ isolate_ = i::Isolate::GetDefaultIsolateForLocking();
+ }
+ ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
+ if (isolate_->IsDefaultIsolate()) {
+ isolate_->Exit();
+ }
+ isolate_->thread_manager()->ArchiveThread();
+ isolate_->thread_manager()->Unlock();
}
Unlocker::~Unlocker() {
- // TODO(isolates): check it's the isolate we unlocked.
- internal::Isolate* isolate = internal::Isolate::Current();
- ASSERT(!isolate->thread_manager()->IsLockedByCurrentThread());
- isolate->thread_manager()->Lock();
- isolate->thread_manager()->RestoreThread();
+ ASSERT(!isolate_->thread_manager()->IsLockedByCurrentThread());
+ isolate_->thread_manager()->Lock();
+ isolate_->thread_manager()->RestoreThread();
+ if (isolate_->IsDefaultIsolate()) {
+ isolate_->Enter();
+ }
}
@@ -144,17 +148,20 @@ namespace internal {
bool ThreadManager::RestoreThread() {
+ ASSERT(IsLockedByCurrentThread());
// First check whether the current thread has been 'lazily archived', ie
// not archived at all. If that is the case we put the state storage we
// had prepared back in the free list, since we didn't need it after all.
if (lazily_archived_thread_.Equals(ThreadId::Current())) {
lazily_archived_thread_ = ThreadId::Invalid();
- ASSERT(Isolate::CurrentPerIsolateThreadData()->thread_state() ==
- lazily_archived_thread_state_);
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindPerThreadDataForThisThread();
+ ASSERT(per_thread != NULL);
+ ASSERT(per_thread->thread_state() == lazily_archived_thread_state_);
lazily_archived_thread_state_->set_id(ThreadId::Invalid());
lazily_archived_thread_state_->LinkInto(ThreadState::FREE_LIST);
lazily_archived_thread_state_ = NULL;
- Isolate::CurrentPerIsolateThreadData()->set_thread_state(NULL);
+ per_thread->set_thread_state(NULL);
return true;
}
@@ -168,7 +175,7 @@ bool ThreadManager::RestoreThread() {
EagerlyArchiveThread();
}
Isolate::PerIsolateThreadData* per_thread =
- Isolate::CurrentPerIsolateThreadData();
+ isolate_->FindPerThreadDataForThisThread();
if (per_thread == NULL || per_thread->thread_state() == NULL) {
// This is a new thread.
isolate_->stack_guard()->InitThread(access);
@@ -178,7 +185,7 @@ bool ThreadManager::RestoreThread() {
char* from = state->data();
from = isolate_->handle_scope_implementer()->RestoreThread(from);
from = isolate_->RestoreThread(from);
- from = Relocatable::RestoreState(from);
+ from = Relocatable::RestoreState(isolate_, from);
#ifdef ENABLE_DEBUGGER_SUPPORT
from = isolate_->debug()->RestoreDebug(from);
#endif
@@ -300,9 +307,12 @@ ThreadManager::~ThreadManager() {
void ThreadManager::ArchiveThread() {
ASSERT(lazily_archived_thread_.Equals(ThreadId::Invalid()));
ASSERT(!IsArchived());
+ ASSERT(IsLockedByCurrentThread());
ThreadState* state = GetFreeThreadState();
state->Unlink();
- Isolate::CurrentPerIsolateThreadData()->set_thread_state(state);
+ Isolate::PerIsolateThreadData* per_thread =
+ isolate_->FindOrAllocatePerThreadDataForThisThread();
+ per_thread->set_thread_state(state);
lazily_archived_thread_ = ThreadId::Current();
lazily_archived_thread_state_ = state;
ASSERT(state->id().Equals(ThreadId::Invalid()));
@@ -312,6 +322,7 @@ void ThreadManager::ArchiveThread() {
void ThreadManager::EagerlyArchiveThread() {
+ ASSERT(IsLockedByCurrentThread());
ThreadState* state = lazily_archived_thread_state_;
state->LinkInto(ThreadState::IN_USE_LIST);
char* to = state->data();
@@ -319,7 +330,7 @@ void ThreadManager::EagerlyArchiveThread() {
// in ThreadManager::Iterate(ObjectVisitor*).
to = isolate_->handle_scope_implementer()->ArchiveThread(to);
to = isolate_->ArchiveThread(to);
- to = Relocatable::ArchiveState(to);
+ to = Relocatable::ArchiveState(isolate_, to);
#ifdef ENABLE_DEBUGGER_SUPPORT
to = isolate_->debug()->ArchiveDebug(to);
#endif
@@ -344,11 +355,11 @@ void ThreadManager::FreeThreadResources() {
bool ThreadManager::IsArchived() {
- Isolate::PerIsolateThreadData* data = Isolate::CurrentPerIsolateThreadData();
+ Isolate::PerIsolateThreadData* data =
+ isolate_->FindPerThreadDataForThisThread();
return data != NULL && data->thread_state() != NULL;
}
-
void ThreadManager::Iterate(ObjectVisitor* v) {
// Expecting no threads during serialization/deserialization
for (ThreadState* state = FirstThreadStateInUse();
diff --git a/src/version.cc b/src/version.cc
index 47e7fe25..34549faa 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -33,9 +33,9 @@
// NOTE these macros are used by the SCons build script so their names
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
-#define MINOR_VERSION 2
+#define MINOR_VERSION 3
#define BUILD_NUMBER 10
-#define PATCH_LEVEL 40
+#define PATCH_LEVEL 39
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
diff --git a/src/vm-state.h b/src/vm-state.h
index 11fc6d67..2062340f 100644
--- a/src/vm-state.h
+++ b/src/vm-state.h
@@ -28,6 +28,7 @@
#ifndef V8_VM_STATE_H_
#define V8_VM_STATE_H_
+#include "allocation.h"
#include "isolate.h"
namespace v8 {
diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h
index 9541a58b..8db54f07 100644
--- a/src/x64/assembler-x64-inl.h
+++ b/src/x64/assembler-x64-inl.h
@@ -61,9 +61,15 @@ void Assembler::emitw(uint16_t x) {
}
-void Assembler::emit_code_target(Handle<Code> target, RelocInfo::Mode rmode) {
+void Assembler::emit_code_target(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ unsigned ast_id) {
ASSERT(RelocInfo::IsCodeTarget(rmode));
- RecordRelocInfo(rmode);
+ if (rmode == RelocInfo::CODE_TARGET && ast_id != kNoASTId) {
+ RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id);
+ } else {
+ RecordRelocInfo(rmode);
+ }
int current = code_targets_.length();
if (current > 0 && code_targets_.last().is_identical_to(target)) {
// Optimization if we keep jumping to the same code target.
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 6e4f0050..745fdaeb 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -458,6 +458,20 @@ void Assembler::bind_to(Label* L, int pos) {
int last_imm32 = pos - (current + sizeof(int32_t));
long_at_put(current, last_imm32);
}
+ while (L->is_near_linked()) {
+ int fixup_pos = L->near_link_pos();
+ int offset_to_next =
+ static_cast<int>(*reinterpret_cast<int8_t*>(addr_at(fixup_pos)));
+ ASSERT(offset_to_next <= 0);
+ int disp = pos - (fixup_pos + sizeof(int8_t));
+ ASSERT(is_int8(disp));
+ set_byte_at(fixup_pos, disp);
+ if (offset_to_next < 0) {
+ L->link_to(fixup_pos + offset_to_next, Label::kNear);
+ } else {
+ L->UnuseNear();
+ }
+ }
L->bind_to(pos);
}
@@ -467,19 +481,6 @@ void Assembler::bind(Label* L) {
}
-void Assembler::bind(NearLabel* L) {
- ASSERT(!L->is_bound());
- while (L->unresolved_branches_ > 0) {
- int branch_pos = L->unresolved_positions_[L->unresolved_branches_ - 1];
- int disp = pc_offset() - branch_pos;
- ASSERT(is_int8(disp));
- set_byte_at(branch_pos - sizeof(int8_t), disp);
- L->unresolved_branches_--;
- }
- L->bind_to(pc_offset());
-}
-
-
void Assembler::GrowBuffer() {
ASSERT(buffer_overflow());
if (!own_buffer_) FATAL("external code buffer is too small");
@@ -869,12 +870,14 @@ void Assembler::call(Label* L) {
}
-void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) {
+void Assembler::call(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ unsigned ast_id) {
positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
// 1110 1000 #32-bit disp.
emit(0xE8);
- emit_code_target(target, rmode);
+ emit_code_target(target, rmode, ast_id);
}
@@ -1212,7 +1215,7 @@ void Assembler::int3() {
}
-void Assembler::j(Condition cc, Label* L) {
+void Assembler::j(Condition cc, Label* L, Label::Distance distance) {
if (cc == always) {
jmp(L);
return;
@@ -1236,6 +1239,17 @@ void Assembler::j(Condition cc, Label* L) {
emit(0x80 | cc);
emitl(offs - long_size);
}
+ } else if (distance == Label::kNear) {
+ // 0111 tttn #8-bit disp
+ emit(0x70 | cc);
+ byte disp = 0x00;
+ if (L->is_near_linked()) {
+ int offset = L->near_link_pos() - pc_offset();
+ ASSERT(is_int8(offset));
+ disp = static_cast<byte>(offset & 0xFF);
+ }
+ L->link_to(pc_offset(), Label::kNear);
+ emit(disp);
} else if (L->is_linked()) {
// 0000 1111 1000 tttn #32-bit disp.
emit(0x0F);
@@ -1265,27 +1279,7 @@ void Assembler::j(Condition cc,
}
-void Assembler::j(Condition cc, NearLabel* L, Hint hint) {
- EnsureSpace ensure_space(this);
- ASSERT(0 <= cc && cc < 16);
- if (FLAG_emit_branch_hints && hint != no_hint) emit(hint);
- if (L->is_bound()) {
- const int short_size = 2;
- int offs = L->pos() - pc_offset();
- ASSERT(offs <= 0);
- ASSERT(is_int8(offs - short_size));
- // 0111 tttn #8-bit disp
- emit(0x70 | cc);
- emit((offs - short_size) & 0xFF);
- } else {
- emit(0x70 | cc);
- emit(0x00); // The displacement will be resolved later.
- L->link_to(pc_offset());
- }
-}
-
-
-void Assembler::jmp(Label* L) {
+void Assembler::jmp(Label* L, Label::Distance distance) {
EnsureSpace ensure_space(this);
const int short_size = sizeof(int8_t);
const int long_size = sizeof(int32_t);
@@ -1301,7 +1295,17 @@ void Assembler::jmp(Label* L) {
emit(0xE9);
emitl(offs - long_size);
}
- } else if (L->is_linked()) {
+ } else if (distance == Label::kNear) {
+ emit(0xEB);
+ byte disp = 0x00;
+ if (L->is_near_linked()) {
+ int offset = L->near_link_pos() - pc_offset();
+ ASSERT(is_int8(offset));
+ disp = static_cast<byte>(offset & 0xFF);
+ }
+ L->link_to(pc_offset(), Label::kNear);
+ emit(disp);
+ } else if (L->is_linked()) {
// 1110 1001 #32-bit disp.
emit(0xE9);
emitl(L->pos());
@@ -1325,24 +1329,6 @@ void Assembler::jmp(Handle<Code> target, RelocInfo::Mode rmode) {
}
-void Assembler::jmp(NearLabel* L) {
- EnsureSpace ensure_space(this);
- if (L->is_bound()) {
- const int short_size = 2;
- int offs = L->pos() - pc_offset();
- ASSERT(offs <= 0);
- ASSERT(is_int8(offs - short_size));
- // 1110 1011 #8-bit disp.
- emit(0xEB);
- emit((offs - short_size) & 0xFF);
- } else {
- emit(0xEB);
- emit(0x00); // The displacement will be resolved later.
- L->link_to(pc_offset());
- }
-}
-
-
void Assembler::jmp(Register target) {
EnsureSpace ensure_space(this);
// Opcode FF/4 r64.
@@ -2540,6 +2526,24 @@ void Assembler::movq(Register dst, XMMRegister src) {
}
+void Assembler::movq(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (dst.low_bits() == 4) {
+ // Avoid unnecessary SIB byte.
+ emit(0xf3);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x7e);
+ emit_sse_operand(dst, src);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0xD6);
+ emit_sse_operand(src, dst);
+ }
+}
+
void Assembler::movdqa(const Operand& dst, XMMRegister src) {
EnsureSpace ensure_space(this);
emit(0x66);
@@ -2603,6 +2607,42 @@ void Assembler::movsd(XMMRegister dst, const Operand& src) {
}
+void Assembler::movaps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ // Try to avoid an unnecessary SIB byte.
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x29);
+ emit_sse_operand(src, dst);
+ } else {
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x28);
+ emit_sse_operand(dst, src);
+ }
+}
+
+
+void Assembler::movapd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ if (src.low_bits() == 4) {
+ // Try to avoid an unnecessary SIB byte.
+ emit(0x66);
+ emit_optional_rex_32(src, dst);
+ emit(0x0F);
+ emit(0x29);
+ emit_sse_operand(src, dst);
+ } else {
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x28);
+ emit_sse_operand(dst, src);
+ }
+}
+
+
void Assembler::movss(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
emit(0xF3); // single
@@ -2833,6 +2873,15 @@ void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
}
+void Assembler::xorps(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
EnsureSpace ensure_space(this);
emit(0xF2);
@@ -2863,6 +2912,21 @@ void Assembler::ucomisd(XMMRegister dst, const Operand& src) {
}
+void Assembler::roundsd(XMMRegister dst, XMMRegister src,
+ Assembler::RoundingMode mode) {
+ ASSERT(CpuFeatures::IsEnabled(SSE4_1));
+ EnsureSpace ensure_space(this);
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x3a);
+ emit(0x0b);
+ emit_sse_operand(dst, src);
+ // Mask precision exeption.
+ emit(static_cast<byte>(mode) | 0x8);
+}
+
+
void Assembler::movmskpd(Register dst, XMMRegister src) {
EnsureSpace ensure_space(this);
emit(0x66);
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index 94532778..7769b032 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -327,22 +327,6 @@ inline Condition ReverseCondition(Condition cc) {
}
-enum Hint {
- no_hint = 0,
- not_taken = 0x2e,
- taken = 0x3e
-};
-
-// The result of negating a hint is as if the corresponding condition
-// were negated by NegateCondition. That is, no_hint is mapped to
-// itself and not_taken and taken are mapped to each other.
-inline Hint NegateHint(Hint hint) {
- return (hint == no_hint)
- ? no_hint
- : ((hint == not_taken) ? taken : not_taken);
-}
-
-
// -----------------------------------------------------------------------------
// Machine instruction Immediates
@@ -1178,12 +1162,13 @@ class Assembler : public AssemblerBase {
// but it may be bound only once.
void bind(Label* L); // binds an unbound label L to the current code position
- void bind(NearLabel* L);
// Calls
// Call near relative 32-bit displacement, relative to next instruction.
void call(Label* L);
- void call(Handle<Code> target, RelocInfo::Mode rmode);
+ void call(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ unsigned ast_id = kNoASTId);
// Calls directly to the given address using a relative offset.
// Should only ever be used in Code objects for calls within the
@@ -1200,7 +1185,8 @@ class Assembler : public AssemblerBase {
// Jumps
// Jump short or near relative.
// Use a 32-bit signed displacement.
- void jmp(Label* L); // unconditional jump to L
+ // Unconditional jump to L
+ void jmp(Label* L, Label::Distance distance = Label::kFar);
void jmp(Handle<Code> target, RelocInfo::Mode rmode);
// Jump near absolute indirect (r64)
@@ -1209,16 +1195,12 @@ class Assembler : public AssemblerBase {
// Jump near absolute indirect (m64)
void jmp(const Operand& src);
- // Short jump
- void jmp(NearLabel* L);
-
// Conditional jumps
- void j(Condition cc, Label* L);
+ void j(Condition cc,
+ Label* L,
+ Label::Distance distance = Label::kFar);
void j(Condition cc, Handle<Code> target, RelocInfo::Mode rmode);
- // Conditional short jump
- void j(Condition cc, NearLabel* L, Hint hint = no_hint);
-
// Floating-point operations
void fld(int i);
@@ -1291,15 +1273,24 @@ class Assembler : public AssemblerBase {
void movd(Register dst, XMMRegister src);
void movq(XMMRegister dst, Register src);
void movq(Register dst, XMMRegister src);
+ void movq(XMMRegister dst, XMMRegister src);
void extractps(Register dst, XMMRegister src, byte imm8);
- void movsd(const Operand& dst, XMMRegister src);
+ // Don't use this unless it's important to keep the
+ // top half of the destination register unchanged.
+ // Used movaps when moving double values and movq for integer
+ // values in xmm registers.
void movsd(XMMRegister dst, XMMRegister src);
+
+ void movsd(const Operand& dst, XMMRegister src);
void movsd(XMMRegister dst, const Operand& src);
void movdqa(const Operand& dst, XMMRegister src);
void movdqa(XMMRegister dst, const Operand& src);
+ void movapd(XMMRegister dst, XMMRegister src);
+ void movaps(XMMRegister dst, XMMRegister src);
+
void movss(XMMRegister dst, const Operand& src);
void movss(const Operand& dst, XMMRegister src);
@@ -1331,11 +1322,21 @@ class Assembler : public AssemblerBase {
void andpd(XMMRegister dst, XMMRegister src);
void orpd(XMMRegister dst, XMMRegister src);
void xorpd(XMMRegister dst, XMMRegister src);
+ void xorps(XMMRegister dst, XMMRegister src);
void sqrtsd(XMMRegister dst, XMMRegister src);
void ucomisd(XMMRegister dst, XMMRegister src);
void ucomisd(XMMRegister dst, const Operand& src);
+ enum RoundingMode {
+ kRoundToNearest = 0x0,
+ kRoundDown = 0x1,
+ kRoundUp = 0x2,
+ kRoundToZero = 0x3
+ };
+
+ void roundsd(XMMRegister dst, XMMRegister src, RoundingMode mode);
+
void movmskpd(Register dst, XMMRegister src);
// The first argument is the reg field, the second argument is the r/m field.
@@ -1408,7 +1409,9 @@ class Assembler : public AssemblerBase {
inline void emitl(uint32_t x);
inline void emitq(uint64_t x, RelocInfo::Mode rmode);
inline void emitw(uint16_t x);
- inline void emit_code_target(Handle<Code> target, RelocInfo::Mode rmode);
+ inline void emit_code_target(Handle<Code> target,
+ RelocInfo::Mode rmode,
+ unsigned ast_id = kNoASTId);
void emit(Immediate x) { emitl(x.value_); }
// Emits a REX prefix that encodes a 64-bit operand size and
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index a5496331..fc4581c3 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -98,6 +98,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// Set expected number of arguments to zero (not changing rax).
__ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
}
@@ -342,11 +343,12 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
Handle<Code> code =
masm->isolate()->builtins()->HandleApiCallConstruct();
ParameterCount expected(0);
- __ InvokeCode(code, expected, expected,
- RelocInfo::CODE_TARGET, CALL_FUNCTION);
+ __ InvokeCode(code, expected, expected, RelocInfo::CODE_TARGET,
+ CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
} else {
ParameterCount actual(rax);
- __ InvokeFunction(rdi, actual, CALL_FUNCTION);
+ __ InvokeFunction(rdi, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// Restore context from the frame.
@@ -498,7 +500,8 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
} else {
ParameterCount actual(rax);
// Function must be in rdi.
- __ InvokeFunction(rdi, actual, CALL_FUNCTION);
+ __ InvokeFunction(rdi, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
// Exit the JS frame. Notice that this also removes the empty
@@ -526,17 +529,23 @@ void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
// Push a copy of the function onto the stack.
__ push(rdi);
+ // Push call kind information.
+ __ push(rcx);
__ push(rdi); // Function is also the parameter to the runtime call.
__ CallRuntime(Runtime::kLazyCompile, 1);
+
+ // Restore call kind information.
+ __ pop(rcx);
+ // Restore receiver.
__ pop(rdi);
// Tear down temporary frame.
__ LeaveInternalFrame();
// Do a tail-call of the compiled function.
- __ lea(rcx, FieldOperand(rax, Code::kHeaderSize));
- __ jmp(rcx);
+ __ lea(rax, FieldOperand(rax, Code::kHeaderSize));
+ __ jmp(rax);
}
@@ -546,17 +555,23 @@ void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
// Push a copy of the function onto the stack.
__ push(rdi);
+ // Push call kind information.
+ __ push(rcx);
__ push(rdi); // Function is also the parameter to the runtime call.
__ CallRuntime(Runtime::kLazyRecompile, 1);
- // Restore function and tear down temporary frame.
+ // Restore call kind information.
+ __ pop(rcx);
+ // Restore function.
__ pop(rdi);
+
+ // Tear down temporary frame.
__ LeaveInternalFrame();
// Do a tail-call of the compiled function.
- __ lea(rcx, FieldOperand(rax, Code::kHeaderSize));
- __ jmp(rcx);
+ __ lea(rax, FieldOperand(rax, Code::kHeaderSize));
+ __ jmp(rax);
}
@@ -576,15 +591,15 @@ static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
__ SmiToInteger32(rcx, Operand(rsp, 1 * kPointerSize));
// Switch on the state.
- NearLabel not_no_registers, not_tos_rax;
+ Label not_no_registers, not_tos_rax;
__ cmpq(rcx, Immediate(FullCodeGenerator::NO_REGISTERS));
- __ j(not_equal, &not_no_registers);
+ __ j(not_equal, &not_no_registers, Label::kNear);
__ ret(1 * kPointerSize); // Remove state.
__ bind(&not_no_registers);
__ movq(rax, Operand(rsp, 2 * kPointerSize));
__ cmpq(rcx, Immediate(FullCodeGenerator::TOS_REG));
- __ j(not_equal, &not_tos_rax);
+ __ j(not_equal, &not_tos_rax, Label::kNear);
__ ret(2 * kPointerSize); // Remove state, rax.
__ bind(&not_tos_rax);
@@ -658,19 +673,25 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
__ j(not_equal, &shift_arguments);
+ // Do not transform the receiver for natives.
+ // SharedFunctionInfo is already loaded into rbx.
+ __ testb(FieldOperand(rbx, SharedFunctionInfo::kES5NativeByteOffset),
+ Immediate(1 << SharedFunctionInfo::kES5NativeBitWithinByte));
+ __ j(not_zero, &shift_arguments);
+
// Compute the receiver in non-strict mode.
__ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
- __ JumpIfSmi(rbx, &convert_to_object);
+ __ JumpIfSmi(rbx, &convert_to_object, Label::kNear);
__ CompareRoot(rbx, Heap::kNullValueRootIndex);
__ j(equal, &use_global_receiver);
__ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
__ j(equal, &use_global_receiver);
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &convert_to_object);
- __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
- __ j(below_equal, &shift_arguments);
+ __ j(above_equal, &shift_arguments);
__ bind(&convert_to_object);
__ EnterInternalFrame(); // In order to preserve argument count.
@@ -686,7 +707,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ LeaveInternalFrame();
// Restore the function to rdi.
__ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
- __ jmp(&patch_receiver);
+ __ jmp(&patch_receiver, Label::kNear);
// Use the global receiver object from the called function as the
// receiver.
@@ -734,6 +755,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ j(not_zero, &function);
__ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
__ bind(&function);
@@ -747,13 +769,15 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
FieldOperand(rdx,
SharedFunctionInfo::kFormalParameterCountOffset));
__ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+ __ SetCallKind(rcx, CALL_AS_METHOD);
__ cmpq(rax, rbx);
__ j(not_equal,
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
ParameterCount expected(0);
- __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
+ __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
@@ -822,8 +846,13 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
__ j(not_equal, &push_receiver);
+ // Do not transform the receiver for natives.
+ __ testb(FieldOperand(rdx, SharedFunctionInfo::kES5NativeByteOffset),
+ Immediate(1 << SharedFunctionInfo::kES5NativeBitWithinByte));
+ __ j(not_zero, &push_receiver);
+
// Compute the receiver in non-strict mode.
- __ JumpIfSmi(rbx, &call_to_object);
+ __ JumpIfSmi(rbx, &call_to_object, Label::kNear);
__ CompareRoot(rbx, Heap::kNullValueRootIndex);
__ j(equal, &use_global_receiver);
__ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
@@ -831,17 +860,17 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// If given receiver is already a JavaScript object then there's no
// reason for converting it.
+ STATIC_ASSERT(LAST_JS_OBJECT_TYPE + 1 == LAST_TYPE);
+ STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &call_to_object);
- __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
- __ j(below_equal, &push_receiver);
+ __ j(above_equal, &push_receiver);
// Convert the receiver to an object.
__ bind(&call_to_object);
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
- __ jmp(&push_receiver);
+ __ jmp(&push_receiver, Label::kNear);
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
@@ -888,7 +917,8 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
ParameterCount actual(rax);
__ SmiToInteger32(rax, rax);
__ movq(rdi, Operand(rbp, kFunctionOffset));
- __ InvokeFunction(rdi, actual, CALL_FUNCTION);
+ __ InvokeFunction(rdi, actual, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ LeaveInternalFrame();
__ ret(3 * kPointerSize); // remove function, receiver, and arguments
@@ -1324,11 +1354,11 @@ static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
// Push the function on the stack.
__ push(rdi);
- // Preserve the number of arguments on the stack. Must preserve both
- // rax and rbx because these registers are used when copying the
+ // Preserve the number of arguments on the stack. Must preserve rax,
+ // rbx and rcx because these registers are used when copying the
// arguments and the receiver.
- __ Integer32ToSmi(rcx, rax);
- __ push(rcx);
+ __ Integer32ToSmi(r8, rax);
+ __ push(r8);
}
@@ -1352,6 +1382,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : actual number of arguments
// -- rbx : expected number of arguments
+ // -- rcx : call kind information
// -- rdx : code entry to call
// -----------------------------------
@@ -1372,14 +1403,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// Copy receiver and all expected arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(rax, Operand(rbp, rax, times_pointer_size, offset));
- __ Set(rcx, -1); // account for receiver
+ __ Set(r8, -1); // account for receiver
Label copy;
__ bind(&copy);
- __ incq(rcx);
+ __ incq(r8);
__ push(Operand(rax, 0));
__ subq(rax, Immediate(kPointerSize));
- __ cmpq(rcx, rbx);
+ __ cmpq(r8, rbx);
__ j(less, &copy);
__ jmp(&invoke);
}
@@ -1391,23 +1422,23 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// Copy receiver and all actual arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(rdi, Operand(rbp, rax, times_pointer_size, offset));
- __ Set(rcx, -1); // account for receiver
+ __ Set(r8, -1); // account for receiver
Label copy;
__ bind(&copy);
- __ incq(rcx);
+ __ incq(r8);
__ push(Operand(rdi, 0));
__ subq(rdi, Immediate(kPointerSize));
- __ cmpq(rcx, rax);
+ __ cmpq(r8, rax);
__ j(less, &copy);
// Fill remaining expected arguments with undefined values.
Label fill;
__ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
__ bind(&fill);
- __ incq(rcx);
+ __ incq(r8);
__ push(kScratchRegister);
- __ cmpq(rcx, rbx);
+ __ cmpq(r8, rbx);
__ j(less, &fill);
// Restore function pointer.
@@ -1456,17 +1487,17 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
// If the result was -1 it means that we couldn't optimize the
// function. Just return and continue in the unoptimized version.
- NearLabel skip;
+ Label skip;
__ SmiCompare(rax, Smi::FromInt(-1));
- __ j(not_equal, &skip);
+ __ j(not_equal, &skip, Label::kNear);
__ ret(0);
// If we decide not to perform on-stack replacement we perform a
// stack guard check to enable interrupts.
__ bind(&stack_check);
- NearLabel ok;
+ Label ok;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
- __ j(above_equal, &ok);
+ __ j(above_equal, &ok, Label::kNear);
StackCheckStub stub;
__ TailCallStub(&stub);
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index c3653857..7075e66a 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -40,15 +40,15 @@ namespace internal {
void ToNumberStub::Generate(MacroAssembler* masm) {
// The ToNumber stub takes one argument in eax.
- NearLabel check_heap_number, call_builtin;
+ Label check_heap_number, call_builtin;
__ SmiTest(rax);
- __ j(not_zero, &check_heap_number);
+ __ j(not_zero, &check_heap_number, Label::kNear);
__ Ret();
__ bind(&check_heap_number);
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &call_builtin);
+ __ j(not_equal, &call_builtin, Label::kNear);
__ Ret();
__ bind(&call_builtin);
@@ -232,12 +232,28 @@ void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
void ToBooleanStub::Generate(MacroAssembler* masm) {
- NearLabel false_result, true_result, not_string;
+ Label false_result, true_result, not_string;
__ movq(rax, Operand(rsp, 1 * kPointerSize));
+ // undefined -> false
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &false_result);
+
+ // Boolean -> its value
+ __ CompareRoot(rax, Heap::kFalseValueRootIndex);
+ __ j(equal, &false_result);
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ __ j(equal, &true_result);
+
+ // Smis: 0 -> false, all other -> true
+ __ Cmp(rax, Smi::FromInt(0));
+ __ j(equal, &false_result);
+ Condition is_smi = __ CheckSmi(rax);
+ __ j(is_smi, &true_result);
+
// 'null' => false.
__ CompareRoot(rax, Heap::kNullValueRootIndex);
- __ j(equal, &false_result);
+ __ j(equal, &false_result, Label::kNear);
// Get the map and type of the heap object.
// We don't use CmpObjectType because we manipulate the type field.
@@ -247,28 +263,28 @@ void ToBooleanStub::Generate(MacroAssembler* masm) {
// Undetectable => false.
__ movzxbq(rbx, FieldOperand(rdx, Map::kBitFieldOffset));
__ and_(rbx, Immediate(1 << Map::kIsUndetectable));
- __ j(not_zero, &false_result);
+ __ j(not_zero, &false_result, Label::kNear);
// JavaScript object => true.
__ cmpq(rcx, Immediate(FIRST_JS_OBJECT_TYPE));
- __ j(above_equal, &true_result);
+ __ j(above_equal, &true_result, Label::kNear);
// String value => false iff empty.
__ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE));
- __ j(above_equal, &not_string);
+ __ j(above_equal, &not_string, Label::kNear);
__ movq(rdx, FieldOperand(rax, String::kLengthOffset));
__ SmiTest(rdx);
- __ j(zero, &false_result);
- __ jmp(&true_result);
+ __ j(zero, &false_result, Label::kNear);
+ __ jmp(&true_result, Label::kNear);
__ bind(&not_string);
__ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &true_result);
+ __ j(not_equal, &true_result, Label::kNear);
// HeapNumber => false iff +0, -0, or NaN.
// These three cases set the zero flag when compared to zero using ucomisd.
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset));
- __ j(zero, &false_result);
+ __ j(zero, &false_result, Label::kNear);
// Fall through to |true_result|.
// Return 1/0 for true/false in rax.
@@ -322,15 +338,354 @@ class FloatingPointHelper : public AllStatic {
};
-Handle<Code> GetTypeRecordingBinaryOpStub(int key,
- TRBinaryOpIC::TypeInfo type_info,
- TRBinaryOpIC::TypeInfo result_type_info) {
- TypeRecordingBinaryOpStub stub(key, type_info, result_type_info);
+// Get the integer part of a heap number.
+// Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx.
+void IntegerConvert(MacroAssembler* masm,
+ Register result,
+ Register source) {
+ // Result may be rcx. If result and source are the same register, source will
+ // be overwritten.
+ ASSERT(!result.is(rdi) && !result.is(rbx));
+ // TODO(lrn): When type info reaches here, if value is a 32-bit integer, use
+ // cvttsd2si (32-bit version) directly.
+ Register double_exponent = rbx;
+ Register double_value = rdi;
+ Label done, exponent_63_plus;
+ // Get double and extract exponent.
+ __ movq(double_value, FieldOperand(source, HeapNumber::kValueOffset));
+ // Clear result preemptively, in case we need to return zero.
+ __ xorl(result, result);
+ __ movq(xmm0, double_value); // Save copy in xmm0 in case we need it there.
+ // Double to remove sign bit, shift exponent down to least significant bits.
+ // and subtract bias to get the unshifted, unbiased exponent.
+ __ lea(double_exponent, Operand(double_value, double_value, times_1, 0));
+ __ shr(double_exponent, Immediate(64 - HeapNumber::kExponentBits));
+ __ subl(double_exponent, Immediate(HeapNumber::kExponentBias));
+ // Check whether the exponent is too big for a 63 bit unsigned integer.
+ __ cmpl(double_exponent, Immediate(63));
+ __ j(above_equal, &exponent_63_plus, Label::kNear);
+ // Handle exponent range 0..62.
+ __ cvttsd2siq(result, xmm0);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&exponent_63_plus);
+ // Exponent negative or 63+.
+ __ cmpl(double_exponent, Immediate(83));
+ // If exponent negative or above 83, number contains no significant bits in
+ // the range 0..2^31, so result is zero, and rcx already holds zero.
+ __ j(above, &done, Label::kNear);
+
+ // Exponent in rage 63..83.
+ // Mantissa * 2^exponent contains bits in the range 2^0..2^31, namely
+ // the least significant exponent-52 bits.
+
+ // Negate low bits of mantissa if value is negative.
+ __ addq(double_value, double_value); // Move sign bit to carry.
+ __ sbbl(result, result); // And convert carry to -1 in result register.
+ // if scratch2 is negative, do (scratch2-1)^-1, otherwise (scratch2-0)^0.
+ __ addl(double_value, result);
+ // Do xor in opposite directions depending on where we want the result
+ // (depending on whether result is rcx or not).
+
+ if (result.is(rcx)) {
+ __ xorl(double_value, result);
+ // Left shift mantissa by (exponent - mantissabits - 1) to save the
+ // bits that have positional values below 2^32 (the extra -1 comes from the
+ // doubling done above to move the sign bit into the carry flag).
+ __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
+ __ shll_cl(double_value);
+ __ movl(result, double_value);
+ } else {
+ // As the then-branch, but move double-value to result before shifting.
+ __ xorl(result, double_value);
+ __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
+ __ shll_cl(result);
+ }
+
+ __ bind(&done);
+}
+
+
+Handle<Code> GetUnaryOpStub(int key, UnaryOpIC::TypeInfo type_info) {
+ UnaryOpStub stub(key, type_info);
+ return stub.GetCode();
+}
+
+
+void UnaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operand_type_) {
+ case UnaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case UnaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case UnaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case UnaryOpIC::GENERIC:
+ GenerateGenericStub(masm);
+ break;
+ }
+}
+
+
+void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ __ pop(rcx); // Save return address.
+ __ push(rax);
+ // Left and right arguments are now on top.
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ Push(Smi::FromInt(MinorKey()));
+ __ Push(Smi::FromInt(op_));
+ __ Push(Smi::FromInt(operand_type_));
+
+ __ push(rcx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kUnaryOp_Patch),
+ masm->isolate()),
+ 4,
+ 1);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateSmiStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateSmiStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
+ Label slow;
+ GenerateSmiCodeSub(masm, &slow, &slow, Label::kNear, Label::kNear);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
+ Label non_smi;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* slow,
+ Label::Distance non_smi_near,
+ Label::Distance slow_near) {
+ Label done;
+ __ JumpIfNotSmi(rax, non_smi, non_smi_near);
+ __ SmiNeg(rax, rax, &done, Label::kNear);
+ __ jmp(slow, slow_near);
+ __ bind(&done);
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeBitNot(MacroAssembler* masm,
+ Label* non_smi,
+ Label::Distance non_smi_near) {
+ __ JumpIfNotSmi(rax, non_smi, non_smi_near);
+ __ SmiNot(rax, rax);
+ __ ret(0);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateHeapNumberStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateHeapNumberStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
+ Label non_smi, slow, call_builtin;
+ GenerateSmiCodeSub(masm, &non_smi, &call_builtin, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+ __ bind(&call_builtin);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubBitNot(
+ MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
+ Label* slow) {
+ // Check if the operand is a heap number.
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ __ j(not_equal, slow);
+
+ // Operand is a float, negate its value by flipping the sign bit.
+ if (mode_ == UNARY_OVERWRITE) {
+ __ Set(kScratchRegister, 0x01);
+ __ shl(kScratchRegister, Immediate(63));
+ __ xor_(FieldOperand(rax, HeapNumber::kValueOffset), kScratchRegister);
+ } else {
+ // Allocate a heap number before calculating the answer,
+ // so we don't have an untagged double around during GC.
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(rcx, rbx, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(rax);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ movq(rcx, rax);
+ __ pop(rax);
+ __ LeaveInternalFrame();
+ __ bind(&heapnumber_allocated);
+ // rcx: allocated 'empty' number
+
+ // Copy the double value to the new heap number, flipping the sign.
+ __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ Set(kScratchRegister, 0x01);
+ __ shl(kScratchRegister, Immediate(63));
+ __ xor_(rdx, kScratchRegister); // Flip sign.
+ __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx);
+ __ movq(rax, rcx);
+ }
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm,
+ Label* slow) {
+ // Check if the operand is a heap number.
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ __ j(not_equal, slow);
+
+ // Convert the heap number in rax to an untagged integer in rcx.
+ IntegerConvert(masm, rax, rax);
+
+ // Do the bitwise operation and smi tag the result.
+ __ notl(rax);
+ __ Integer32ToSmi(rax, rax);
+ __ ret(0);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateGenericStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateGenericStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &slow, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
+ // Handle the slow case by jumping to the JavaScript builtin.
+ __ pop(rcx); // pop return address
+ __ push(rax);
+ __ push(rcx); // push return address
+ switch (op_) {
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
+ break;
+ case Token::BIT_NOT:
+ __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+const char* UnaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name = NULL; // Make g++ happy.
+ switch (mode_) {
+ case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "UnaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ UnaryOpIC::GetName(operand_type_));
+ return name_;
+}
+
+
+Handle<Code> GetBinaryOpStub(int key,
+ BinaryOpIC::TypeInfo type_info,
+ BinaryOpIC::TypeInfo result_type_info) {
+ BinaryOpStub stub(key, type_info, result_type_info);
return stub.GetCode();
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
__ pop(rcx); // Save return address.
__ push(rdx);
__ push(rax);
@@ -346,36 +701,39 @@ void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
// Patch the caller to an appropriate specialized stub and return the
// operation result to the caller of the stub.
__ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch),
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
masm->isolate()),
5,
1);
}
-void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
+void BinaryOpStub::Generate(MacroAssembler* masm) {
switch (operands_type_) {
- case TRBinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::UNINITIALIZED:
GenerateTypeTransition(masm);
break;
- case TRBinaryOpIC::SMI:
+ case BinaryOpIC::SMI:
GenerateSmiStub(masm);
break;
- case TRBinaryOpIC::INT32:
+ case BinaryOpIC::INT32:
UNREACHABLE();
// The int32 case is identical to the Smi case. We avoid creating this
// ic state on x64.
break;
- case TRBinaryOpIC::HEAP_NUMBER:
+ case BinaryOpIC::HEAP_NUMBER:
GenerateHeapNumberStub(masm);
break;
- case TRBinaryOpIC::ODDBALL:
+ case BinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
- case TRBinaryOpIC::STRING:
+ case BinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
+ case BinaryOpIC::STRING:
GenerateStringStub(masm);
break;
- case TRBinaryOpIC::GENERIC:
+ case BinaryOpIC::GENERIC:
GenerateGeneric(masm);
break;
default:
@@ -384,7 +742,7 @@ void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
}
-const char* TypeRecordingBinaryOpStub::GetName() {
+const char* BinaryOpStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
@@ -400,19 +758,20 @@ const char* TypeRecordingBinaryOpStub::GetName() {
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "TypeRecordingBinaryOpStub_%s_%s_%s",
+ "BinaryOpStub_%s_%s_%s",
op_name,
overwrite_name,
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
return name_;
}
-void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+void BinaryOpStub::GenerateSmiCode(
+ MacroAssembler* masm,
Label* slow,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
- // Arguments to TypeRecordingBinaryOpStub are in rdx and rax.
+ // Arguments to BinaryOpStub are in rdx and rax.
Register left = rdx;
Register right = rax;
@@ -558,10 +917,9 @@ void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
}
-void TypeRecordingBinaryOpStub::GenerateFloatingPointCode(
- MacroAssembler* masm,
- Label* allocation_failure,
- Label* non_numeric_failure) {
+void BinaryOpStub::GenerateFloatingPointCode(MacroAssembler* masm,
+ Label* allocation_failure,
+ Label* non_numeric_failure) {
switch (op_) {
case Token::ADD:
case Token::SUB:
@@ -660,32 +1018,32 @@ void TypeRecordingBinaryOpStub::GenerateFloatingPointCode(
// No fall-through from this generated code.
if (FLAG_debug_code) {
__ Abort("Unexpected fall-through in "
- "TypeRecordingBinaryStub::GenerateFloatingPointCode.");
+ "BinaryStub::GenerateFloatingPointCode.");
}
}
-void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
+void BinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
ASSERT(op_ == Token::ADD);
- NearLabel left_not_string, call_runtime;
+ Label left_not_string, call_runtime;
// Registers containing left and right operands respectively.
Register left = rdx;
Register right = rax;
// Test if left operand is a string.
- __ JumpIfSmi(left, &left_not_string);
+ __ JumpIfSmi(left, &left_not_string, Label::kNear);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
- __ j(above_equal, &left_not_string);
+ __ j(above_equal, &left_not_string, Label::kNear);
StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_left_stub);
// Left operand is not a string, test right.
__ bind(&left_not_string);
- __ JumpIfSmi(right, &call_runtime);
+ __ JumpIfSmi(right, &call_runtime, Label::kNear);
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
- __ j(above_equal, &call_runtime);
+ __ j(above_equal, &call_runtime, Label::kNear);
StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
GenerateRegisterArgsPush(masm);
@@ -696,7 +1054,7 @@ void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateCallRuntimeCode(MacroAssembler* masm) {
+void BinaryOpStub::GenerateCallRuntimeCode(MacroAssembler* masm) {
GenerateRegisterArgsPush(masm);
switch (op_) {
case Token::ADD:
@@ -738,10 +1096,10 @@ void TypeRecordingBinaryOpStub::GenerateCallRuntimeCode(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
Label call_runtime;
- if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
- result_type_ == TRBinaryOpIC::SMI) {
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
// Only allow smi results.
GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS);
} else {
@@ -761,17 +1119,47 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
- ASSERT(operands_type_ == TRBinaryOpIC::STRING);
+void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::STRING);
ASSERT(op_ == Token::ADD);
GenerateStringAddCode(masm);
// Try to add arguments as strings, otherwise, transition to the generic
- // TRBinaryOpIC type.
+ // BinaryOpIC type.
GenerateTypeTransition(masm);
}
-void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
Label call_runtime;
if (op_ == Token::ADD) {
@@ -781,18 +1169,18 @@ void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
}
// Convert oddball arguments to numbers.
- NearLabel check, done;
+ Label check, done;
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &check);
+ __ j(not_equal, &check, Label::kNear);
if (Token::IsBitOp(op_)) {
__ xor_(rdx, rdx);
} else {
__ LoadRoot(rdx, Heap::kNanValueRootIndex);
}
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&check);
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
if (Token::IsBitOp(op_)) {
__ xor_(rax, rax);
} else {
@@ -804,7 +1192,7 @@ void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
Label gc_required, not_number;
GenerateFloatingPointCode(masm, &gc_required, &not_number);
@@ -816,7 +1204,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
Label call_runtime, call_string_add_or_runtime;
GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
@@ -833,9 +1221,8 @@ void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
}
-void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
- MacroAssembler* masm,
- Label* alloc_failure) {
+void BinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm,
+ Label* alloc_failure) {
Label skip_allocation;
OverwriteMode mode = mode_;
switch (mode) {
@@ -873,7 +1260,7 @@ void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
}
-void TypeRecordingBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
__ pop(rcx);
__ push(rdx);
__ push(rax);
@@ -900,11 +1287,10 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
Label skip_cache;
const bool tagged = (argument_type_ == TAGGED);
if (tagged) {
- NearLabel input_not_smi;
- NearLabel loaded;
+ Label input_not_smi, loaded;
// Test that rax is a number.
__ movq(rax, Operand(rsp, kPointerSize));
- __ JumpIfNotSmi(rax, &input_not_smi);
+ __ JumpIfNotSmi(rax, &input_not_smi, Label::kNear);
// Input is a smi. Untag and load it onto the FPU stack.
// Then load the bits of the double into rbx.
__ SmiToInteger32(rax, rax);
@@ -915,7 +1301,7 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
__ movq(rdx, xmm1);
__ fld_d(Operand(rsp, 0));
__ addq(rsp, Immediate(kDoubleSize));
- __ jmp(&loaded);
+ __ jmp(&loaded, Label::kNear);
__ bind(&input_not_smi);
// Check if input is a HeapNumber.
@@ -990,9 +1376,9 @@ void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
__ addl(rcx, rcx);
__ lea(rcx, Operand(rax, rcx, times_8, 0));
// Check if cache matches: Double value is stored in uint32_t[2] array.
- NearLabel cache_miss;
+ Label cache_miss;
__ cmpq(rbx, Operand(rcx, 0));
- __ j(not_equal, &cache_miss);
+ __ j(not_equal, &cache_miss, Label::kNear);
// Cache hit!
__ movq(rax, Operand(rcx, 2 * kIntSize));
if (tagged) {
@@ -1100,8 +1486,8 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
__ j(below, &in_range);
// Check for infinity and NaN. Both return NaN for sin.
__ cmpl(rdi, Immediate(0x7ff));
- NearLabel non_nan_result;
- __ j(not_equal, &non_nan_result);
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, Label::kNear);
// Input is +/-Infinity or NaN. Result is NaN.
__ fstp(0);
__ LoadRoot(kScratchRegister, Heap::kNanValueRootIndex);
@@ -1129,7 +1515,7 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
// Compute st(0) % st(1)
{
- NearLabel partial_remainder_loop;
+ Label partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
@@ -1166,74 +1552,6 @@ void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
}
-// Get the integer part of a heap number.
-// Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx.
-void IntegerConvert(MacroAssembler* masm,
- Register result,
- Register source) {
- // Result may be rcx. If result and source are the same register, source will
- // be overwritten.
- ASSERT(!result.is(rdi) && !result.is(rbx));
- // TODO(lrn): When type info reaches here, if value is a 32-bit integer, use
- // cvttsd2si (32-bit version) directly.
- Register double_exponent = rbx;
- Register double_value = rdi;
- NearLabel done, exponent_63_plus;
- // Get double and extract exponent.
- __ movq(double_value, FieldOperand(source, HeapNumber::kValueOffset));
- // Clear result preemptively, in case we need to return zero.
- __ xorl(result, result);
- __ movq(xmm0, double_value); // Save copy in xmm0 in case we need it there.
- // Double to remove sign bit, shift exponent down to least significant bits.
- // and subtract bias to get the unshifted, unbiased exponent.
- __ lea(double_exponent, Operand(double_value, double_value, times_1, 0));
- __ shr(double_exponent, Immediate(64 - HeapNumber::kExponentBits));
- __ subl(double_exponent, Immediate(HeapNumber::kExponentBias));
- // Check whether the exponent is too big for a 63 bit unsigned integer.
- __ cmpl(double_exponent, Immediate(63));
- __ j(above_equal, &exponent_63_plus);
- // Handle exponent range 0..62.
- __ cvttsd2siq(result, xmm0);
- __ jmp(&done);
-
- __ bind(&exponent_63_plus);
- // Exponent negative or 63+.
- __ cmpl(double_exponent, Immediate(83));
- // If exponent negative or above 83, number contains no significant bits in
- // the range 0..2^31, so result is zero, and rcx already holds zero.
- __ j(above, &done);
-
- // Exponent in rage 63..83.
- // Mantissa * 2^exponent contains bits in the range 2^0..2^31, namely
- // the least significant exponent-52 bits.
-
- // Negate low bits of mantissa if value is negative.
- __ addq(double_value, double_value); // Move sign bit to carry.
- __ sbbl(result, result); // And convert carry to -1 in result register.
- // if scratch2 is negative, do (scratch2-1)^-1, otherwise (scratch2-0)^0.
- __ addl(double_value, result);
- // Do xor in opposite directions depending on where we want the result
- // (depending on whether result is rcx or not).
-
- if (result.is(rcx)) {
- __ xorl(double_value, result);
- // Left shift mantissa by (exponent - mantissabits - 1) to save the
- // bits that have positional values below 2^32 (the extra -1 comes from the
- // doubling done above to move the sign bit into the carry flag).
- __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
- __ shll_cl(double_value);
- __ movl(result, double_value);
- } else {
- // As the then-branch, but move double-value to result before shifting.
- __ xorl(result, double_value);
- __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
- __ shll_cl(result);
- }
-
- __ bind(&done);
-}
-
-
// Input: rdx, rax are the left and right objects of a bit op.
// Output: rax, rcx are left and right integers for a bit op.
void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm) {
@@ -1390,8 +1708,8 @@ void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- NearLabel first_smi, check_second;
- __ JumpIfSmi(first, &first_smi);
+ Label first_smi;
+ __ JumpIfSmi(first, &first_smi, Label::kNear);
__ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map);
__ j(not_equal, on_not_smis);
// Convert HeapNumber to smi if possible.
@@ -1406,7 +1724,6 @@ void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
__ j(not_equal, on_not_smis);
__ Integer32ToSmi(first, smi_result);
- __ bind(&check_second);
__ JumpIfSmi(second, (on_success != NULL) ? on_success : &done);
__ bind(&first_smi);
if (FLAG_debug_code) {
@@ -1432,91 +1749,6 @@ void FloatingPointHelper::NumbersToSmis(MacroAssembler* masm,
}
-void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
- Label slow, done;
-
- if (op_ == Token::SUB) {
- if (include_smi_code_) {
- // Check whether the value is a smi.
- Label try_float;
- __ JumpIfNotSmi(rax, &try_float);
- if (negative_zero_ == kIgnoreNegativeZero) {
- __ SmiCompare(rax, Smi::FromInt(0));
- __ j(equal, &done);
- }
- __ SmiNeg(rax, rax, &done);
- __ jmp(&slow); // zero, if not handled above, and Smi::kMinValue.
-
- // Try floating point case.
- __ bind(&try_float);
- } else if (FLAG_debug_code) {
- __ AbortIfSmi(rax);
- }
-
- __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &slow);
- // Operand is a float, negate its value by flipping sign bit.
- __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
- __ Set(kScratchRegister, 0x01);
- __ shl(kScratchRegister, Immediate(63));
- __ xor_(rdx, kScratchRegister); // Flip sign.
- // rdx is value to store.
- if (overwrite_ == UNARY_OVERWRITE) {
- __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rdx);
- } else {
- __ AllocateHeapNumber(rcx, rbx, &slow);
- // rcx: allocated 'empty' number
- __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx);
- __ movq(rax, rcx);
- }
- } else if (op_ == Token::BIT_NOT) {
- if (include_smi_code_) {
- Label try_float;
- __ JumpIfNotSmi(rax, &try_float);
- __ SmiNot(rax, rax);
- __ jmp(&done);
- // Try floating point case.
- __ bind(&try_float);
- } else if (FLAG_debug_code) {
- __ AbortIfSmi(rax);
- }
-
- // Check if the operand is a heap number.
- __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &slow);
-
- // Convert the heap number in rax to an untagged integer in rcx.
- IntegerConvert(masm, rax, rax);
-
- // Do the bitwise operation and smi tag the result.
- __ notl(rax);
- __ Integer32ToSmi(rax, rax);
- }
-
- // Return from the stub.
- __ bind(&done);
- __ StubReturn(1);
-
- // Handle the slow case by jumping to the JavaScript builtin.
- __ bind(&slow);
- __ pop(rcx); // pop return address
- __ push(rax);
- __ push(rcx); // push return address
- switch (op_) {
- case Token::SUB:
- __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
- break;
- case Token::BIT_NOT:
- __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
void MathPowStub::Generate(MacroAssembler* masm) {
// Registers are used as follows:
// rdx = base
@@ -1562,20 +1794,20 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ movq(rdx, rax);
// Get absolute value of exponent.
- NearLabel no_neg;
+ Label no_neg;
__ cmpl(rax, Immediate(0));
- __ j(greater_equal, &no_neg);
+ __ j(greater_equal, &no_neg, Label::kNear);
__ negl(rax);
__ bind(&no_neg);
// Load xmm1 with 1.
- __ movsd(xmm1, xmm3);
- NearLabel while_true;
- NearLabel no_multiply;
+ __ movaps(xmm1, xmm3);
+ Label while_true;
+ Label no_multiply;
__ bind(&while_true);
__ shrl(rax, Immediate(1));
- __ j(not_carry, &no_multiply);
+ __ j(not_carry, &no_multiply, Label::kNear);
__ mulsd(xmm1, xmm0);
__ bind(&no_multiply);
__ mulsd(xmm0, xmm0);
@@ -1587,8 +1819,8 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ j(positive, &allocate_return);
// Special case if xmm1 has reached infinity.
__ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ xorpd(xmm0, xmm0);
+ __ movaps(xmm1, xmm3);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, xmm1);
__ j(equal, &call_runtime);
@@ -1605,12 +1837,11 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ ucomisd(xmm1, xmm1);
__ j(parity_even, &call_runtime);
- NearLabel base_not_smi;
- NearLabel handle_special_cases;
- __ JumpIfNotSmi(rdx, &base_not_smi);
+ Label base_not_smi, handle_special_cases;
+ __ JumpIfNotSmi(rdx, &base_not_smi, Label::kNear);
__ SmiToInteger32(rdx, rdx);
__ cvtlsi2sd(xmm0, rdx);
- __ jmp(&handle_special_cases);
+ __ jmp(&handle_special_cases, Label::kNear);
__ bind(&base_not_smi);
__ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset),
@@ -1625,22 +1856,22 @@ void MathPowStub::Generate(MacroAssembler* masm) {
// base is in xmm0 and exponent is in xmm1.
__ bind(&handle_special_cases);
- NearLabel not_minus_half;
+ Label not_minus_half;
// Test for -0.5.
// Load xmm2 with -0.5.
__ movq(rcx, V8_UINT64_C(0xBFE0000000000000), RelocInfo::NONE);
__ movq(xmm2, rcx);
// xmm2 now has -0.5.
__ ucomisd(xmm2, xmm1);
- __ j(not_equal, &not_minus_half);
+ __ j(not_equal, &not_minus_half, Label::kNear);
// Calculates reciprocal of square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
+ __ xorps(xmm1, xmm1);
__ addsd(xmm1, xmm0);
__ sqrtsd(xmm1, xmm1);
__ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
+ __ movaps(xmm1, xmm3);
__ jmp(&allocate_return);
// Test for 0.5.
@@ -1653,8 +1884,8 @@ void MathPowStub::Generate(MacroAssembler* masm) {
__ j(not_equal, &call_runtime);
// Calculates square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
+ __ xorps(xmm1, xmm1);
+ __ addsd(xmm1, xmm0); // Convert -0 to 0.
__ sqrtsd(xmm1, xmm1);
__ bind(&allocate_return);
@@ -1951,7 +2182,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// rax: RegExp data (FixedArray)
// Check the representation and encoding of the subject string.
- NearLabel seq_ascii_string, seq_two_byte_string, check_code;
+ Label seq_ascii_string, seq_two_byte_string, check_code;
__ movq(rdi, Operand(rsp, kSubjectOffset));
__ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
@@ -1959,10 +2190,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ andb(rbx, Immediate(
kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask));
STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
- __ j(zero, &seq_two_byte_string);
+ __ j(zero, &seq_two_byte_string, Label::kNear);
// Any other flat string must be a flat ascii string.
__ testb(rbx, Immediate(kIsNotStringMask | kStringRepresentationMask));
- __ j(zero, &seq_ascii_string);
+ __ j(zero, &seq_ascii_string, Label::kNear);
// Check for flat cons string.
// A flat cons string is a cons string where the second part is the empty
@@ -1986,7 +2217,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ testb(FieldOperand(rbx, Map::kInstanceTypeOffset),
Immediate(kStringRepresentationMask | kStringEncodingMask));
STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
- __ j(zero, &seq_two_byte_string);
+ __ j(zero, &seq_two_byte_string, Label::kNear);
// Any other flat string must be ascii.
__ testb(FieldOperand(rbx, Map::kInstanceTypeOffset),
Immediate(kStringRepresentationMask));
@@ -1997,7 +2228,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// rax: RegExp data (FixedArray)
__ movq(r11, FieldOperand(rax, JSRegExp::kDataAsciiCodeOffset));
__ Set(rcx, 1); // Type is ascii.
- __ jmp(&check_code);
+ __ jmp(&check_code, Label::kNear);
__ bind(&seq_two_byte_string);
// rdi: subject string (flat two-byte)
@@ -2008,9 +2239,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ bind(&check_code);
// Check that the irregexp code has been generated for the actual string
// encoding. If it has, the field contains a code object otherwise it contains
- // the hole.
- __ CmpObjectType(r11, CODE_TYPE, kScratchRegister);
- __ j(not_equal, &runtime);
+ // smi (code flushing support)
+ __ JumpIfSmi(r11, &runtime);
// rdi: subject string
// rcx: encoding of subject string (1 if ascii, 0 if two_byte);
@@ -2083,13 +2313,13 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// Argument 4: End of string data
// Argument 3: Start of string data
- NearLabel setup_two_byte, setup_rest;
+ Label setup_two_byte, setup_rest;
__ testb(rcx, rcx); // Last use of rcx as encoding of subject string.
- __ j(zero, &setup_two_byte);
+ __ j(zero, &setup_two_byte, Label::kNear);
__ SmiToInteger32(rcx, FieldOperand(rdi, String::kLengthOffset));
__ lea(arg4, FieldOperand(rdi, rcx, times_1, SeqAsciiString::kHeaderSize));
__ lea(arg3, FieldOperand(rdi, rbx, times_1, SeqAsciiString::kHeaderSize));
- __ jmp(&setup_rest);
+ __ jmp(&setup_rest, Label::kNear);
__ bind(&setup_two_byte);
__ SmiToInteger32(rcx, FieldOperand(rdi, String::kLengthOffset));
__ lea(arg4, FieldOperand(rdi, rcx, times_2, SeqTwoByteString::kHeaderSize));
@@ -2114,10 +2344,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ LeaveApiExitFrame();
// Check the result.
- NearLabel success;
+ Label success;
Label exception;
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS));
- __ j(equal, &success);
+ __ j(equal, &success, Label::kNear);
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION));
__ j(equal, &exception);
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE));
@@ -2166,12 +2396,12 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// rbx: last_match_info backing store (FixedArray)
// rcx: offsets vector
// rdx: number of capture registers
- NearLabel next_capture, done;
+ Label next_capture, done;
// Capture register counter starts from number of capture registers and
// counts down until wraping after zero.
__ bind(&next_capture);
__ subq(rdx, Immediate(1));
- __ j(negative, &done);
+ __ j(negative, &done, Label::kNear);
// Read the value from the static offsets vector buffer and make it a smi.
__ movl(rdi, Operand(rcx, rdx, times_int_size, 0));
__ Integer32ToSmi(rdi, rdi);
@@ -2204,8 +2434,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ movq(pending_exception_operand, rdx);
__ CompareRoot(rax, Heap::kTerminationExceptionRootIndex);
- NearLabel termination_exception;
- __ j(equal, &termination_exception);
+ Label termination_exception;
+ __ j(equal, &termination_exception, Label::kNear);
__ Throw(rax);
__ bind(&termination_exception);
@@ -2330,9 +2560,13 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
// Heap::GetNumberStringCache.
Label is_smi;
Label load_result_from_cache;
+ Factory* factory = masm->isolate()->factory();
if (!object_is_smi) {
__ JumpIfSmi(object, &is_smi);
- __ CheckMap(object, FACTORY->heap_number_map(), not_found, true);
+ __ CheckMap(object,
+ factory->heap_number_map(),
+ not_found,
+ DONT_DO_SMI_CHECK);
STATIC_ASSERT(8 == kDoubleSize);
__ movl(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
@@ -2419,6 +2653,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
Label check_unequal_objects, done;
+ Factory* factory = masm->isolate()->factory();
// Compare two smis if required.
if (include_smi_compare_) {
@@ -2446,16 +2681,16 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Two identical objects are equal unless they are both NaN or undefined.
{
- NearLabel not_identical;
+ Label not_identical;
__ cmpq(rax, rdx);
- __ j(not_equal, &not_identical);
+ __ j(not_equal, &not_identical, Label::kNear);
if (cc_ != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
- NearLabel check_for_nan;
+ Label check_for_nan;
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &check_for_nan);
+ __ j(not_equal, &check_for_nan, Label::kNear);
__ Set(rax, NegativeComparisonResult(cc_));
__ ret(0);
__ bind(&check_for_nan);
@@ -2466,20 +2701,19 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Note: if cc_ != equal, never_nan_nan_ is not used.
// We cannot set rax to EQUAL until just before return because
// rax must be unchanged on jump to not_identical.
-
if (never_nan_nan_ && (cc_ == equal)) {
__ Set(rax, EQUAL);
__ ret(0);
} else {
- NearLabel heap_number;
+ Label heap_number;
// If it's not a heap number, then return equal for (in)equality operator.
__ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- __ j(equal, &heap_number);
+ factory->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
if (cc_ != equal) {
// Call runtime on identical JSObjects. Otherwise return equal.
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(above_equal, &not_identical);
+ __ j(above_equal, &not_identical, Label::kNear);
}
__ Set(rax, EQUAL);
__ ret(0);
@@ -2519,7 +2753,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Check if the non-smi operand is a heap number.
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
+ factory->heap_number_map());
// If heap number, handle it in the slow case.
__ j(equal, &slow);
// Return non-equal. ebx (the lower half of rbx) is not zero.
@@ -2535,9 +2769,9 @@ void CompareStub::Generate(MacroAssembler* masm) {
// If the first object is a JS object, we have done pointer comparison.
STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- NearLabel first_non_object;
+ Label first_non_object;
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &first_non_object);
+ __ j(below, &first_non_object, Label::kNear);
// Return non-zero (eax (not rax) is not zero)
Label return_not_equal;
STATIC_ASSERT(kHeapObjectTag != 0);
@@ -2564,14 +2798,14 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Generate the number comparison code.
if (include_number_compare_) {
Label non_number_comparison;
- NearLabel unordered;
+ Label unordered;
FloatingPointHelper::LoadSSE2UnknownOperands(masm, &non_number_comparison);
__ xorl(rax, rax);
__ xorl(rcx, rcx);
__ ucomisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered);
+ __ j(parity_even, &unordered, Label::kNear);
// Return a result of -1, 0, or 1, based on EFLAGS.
__ setcc(above, rax);
__ setcc(below, rcx);
@@ -2611,13 +2845,21 @@ void CompareStub::Generate(MacroAssembler* masm) {
rdx, rax, rcx, rbx, &check_unequal_objects);
// Inline comparison of ascii strings.
- StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ if (cc_ == equal) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
rdx,
rax,
rcx,
- rbx,
- rdi,
- r8);
+ rbx);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx,
+ rdi,
+ r8);
+ }
#ifdef DEBUG
__ Abort("Unexpected fall-through from string comparison");
@@ -2628,7 +2870,7 @@ void CompareStub::Generate(MacroAssembler* masm) {
// Not strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
- NearLabel not_both_objects, return_unequal;
+ Label not_both_objects, return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
// a heap object has the low bit clear.
@@ -2636,17 +2878,17 @@ void CompareStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT(kSmiTagMask == 1);
__ lea(rcx, Operand(rax, rdx, times_1, 0));
__ testb(rcx, Immediate(kSmiTagMask));
- __ j(not_zero, &not_both_objects);
+ __ j(not_zero, &not_both_objects, Label::kNear);
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx);
- __ j(below, &not_both_objects);
+ __ j(below, &not_both_objects, Label::kNear);
__ CmpObjectType(rdx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &not_both_objects);
+ __ j(below, &not_both_objects, Label::kNear);
__ testb(FieldOperand(rbx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
- __ j(zero, &return_unequal);
+ __ j(zero, &return_unequal, Label::kNear);
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
- __ j(zero, &return_unequal);
+ __ j(zero, &return_unequal, Label::kNear);
// The objects are both undetectable, so they both compare as the value
// undefined, and are equal.
__ Set(rax, EQUAL);
@@ -2704,30 +2946,22 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow;
- // If the receiver might be a value (string, number or boolean) check for this
- // and box it if it is.
- if (ReceiverMightBeValue()) {
+ // The receiver might implicitly be the global object. This is
+ // indicated by passing the hole as the receiver to the call
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
// Get the receiver from the stack.
// +1 ~ return address
- Label receiver_is_value, receiver_is_js_object;
__ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
-
- // Check if receiver is a smi (which is a number value).
- __ JumpIfSmi(rax, &receiver_is_value);
-
- // Check if the receiver is a valid JS object.
- __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rdi);
- __ j(above_equal, &receiver_is_js_object);
-
- // Call the runtime to box the value.
- __ bind(&receiver_is_value);
- __ EnterInternalFrame();
- __ push(rax);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
- __ LeaveInternalFrame();
- __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rax);
-
- __ bind(&receiver_is_js_object);
+ // Call as function is indicated with the hole.
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &call, Label::kNear);
+ // Patch the receiver on the stack with the global receiver object.
+ __ movq(rbx, GlobalObjectOperand());
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rbx);
+ __ bind(&call);
}
// Get the function to call from the stack.
@@ -2742,7 +2976,23 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Fast-case: Just invoke the function.
ParameterCount actual(argc_);
- __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &call_as_function);
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
// Slow-case: Non-function called.
__ bind(&slow);
@@ -2876,11 +3126,11 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
// Handling of failure.
__ bind(&failure_returned);
- NearLabel retry;
+ Label retry;
// If the returned exception is RETRY_AFTER_GC continue at retry label
STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
__ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
- __ j(zero, &retry);
+ __ j(zero, &retry, Label::kNear);
// Special handling of out of memory exceptions.
__ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
@@ -3180,11 +3430,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// real lookup and update the call site cache.
if (!HasCallSiteInlineCheck()) {
// Look up the function and the map in the instanceof cache.
- NearLabel miss;
+ Label miss;
__ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
__ ret(2 * kPointerSize);
__ bind(&miss);
@@ -3220,15 +3470,15 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
// Loop through the prototype chain looking for the function prototype.
- NearLabel loop, is_instance, is_not_instance;
+ Label loop, is_instance, is_not_instance;
__ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
__ bind(&loop);
__ cmpq(rcx, rbx);
- __ j(equal, &is_instance);
+ __ j(equal, &is_instance, Label::kNear);
__ cmpq(rcx, kScratchRegister);
// The code at is_not_instance assumes that kScratchRegister contains a
// non-zero GCable value (the null object in this case).
- __ j(equal, &is_not_instance);
+ __ j(equal, &is_not_instance, Label::kNear);
__ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
__ movq(rcx, FieldOperand(rcx, Map::kPrototypeOffset));
__ jmp(&loop);
@@ -3449,10 +3699,14 @@ void StringCharCodeAtGenerator::GenerateSlow(
MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
__ Abort("Unexpected fallthrough to CharCodeAt slow case");
+ Factory* factory = masm->isolate()->factory();
// Index is not a smi.
__ bind(&index_not_smi_);
// If index is a heap number, try converting it to an integer.
- __ CheckMap(index_, FACTORY->heap_number_map(), index_not_number_, true);
+ __ CheckMap(index_,
+ factory->heap_number_map(),
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
call_helper.BeforeCall(masm);
__ push(object_);
__ push(index_);
@@ -3592,10 +3846,10 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// rax: first string
// rdx: second string
// Check if either of the strings are empty. In that case return the other.
- NearLabel second_not_zero_length, both_not_zero_length;
+ Label second_not_zero_length, both_not_zero_length;
__ movq(rcx, FieldOperand(rdx, String::kLengthOffset));
__ SmiTest(rcx);
- __ j(not_zero, &second_not_zero_length);
+ __ j(not_zero, &second_not_zero_length, Label::kNear);
// Second string is empty, result is first string which is already in rax.
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->string_add_native(), 1);
@@ -3603,7 +3857,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ bind(&second_not_zero_length);
__ movq(rbx, FieldOperand(rax, String::kLengthOffset));
__ SmiTest(rbx);
- __ j(not_zero, &both_not_zero_length);
+ __ j(not_zero, &both_not_zero_length, Label::kNear);
// First string is empty, result is second string which is in rdx.
__ movq(rax, rdx);
__ IncrementCounter(counters->string_add_native(), 1);
@@ -3897,9 +4151,9 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
ASSERT(count.is(rcx)); // rep movs count
// Nothing to do for zero characters.
- NearLabel done;
+ Label done;
__ testl(count, count);
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// Make count the number of bytes to copy.
if (!ascii) {
@@ -3908,9 +4162,9 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
}
// Don't enter the rep movs if there are less than 4 bytes to copy.
- NearLabel last_bytes;
+ Label last_bytes;
__ testl(count, Immediate(~7));
- __ j(zero, &last_bytes);
+ __ j(zero, &last_bytes, Label::kNear);
// Copy from edi to esi using rep movs instruction.
__ movl(kScratchRegister, count);
@@ -3924,7 +4178,7 @@ void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
// Check if there are more bytes to copy.
__ bind(&last_bytes);
__ testl(count, count);
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// Copy remaining characters.
Label loop;
@@ -3952,10 +4206,10 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
// Make sure that both characters are not digits as such strings has a
// different hash algorithm. Don't try to look for these in the symbol table.
- NearLabel not_array_index;
+ Label not_array_index;
__ leal(scratch, Operand(c1, -'0'));
__ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
- __ j(above, &not_array_index);
+ __ j(above, &not_array_index, Label::kNear);
__ leal(scratch, Operand(c2, -'0'));
__ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
__ j(below_equal, not_found);
@@ -4017,9 +4271,9 @@ void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
SymbolTable::kElementsStartOffset));
// If entry is undefined no string with this hash can be found.
- NearLabel is_string;
+ Label is_string;
__ CmpObjectType(candidate, ODDBALL_TYPE, map);
- __ j(not_equal, &is_string);
+ __ j(not_equal, &is_string, Label::kNear);
__ CompareRoot(candidate, Heap::kUndefinedValueRootIndex);
__ j(equal, not_found);
@@ -4263,6 +4517,47 @@ void SubStringStub::Generate(MacroAssembler* masm) {
}
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label check_zero_length;
+ __ movq(length, FieldOperand(left, String::kLengthOffset));
+ __ SmiCompare(length, FieldOperand(right, String::kLengthOffset));
+ __ j(equal, &check_zero_length, Label::kNear);
+ __ Move(rax, Smi::FromInt(NOT_EQUAL));
+ __ ret(0);
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ SmiTest(length);
+ __ j(not_zero, &compare_chars, Label::kNear);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ Label strings_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
+ &strings_not_equal, Label::kNear);
+
+ // Characters are equal.
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Characters are not equal.
+ __ bind(&strings_not_equal);
+ __ Move(rax, Smi::FromInt(NOT_EQUAL));
+ __ ret(0);
+}
+
+
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -4282,8 +4577,8 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
FieldOperand(right, String::kLengthOffset));
// Register scratch4 now holds left.length - right.length.
const Register length_difference = scratch4;
- NearLabel left_shorter;
- __ j(less, &left_shorter);
+ Label left_shorter;
+ __ j(less, &left_shorter, Label::kNear);
// The right string isn't longer that the left one.
// Get the right string's length by subtracting the (non-negative) difference
// from the left string's length.
@@ -4292,54 +4587,30 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
// Register scratch1 now holds Min(left.length, right.length).
const Register min_length = scratch1;
- NearLabel compare_lengths;
+ Label compare_lengths;
// If min-length is zero, go directly to comparing lengths.
__ SmiTest(min_length);
- __ j(zero, &compare_lengths);
+ __ j(zero, &compare_lengths, Label::kNear);
- __ SmiToInteger32(min_length, min_length);
+ // Compare loop.
+ Label result_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
+ &result_not_equal, Label::kNear);
- // Registers scratch2 and scratch3 are free.
- NearLabel result_not_equal;
- Label loop;
- {
- // Check characters 0 .. min_length - 1 in a loop.
- // Use scratch3 as loop index, min_length as limit and scratch2
- // for computation.
- const Register index = scratch3;
- __ Set(index, 0); // Index into strings.
- __ bind(&loop);
- // Compare characters.
- // TODO(lrn): Could we load more than one character at a time?
- __ movb(scratch2, FieldOperand(left,
- index,
- times_1,
- SeqAsciiString::kHeaderSize));
- // Increment index and use -1 modifier on next load to give
- // the previous load extra time to complete.
- __ addl(index, Immediate(1));
- __ cmpb(scratch2, FieldOperand(right,
- index,
- times_1,
- SeqAsciiString::kHeaderSize - 1));
- __ j(not_equal, &result_not_equal);
- __ cmpl(index, min_length);
- __ j(not_equal, &loop);
- }
// Completed loop without finding different characters.
// Compare lengths (precomputed).
__ bind(&compare_lengths);
__ SmiTest(length_difference);
- __ j(not_zero, &result_not_equal);
+ __ j(not_zero, &result_not_equal, Label::kNear);
// Result is EQUAL.
__ Move(rax, Smi::FromInt(EQUAL));
__ ret(0);
- NearLabel result_greater;
+ Label result_greater;
__ bind(&result_not_equal);
// Unequal comparison of left to right, either character or length.
- __ j(greater, &result_greater);
+ __ j(greater, &result_greater, Label::kNear);
// Result is LESS.
__ Move(rax, Smi::FromInt(LESS));
@@ -4352,6 +4623,36 @@ void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
}
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance near_jump) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiToInteger32(length, length);
+ __ lea(left,
+ FieldOperand(left, length, times_1, SeqAsciiString::kHeaderSize));
+ __ lea(right,
+ FieldOperand(right, length, times_1, SeqAsciiString::kHeaderSize));
+ __ neg(length);
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ movb(scratch, Operand(left, index, times_1, 0));
+ __ cmpb(scratch, Operand(right, index, times_1, 0));
+ __ j(not_equal, chars_not_equal, near_jump);
+ __ addq(index, Immediate(1));
+ __ j(not_zero, &loop);
+}
+
+
void StringCompareStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -4364,9 +4665,9 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
__ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
// Check for identity.
- NearLabel not_same;
+ Label not_same;
__ cmpq(rdx, rax);
- __ j(not_equal, &not_same);
+ __ j(not_equal, &not_same, Label::kNear);
__ Move(rax, Smi::FromInt(EQUAL));
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->string_compare_native(), 1);
@@ -4394,16 +4695,16 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::SMIS);
- NearLabel miss;
- __ JumpIfNotBothSmi(rdx, rax, &miss);
+ Label miss;
+ __ JumpIfNotBothSmi(rdx, rax, &miss, Label::kNear);
if (GetCondition() == equal) {
// For equality we do not care about the sign of the result.
__ subq(rax, rdx);
} else {
- NearLabel done;
+ Label done;
__ subq(rdx, rax);
- __ j(no_overflow, &done);
+ __ j(no_overflow, &done, Label::kNear);
// Correct sign of result in case of overflow.
__ SmiNot(rdx, rdx);
__ bind(&done);
@@ -4419,16 +4720,16 @@ void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::HEAP_NUMBERS);
- NearLabel generic_stub;
- NearLabel unordered;
- NearLabel miss;
+ Label generic_stub;
+ Label unordered;
+ Label miss;
Condition either_smi = masm->CheckEitherSmi(rax, rdx);
- __ j(either_smi, &generic_stub);
+ __ j(either_smi, &generic_stub, Label::kNear);
__ CmpObjectType(rax, HEAP_NUMBER_TYPE, rcx);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
// Load left and right operand
__ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
@@ -4438,7 +4739,7 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
__ ucomisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered);
+ __ j(parity_even, &unordered, Label::kNear);
// Return a result of -1, 0, or 1, based on EFLAGS.
// Performing mov, because xor would destroy the flag register.
@@ -4459,16 +4760,133 @@ void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
}
+void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SYMBOLS);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss, Label::kNear);
+
+ // Check that both operands are symbols.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &miss, Label::kNear);
+
+ // Symbols are compared by identity.
+ Label done;
+ __ cmpq(left, right);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRINGS);
+ ASSERT(GetCondition() == equal);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+ Register tmp3 = rdi;
+
+ // Check that both operands are heap objects.
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ __ movq(tmp3, tmp1);
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ or_(tmp3, tmp2);
+ __ testb(tmp3, Immediate(kIsNotStringMask));
+ __ j(not_zero, &miss);
+
+ // Fast check for identical strings.
+ Label not_same;
+ __ cmpq(left, right);
+ __ j(not_equal, &not_same, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Handle not identical strings.
+ __ bind(&not_same);
+
+ // Check that both strings are symbols. If they are, we're done
+ // because we already know they are not identical.
+ Label do_compare;
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &do_compare, Label::kNear);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ ret(0);
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ bind(&do_compare);
+ __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ pop(tmp1); // Return address.
+ __ push(left);
+ __ push(right);
+ __ push(tmp1);
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::OBJECTS);
- NearLabel miss;
+ Label miss;
Condition either_smi = masm->CheckEitherSmi(rdx, rax);
- __ j(either_smi, &miss);
+ __ j(either_smi, &miss, Label::kNear);
__ CmpObjectType(rax, JS_OBJECT_TYPE, rcx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
__ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
ASSERT(GetCondition() == equal);
__ subq(rax, rdx);
@@ -4510,6 +4928,206 @@ void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
}
+MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ String* name,
+ Register r0) {
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // r0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = r0;
+ // Capacity is smi 2^n.
+ __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset));
+ __ decl(index);
+ __ and_(index,
+ Immediate(name->Hash() + StringDictionary::GetProbeOffset(i)));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
+
+ Register entity_name = r0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ movq(entity_name, Operand(properties,
+ index,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ Cmp(entity_name, masm->isolate()->factory()->undefined_value());
+ __ j(equal, done);
+
+ // Stop if found the property.
+ __ Cmp(entity_name, Handle<String>(name));
+ __ j(equal, miss);
+
+ // Check if the entry name is not a symbol.
+ __ movq(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
+ __ testb(FieldOperand(entity_name, Map::kInstanceTypeOffset),
+ Immediate(kIsSymbolMask));
+ __ j(zero, miss);
+ }
+
+ StringDictionaryLookupStub stub(properties,
+ r0,
+ r0,
+ StringDictionaryLookupStub::NEGATIVE_LOOKUP);
+ __ Push(Handle<Object>(name));
+ __ push(Immediate(name->Hash()));
+ MaybeObject* result = masm->TryCallStub(&stub);
+ if (result->IsFailure()) return result;
+ __ testq(r0, r0);
+ __ j(not_zero, miss);
+ __ jmp(done);
+ return result;
+}
+
+
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r1|. Jump to the |miss| label
+// otherwise.
+void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
+ // Assert that name contains a string.
+ if (FLAG_debug_code) __ AbortIfNotString(name);
+
+ __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset));
+ __ decl(r0);
+
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ movl(r1, FieldOperand(name, String::kHashFieldOffset));
+ __ shrl(r1, Immediate(String::kHashShift));
+ if (i > 0) {
+ __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i)));
+ }
+ __ and_(r1, r0);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
+
+ // Check if the key is identical to the name.
+ __ cmpq(name, Operand(elements, r1, times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ j(equal, done);
+ }
+
+ StringDictionaryLookupStub stub(elements,
+ r0,
+ r1,
+ POSITIVE_LOOKUP);
+ __ push(name);
+ __ movl(r0, FieldOperand(name, String::kHashFieldOffset));
+ __ shrl(r0, Immediate(String::kHashShift));
+ __ push(r0);
+ __ CallStub(&stub);
+
+ __ testq(r0, r0);
+ __ j(zero, miss);
+ __ jmp(done);
+}
+
+
+void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
+ // Stack frame on entry:
+ // esp[0 * kPointerSize]: return address.
+ // esp[1 * kPointerSize]: key's hash.
+ // esp[2 * kPointerSize]: key.
+ // Registers:
+ // dictionary_: StringDictionary to probe.
+ // result_: used as scratch.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ Register scratch = result_;
+
+ __ SmiToInteger32(scratch, FieldOperand(dictionary_, kCapacityOffset));
+ __ decl(scratch);
+ __ push(scratch);
+
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ movq(scratch, Operand(rsp, 2 * kPointerSize));
+ if (i > 0) {
+ __ addl(scratch, Immediate(StringDictionary::GetProbeOffset(i)));
+ }
+ __ and_(scratch, Operand(rsp, 0));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
+
+ // Having undefined at this place means the name is not contained.
+ __ movq(scratch, Operand(dictionary_,
+ index_,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+
+ __ Cmp(scratch, masm->isolate()->factory()->undefined_value());
+ __ j(equal, &not_in_dictionary);
+
+ // Stop if found the property.
+ __ cmpq(scratch, Operand(rsp, 3 * kPointerSize));
+ __ j(equal, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // If we hit a non symbol key during negative lookup
+ // we have to bailout as this key might be equal to the
+ // key we are looking for.
+
+ // Check if the entry name is not a symbol.
+ __ movq(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
+ __ testb(FieldOperand(scratch, Map::kInstanceTypeOffset),
+ Immediate(kIsSymbolMask));
+ __ j(zero, &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ movq(scratch, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+ }
+
+ __ bind(&in_dictionary);
+ __ movq(scratch, Immediate(1));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&not_in_dictionary);
+ __ movq(scratch, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+}
+
+
#undef __
} } // namespace v8::internal
diff --git a/src/x64/code-stubs-x64.h b/src/x64/code-stubs-x64.h
index f97d0996..27744034 100644
--- a/src/x64/code-stubs-x64.h
+++ b/src/x64/code-stubs-x64.h
@@ -71,21 +71,113 @@ class ToBooleanStub: public CodeStub {
};
-class TypeRecordingBinaryOpStub: public CodeStub {
+class UnaryOpStub: public CodeStub {
public:
- TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode)
+ UnaryOpStub(Token::Value op, UnaryOverwriteMode mode)
: op_(op),
mode_(mode),
- operands_type_(TRBinaryOpIC::UNINITIALIZED),
- result_type_(TRBinaryOpIC::UNINITIALIZED),
+ operand_type_(UnaryOpIC::UNINITIALIZED),
+ name_(NULL) {
+ }
+
+ UnaryOpStub(
+ int key,
+ UnaryOpIC::TypeInfo operand_type)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ operand_type_(operand_type),
+ name_(NULL) {
+ }
+
+ private:
+ Token::Value op_;
+ UnaryOverwriteMode mode_;
+
+ // Operand type information determined at runtime.
+ UnaryOpIC::TypeInfo operand_type_;
+
+ char* name_;
+
+ const char* GetName();
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("UnaryOpStub %d (op %s), "
+ "(mode %d, runtime_type_info %s)\n",
+ MinorKey(),
+ Token::String(op_),
+ static_cast<int>(mode_),
+ UnaryOpIC::GetName(operand_type_));
+ }
+#endif
+
+ class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
+ class OpBits: public BitField<Token::Value, 1, 7> {};
+ class OperandTypeInfoBits: public BitField<UnaryOpIC::TypeInfo, 8, 3> {};
+
+ Major MajorKey() { return UnaryOp; }
+ int MinorKey() {
+ return ModeBits::encode(mode_)
+ | OpBits::encode(op_)
+ | OperandTypeInfoBits::encode(operand_type_);
+ }
+
+ // Note: A lot of the helper functions below will vanish when we use virtual
+ // function instead of switch more often.
+ void Generate(MacroAssembler* masm);
+
+ void GenerateTypeTransition(MacroAssembler* masm);
+
+ void GenerateSmiStub(MacroAssembler* masm);
+ void GenerateSmiStubSub(MacroAssembler* masm);
+ void GenerateSmiStubBitNot(MacroAssembler* masm);
+ void GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* slow,
+ Label::Distance non_smi_near = Label::kFar,
+ Label::Distance slow_near = Label::kFar);
+ void GenerateSmiCodeBitNot(MacroAssembler* masm,
+ Label* non_smi,
+ Label::Distance non_smi_near);
+
+ void GenerateHeapNumberStub(MacroAssembler* masm);
+ void GenerateHeapNumberStubSub(MacroAssembler* masm);
+ void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
+ void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
+ void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
+
+ void GenerateGenericStub(MacroAssembler* masm);
+ void GenerateGenericStubSub(MacroAssembler* masm);
+ void GenerateGenericStubBitNot(MacroAssembler* masm);
+ void GenerateGenericCodeFallback(MacroAssembler* masm);
+
+ virtual int GetCodeKind() { return Code::UNARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return UnaryOpIC::ToState(operand_type_);
+ }
+
+ virtual void FinishCode(Code* code) {
+ code->set_unary_op_type(operand_type_);
+ }
+};
+
+
+class BinaryOpStub: public CodeStub {
+ public:
+ BinaryOpStub(Token::Value op, OverwriteMode mode)
+ : op_(op),
+ mode_(mode),
+ operands_type_(BinaryOpIC::UNINITIALIZED),
+ result_type_(BinaryOpIC::UNINITIALIZED),
name_(NULL) {
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
- TypeRecordingBinaryOpStub(
+ BinaryOpStub(
int key,
- TRBinaryOpIC::TypeInfo operands_type,
- TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED)
+ BinaryOpIC::TypeInfo operands_type,
+ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
: op_(OpBits::decode(key)),
mode_(ModeBits::decode(key)),
operands_type_(operands_type),
@@ -102,8 +194,8 @@ class TypeRecordingBinaryOpStub: public CodeStub {
OverwriteMode mode_;
// Operand type information determined at runtime.
- TRBinaryOpIC::TypeInfo operands_type_;
- TRBinaryOpIC::TypeInfo result_type_;
+ BinaryOpIC::TypeInfo operands_type_;
+ BinaryOpIC::TypeInfo result_type_;
char* name_;
@@ -111,22 +203,22 @@ class TypeRecordingBinaryOpStub: public CodeStub {
#ifdef DEBUG
void Print() {
- PrintF("TypeRecordingBinaryOpStub %d (op %s), "
+ PrintF("BinaryOpStub %d (op %s), "
"(mode %d, runtime_type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
}
#endif
// Minor key encoding in 15 bits RRRTTTOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
- class OperandTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 9, 3> {};
- class ResultTypeInfoBits: public BitField<TRBinaryOpIC::TypeInfo, 12, 3> {};
+ class OperandTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 9, 3> {};
+ class ResultTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 12, 3> {};
- Major MajorKey() { return TypeRecordingBinaryOp; }
+ Major MajorKey() { return BinaryOp; }
int MinorKey() {
return OpBits::encode(op_)
| ModeBits::encode(mode_)
@@ -152,6 +244,7 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateHeapNumberStub(MacroAssembler* masm);
void GenerateOddballStub(MacroAssembler* masm);
void GenerateStringStub(MacroAssembler* masm);
+ void GenerateBothStringStub(MacroAssembler* masm);
void GenerateGenericStub(MacroAssembler* masm);
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
@@ -159,15 +252,15 @@ class TypeRecordingBinaryOpStub: public CodeStub {
void GenerateTypeTransition(MacroAssembler* masm);
void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
- virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; }
+ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
virtual InlineCacheState GetICState() {
- return TRBinaryOpIC::ToState(operands_type_);
+ return BinaryOpIC::ToState(operands_type_);
}
virtual void FinishCode(Code* code) {
- code->set_type_recording_binary_op_type(operands_type_);
- code->set_type_recording_binary_op_result_type(result_type_);
+ code->set_binary_op_type(operands_type_);
+ code->set_binary_op_result_type(result_type_);
}
friend class CodeGenerator;
@@ -276,10 +369,9 @@ class SubStringStub: public CodeStub {
class StringCompareStub: public CodeStub {
public:
- explicit StringCompareStub() {}
+ StringCompareStub() {}
- // Compare two flat ascii strings and returns result in rax after popping two
- // arguments from the stack.
+ // Compares two flat ASCII strings and returns result in rax.
static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -288,11 +380,27 @@ class StringCompareStub: public CodeStub {
Register scratch3,
Register scratch4);
- private:
- Major MajorKey() { return StringCompare; }
- int MinorKey() { return 0; }
+ // Compares two flat ASCII strings for equality and returns result
+ // in rax.
+ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2);
- void Generate(MacroAssembler* masm);
+ private:
+ virtual Major MajorKey() { return StringCompare; }
+ virtual int MinorKey() { return 0; }
+ virtual void Generate(MacroAssembler* masm);
+
+ static void GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance near_jump = Label::kFar);
};
@@ -333,6 +441,74 @@ class NumberToStringStub: public CodeStub {
};
+class StringDictionaryLookupStub: public CodeStub {
+ public:
+ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
+
+ StringDictionaryLookupStub(Register dictionary,
+ Register result,
+ Register index,
+ LookupMode mode)
+ : dictionary_(dictionary), result_(result), index_(index), mode_(mode) { }
+
+ void Generate(MacroAssembler* masm);
+
+ MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ String* name,
+ Register r0);
+
+ static void GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1);
+
+ private:
+ static const int kInlinedProbes = 4;
+ static const int kTotalProbes = 20;
+
+ static const int kCapacityOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kCapacityIndex * kPointerSize;
+
+ static const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("StringDictionaryLookupStub\n");
+ }
+#endif
+
+ Major MajorKey() { return StringDictionaryNegativeLookup; }
+
+ int MinorKey() {
+ return DictionaryBits::encode(dictionary_.code()) |
+ ResultBits::encode(result_.code()) |
+ IndexBits::encode(index_.code()) |
+ LookupModeBits::encode(mode_);
+ }
+
+ class DictionaryBits: public BitField<int, 0, 4> {};
+ class ResultBits: public BitField<int, 4, 4> {};
+ class IndexBits: public BitField<int, 8, 4> {};
+ class LookupModeBits: public BitField<LookupMode, 12, 1> {};
+
+ Register dictionary_;
+ Register result_;
+ Register index_;
+ LookupMode mode_;
+};
+
+
} } // namespace v8::internal
#endif // V8_X64_CODE_STUBS_X64_H_
diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc
index 2b7b7b7d..7bb2e61c 100644
--- a/src/x64/disasm-x64.cc
+++ b/src/x64/disasm-x64.cc
@@ -1021,12 +1021,26 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
current += PrintRightOperand(current);
AppendToBuffer(", %s, %d", NameOfCPURegister(regop), (*current) & 3);
current += 1;
+ } else if (third_byte == 0x0b) {
+ get_modrm(*current, &mod, &regop, &rm);
+ // roundsd xmm, xmm/m64, imm8
+ AppendToBuffer("roundsd %s, ", NameOfCPURegister(regop));
+ current += PrintRightOperand(current);
+ AppendToBuffer(", %d", (*current) & 3);
+ current += 1;
} else {
UnimplementedInstruction();
}
} else {
get_modrm(*current, &mod, &regop, &rm);
- if (opcode == 0x6E) {
+ if (opcode == 0x28) {
+ AppendToBuffer("movapd %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x29) {
+ AppendToBuffer("movapd ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0x6E) {
AppendToBuffer("mov%c %s,",
rex_w() ? 'q' : 'd',
NameOfXMMRegister(regop));
@@ -1044,6 +1058,10 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
AppendToBuffer("movdqa ");
current += PrintRightXMMOperand(current);
AppendToBuffer(", %s", NameOfXMMRegister(regop));
+ } else if (opcode == 0xD6) {
+ AppendToBuffer("movq ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
} else {
const char* mnemonic = "?";
if (opcode == 0x50) {
@@ -1145,6 +1163,11 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
get_modrm(*current, &mod, &regop, &rm);
AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop));
current += PrintRightXMMOperand(current);
+ } else if (opcode == 0x7E) {
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movq %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
} else {
UnimplementedInstruction();
}
@@ -1162,6 +1185,22 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
current += 4;
} // else no immediate displacement.
AppendToBuffer("nop");
+
+ } else if (opcode == 0x28) {
+ // movaps xmm, xmm/m128
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movaps %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+
+ } else if (opcode == 0x29) {
+ // movaps xmm/m128, xmm
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("movaps ");
+ current += PrintRightXMMOperand(current);
+ AppendToBuffer(", %s", NameOfXMMRegister(regop));
+
} else if (opcode == 0xA2 || opcode == 0x31) {
// RDTSC or CPUID
AppendToBuffer("%s", mnemonic);
@@ -1173,6 +1212,13 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
byte_size_operand_ = idesc.byte_size_operation;
current += PrintOperands(idesc.mnem, idesc.op_order_, current);
+ } else if (opcode == 0x57) {
+ // xorps xmm, xmm/m128
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("xorps %s, ", NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+
} else if ((opcode & 0xF0) == 0x80) {
// Jcc: Conditional jump (branch).
current = data + JumpConditional(data);
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 6933d780..57baf77c 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -44,6 +44,12 @@ namespace internal {
#define __ ACCESS_MASM(masm_)
+static unsigned GetPropertyId(Property* property) {
+ if (property->is_synthetic()) return AstNode::kNoNumber;
+ return property->id();
+}
+
+
class JumpPatchSite BASE_EMBEDDED {
public:
explicit JumpPatchSite(MacroAssembler* masm)
@@ -57,14 +63,18 @@ class JumpPatchSite BASE_EMBEDDED {
ASSERT(patch_site_.is_bound() == info_emitted_);
}
- void EmitJumpIfNotSmi(Register reg, NearLabel* target) {
+ void EmitJumpIfNotSmi(Register reg,
+ Label* target,
+ Label::Distance near_jump = Label::kFar) {
__ testb(reg, Immediate(kSmiTagMask));
- EmitJump(not_carry, target); // Always taken before patched.
+ EmitJump(not_carry, target, near_jump); // Always taken before patched.
}
- void EmitJumpIfSmi(Register reg, NearLabel* target) {
+ void EmitJumpIfSmi(Register reg,
+ Label* target,
+ Label::Distance near_jump = Label::kFar) {
__ testb(reg, Immediate(kSmiTagMask));
- EmitJump(carry, target); // Never taken before patched.
+ EmitJump(carry, target, near_jump); // Never taken before patched.
}
void EmitPatchInfo() {
@@ -80,11 +90,11 @@ class JumpPatchSite BASE_EMBEDDED {
private:
// jc will be patched with jz, jnc will become jnz.
- void EmitJump(Condition cc, NearLabel* target) {
+ void EmitJump(Condition cc, Label* target, Label::Distance near_jump) {
ASSERT(!patch_site_.is_bound() && !info_emitted_);
ASSERT(cc == carry || cc == not_carry);
__ bind(&patch_site_);
- __ j(cc, target);
+ __ j(cc, target, near_jump);
}
MacroAssembler* masm_;
@@ -120,6 +130,22 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
__ int3();
}
#endif
+
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). rcx is zero for method calls and non-zero for function
+ // calls.
+ if (info->is_strict_mode()) {
+ Label ok;
+ __ testq(rcx, rcx);
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ movq(Operand(rsp, receiver_offset), kScratchRegister);
+ __ bind(&ok);
+ }
+
__ push(rbp); // Caller's frame pointer.
__ movq(rbp, rsp);
__ push(rsi); // Callee's context.
@@ -233,9 +259,9 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
{ Comment cmnt(masm_, "[ Stack check");
PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS);
- NearLabel ok;
+ Label ok;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
- __ j(above_equal, &ok);
+ __ j(above_equal, &ok, Label::kNear);
StackCheckStub stub;
__ CallStub(&stub);
__ bind(&ok);
@@ -264,9 +290,9 @@ void FullCodeGenerator::ClearAccumulator() {
void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) {
Comment cmnt(masm_, "[ Stack check");
- NearLabel ok;
+ Label ok;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
- __ j(above_equal, &ok);
+ __ j(above_equal, &ok, Label::kNear);
StackCheckStub stub;
__ CallStub(&stub);
// Record a mapping of this PC offset to the OSR id. This is used to find
@@ -479,10 +505,10 @@ void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
void FullCodeGenerator::AccumulatorValueContext::Plug(
Label* materialize_true,
Label* materialize_false) const {
- NearLabel done;
+ Label done;
__ bind(materialize_true);
__ Move(result_register(), isolate()->factory()->true_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(materialize_false);
__ Move(result_register(), isolate()->factory()->false_value());
__ bind(&done);
@@ -492,10 +518,10 @@ void FullCodeGenerator::AccumulatorValueContext::Plug(
void FullCodeGenerator::StackValueContext::Plug(
Label* materialize_true,
Label* materialize_false) const {
- NearLabel done;
+ Label done;
__ bind(materialize_true);
__ Push(isolate()->factory()->true_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(materialize_false);
__ Push(isolate()->factory()->false_value());
__ bind(&done);
@@ -543,25 +569,10 @@ void FullCodeGenerator::TestContext::Plug(bool flag) const {
void FullCodeGenerator::DoTest(Label* if_true,
Label* if_false,
Label* fall_through) {
- // Emit the inlined tests assumed by the stub.
- __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex);
- __ j(equal, if_false);
- __ CompareRoot(result_register(), Heap::kTrueValueRootIndex);
- __ j(equal, if_true);
- __ CompareRoot(result_register(), Heap::kFalseValueRootIndex);
- __ j(equal, if_false);
- STATIC_ASSERT(kSmiTag == 0);
- __ Cmp(result_register(), Smi::FromInt(0));
- __ j(equal, if_false);
- Condition is_smi = masm_->CheckSmi(result_register());
- __ j(is_smi, if_true);
-
- // Call the ToBoolean stub for all other cases.
ToBooleanStub stub;
__ push(result_register());
__ CallStub(&stub);
__ testq(rax, rax);
-
// The stub returns nonzero for true.
Split(not_zero, if_true, if_false, fall_through);
}
@@ -632,8 +643,8 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state,
// preparation to avoid preparing with the same AST id twice.
if (!context()->IsTest() || !info_->IsOptimizable()) return;
- NearLabel skip;
- if (should_normalize) __ jmp(&skip);
+ Label skip;
+ if (should_normalize) __ jmp(&skip, Label::kNear);
ForwardBailoutStack* current = forward_bailout_stack_;
while (current != NULL) {
@@ -719,23 +730,20 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
}
} else if (prop != NULL) {
- if (function != NULL || mode == Variable::CONST) {
- // We are declaring a function or constant that rewrites to a
- // property. Use (keyed) IC to set the initial value. We
- // cannot visit the rewrite because it's shared and we risk
- // recording duplicate AST IDs for bailouts from optimized code.
+ // A const declaration aliasing a parameter is an illegal redeclaration.
+ ASSERT(mode != Variable::CONST);
+ if (function != NULL) {
+ // We are declaring a function that rewrites to a property.
+ // Use (keyed) IC to set the initial value. We cannot visit the
+ // rewrite because it's shared and we risk recording duplicate AST
+ // IDs for bailouts from optimized code.
ASSERT(prop->obj()->AsVariableProxy() != NULL);
{ AccumulatorValueContext for_object(this);
EmitVariableLoad(prop->obj()->AsVariableProxy()->var());
}
- if (function != NULL) {
- __ push(rax);
- VisitForAccumulatorValue(function);
- __ pop(rdx);
- } else {
- __ movq(rdx, rax);
- __ LoadRoot(rax, Heap::kTheHoleValueRootIndex);
- }
+ __ push(rax);
+ VisitForAccumulatorValue(function);
+ __ pop(rdx);
ASSERT(prop->key()->AsLiteral() != NULL &&
prop->key()->AsLiteral()->handle()->IsSmi());
__ Move(rcx, prop->key()->AsLiteral()->handle());
@@ -743,7 +751,7 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
}
}
}
@@ -801,10 +809,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
JumpPatchSite patch_site(masm_);
if (inline_smi_code) {
- NearLabel slow_case;
+ Label slow_case;
__ movq(rcx, rdx);
__ or_(rcx, rax);
- patch_site.EmitJumpIfNotSmi(rcx, &slow_case);
+ patch_site.EmitJumpIfNotSmi(rcx, &slow_case, Label::kNear);
__ cmpq(rdx, rax);
__ j(not_equal, &next_test);
@@ -816,7 +824,7 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
// Record position before stub call for type feedback.
SetSourcePosition(clause->position());
Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT);
- EmitCallIC(ic, &patch_site);
+ EmitCallIC(ic, &patch_site, clause->CompareId());
__ testq(rax, rax);
__ j(not_equal, &next_test);
@@ -901,9 +909,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// check for an enum cache. Leave the map in rbx for the subsequent
// prototype load.
__ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
- __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
- __ cmpq(rdx, empty_descriptor_array_value);
- __ j(equal, &call_runtime);
+ __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOrBitField3Offset));
+ __ JumpIfSmi(rdx, &call_runtime);
// Check that there is an enum cache in the non-empty instance
// descriptors (rdx). This is the case if the next enumeration
@@ -912,9 +919,9 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
__ JumpIfSmi(rdx, &call_runtime);
// For all objects but the receiver, check that the cache is empty.
- NearLabel check_prototype;
+ Label check_prototype;
__ cmpq(rcx, rax);
- __ j(equal, &check_prototype);
+ __ j(equal, &check_prototype, Label::kNear);
__ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset));
__ cmpq(rdx, empty_fixed_array_value);
__ j(not_equal, &call_runtime);
@@ -927,9 +934,9 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.
- NearLabel use_cache;
+ Label use_cache;
__ movq(rax, FieldOperand(rax, HeapObject::kMapOffset));
- __ jmp(&use_cache);
+ __ jmp(&use_cache, Label::kNear);
// Get the set of properties to enumerate.
__ bind(&call_runtime);
@@ -939,14 +946,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// If we got a map from the runtime call, we can do a fast
// modification check. Otherwise, we got a fixed array, and we have
// to do a slow check.
- NearLabel fixed_array;
+ Label fixed_array;
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
Heap::kMetaMapRootIndex);
- __ j(not_equal, &fixed_array);
+ __ j(not_equal, &fixed_array, Label::kNear);
// We got a map in register rax. Get the enumeration cache from it.
__ bind(&use_cache);
- __ movq(rcx, FieldOperand(rax, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(rax, rcx);
__ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset));
__ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset));
@@ -986,10 +993,10 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// Check if the expected map still matches that of the enumerable.
// If not, we have to filter the key.
- NearLabel update_each;
+ Label update_each;
__ movq(rcx, Operand(rsp, 4 * kPointerSize));
__ cmpq(rdx, FieldOperand(rcx, HeapObject::kMapOffset));
- __ j(equal, &update_each);
+ __ j(equal, &update_each, Label::kNear);
// Convert the entry to a string or null if it isn't a property
// anymore. If the property has been removed while iterating, we
@@ -1097,7 +1104,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
if (s != NULL && s->is_eval_scope()) {
// Loop up the context chain. There is no frame effect so it is
// safe to use raw labels here.
- NearLabel next, fast;
+ Label next, fast;
if (!context.is(temp)) {
__ movq(temp, context);
}
@@ -1106,7 +1113,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
__ bind(&next);
// Terminate at global context.
__ cmpq(kScratchRegister, FieldOperand(temp, HeapObject::kMapOffset));
- __ j(equal, &fast);
+ __ j(equal, &fast, Label::kNear);
// Check that extension is NULL.
__ cmpq(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0));
__ j(not_equal, slow);
@@ -1125,7 +1132,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
? RelocInfo::CODE_TARGET
: RelocInfo::CODE_TARGET_CONTEXT;
- EmitCallIC(ic, mode);
+ EmitCallIC(ic, mode, AstNode::kNoNumber);
}
@@ -1206,7 +1213,7 @@ void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
__ Move(rax, key_literal->handle());
Handle<Code> ic =
isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
__ jmp(done);
}
}
@@ -1229,7 +1236,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
__ Move(rcx, var->name());
__ movq(rax, GlobalObjectOperand());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
context()->Plug(rax);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
@@ -1255,11 +1262,11 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
if (var->mode() == Variable::CONST) {
// Constants may be the hole value if they have not been initialized.
// Unhole them.
- NearLabel done;
+ Label done;
MemOperand slot_operand = EmitSlotSearch(slot, rax);
__ movq(rax, slot_operand);
__ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
__ bind(&done);
context()->Plug(rax);
@@ -1292,7 +1299,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) {
// Do a keyed property load.
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(property));
context()->Plug(rax);
}
}
@@ -1405,7 +1412,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, key->id());
PrepareForBailoutForId(key->id(), NO_REGISTERS);
} else {
VisitForEffect(value);
@@ -1610,13 +1617,13 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) {
SetSourcePosition(expr->position() + 1);
AccumulatorValueContext context(this);
if (ShouldInlineSmiCase(op)) {
- EmitInlineSmiBinaryOp(expr,
+ EmitInlineSmiBinaryOp(expr->binary_operation(),
op,
mode,
expr->target(),
expr->value());
} else {
- EmitBinaryOp(op, mode);
+ EmitBinaryOp(expr->binary_operation(), op, mode);
}
// Deoptimization point in case the binary operation may have side effects.
PrepareForBailout(expr->binary_operation(), TOS_REG);
@@ -1650,18 +1657,18 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
Literal* key = prop->key()->AsLiteral();
__ Move(rcx, key->handle());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
}
-void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
+void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
Token::Value op,
OverwriteMode mode,
Expression* left,
@@ -1669,18 +1676,18 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
// Do combined smi check of the operands. Left operand is on the
// stack (popped into rdx). Right operand is in rax but moved into
// rcx to make the shifts easier.
- NearLabel done, stub_call, smi_case;
+ Label done, stub_call, smi_case;
__ pop(rdx);
__ movq(rcx, rax);
__ or_(rax, rdx);
JumpPatchSite patch_site(masm_);
- patch_site.EmitJumpIfSmi(rax, &smi_case);
+ patch_site.EmitJumpIfSmi(rax, &smi_case, Label::kNear);
__ bind(&stub_call);
__ movq(rax, rcx);
- TypeRecordingBinaryOpStub stub(op, mode);
- EmitCallIC(stub.GetCode(), &patch_site);
- __ jmp(&done);
+ BinaryOpStub stub(op, mode);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->id());
+ __ jmp(&done, Label::kNear);
__ bind(&smi_case);
switch (op) {
@@ -1721,11 +1728,13 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr,
}
-void FullCodeGenerator::EmitBinaryOp(Token::Value op,
+void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
+ Token::Value op,
OverwriteMode mode) {
__ pop(rdx);
- TypeRecordingBinaryOpStub stub(op, mode);
- EmitCallIC(stub.GetCode(), NULL); // NULL signals no inlined smi code.
+ BinaryOpStub stub(op, mode);
+ // NULL signals no inlined smi code.
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
context()->Plug(rax);
}
@@ -1765,7 +1774,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
break;
}
case KEYED_PROPERTY: {
@@ -1788,7 +1797,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
break;
}
}
@@ -1814,7 +1823,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT, AstNode::kNoNumber);
} else if (op == Token::INIT_CONST) {
// Like var declarations, const declarations are hoisted to function
@@ -1917,7 +1926,7 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -1957,7 +1966,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -2008,8 +2017,8 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- EmitCallIC(ic, mode);
+ ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ EmitCallIC(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
@@ -2018,8 +2027,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
- Expression* key,
- RelocInfo::Mode mode) {
+ Expression* key) {
// Load the key.
VisitForAccumulatorValue(key);
@@ -2044,7 +2052,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
Handle<Code> ic =
ISOLATE->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
__ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key.
- EmitCallIC(ic, mode);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
@@ -2052,7 +2060,7 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
}
-void FullCodeGenerator::EmitCallWithStub(Call* expr) {
+void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
@@ -2064,7 +2072,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Record source position for debugger.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arg_count, in_loop, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2155,7 +2163,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Record source position for debugger.
SetSourcePosition(expr->position());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2193,18 +2201,21 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// function and receiver and have the slow path jump around this
// code.
if (done.is_linked()) {
- NearLabel call;
- __ jmp(&call);
+ Label call;
+ __ jmp(&call, Label::kNear);
__ bind(&done);
// Push function.
__ push(rax);
// Push global receiver.
- __ movq(rbx, GlobalObjectOperand());
- __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
- __ bind(&call);
+ __ movq(rbx, GlobalObjectOperand());
+ __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ __ bind(&call);
}
- EmitCallWithStub(expr);
+ // The receiver is either the global receiver or an object found
+ // by LoadContextSlot. That object could be the hole if the
+ // receiver is implicitly the global object.
+ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
} else if (fun->AsProperty() != NULL) {
// Call to an object property.
Property* prop = fun->AsProperty();
@@ -2236,18 +2247,18 @@ void FullCodeGenerator::VisitCall(Call* expr) {
SetSourcePosition(prop->position());
Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop));
// Push result (function).
__ push(rax);
// Push Global receiver.
__ movq(rcx, GlobalObjectOperand());
__ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset));
- EmitCallWithStub(expr);
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
} else {
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(prop->obj());
}
- EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
+ EmitKeyedCallWithIC(expr, prop->key());
}
}
} else {
@@ -2258,7 +2269,7 @@ void FullCodeGenerator::VisitCall(Call* expr) {
__ movq(rbx, GlobalObjectOperand());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
// Emit function call.
- EmitCallWithStub(expr);
+ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
}
#ifdef DEBUG
@@ -2447,7 +2458,7 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
- __ movq(rbx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(rbx, rbx);
__ movq(rcx, FieldOperand(rbx, FixedArray::kLengthOffset));
// rbx: descriptor array
// rcx: length of descriptor array
@@ -2633,7 +2644,7 @@ void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
ASSERT(args->length() == 0);
- NearLabel exit;
+ Label exit;
// Get the number of formal parameters.
__ Move(rax, Smi::FromInt(scope()->num_parameters()));
@@ -2641,7 +2652,7 @@ void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
__ movq(rbx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
__ Cmp(Operand(rbx, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(not_equal, &exit);
+ __ j(not_equal, &exit, Label::kNear);
// Arguments adaptor case: Read the arguments length from the
// adaptor frame.
@@ -2762,7 +2773,7 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
__ movd(xmm1, rcx);
__ movd(xmm0, rax);
__ cvtss2sd(xmm1, xmm1);
- __ xorpd(xmm0, xmm1);
+ __ xorps(xmm0, xmm1);
__ subsd(xmm0, xmm1);
__ movsd(FieldOperand(rbx, HeapNumber::kValueOffset), xmm0);
@@ -3047,17 +3058,17 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
ASSERT(args->length() >= 2);
- int arg_count = args->length() - 2; // For receiver and function.
- VisitForStackValue(args->at(0)); // Receiver.
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i + 1));
+ int arg_count = args->length() - 2; // 2 ~ receiver and function.
+ for (int i = 0; i < arg_count + 1; i++) {
+ VisitForStackValue(args->at(i));
}
- VisitForAccumulatorValue(args->at(arg_count + 1)); // Function.
+ VisitForAccumulatorValue(args->last()); // Function.
- // InvokeFunction requires function in rdi. Move it in there.
- if (!result_register().is(rdi)) __ movq(rdi, result_register());
+ // InvokeFunction requires the function in rdi. Move it in there.
+ __ movq(rdi, result_register());
ParameterCount count(arg_count);
- __ InvokeFunction(rdi, count, CALL_FUNCTION);
+ __ InvokeFunction(rdi, count, CALL_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
context()->Plug(rax);
}
@@ -3178,7 +3189,7 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
__ movq(cache,
FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
- NearLabel done, not_found;
+ Label done, not_found;
// tmp now holds finger offset as a smi.
ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
__ movq(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
@@ -3188,12 +3199,12 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
index.reg,
index.scale,
FixedArray::kHeaderSize));
- __ j(not_equal, &not_found);
+ __ j(not_equal, &not_found, Label::kNear);
__ movq(rax, FieldOperand(cache,
index.reg,
index.scale,
FixedArray::kHeaderSize + kPointerSize));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&not_found);
// Call runtime to perform the lookup.
@@ -3217,25 +3228,25 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) {
VisitForAccumulatorValue(args->at(1));
__ pop(left);
- NearLabel done, fail, ok;
+ Label done, fail, ok;
__ cmpq(left, right);
- __ j(equal, &ok);
+ __ j(equal, &ok, Label::kNear);
// Fail if either is a non-HeapObject.
Condition either_smi = masm()->CheckEitherSmi(left, right, tmp);
- __ j(either_smi, &fail);
- __ j(zero, &fail);
+ __ j(either_smi, &fail, Label::kNear);
+ __ j(zero, &fail, Label::kNear);
__ movq(tmp, FieldOperand(left, HeapObject::kMapOffset));
__ cmpb(FieldOperand(tmp, Map::kInstanceTypeOffset),
Immediate(JS_REGEXP_TYPE));
- __ j(not_equal, &fail);
+ __ j(not_equal, &fail, Label::kNear);
__ cmpq(tmp, FieldOperand(right, HeapObject::kMapOffset));
- __ j(not_equal, &fail);
+ __ j(not_equal, &fail, Label::kNear);
__ movq(tmp, FieldOperand(left, JSRegExp::kDataOffset));
__ cmpq(tmp, FieldOperand(right, JSRegExp::kDataOffset));
- __ j(equal, &ok);
+ __ j(equal, &ok, Label::kNear);
__ bind(&fail);
__ Move(rax, isolate()->factory()->false_value());
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&ok);
__ Move(rax, isolate()->factory()->true_value());
__ bind(&done);
@@ -3595,9 +3606,10 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
// Call the JS runtime function using a call IC.
__ Move(rcx, expr->name());
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop);
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ EmitCallIC(ic, mode, expr->id());
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
} else {
@@ -3709,46 +3721,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
break;
}
- case Token::SUB: {
- Comment cmt(masm_, "[ UnaryOperation (SUB)");
- bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode overwrite =
- can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
- // GenericUnaryOpStub expects the argument to be in the
- // accumulator register rax.
- VisitForAccumulatorValue(expr->expression());
- __ CallStub(&stub);
- context()->Plug(rax);
+ case Token::SUB:
+ EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
break;
- }
- case Token::BIT_NOT: {
- Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
- // The generic unary operation stub expects the argument to be
- // in the accumulator register rax.
- VisitForAccumulatorValue(expr->expression());
- Label done;
- bool inline_smi_case = ShouldInlineSmiCase(expr->op());
- if (inline_smi_case) {
- Label call_stub;
- __ JumpIfNotSmi(rax, &call_stub);
- __ SmiNot(rax, rax);
- __ jmp(&done);
- __ bind(&call_stub);
- }
- bool overwrite = expr->expression()->ResultOverwriteAllowed();
- UnaryOverwriteMode mode =
- overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
- UnaryOpFlags flags = inline_smi_case
- ? NO_UNARY_SMI_CODE_IN_STUB
- : NO_UNARY_FLAGS;
- GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
- __ CallStub(&stub);
- __ bind(&done);
- context()->Plug(rax);
+ case Token::BIT_NOT:
+ EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
break;
- }
default:
UNREACHABLE();
@@ -3756,6 +3735,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
+void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
+ const char* comment) {
+ // TODO(svenpanne): Allowing format strings in Comment would be nice here...
+ Comment cmt(masm_, comment);
+ bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+ UnaryOpStub stub(expr->op(), overwrite);
+ // UnaryOpStub expects the argument to be in the
+ // accumulator register rax.
+ VisitForAccumulatorValue(expr->expression());
+ SetSourcePosition(expr->position());
+ EmitCallIC(stub.GetCode(), NULL, expr->id());
+ context()->Plug(rax);
+}
+
+
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Comment cmnt(masm_, "[ CountOperation");
SetSourcePosition(expr->position());
@@ -3819,10 +3815,10 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
}
// Call ToNumber only if operand is not a smi.
- NearLabel no_conversion;
+ Label no_conversion;
Condition is_smi;
is_smi = masm_->CheckSmi(rax);
- __ j(is_smi, &no_conversion);
+ __ j(is_smi, &no_conversion, Label::kNear);
ToNumberStub convert_stub;
__ CallStub(&convert_stub);
__ bind(&no_conversion);
@@ -3848,7 +3844,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
}
// Inline smi case if we are in a loop.
- NearLabel stub_call, done;
+ Label done, stub_call;
JumpPatchSite patch_site(masm_);
if (ShouldInlineSmiCase(expr->op())) {
@@ -3857,10 +3853,10 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
} else {
__ SmiSubConstant(rax, rax, Smi::FromInt(1));
}
- __ j(overflow, &stub_call);
+ __ j(overflow, &stub_call, Label::kNear);
// We could eliminate this smi check if we split the code at
// the first smi check before calling ToNumber.
- patch_site.EmitJumpIfSmi(rax, &done);
+ patch_site.EmitJumpIfSmi(rax, &done, Label::kNear);
__ bind(&stub_call);
// Call stub. Undo operation first.
@@ -3875,14 +3871,14 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
SetSourcePosition(expr->position());
// Call stub for +1/-1.
- TypeRecordingBinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
+ BinaryOpStub stub(expr->binary_op(), NO_OVERWRITE);
if (expr->op() == Token::INC) {
__ Move(rdx, Smi::FromInt(1));
} else {
__ movq(rdx, rax);
__ Move(rax, Smi::FromInt(1));
}
- EmitCallIC(stub.GetCode(), &patch_site);
+ EmitCallIC(stub.GetCode(), &patch_site, expr->CountId());
__ bind(&done);
// Store the value returned in rax.
@@ -3915,7 +3911,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->StoreIC_Initialize_Strict()
: isolate()->builtins()->StoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
@@ -3932,7 +3928,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
Handle<Code> ic = is_strict_mode()
? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
: isolate()->builtins()->KeyedStoreIC_Initialize();
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, expr->id());
PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
if (expr->is_postfix()) {
if (!context()->IsEffect()) {
@@ -3959,7 +3955,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
// Use a regular load, not a contextual load, to avoid a reference
// error.
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ EmitCallIC(ic, RelocInfo::CODE_TARGET, AstNode::kNoNumber);
PrepareForBailout(expr, TOS_REG);
context()->Plug(rax);
} else if (proxy != NULL &&
@@ -4145,10 +4141,10 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
bool inline_smi_code = ShouldInlineSmiCase(op);
JumpPatchSite patch_site(masm_);
if (inline_smi_code) {
- NearLabel slow_case;
+ Label slow_case;
__ movq(rcx, rdx);
__ or_(rcx, rax);
- patch_site.EmitJumpIfNotSmi(rcx, &slow_case);
+ patch_site.EmitJumpIfNotSmi(rcx, &slow_case, Label::kNear);
__ cmpq(rdx, rax);
Split(cc, if_true, if_false, NULL);
__ bind(&slow_case);
@@ -4157,7 +4153,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
// Record position and call the compare IC.
SetSourcePosition(expr->position());
Handle<Code> ic = CompareIC::GetUninitialized(op);
- EmitCallIC(ic, &patch_site);
+ EmitCallIC(ic, &patch_site, expr->id());
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
__ testq(rax, rax);
@@ -4217,7 +4213,9 @@ Register FullCodeGenerator::context_register() {
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ RelocInfo::Mode mode,
+ unsigned ast_id) {
ASSERT(mode == RelocInfo::CODE_TARGET ||
mode == RelocInfo::CODE_TARGET_CONTEXT);
Counters* counters = isolate()->counters();
@@ -4236,34 +4234,13 @@ void FullCodeGenerator::EmitCallIC(Handle<Code> ic, RelocInfo::Mode mode) {
default:
break;
}
-
- __ call(ic, mode);
-
- // Crankshaft doesn't need patching of inlined loads and stores.
- // When compiling the snapshot we need to produce code that works
- // with and without Crankshaft.
- if (V8::UseCrankshaft() && !Serializer::enabled()) {
- return;
- }
-
- // If we're calling a (keyed) load or store stub, we have to mark
- // the call as containing no inlined code so we will not attempt to
- // patch it.
- switch (ic->kind()) {
- case Code::LOAD_IC:
- case Code::KEYED_LOAD_IC:
- case Code::STORE_IC:
- case Code::KEYED_STORE_IC:
- __ nop(); // Signals no inlined code.
- break;
- default:
- // Do nothing.
- break;
- }
+ __ call(ic, mode, ast_id);
}
-void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) {
+void FullCodeGenerator::EmitCallIC(Handle<Code> ic,
+ JumpPatchSite* patch_site,
+ unsigned ast_id) {
Counters* counters = isolate()->counters();
switch (ic->kind()) {
case Code::LOAD_IC:
@@ -4280,8 +4257,7 @@ void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) {
default:
break;
}
-
- __ call(ic, RelocInfo::CODE_TARGET);
+ __ call(ic, RelocInfo::CODE_TARGET, ast_id);
if (patch_site != NULL && patch_site->is_bound()) {
patch_site->EmitPatchInfo();
} else {
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index 5ca56ac7..cec8894e 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -97,58 +97,6 @@ static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
}
-// Probe the string dictionary in the |elements| register. Jump to the
-// |done| label if a property with the given name is found leaving the
-// index into the dictionary in |r1|. Jump to the |miss| label
-// otherwise.
-static void GenerateStringDictionaryProbes(MacroAssembler* masm,
- Label* miss,
- Label* done,
- Register elements,
- Register name,
- Register r0,
- Register r1) {
- // Assert that name contains a string.
- if (FLAG_debug_code) __ AbortIfNotString(name);
-
- // Compute the capacity mask.
- const int kCapacityOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kCapacityIndex * kPointerSize;
- __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset));
- __ decl(r0);
-
- // Generate an unrolled loop that performs a few probes before
- // giving up. Measurements done on Gmail indicate that 2 probes
- // cover ~93% of loads from dictionaries.
- static const int kProbes = 4;
- const int kElementsStartOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kElementsStartIndex * kPointerSize;
- for (int i = 0; i < kProbes; i++) {
- // Compute the masked index: (hash + i + i * i) & mask.
- __ movl(r1, FieldOperand(name, String::kHashFieldOffset));
- __ shrl(r1, Immediate(String::kHashShift));
- if (i > 0) {
- __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i)));
- }
- __ and_(r1, r0);
-
- // Scale the index by multiplying by the entry size.
- ASSERT(StringDictionary::kEntrySize == 3);
- __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
-
- // Check if the key is identical to the name.
- __ cmpq(name, Operand(elements, r1, times_pointer_size,
- kElementsStartOffset - kHeapObjectTag));
- if (i != kProbes - 1) {
- __ j(equal, done);
- } else {
- __ j(not_equal, miss);
- }
- }
-}
-
// Helper function used to load a property from a dictionary backing storage.
// This function may return false negatives, so miss_label
@@ -179,13 +127,13 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
Label done;
// Probe the dictionary.
- GenerateStringDictionaryProbes(masm,
- miss_label,
- &done,
- elements,
- name,
- r0,
- r1);
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
// If probing finds an entry in the dictionary, r0 contains the
// index into the dictionary. Check that the value is a normal
@@ -237,13 +185,13 @@ static void GenerateDictionaryStore(MacroAssembler* masm,
Label done;
// Probe the dictionary.
- GenerateStringDictionaryProbes(masm,
- miss_label,
- &done,
- elements,
- name,
- scratch0,
- scratch1);
+ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ scratch0,
+ scratch1);
// If probing finds an entry in the dictionary, scratch0 contains the
// index into the dictionary. Check that the value is a normal
@@ -381,11 +329,6 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
}
-// The offset from the inlined patch site to the start of the inlined
-// load instruction.
-const int LoadIC::kOffsetToLoadInstruction = 20;
-
-
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : receiver
@@ -715,7 +658,7 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
char_at_generator.GenerateSlow(masm, call_helper);
__ bind(&miss);
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
}
@@ -758,7 +701,7 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
1);
__ bind(&slow);
- GenerateMiss(masm);
+ GenerateMiss(masm, false);
}
@@ -852,10 +795,10 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// rax: value
// rbx: receiver's elements array (a FixedArray)
// rcx: index
- NearLabel non_smi_value;
+ Label non_smi_value;
__ movq(FieldOperand(rbx, rcx, times_pointer_size, FixedArray::kHeaderSize),
rax);
- __ JumpIfNotSmi(rax, &non_smi_value);
+ __ JumpIfNotSmi(rax, &non_smi_value, Label::kNear);
__ ret(0);
__ bind(&non_smi_value);
// Slow case that needs to retain rcx for use by RecordWrite.
@@ -870,7 +813,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
int argc,
- Code::Kind kind) {
+ Code::Kind kind,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// rcx : function name
// rdx : receiver
@@ -881,7 +825,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
MONOMORPHIC,
- Code::kNoExtraICState,
+ extra_ic_state,
NORMAL,
argc);
Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx,
@@ -948,7 +892,8 @@ static void GenerateFunctionTailCall(MacroAssembler* masm,
// Invoke the function.
ParameterCount actual(argc);
- __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
+ __ InvokeFunction(rdi, actual, JUMP_FUNCTION,
+ NullCallWrapper(), CALL_AS_METHOD);
}
@@ -980,7 +925,10 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
}
-static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
+static void GenerateCallMiss(MacroAssembler* masm,
+ int argc,
+ IC::UtilityId id,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
@@ -1037,12 +985,21 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
}
// Invoke the function.
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
ParameterCount actual(argc);
- __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ call_kind);
}
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMegamorphic(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
@@ -1055,8 +1012,8 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack; 1 ~ return address.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
- GenerateMiss(masm, argc);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
+ GenerateMiss(masm, argc, extra_ic_state);
}
@@ -1072,11 +1029,13 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
// -----------------------------------
GenerateCallNormal(masm, argc);
- GenerateMiss(masm, argc);
+ GenerateMiss(masm, argc, Code::kNoExtraICState);
}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+void CallIC::GenerateMiss(MacroAssembler* masm,
+ int argc,
+ Code::ExtraICState extra_ic_state) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
@@ -1087,7 +1046,7 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss, extra_ic_state);
}
@@ -1178,7 +1137,10 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
__ bind(&lookup_monomorphic_cache);
__ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1);
- GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
+ GenerateMonomorphicCacheProbe(masm,
+ argc,
+ Code::KEYED_CALL_IC,
+ Code::kNoExtraICState);
// Fall through on miss.
__ bind(&slow_call);
@@ -1231,7 +1193,7 @@ void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
- GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss, Code::kNoExtraICState);
}
@@ -1297,131 +1259,7 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
}
-bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // If the instruction following the call is not a test rax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- Address delta_address = test_instruction_address + 1;
- // The delta to the start of the map check instruction.
- int delta = *reinterpret_cast<int*>(delta_address);
-
- // The map address is the last 8 bytes of the 10-byte
- // immediate move instruction, so we add 2 to get the
- // offset to the last 8 bytes.
- Address map_address = test_instruction_address + delta + 2;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // The offset is in the 32-bit displacement of a seven byte
- // memory-to-register move instruction (REX.W 0x88 ModR/M disp32),
- // so we add 3 to get the offset of the displacement.
- Address offset_address =
- test_instruction_address + delta + kOffsetToLoadInstruction + 3;
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
- return true;
-}
-
-
-bool LoadIC::PatchInlinedContextualLoad(Address address,
- Object* map,
- Object* cell,
- bool is_dont_delete) {
- // TODO(<bug#>): implement this.
- return false;
-}
-
-
-bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) {
- if (V8::UseCrankshaft()) return false;
-
- // The address of the instruction following the call.
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
-
- // If the instruction following the call is not a test rax, nothing
- // was inlined.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Extract the encoded deltas from the test rax instruction.
- Address encoded_offsets_address = test_instruction_address + 1;
- int encoded_offsets = *reinterpret_cast<int*>(encoded_offsets_address);
- int delta_to_map_check = -(encoded_offsets & 0xFFFF);
- int delta_to_record_write = encoded_offsets >> 16;
-
- // Patch the map to check. The map address is the last 8 bytes of
- // the 10-byte immediate move instruction.
- Address map_check_address = test_instruction_address + delta_to_map_check;
- Address map_address = map_check_address + 2;
- *(reinterpret_cast<Object**>(map_address)) = map;
-
- // Patch the offset in the store instruction. The offset is in the
- // last 4 bytes of a 7 byte register-to-memory move instruction.
- Address offset_address =
- map_check_address + StoreIC::kOffsetToStoreInstruction + 3;
- // The offset should have initial value (kMaxInt - 1), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt - 1 ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- // Patch the offset in the write-barrier code. The offset is the
- // last 4 bytes of a 7 byte lea instruction.
- offset_address = map_check_address + delta_to_record_write + 3;
- // The offset should have initial value (kMaxInt), cleared value
- // (-1) or we should be clearing the inlined version.
- ASSERT(*reinterpret_cast<int*>(offset_address) == kMaxInt ||
- *reinterpret_cast<int*>(offset_address) == -1 ||
- (offset == 0 && map == HEAP->null_value()));
- *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
-
- return true;
-}
-
-
-static bool PatchInlinedMapCheck(Address address, Object* map) {
- if (V8::UseCrankshaft()) return false;
-
- // Arguments are address of start of call sequence that called
- // the IC,
- Address test_instruction_address =
- address + Assembler::kCallTargetAddressOffset;
- // The keyed load has a fast inlined case if the IC call instruction
- // is immediately followed by a test instruction.
- if (*test_instruction_address != Assembler::kTestEaxByte) return false;
-
- // Fetch the offset from the test instruction to the map compare
- // instructions (starting with the 64-bit immediate mov of the map
- // address). This offset is stored in the last 4 bytes of the 5
- // byte test instruction.
- Address delta_address = test_instruction_address + 1;
- int delta = *reinterpret_cast<int*>(delta_address);
- // Compute the map address. The map address is in the last 8 bytes
- // of the 10-byte immediate mov instruction (incl. REX prefix), so we add 2
- // to the offset to get the map address.
- Address map_address = test_instruction_address + delta + 2;
- // Patch the map check.
- *(reinterpret_cast<Object**>(map_address)) = map;
- return true;
-}
-
-
-bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
-bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) {
- return PatchInlinedMapCheck(address, map);
-}
-
-
-void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
@@ -1437,8 +1275,10 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
__ push(rbx); // return address
// Perform tail call to the entry.
- ExternalReference ref
- = ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
__ TailCallExternalReference(ref, 2, 1);
}
@@ -1503,11 +1343,6 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) {
}
-// The offset from the inlined patch site to the start of the inlined
-// store instruction.
-const int StoreIC::kOffsetToStoreInstruction = 20;
-
-
void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
@@ -1627,7 +1462,7 @@ void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
}
-void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
@@ -1642,8 +1477,30 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
__ push(rbx); // return address
// Do tail-call to runtime routine.
- ExternalReference ref =
- ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
+ ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ __ pop(rbx);
+ __ push(rdx); // receiver
+ __ push(rcx); // key
+ __ push(rax); // value
+ __ push(rbx); // return address
+
+ // Do tail-call to runtime routine.
+ ExternalReference ref = force_generic
+ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
+ masm->isolate())
+ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
__ TailCallExternalReference(ref, 3, 1);
}
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 822295e2..9fa11fa8 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -49,7 +49,7 @@ class SafepointGenerator : public CallWrapper {
deoptimization_index_(deoptimization_index) { }
virtual ~SafepointGenerator() { }
- virtual void BeforeCall(int call_size) {
+ virtual void BeforeCall(int call_size) const {
ASSERT(call_size >= 0);
// Ensure that we have enough space after the previous safepoint position
// for the jump generated there.
@@ -62,7 +62,7 @@ class SafepointGenerator : public CallWrapper {
}
}
- virtual void AfterCall() {
+ virtual void AfterCall() const {
codegen_->RecordSafepoint(pointers_, deoptimization_index_);
}
@@ -91,7 +91,7 @@ bool LCodeGen::GenerateCode() {
void LCodeGen::FinishCode(Handle<Code> code) {
ASSERT(is_done());
- code->set_stack_slots(StackSlotCount());
+ code->set_stack_slots(GetStackSlotCount());
code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
PopulateDeoptimizationData(code);
Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(code);
@@ -140,13 +140,28 @@ bool LCodeGen::GeneratePrologue() {
}
#endif
+ // Strict mode functions need to replace the receiver with undefined
+ // when called as functions (without an explicit receiver
+ // object). rcx is zero for method calls and non-zero for function
+ // calls.
+ if (info_->is_strict_mode()) {
+ Label ok;
+ __ testq(rcx, rcx);
+ __ j(zero, &ok, Label::kNear);
+ // +1 for return address.
+ int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
+ __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
+ __ movq(Operand(rsp, receiver_offset), kScratchRegister);
+ __ bind(&ok);
+ }
+
__ push(rbp); // Caller's frame pointer.
__ movq(rbp, rsp);
__ push(rsi); // Callee's context.
__ push(rdi); // Callee's JS function.
// Reserve space for the stack slots needed by the code.
- int slots = StackSlotCount();
+ int slots = GetStackSlotCount();
if (slots > 0) {
if (FLAG_debug_code) {
__ Set(rax, slots);
@@ -290,7 +305,7 @@ bool LCodeGen::GenerateSafepointTable() {
while (byte_count-- > 0) {
__ int3();
}
- safepoints_.Emit(masm(), StackSlotCount());
+ safepoints_.Emit(masm(), GetStackSlotCount());
return !is_aborted();
}
@@ -418,7 +433,7 @@ void LCodeGen::AddToTranslation(Translation* translation,
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsArgument()) {
ASSERT(is_tagged);
- int src_index = StackSlotCount() + op->index();
+ int src_index = GetStackSlotCount() + op->index();
translation->StoreStackSlot(src_index);
} else if (op->IsRegister()) {
Register reg = ToRegister(op);
@@ -453,7 +468,7 @@ void LCodeGen::CallCodeGeneric(Handle<Code> code,
// Signal that we don't inline smi code before these stubs in the
// optimizing code generator.
- if (code->kind() == Code::TYPE_RECORDING_BINARY_OP_IC ||
+ if (code->kind() == Code::BINARY_OP_IC ||
code->kind() == Code::COMPARE_IC) {
__ nop();
}
@@ -690,7 +705,7 @@ void LCodeGen::DoLabel(LLabel* label) {
}
__ bind(label->label());
current_block_ = label->block_id();
- LCodeGen::DoGap(label);
+ DoGap(label);
}
@@ -716,6 +731,11 @@ void LCodeGen::DoGap(LGap* gap) {
}
+void LCodeGen::DoInstructionGap(LInstructionGap* instr) {
+ DoGap(instr);
+}
+
+
void LCodeGen::DoParameter(LParameter* instr) {
// Nothing to do.
}
@@ -780,27 +800,29 @@ void LCodeGen::DoModI(LModI* instr) {
if (divisor < 0) divisor = -divisor;
- NearLabel positive_dividend, done;
+ Label positive_dividend, done;
__ testl(dividend, dividend);
- __ j(not_sign, &positive_dividend);
+ __ j(not_sign, &positive_dividend, Label::kNear);
__ negl(dividend);
__ andl(dividend, Immediate(divisor - 1));
__ negl(dividend);
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- __ j(not_zero, &done);
+ __ j(not_zero, &done, Label::kNear);
DeoptimizeIf(no_condition, instr->environment());
} else {
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
}
__ bind(&positive_dividend);
__ andl(dividend, Immediate(divisor - 1));
__ bind(&done);
} else {
- LOperand* right = instr->InputAt(1);
- Register right_reg = ToRegister(right);
+ Label done, remainder_eq_dividend, slow, do_subtraction, both_positive;
+ Register left_reg = ToRegister(instr->InputAt(0));
+ Register right_reg = ToRegister(instr->InputAt(1));
+ Register result_reg = ToRegister(instr->result());
- ASSERT(ToRegister(instr->result()).is(rdx));
- ASSERT(ToRegister(instr->InputAt(0)).is(rax));
+ ASSERT(left_reg.is(rax));
+ ASSERT(result_reg.is(rdx));
ASSERT(!right_reg.is(rax));
ASSERT(!right_reg.is(rdx));
@@ -810,21 +832,60 @@ void LCodeGen::DoModI(LModI* instr) {
DeoptimizeIf(zero, instr->environment());
}
+ __ testl(left_reg, left_reg);
+ __ j(zero, &remainder_eq_dividend, Label::kNear);
+ __ j(sign, &slow, Label::kNear);
+
+ __ testl(right_reg, right_reg);
+ __ j(not_sign, &both_positive, Label::kNear);
+ // The sign of the divisor doesn't matter.
+ __ neg(right_reg);
+
+ __ bind(&both_positive);
+ // If the dividend is smaller than the nonnegative
+ // divisor, the dividend is the result.
+ __ cmpl(left_reg, right_reg);
+ __ j(less, &remainder_eq_dividend, Label::kNear);
+
+ // Check if the divisor is a PowerOfTwo integer.
+ Register scratch = ToRegister(instr->TempAt(0));
+ __ movl(scratch, right_reg);
+ __ subl(scratch, Immediate(1));
+ __ testl(scratch, right_reg);
+ __ j(not_zero, &do_subtraction, Label::kNear);
+ __ andl(left_reg, scratch);
+ __ jmp(&remainder_eq_dividend, Label::kNear);
+
+ __ bind(&do_subtraction);
+ const int kUnfolds = 3;
+ // Try a few subtractions of the dividend.
+ __ movl(scratch, left_reg);
+ for (int i = 0; i < kUnfolds; i++) {
+ // Reduce the dividend by the divisor.
+ __ subl(left_reg, right_reg);
+ // Check if the dividend is less than the divisor.
+ __ cmpl(left_reg, right_reg);
+ __ j(less, &remainder_eq_dividend, Label::kNear);
+ }
+ __ movl(left_reg, scratch);
+
+ // Slow case, using idiv instruction.
+ __ bind(&slow);
// Sign extend eax to edx.
// (We are using only the low 32 bits of the values.)
__ cdq();
// Check for (0 % -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel positive_left;
- NearLabel done;
- __ testl(rax, rax);
- __ j(not_sign, &positive_left);
+ Label positive_left;
+ Label done;
+ __ testl(left_reg, left_reg);
+ __ j(not_sign, &positive_left, Label::kNear);
__ idivl(right_reg);
// Test the remainder for 0, because then the result would be -0.
- __ testl(rdx, rdx);
- __ j(not_zero, &done);
+ __ testl(result_reg, result_reg);
+ __ j(not_zero, &done, Label::kNear);
DeoptimizeIf(no_condition, instr->environment());
__ bind(&positive_left);
@@ -833,6 +894,12 @@ void LCodeGen::DoModI(LModI* instr) {
} else {
__ idivl(right_reg);
}
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&remainder_eq_dividend);
+ __ movl(result_reg, left_reg);
+
+ __ bind(&done);
}
}
@@ -855,9 +922,9 @@ void LCodeGen::DoDivI(LDivI* instr) {
// Check for (0 / -x) that will produce negative zero.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel left_not_zero;
+ Label left_not_zero;
__ testl(left_reg, left_reg);
- __ j(not_zero, &left_not_zero);
+ __ j(not_zero, &left_not_zero, Label::kNear);
__ testl(right_reg, right_reg);
DeoptimizeIf(sign, instr->environment());
__ bind(&left_not_zero);
@@ -865,9 +932,9 @@ void LCodeGen::DoDivI(LDivI* instr) {
// Check for (-kMinInt / -1).
if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
- NearLabel left_not_min_int;
+ Label left_not_min_int;
__ cmpl(left_reg, Immediate(kMinInt));
- __ j(not_zero, &left_not_min_int);
+ __ j(not_zero, &left_not_min_int, Label::kNear);
__ cmpl(right_reg, Immediate(-1));
DeoptimizeIf(zero, instr->environment());
__ bind(&left_not_min_int);
@@ -946,9 +1013,9 @@ void LCodeGen::DoMulI(LMulI* instr) {
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
// Bail out if the result is supposed to be negative zero.
- NearLabel done;
+ Label done;
__ testl(left, left);
- __ j(not_zero, &done);
+ __ j(not_zero, &done, Label::kNear);
if (right->IsConstantOperand()) {
if (ToInteger32(LConstantOperand::cast(right)) <= 0) {
DeoptimizeIf(no_condition, instr->environment());
@@ -1113,7 +1180,7 @@ void LCodeGen::DoConstantD(LConstantD* instr) {
// Use xor to produce +0.0 in a fast and compact way, but avoid to
// do so if the constant is -0.0.
if (int_val == 0) {
- __ xorpd(res, res);
+ __ xorps(res, res);
} else {
Register tmp = ToRegister(instr->TempAt(0));
__ Set(tmp, int_val);
@@ -1153,13 +1220,13 @@ void LCodeGen::DoValueOf(LValueOf* instr) {
Register input = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
ASSERT(input.is(result));
- NearLabel done;
+ Label done;
// If the object is a smi return the object.
- __ JumpIfSmi(input, &done);
+ __ JumpIfSmi(input, &done, Label::kNear);
// If the object is not a value type, return the object.
__ CmpObjectType(input, JS_VALUE_TYPE, kScratchRegister);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
__ movq(result, FieldOperand(input, JSValue::kValueOffset));
__ bind(&done);
@@ -1225,12 +1292,12 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
break;
case Token::MOD:
__ PrepareCallCFunction(2);
- __ movsd(xmm0, left);
+ __ movaps(xmm0, left);
ASSERT(right.is(xmm1));
__ CallCFunction(
ExternalReference::double_fp_operation(Token::MOD, isolate()), 2);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
- __ movsd(result, xmm0);
+ __ movaps(result, xmm0);
break;
default:
UNREACHABLE();
@@ -1244,7 +1311,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
ASSERT(ToRegister(instr->InputAt(1)).is(rax));
ASSERT(ToRegister(instr->result()).is(rax));
- TypeRecordingBinaryOpStub stub(instr->op(), NO_OVERWRITE);
+ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ nop(); // Signals no inlined code.
}
@@ -1290,7 +1357,7 @@ void LCodeGen::DoBranch(LBranch* instr) {
EmitBranch(true_block, false_block, not_zero);
} else if (r.IsDouble()) {
XMMRegister reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(reg, xmm0);
EmitBranch(true_block, false_block, not_equal);
} else {
@@ -1318,14 +1385,14 @@ void LCodeGen::DoBranch(LBranch* instr) {
__ JumpIfSmi(reg, true_label);
// Test for double values. Plus/minus zero and NaN are false.
- NearLabel call_stub;
+ Label call_stub;
__ CompareRoot(FieldOperand(reg, HeapObject::kMapOffset),
Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &call_stub);
+ __ j(not_equal, &call_stub, Label::kNear);
// HeapNumber => false iff +0, -0, or NaN. These three cases set the
// zero flag when compared to zero using ucomisd.
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, FieldOperand(reg, HeapNumber::kValueOffset));
__ j(zero, false_label);
__ jmp(true_label);
@@ -1435,20 +1502,20 @@ void LCodeGen::DoCmpID(LCmpID* instr) {
LOperand* right = instr->InputAt(1);
LOperand* result = instr->result();
- NearLabel unordered;
+ Label unordered;
if (instr->is_double()) {
// Don't base result on EFLAGS when a NaN is involved. Instead
// jump to the unordered case, which produces a false value.
__ ucomisd(ToDoubleRegister(left), ToDoubleRegister(right));
- __ j(parity_even, &unordered);
+ __ j(parity_even, &unordered, Label::kNear);
} else {
EmitCmpI(left, right);
}
- NearLabel done;
+ Label done;
Condition cc = TokenToCondition(instr->op(), instr->is_double());
__ LoadRoot(ToRegister(result), Heap::kTrueValueRootIndex);
- __ j(cc, &done);
+ __ j(cc, &done, Label::kNear);
__ bind(&unordered);
__ LoadRoot(ToRegister(result), Heap::kFalseValueRootIndex);
@@ -1481,11 +1548,11 @@ void LCodeGen::DoCmpJSObjectEq(LCmpJSObjectEq* instr) {
Register right = ToRegister(instr->InputAt(1));
Register result = ToRegister(instr->result());
- NearLabel different, done;
+ Label different, done;
__ cmpq(left, right);
- __ j(not_equal, &different);
+ __ j(not_equal, &different, Label::kNear);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&different);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ bind(&done);
@@ -1503,6 +1570,31 @@ void LCodeGen::DoCmpJSObjectEqAndBranch(LCmpJSObjectEqAndBranch* instr) {
}
+void LCodeGen::DoCmpSymbolEq(LCmpSymbolEq* instr) {
+ Register left = ToRegister(instr->InputAt(0));
+ Register right = ToRegister(instr->InputAt(1));
+ Register result = ToRegister(instr->result());
+
+ Label done;
+ __ cmpq(left, right);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex);
+ __ j(not_equal, &done, Label::kNear);
+ __ LoadRoot(result, Heap::kTrueValueRootIndex);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoCmpSymbolEqAndBranch(LCmpSymbolEqAndBranch* instr) {
+ Register left = ToRegister(instr->InputAt(0));
+ Register right = ToRegister(instr->InputAt(1));
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+
+ __ cmpq(left, right);
+ EmitBranch(true_block, false_block, equal);
+}
+
+
void LCodeGen::DoIsNull(LIsNull* instr) {
Register reg = ToRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
@@ -1519,27 +1611,27 @@ void LCodeGen::DoIsNull(LIsNull* instr) {
if (instr->is_strict()) {
ASSERT(Heap::kTrueValueRootIndex >= 0);
__ movl(result, Immediate(Heap::kTrueValueRootIndex));
- NearLabel load;
- __ j(equal, &load);
+ Label load;
+ __ j(equal, &load, Label::kNear);
__ Set(result, Heap::kFalseValueRootIndex);
__ bind(&load);
__ LoadRootIndexed(result, result, 0);
} else {
- NearLabel true_value, false_value, done;
- __ j(equal, &true_value);
+ Label false_value, true_value, done;
+ __ j(equal, &true_value, Label::kNear);
__ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
- __ j(equal, &true_value);
- __ JumpIfSmi(reg, &false_value);
+ __ j(equal, &true_value, Label::kNear);
+ __ JumpIfSmi(reg, &false_value, Label::kNear);
// Check for undetectable objects by looking in the bit field in
// the map. The object has already been smi checked.
Register scratch = result;
__ movq(scratch, FieldOperand(reg, HeapObject::kMapOffset));
__ testb(FieldOperand(scratch, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
- __ j(not_zero, &true_value);
+ __ j(not_zero, &true_value, Label::kNear);
__ bind(&false_value);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ bind(&done);
@@ -1674,6 +1766,40 @@ void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
}
+void LCodeGen::DoIsUndetectable(LIsUndetectable* instr) {
+ Register input = ToRegister(instr->InputAt(0));
+ Register result = ToRegister(instr->result());
+
+ ASSERT(instr->hydrogen()->value()->representation().IsTagged());
+ Label false_label, done;
+ __ JumpIfSmi(input, &false_label);
+ __ movq(result, FieldOperand(input, HeapObject::kMapOffset));
+ __ testb(FieldOperand(result, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(zero, &false_label);
+ __ LoadRoot(result, Heap::kTrueValueRootIndex);
+ __ jmp(&done);
+ __ bind(&false_label);
+ __ LoadRoot(result, Heap::kFalseValueRootIndex);
+ __ bind(&done);
+}
+
+
+void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
+ Register input = ToRegister(instr->InputAt(0));
+ Register temp = ToRegister(instr->TempAt(0));
+
+ int true_block = chunk_->LookupDestination(instr->true_block_id());
+ int false_block = chunk_->LookupDestination(instr->false_block_id());
+
+ __ JumpIfSmi(input, chunk_->GetAssemblyLabel(false_block));
+ __ movq(temp, FieldOperand(input, HeapObject::kMapOffset));
+ __ testb(FieldOperand(temp, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ EmitBranch(true_block, false_block, not_zero);
+}
+
+
static InstanceType TestType(HHasInstanceType* instr) {
InstanceType from = instr->from();
InstanceType to = instr->to();
@@ -1700,12 +1826,13 @@ void LCodeGen::DoHasInstanceType(LHasInstanceType* instr) {
ASSERT(instr->hydrogen()->value()->representation().IsTagged());
__ testl(input, Immediate(kSmiTagMask));
- NearLabel done, is_false;
+ Label done, is_false;
__ j(zero, &is_false);
__ CmpObjectType(input, TestType(instr->hydrogen()), result);
- __ j(NegateCondition(BranchCondition(instr->hydrogen())), &is_false);
+ __ j(NegateCondition(BranchCondition(instr->hydrogen())),
+ &is_false, Label::kNear);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&is_false);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ bind(&done);
@@ -1749,8 +1876,8 @@ void LCodeGen::DoHasCachedArrayIndex(LHasCachedArrayIndex* instr) {
__ LoadRoot(result, Heap::kTrueValueRootIndex);
__ testl(FieldOperand(input, String::kHashFieldOffset),
Immediate(String::kContainsCachedArrayIndexMask));
- NearLabel done;
- __ j(zero, &done);
+ Label done;
+ __ j(zero, &done, Label::kNear);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
__ bind(&done);
}
@@ -1829,7 +1956,7 @@ void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
ASSERT(input.is(result));
Register temp = ToRegister(instr->TempAt(0));
Handle<String> class_name = instr->hydrogen()->class_name();
- NearLabel done;
+ Label done;
Label is_true, is_false;
EmitClassOfTest(&is_true, &is_false, class_name, input, temp);
@@ -1838,7 +1965,7 @@ void LCodeGen::DoClassOfTest(LClassOfTest* instr) {
__ bind(&is_true);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&is_false);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
@@ -1878,11 +2005,11 @@ void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
__ push(ToRegister(instr->InputAt(0)));
__ push(ToRegister(instr->InputAt(1)));
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
- NearLabel true_value, done;
+ Label true_value, done;
__ testq(rax, rax);
- __ j(zero, &true_value);
+ __ j(zero, &true_value, Label::kNear);
__ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
__ bind(&done);
@@ -1932,7 +2059,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
// This is the inlined call site instanceof cache. The two occurences of the
// hole value will be patched to the last map/result pair generated by the
// instanceof stub.
- NearLabel cache_miss;
+ Label cache_miss;
// Use a temp register to avoid memory operands with variable lengths.
Register map = ToRegister(instr->TempAt(0));
__ movq(map, FieldOperand(object, HeapObject::kMapOffset));
@@ -1940,7 +2067,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
__ movq(kScratchRegister, factory()->the_hole_value(),
RelocInfo::EMBEDDED_OBJECT);
__ cmpq(map, kScratchRegister); // Patched to cached map.
- __ j(not_equal, &cache_miss);
+ __ j(not_equal, &cache_miss, Label::kNear);
// Patched to load either true or false.
__ LoadRoot(ToRegister(instr->result()), Heap::kTheHoleValueRootIndex);
#ifdef DEBUG
@@ -1955,7 +2082,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
// before calling the deferred code.
__ bind(&cache_miss); // Null is not an instance of anything.
__ CompareRoot(object, Heap::kNullValueRootIndex);
- __ j(equal, &false_result);
+ __ j(equal, &false_result, Label::kNear);
// String values are not instances of anything.
__ JumpIfNotString(object, kScratchRegister, deferred->entry());
@@ -2022,11 +2149,11 @@ void LCodeGen::DoCmpT(LCmpT* instr) {
if (op == Token::GT || op == Token::LTE) {
condition = ReverseCondition(condition);
}
- NearLabel true_value, done;
+ Label true_value, done;
__ testq(rax, rax);
- __ j(condition, &true_value);
+ __ j(condition, &true_value, Label::kNear);
__ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_value);
__ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
__ bind(&done);
@@ -2061,7 +2188,7 @@ void LCodeGen::DoReturn(LReturn* instr) {
}
__ movq(rsp, rbp);
__ pop(rbp);
- __ Ret((ParameterCount() + 1) * kPointerSize, rcx);
+ __ Ret((GetParameterCount() + 1) * kPointerSize, rcx);
}
@@ -2159,23 +2286,29 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
}
-void LCodeGen::EmitLoadField(Register result,
- Register object,
- Handle<Map> type,
- Handle<String> name) {
+void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
+ Register object,
+ Handle<Map> type,
+ Handle<String> name) {
LookupResult lookup;
type->LookupInDescriptors(NULL, *name, &lookup);
- ASSERT(lookup.IsProperty() && lookup.type() == FIELD);
- int index = lookup.GetLocalFieldIndexFromMap(*type);
- int offset = index * kPointerSize;
- if (index < 0) {
- // Negative property indices are in-object properties, indexed
- // from the end of the fixed part of the object.
- __ movq(result, FieldOperand(object, offset + type->instance_size()));
+ ASSERT(lookup.IsProperty() &&
+ (lookup.type() == FIELD || lookup.type() == CONSTANT_FUNCTION));
+ if (lookup.type() == FIELD) {
+ int index = lookup.GetLocalFieldIndexFromMap(*type);
+ int offset = index * kPointerSize;
+ if (index < 0) {
+ // Negative property indices are in-object properties, indexed
+ // from the end of the fixed part of the object.
+ __ movq(result, FieldOperand(object, offset + type->instance_size()));
+ } else {
+ // Non-negative property indices are in the properties array.
+ __ movq(result, FieldOperand(object, JSObject::kPropertiesOffset));
+ __ movq(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
+ }
} else {
- // Non-negative property indices are in the properties array.
- __ movq(result, FieldOperand(object, JSObject::kPropertiesOffset));
- __ movq(result, FieldOperand(result, offset + FixedArray::kHeaderSize));
+ Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
+ LoadHeapObject(result, Handle<HeapObject>::cast(function));
}
}
@@ -2193,30 +2326,30 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
- NearLabel done;
+ Label done;
for (int i = 0; i < map_count - 1; ++i) {
Handle<Map> map = instr->hydrogen()->types()->at(i);
- NearLabel next;
+ Label next;
__ Cmp(FieldOperand(object, HeapObject::kMapOffset), map);
- __ j(not_equal, &next);
- EmitLoadField(result, object, map, name);
- __ jmp(&done);
+ __ j(not_equal, &next, Label::kNear);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
+ __ jmp(&done, Label::kNear);
__ bind(&next);
}
Handle<Map> map = instr->hydrogen()->types()->last();
__ Cmp(FieldOperand(object, HeapObject::kMapOffset), map);
if (instr->hydrogen()->need_generic()) {
- NearLabel generic;
- __ j(not_equal, &generic);
- EmitLoadField(result, object, map, name);
- __ jmp(&done);
+ Label generic;
+ __ j(not_equal, &generic, Label::kNear);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
+ __ jmp(&done, Label::kNear);
__ bind(&generic);
__ Move(rcx, instr->hydrogen()->name());
Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
} else {
DeoptimizeIf(not_equal, instr->environment());
- EmitLoadField(result, object, map, name);
+ EmitLoadFieldOrConstantFunction(result, object, map, name);
}
__ bind(&done);
}
@@ -2242,10 +2375,10 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
DeoptimizeIf(not_equal, instr->environment());
// Check whether the function has an instance prototype.
- NearLabel non_instance;
+ Label non_instance;
__ testb(FieldOperand(result, Map::kBitFieldOffset),
Immediate(1 << Map::kHasNonInstancePrototype));
- __ j(not_zero, &non_instance);
+ __ j(not_zero, &non_instance, Label::kNear);
// Get the prototype or initial map from the function.
__ movq(result,
@@ -2256,13 +2389,13 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
DeoptimizeIf(equal, instr->environment());
// If the function does not have an initial map, we're done.
- NearLabel done;
+ Label done;
__ CmpObjectType(result, MAP_TYPE, kScratchRegister);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
// Get the prototype from the initial map.
__ movq(result, FieldOperand(result, Map::kPrototypeOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// Non-instance prototype: Fetch prototype from constructor field
// in the function's map.
@@ -2279,13 +2412,13 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) {
Register input = ToRegister(instr->InputAt(0));
__ movq(result, FieldOperand(input, JSObject::kElementsOffset));
if (FLAG_debug_code) {
- NearLabel done;
+ Label done;
__ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
Heap::kFixedArrayMapRootIndex);
- __ j(equal, &done);
+ __ j(equal, &done, Label::kNear);
__ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
Heap::kFixedCOWArrayMapRootIndex);
- __ j(equal, &done);
+ __ j(equal, &done, Label::kNear);
Register temp((result.is(rax)) ? rbx : rax);
__ push(temp);
__ movq(temp, FieldOperand(result, HeapObject::kMapOffset));
@@ -2339,41 +2472,63 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
FixedArray::kHeaderSize));
// Check for the hole value.
- __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
- DeoptimizeIf(equal, instr->environment());
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+ __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
+ DeoptimizeIf(equal, instr->environment());
+ }
+}
+
+
+Operand LCodeGen::BuildExternalArrayOperand(LOperand* external_pointer,
+ LOperand* key,
+ ExternalArrayType array_type) {
+ Register external_pointer_reg = ToRegister(external_pointer);
+ int shift_size = ExternalArrayTypeToShiftSize(array_type);
+ if (key->IsConstantOperand()) {
+ int constant_value = ToInteger32(LConstantOperand::cast(key));
+ if (constant_value & 0xF0000000) {
+ Abort("array index constant value too big");
+ }
+ return Operand(external_pointer_reg, constant_value * (1 << shift_size));
+ } else {
+ ScaleFactor scale_factor = static_cast<ScaleFactor>(shift_size);
+ return Operand(external_pointer_reg, ToRegister(key), scale_factor, 0);
+ }
}
void LCodeGen::DoLoadKeyedSpecializedArrayElement(
LLoadKeyedSpecializedArrayElement* instr) {
- Register external_pointer = ToRegister(instr->external_pointer());
- Register key = ToRegister(instr->key());
ExternalArrayType array_type = instr->array_type();
+ Operand operand(BuildExternalArrayOperand(instr->external_pointer(),
+ instr->key(), array_type));
if (array_type == kExternalFloatArray) {
XMMRegister result(ToDoubleRegister(instr->result()));
- __ movss(result, Operand(external_pointer, key, times_4, 0));
+ __ movss(result, operand);
__ cvtss2sd(result, result);
+ } else if (array_type == kExternalDoubleArray) {
+ __ movsd(ToDoubleRegister(instr->result()), operand);
} else {
Register result(ToRegister(instr->result()));
switch (array_type) {
case kExternalByteArray:
- __ movsxbq(result, Operand(external_pointer, key, times_1, 0));
+ __ movsxbq(result, operand);
break;
case kExternalUnsignedByteArray:
case kExternalPixelArray:
- __ movzxbq(result, Operand(external_pointer, key, times_1, 0));
+ __ movzxbq(result, operand);
break;
case kExternalShortArray:
- __ movsxwq(result, Operand(external_pointer, key, times_2, 0));
+ __ movsxwq(result, operand);
break;
case kExternalUnsignedShortArray:
- __ movzxwq(result, Operand(external_pointer, key, times_2, 0));
+ __ movzxwq(result, operand);
break;
case kExternalIntArray:
- __ movsxlq(result, Operand(external_pointer, key, times_4, 0));
+ __ movsxlq(result, operand);
break;
case kExternalUnsignedIntArray:
- __ movl(result, Operand(external_pointer, key, times_4, 0));
+ __ movl(result, operand);
__ testl(result, result);
// TODO(danno): we could be more clever here, perhaps having a special
// version of the stub that detects if the overflow case actually
@@ -2381,6 +2536,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
DeoptimizeIf(negative, instr->environment());
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
UNREACHABLE();
break;
}
@@ -2401,15 +2557,15 @@ void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
Register result = ToRegister(instr->result());
// Check for arguments adapter frame.
- NearLabel done, adapted;
+ Label done, adapted;
__ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
__ Cmp(Operand(result, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(equal, &adapted);
+ __ j(equal, &adapted, Label::kNear);
// No arguments adaptor frame.
__ movq(result, rbp);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// Arguments adaptor frame present.
__ bind(&adapted);
@@ -2424,7 +2580,7 @@ void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
Register result = ToRegister(instr->result());
- NearLabel done;
+ Label done;
// If no arguments adaptor frame the number of arguments is fixed.
if (instr->InputAt(0)->IsRegister()) {
@@ -2433,7 +2589,7 @@ void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
__ cmpq(rbp, ToOperand(instr->InputAt(0)));
}
__ movl(result, Immediate(scope()->num_parameters()));
- __ j(equal, &done);
+ __ j(equal, &done, Label::kNear);
// Arguments adaptor frame present. Get argument length from there.
__ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
@@ -2455,27 +2611,31 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
ASSERT(function.is(rdi)); // Required by InvokeFunction.
ASSERT(ToRegister(instr->result()).is(rax));
+ // TODO(1412): This is not correct if the called function is a
+ // strict mode function or a native.
+ //
// If the receiver is null or undefined, we have to pass the global object
// as a receiver.
- NearLabel global_object, receiver_ok;
+ Label global_object, receiver_ok;
__ CompareRoot(receiver, Heap::kNullValueRootIndex);
- __ j(equal, &global_object);
+ __ j(equal, &global_object, Label::kNear);
__ CompareRoot(receiver, Heap::kUndefinedValueRootIndex);
- __ j(equal, &global_object);
+ __ j(equal, &global_object, Label::kNear);
// The receiver should be a JS object.
Condition is_smi = __ CheckSmi(receiver);
DeoptimizeIf(is_smi, instr->environment());
__ CmpObjectType(receiver, FIRST_JS_OBJECT_TYPE, kScratchRegister);
DeoptimizeIf(below, instr->environment());
- __ jmp(&receiver_ok);
+ __ jmp(&receiver_ok, Label::kNear);
__ bind(&global_object);
// TODO(kmillikin): We have a hydrogen value for the global object. See
// if it's better to use it than to explicitly fetch it from the context
// here.
- __ movq(receiver, Operand(rbp, StandardFrameConstants::kContextOffset));
- __ movq(receiver, ContextOperand(receiver, Context::GLOBAL_INDEX));
+ __ movq(receiver, ContextOperand(rsi, Context::GLOBAL_INDEX));
+ __ movq(receiver,
+ FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
__ bind(&receiver_ok);
// Copy the arguments to this function possibly from the
@@ -2489,10 +2649,10 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
// Loop through the arguments pushing them onto the execution
// stack.
- NearLabel invoke, loop;
+ Label invoke, loop;
// length is a small non-negative integer, due to the test above.
__ testl(length, length);
- __ j(zero, &invoke);
+ __ j(zero, &invoke, Label::kNear);
__ bind(&loop);
__ push(Operand(elements, length, times_pointer_size, 1 * kPointerSize));
__ decl(length);
@@ -2509,26 +2669,21 @@ void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
pointers,
env->deoptimization_index());
v8::internal::ParameterCount actual(rax);
- __ InvokeFunction(function, actual, CALL_FUNCTION, &safepoint_generator);
+ __ InvokeFunction(function, actual, CALL_FUNCTION,
+ safepoint_generator, CALL_AS_METHOD);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
void LCodeGen::DoPushArgument(LPushArgument* instr) {
LOperand* argument = instr->InputAt(0);
- if (argument->IsConstantOperand()) {
- EmitPushConstantOperand(argument);
- } else if (argument->IsRegister()) {
- __ push(ToRegister(argument));
- } else {
- ASSERT(!argument->IsDoubleRegister());
- __ push(ToOperand(argument));
- }
+ EmitPushTaggedOperand(argument);
}
void LCodeGen::DoContext(LContext* instr) {
Register result = ToRegister(instr->result());
- __ movq(result, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ movq(result, rsi);
}
@@ -2556,7 +2711,8 @@ void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
int arity,
- LInstruction* instr) {
+ LInstruction* instr,
+ CallKind call_kind) {
// Change context if needed.
bool change_context =
(info()->closure()->context() != function->context()) ||
@@ -2576,6 +2732,7 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
RecordPosition(pointers->position());
// Invoke function.
+ __ SetCallKind(rcx, call_kind);
if (*function == *info()->closure()) {
__ CallSelf();
} else {
@@ -2593,7 +2750,10 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
ASSERT(ToRegister(instr->result()).is(rax));
__ Move(rdi, instr->function());
- CallKnownFunction(instr->function(), instr->arity(), instr);
+ CallKnownFunction(instr->function(),
+ instr->arity(),
+ instr,
+ CALL_AS_METHOD);
}
@@ -2680,7 +2840,7 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
if (r.IsDouble()) {
XMMRegister scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(scratch, scratch);
+ __ xorps(scratch, scratch);
__ subsd(scratch, input_reg);
__ andpd(input_reg, scratch);
} else if (r.IsInteger32()) {
@@ -2703,21 +2863,36 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
XMMRegister xmm_scratch = xmm0;
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
- __ ucomisd(input_reg, xmm_scratch);
- if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- DeoptimizeIf(below_equal, instr->environment());
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ CpuFeatures::Scope scope(SSE4_1);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ // Deoptimize if minus zero.
+ __ movq(output_reg, input_reg);
+ __ subq(output_reg, Immediate(1));
+ DeoptimizeIf(overflow, instr->environment());
+ }
+ __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown);
+ __ cvttsd2si(output_reg, xmm_scratch);
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
} else {
- DeoptimizeIf(below, instr->environment());
- }
+ __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
+ __ ucomisd(input_reg, xmm_scratch);
- // Use truncating instruction (OK because input is positive).
- __ cvttsd2si(output_reg, input_reg);
+ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+ DeoptimizeIf(below_equal, instr->environment());
+ } else {
+ DeoptimizeIf(below, instr->environment());
+ }
- // Overflow is signalled with minint.
- __ cmpl(output_reg, Immediate(0x80000000));
- DeoptimizeIf(equal, instr->environment());
+ // Use truncating instruction (OK because input is positive).
+ __ cvttsd2si(output_reg, input_reg);
+
+ // Overflow is signalled with minint.
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
+ }
}
@@ -2726,33 +2901,45 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
Register output_reg = ToRegister(instr->result());
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
+ Label done;
// xmm_scratch = 0.5
__ movq(kScratchRegister, V8_INT64_C(0x3FE0000000000000), RelocInfo::NONE);
__ movq(xmm_scratch, kScratchRegister);
-
+ Label below_half;
+ __ ucomisd(xmm_scratch, input_reg);
+ // If input_reg is NaN, this doesn't jump.
+ __ j(above, &below_half, Label::kNear);
// input = input + 0.5
+ // This addition might give a result that isn't the correct for
+ // rounding, due to loss of precision, but only for a number that's
+ // so big that the conversion below will overflow anyway.
__ addsd(input_reg, xmm_scratch);
+ // Compute Math.floor(input).
+ // Use truncating instruction (OK because input is positive).
+ __ cvttsd2si(output_reg, input_reg);
+ // Overflow is signalled with minint.
+ __ cmpl(output_reg, Immediate(0x80000000));
+ DeoptimizeIf(equal, instr->environment());
+ __ jmp(&done);
- // We need to return -0 for the input range [-0.5, 0[, otherwise
- // compute Math.floor(value + 0.5).
+ __ bind(&below_half);
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- __ ucomisd(input_reg, xmm_scratch);
- DeoptimizeIf(below_equal, instr->environment());
+ // Bailout if negative (including -0).
+ __ movq(output_reg, input_reg);
+ __ testq(output_reg, output_reg);
+ DeoptimizeIf(negative, instr->environment());
} else {
- // If we don't need to bailout on -0, we check only bailout
- // on negative inputs.
- __ xorpd(xmm_scratch, xmm_scratch); // Zero the register.
+ // Bailout if below -0.5, otherwise round to (positive) zero, even
+ // if negative.
+ // xmm_scrach = -0.5
+ __ movq(kScratchRegister, V8_INT64_C(0xBFE0000000000000), RelocInfo::NONE);
+ __ movq(xmm_scratch, kScratchRegister);
__ ucomisd(input_reg, xmm_scratch);
DeoptimizeIf(below, instr->environment());
}
+ __ xorl(output_reg, output_reg);
- // Compute Math.floor(value + 0.5).
- // Use truncating instruction (OK because input is positive).
- __ cvttsd2si(output_reg, input_reg);
-
- // Overflow is signalled with minint.
- __ cmpl(output_reg, Immediate(0x80000000));
- DeoptimizeIf(equal, instr->environment());
+ __ bind(&done);
}
@@ -2767,7 +2954,7 @@ void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
XMMRegister xmm_scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
- __ xorpd(xmm_scratch, xmm_scratch);
+ __ xorps(xmm_scratch, xmm_scratch);
__ addsd(input_reg, xmm_scratch); // Convert -0 to +0.
__ sqrtsd(input_reg, input_reg);
}
@@ -2783,7 +2970,7 @@ void LCodeGen::DoPower(LPower* instr) {
if (exponent_type.IsDouble()) {
__ PrepareCallCFunction(2);
// Move arguments to correct registers
- __ movsd(xmm0, left_reg);
+ __ movaps(xmm0, left_reg);
ASSERT(ToDoubleRegister(right).is(xmm1));
__ CallCFunction(
ExternalReference::power_double_double_function(isolate()), 2);
@@ -2791,7 +2978,7 @@ void LCodeGen::DoPower(LPower* instr) {
__ PrepareCallCFunction(2);
// Move arguments to correct registers: xmm0 and edi (not rdi).
// On Windows, the registers are xmm0 and edx.
- __ movsd(xmm0, left_reg);
+ __ movaps(xmm0, left_reg);
#ifdef _WIN64
ASSERT(ToRegister(right).is(rdx));
#else
@@ -2817,13 +3004,13 @@ void LCodeGen::DoPower(LPower* instr) {
__ bind(&call);
__ PrepareCallCFunction(2);
// Move arguments to correct registers xmm0 and xmm1.
- __ movsd(xmm0, left_reg);
+ __ movaps(xmm0, left_reg);
// Right argument is already in xmm1.
__ CallCFunction(
ExternalReference::power_double_double_function(isolate()), 2);
}
// Return value is in xmm0.
- __ movsd(result_reg, xmm0);
+ __ movaps(result_reg, xmm0);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
@@ -2886,6 +3073,21 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
}
+void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
+ ASSERT(ToRegister(instr->function()).is(rdi));
+ ASSERT(instr->HasPointerMap());
+ ASSERT(instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ SafepointGenerator generator(this, pointers, env->deoptimization_index());
+ ParameterCount count(instr->arity());
+ __ InvokeFunction(rdi, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+}
+
+
void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
ASSERT(ToRegister(instr->key()).is(rcx));
ASSERT(ToRegister(instr->result()).is(rax));
@@ -2902,10 +3104,11 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) {
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(
- arity, NOT_IN_LOOP);
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
__ Move(rcx, instr->name());
- CallCode(ic, RelocInfo::CODE_TARGET, instr);
+ CallCode(ic, mode, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
@@ -2914,7 +3117,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
- CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
+ CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_IMPLICIT);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ Drop(1);
@@ -2924,10 +3127,11 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(
- arity, NOT_IN_LOOP);
+ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
__ Move(rcx, instr->name());
- CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
+ CallCode(ic, mode, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
@@ -2935,7 +3139,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
ASSERT(ToRegister(instr->result()).is(rax));
__ Move(rdi, instr->target());
- CallKnownFunction(instr->target(), instr->arity(), instr);
+ CallKnownFunction(instr->target(), instr->arity(), instr, CALL_AS_FUNCTION);
}
@@ -2998,40 +3202,33 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
void LCodeGen::DoStoreKeyedSpecializedArrayElement(
LStoreKeyedSpecializedArrayElement* instr) {
- Register external_pointer = ToRegister(instr->external_pointer());
- Register key = ToRegister(instr->key());
ExternalArrayType array_type = instr->array_type();
+ Operand operand(BuildExternalArrayOperand(instr->external_pointer(),
+ instr->key(), array_type));
if (array_type == kExternalFloatArray) {
XMMRegister value(ToDoubleRegister(instr->value()));
__ cvtsd2ss(value, value);
- __ movss(Operand(external_pointer, key, times_4, 0), value);
+ __ movss(operand, value);
+ } else if (array_type == kExternalDoubleArray) {
+ __ movsd(operand, ToDoubleRegister(instr->value()));
} else {
Register value(ToRegister(instr->value()));
switch (array_type) {
case kExternalPixelArray:
- { // Clamp the value to [0..255].
- NearLabel done;
- __ testl(value, Immediate(0xFFFFFF00));
- __ j(zero, &done);
- __ setcc(negative, value); // 1 if negative, 0 if positive.
- __ decb(value); // 0 if negative, 255 if positive.
- __ bind(&done);
- __ movb(Operand(external_pointer, key, times_1, 0), value);
- }
- break;
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ movb(Operand(external_pointer, key, times_1, 0), value);
+ __ movb(operand, value);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ movw(Operand(external_pointer, key, times_2, 0), value);
+ __ movw(operand, value);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ movl(Operand(external_pointer, key, times_4, 0), value);
+ __ movl(operand, value);
break;
case kExternalFloatArray:
+ case kExternalDoubleArray:
UNREACHABLE();
break;
}
@@ -3092,6 +3289,14 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
}
+void LCodeGen::DoStringAdd(LStringAdd* instr) {
+ EmitPushTaggedOperand(instr->left());
+ EmitPushTaggedOperand(instr->right());
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
+}
+
+
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
@@ -3126,7 +3331,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
DeferredStringCharCodeAt* deferred =
new DeferredStringCharCodeAt(this, instr);
- NearLabel flat_string, ascii_string, done;
+ Label flat_string, ascii_string, done;
// Fetch the instance type of the receiver into result register.
__ movq(result, FieldOperand(string, HeapObject::kMapOffset));
@@ -3135,7 +3340,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
// We need special handling for non-sequential strings.
STATIC_ASSERT(kSeqStringTag == 0);
__ testb(result, Immediate(kStringRepresentationMask));
- __ j(zero, &flat_string);
+ __ j(zero, &flat_string, Label::kNear);
// Handle cons strings and go to deferred code for the rest.
__ testb(result, Immediate(kIsConsStringMask));
@@ -3162,7 +3367,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
__ bind(&flat_string);
STATIC_ASSERT(kAsciiStringTag != 0);
__ testb(result, Immediate(kStringEncodingMask));
- __ j(not_zero, &ascii_string);
+ __ j(not_zero, &ascii_string, Label::kNear);
// Two-byte string.
// Load the two-byte character code into the result register.
@@ -3178,7 +3383,7 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
times_2,
SeqTwoByteString::kHeaderSize));
}
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// ASCII string.
// Load the byte into the result register.
@@ -3369,10 +3574,10 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
XMMRegister result_reg,
bool deoptimize_on_undefined,
LEnvironment* env) {
- NearLabel load_smi, done;
+ Label load_smi, done;
// Smi check.
- __ JumpIfSmi(input_reg, &load_smi);
+ __ JumpIfSmi(input_reg, &load_smi, Label::kNear);
// Heap number map check.
__ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset),
@@ -3380,21 +3585,22 @@ void LCodeGen::EmitNumberUntagD(Register input_reg,
if (deoptimize_on_undefined) {
DeoptimizeIf(not_equal, env);
} else {
- NearLabel heap_number;
- __ j(equal, &heap_number);
+ Label heap_number;
+ __ j(equal, &heap_number, Label::kNear);
+
__ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
DeoptimizeIf(not_equal, env);
// Convert undefined to NaN. Compute NaN as 0/0.
- __ xorpd(result_reg, result_reg);
+ __ xorps(result_reg, result_reg);
__ divsd(result_reg, result_reg);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&heap_number);
}
// Heap number to XMM conversion.
__ movsd(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset));
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
// Smi to XMM conversion
__ bind(&load_smi);
@@ -3415,7 +3621,7 @@ class DeferredTaggedToI: public LDeferredCode {
void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
- NearLabel done, heap_number;
+ Label done, heap_number;
Register input_reg = ToRegister(instr->InputAt(0));
// Heap number map check.
@@ -3423,13 +3629,13 @@ void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
Heap::kHeapNumberMapRootIndex);
if (instr->truncating()) {
- __ j(equal, &heap_number);
+ __ j(equal, &heap_number, Label::kNear);
// Check for undefined. Undefined is converted to zero for truncating
// conversions.
__ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
DeoptimizeIf(not_equal, instr->environment());
__ Set(input_reg, 0);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&heap_number);
@@ -3504,7 +3710,7 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
__ cvttsd2siq(result_reg, input_reg);
__ movq(kScratchRegister, V8_INT64_C(0x8000000000000000), RelocInfo::NONE);
__ cmpq(result_reg, kScratchRegister);
- DeoptimizeIf(equal, instr->environment());
+ DeoptimizeIf(equal, instr->environment());
} else {
__ cvttsd2si(result_reg, input_reg);
__ cvtlsi2sd(xmm0, result_reg);
@@ -3512,11 +3718,11 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
DeoptimizeIf(not_equal, instr->environment());
DeoptimizeIf(parity_even, instr->environment()); // NaN.
if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
- NearLabel done;
+ Label done;
// The integer converted back is equal to the original. We
// only have to test if we got -0 as an input.
__ testl(result_reg, result_reg);
- __ j(not_zero, &done);
+ __ j(not_zero, &done, Label::kNear);
__ movmskpd(result_reg, input_reg);
// Bit 0 contains the sign of the double in input_reg.
// If input was positive, we are ok and return 0, otherwise
@@ -3545,30 +3751,45 @@ void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
Register input = ToRegister(instr->InputAt(0));
- InstanceType first = instr->hydrogen()->first();
- InstanceType last = instr->hydrogen()->last();
__ movq(kScratchRegister, FieldOperand(input, HeapObject::kMapOffset));
- // If there is only one type in the interval check for equality.
- if (first == last) {
+ if (instr->hydrogen()->is_interval_check()) {
+ InstanceType first;
+ InstanceType last;
+ instr->hydrogen()->GetCheckInterval(&first, &last);
+
__ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
Immediate(static_cast<int8_t>(first)));
- DeoptimizeIf(not_equal, instr->environment());
- } else if (first == FIRST_STRING_TYPE && last == LAST_STRING_TYPE) {
- // String has a dedicated bit in instance type.
- __ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
- Immediate(kIsNotStringMask));
- DeoptimizeIf(not_zero, instr->environment());
+
+ // If there is only one type in the interval check for equality.
+ if (first == last) {
+ DeoptimizeIf(not_equal, instr->environment());
+ } else {
+ DeoptimizeIf(below, instr->environment());
+ // Omit check for the last type.
+ if (last != LAST_TYPE) {
+ __ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
+ Immediate(static_cast<int8_t>(last)));
+ DeoptimizeIf(above, instr->environment());
+ }
+ }
} else {
- __ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
- Immediate(static_cast<int8_t>(first)));
- DeoptimizeIf(below, instr->environment());
- // Omit check for the last type.
- if (last != LAST_TYPE) {
- __ cmpb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
- Immediate(static_cast<int8_t>(last)));
- DeoptimizeIf(above, instr->environment());
+ uint8_t mask;
+ uint8_t tag;
+ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
+
+ if (IsPowerOf2(mask)) {
+ ASSERT(tag == 0 || IsPowerOf2(tag));
+ __ testb(FieldOperand(kScratchRegister, Map::kInstanceTypeOffset),
+ Immediate(mask));
+ DeoptimizeIf(tag == 0 ? not_zero : zero, instr->environment());
+ } else {
+ __ movzxbl(kScratchRegister,
+ FieldOperand(kScratchRegister, Map::kInstanceTypeOffset));
+ __ andb(kScratchRegister, Immediate(mask));
+ __ cmpb(kScratchRegister, Immediate(tag));
+ DeoptimizeIf(not_equal, instr->environment());
}
}
}
@@ -3592,6 +3813,57 @@ void LCodeGen::DoCheckMap(LCheckMap* instr) {
}
+void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
+ XMMRegister value_reg = ToDoubleRegister(instr->unclamped());
+ Register result_reg = ToRegister(instr->result());
+ Register temp_reg = ToRegister(instr->TempAt(0));
+ __ ClampDoubleToUint8(value_reg, xmm0, result_reg, temp_reg);
+}
+
+
+void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register value_reg = ToRegister(instr->result());
+ __ ClampUint8(value_reg);
+}
+
+
+void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
+ ASSERT(instr->unclamped()->Equals(instr->result()));
+ Register input_reg = ToRegister(instr->unclamped());
+ Register temp_reg = ToRegister(instr->TempAt(0));
+ XMMRegister temp_xmm_reg = ToDoubleRegister(instr->TempAt(1));
+ Label is_smi, done, heap_number;
+
+ __ JumpIfSmi(input_reg, &is_smi);
+
+ // Check for heap number
+ __ Cmp(FieldOperand(input_reg, HeapObject::kMapOffset),
+ factory()->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
+
+ // Check for undefined. Undefined is converted to zero for clamping
+ // conversions.
+ __ Cmp(input_reg, factory()->undefined_value());
+ DeoptimizeIf(not_equal, instr->environment());
+ __ movq(input_reg, Immediate(0));
+ __ jmp(&done, Label::kNear);
+
+ // Heap number
+ __ bind(&heap_number);
+ __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
+ __ ClampDoubleToUint8(xmm0, temp_xmm_reg, input_reg, temp_reg);
+ __ jmp(&done, Label::kNear);
+
+ // smi
+ __ bind(&is_smi);
+ __ SmiToInteger32(input_reg, input_reg);
+ __ ClampUint8(input_reg);
+
+ __ bind(&done);
+}
+
+
void LCodeGen::LoadHeapObject(Register result, Handle<HeapObject> object) {
if (heap()->InNewSpace(*object)) {
Handle<JSGlobalPropertyCell> cell =
@@ -3684,7 +3956,7 @@ void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
- NearLabel materialized;
+ Label materialized;
// Registers will be used as follows:
// rdi = JS function.
// rcx = literals array.
@@ -3696,7 +3968,7 @@ void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
instr->hydrogen()->literal_index() * kPointerSize;
__ movq(rbx, FieldOperand(rcx, literal_offset));
__ CompareRoot(rbx, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &materialized);
+ __ j(not_equal, &materialized, Label::kNear);
// Create regexp literal using runtime function
// Result will be in rax.
@@ -3758,14 +4030,7 @@ void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
void LCodeGen::DoTypeof(LTypeof* instr) {
LOperand* input = instr->InputAt(0);
- if (input->IsConstantOperand()) {
- __ Push(ToHandle(LConstantOperand::cast(input)));
- } else if (input->IsRegister()) {
- __ push(ToRegister(input));
- } else {
- ASSERT(input->IsStackSlot());
- __ push(ToOperand(input));
- }
+ EmitPushTaggedOperand(input);
CallRuntime(Runtime::kTypeof, 1, instr);
}
@@ -3775,7 +4040,7 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
Register result = ToRegister(instr->result());
Label true_label;
Label false_label;
- NearLabel done;
+ Label done;
Condition final_branch_condition = EmitTypeofIs(&true_label,
&false_label,
@@ -3784,7 +4049,7 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
__ j(final_branch_condition, &true_label);
__ bind(&false_label);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_label);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
@@ -3793,19 +4058,14 @@ void LCodeGen::DoTypeofIs(LTypeofIs* instr) {
}
-void LCodeGen::EmitPushConstantOperand(LOperand* operand) {
- ASSERT(operand->IsConstantOperand());
- LConstantOperand* const_op = LConstantOperand::cast(operand);
- Handle<Object> literal = chunk_->LookupLiteral(const_op);
- Representation r = chunk_->LookupLiteralRepresentation(const_op);
- if (r.IsInteger32()) {
- ASSERT(literal->IsNumber());
- __ push(Immediate(static_cast<int32_t>(literal->Number())));
- } else if (r.IsDouble()) {
- Abort("unsupported double immediate");
+void LCodeGen::EmitPushTaggedOperand(LOperand* operand) {
+ ASSERT(!operand->IsDoubleRegister());
+ if (operand->IsConstantOperand()) {
+ __ Push(ToHandle(LConstantOperand::cast(operand)));
+ } else if (operand->IsRegister()) {
+ __ push(ToRegister(operand));
} else {
- ASSERT(r.IsTagged());
- __ Push(literal);
+ __ push(ToOperand(operand));
}
}
@@ -3891,15 +4151,14 @@ Condition LCodeGen::EmitTypeofIs(Label* true_label,
void LCodeGen::DoIsConstructCall(LIsConstructCall* instr) {
Register result = ToRegister(instr->result());
- NearLabel true_label;
- NearLabel false_label;
- NearLabel done;
+ Label true_label;
+ Label done;
EmitIsConstructCall(result);
- __ j(equal, &true_label);
+ __ j(equal, &true_label, Label::kNear);
__ LoadRoot(result, Heap::kFalseValueRootIndex);
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&true_label);
__ LoadRoot(result, Heap::kTrueValueRootIndex);
@@ -3924,10 +4183,10 @@ void LCodeGen::EmitIsConstructCall(Register temp) {
__ movq(temp, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
// Skip the arguments adaptor frame if it exists.
- NearLabel check_frame_marker;
+ Label check_frame_marker;
__ Cmp(Operand(temp, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(not_equal, &check_frame_marker);
+ __ j(not_equal, &check_frame_marker, Label::kNear);
__ movq(temp, Operand(rax, StandardFrameConstants::kCallerFPOffset));
// Check the marker in the calling frame.
@@ -3951,20 +4210,8 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
LOperand* obj = instr->object();
LOperand* key = instr->key();
- // Push object.
- if (obj->IsRegister()) {
- __ push(ToRegister(obj));
- } else {
- __ push(ToOperand(obj));
- }
- // Push key.
- if (key->IsConstantOperand()) {
- EmitPushConstantOperand(key);
- } else if (key->IsRegister()) {
- __ push(ToRegister(key));
- } else {
- __ push(ToOperand(key));
- }
+ EmitPushTaggedOperand(obj);
+ EmitPushTaggedOperand(key);
ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment());
LPointerMap* pointers = instr->pointer_map();
LEnvironment* env = instr->deoptimization_environment();
@@ -3977,15 +4224,35 @@ void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
pointers,
env->deoptimization_index());
__ Push(Smi::FromInt(strict_mode_flag()));
- __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, &safepoint_generator);
+ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator);
+}
+
+
+void LCodeGen::DoIn(LIn* instr) {
+ LOperand* obj = instr->object();
+ LOperand* key = instr->key();
+ EmitPushTaggedOperand(key);
+ EmitPushTaggedOperand(obj);
+ ASSERT(instr->HasPointerMap() && instr->HasDeoptimizationEnvironment());
+ LPointerMap* pointers = instr->pointer_map();
+ LEnvironment* env = instr->deoptimization_environment();
+ RecordPosition(pointers->position());
+ RegisterEnvironmentForDeoptimization(env);
+ // Create safepoint generator that will also ensure enough space in the
+ // reloc info for patching in deoptimization (since this is invoking a
+ // builtin)
+ SafepointGenerator safepoint_generator(this,
+ pointers,
+ env->deoptimization_index());
+ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator);
}
void LCodeGen::DoStackCheck(LStackCheck* instr) {
// Perform stack overflow check.
- NearLabel done;
+ Label done;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
- __ j(above_equal, &done);
+ __ j(above_equal, &done, Label::kNear);
StackCheckStub stub;
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h
index d95ab21d..7c9f2a03 100644
--- a/src/x64/lithium-codegen-x64.h
+++ b/src/x64/lithium-codegen-x64.h
@@ -102,6 +102,7 @@ class LCodeGen BASE_EMBEDDED {
// Parallel move support.
void DoParallelMove(LParallelMove* move);
+ void DoGap(LGap* instr);
// Emit frame translation commands for an environment.
void WriteTranslation(LEnvironment* environment, Translation* translation);
@@ -141,8 +142,8 @@ class LCodeGen BASE_EMBEDDED {
Register input,
Register temporary);
- int StackSlotCount() const { return chunk()->spill_slot_count(); }
- int ParameterCount() const { return scope()->num_parameters(); }
+ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
+ int GetParameterCount() const { return scope()->num_parameters(); }
void Abort(const char* format, ...);
void Comment(const char* format, ...);
@@ -193,7 +194,8 @@ class LCodeGen BASE_EMBEDDED {
// to be in edi.
void CallKnownFunction(Handle<JSFunction> function,
int arity,
- LInstruction* instr);
+ LInstruction* instr,
+ CallKind call_kind);
void LoadHeapObject(Register result, Handle<HeapObject> object);
@@ -213,6 +215,9 @@ class LCodeGen BASE_EMBEDDED {
Register ToRegister(int index) const;
XMMRegister ToDoubleRegister(int index) const;
+ Operand BuildExternalArrayOperand(LOperand* external_pointer,
+ LOperand* key,
+ ExternalArrayType array_type);
// Specific math operations - used from DoUnaryMathOperation.
void EmitIntegerMathAbs(LUnaryMathOperation* instr);
@@ -266,13 +271,14 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition.
void EmitIsConstructCall(Register temp);
- void EmitLoadField(Register result,
- Register object,
- Handle<Map> type,
- Handle<String> name);
+ void EmitLoadFieldOrConstantFunction(Register result,
+ Register object,
+ Handle<Map> type,
+ Handle<String> name);
- // Emits code for pushing a constant operand.
- void EmitPushConstantOperand(LOperand* operand);
+ // Emits code for pushing either a tagged constant, a (non-double)
+ // register, or a stack slot operand.
+ void EmitPushTaggedOperand(LOperand* operand);
struct JumpTableEntry {
explicit inline JumpTableEntry(Address entry)
diff --git a/src/x64/lithium-gap-resolver-x64.cc b/src/x64/lithium-gap-resolver-x64.cc
index cedd0256..c3c617c4 100644
--- a/src/x64/lithium-gap-resolver-x64.cc
+++ b/src/x64/lithium-gap-resolver-x64.cc
@@ -214,7 +214,7 @@ void LGapResolver::EmitMove(int index) {
} else if (source->IsDoubleRegister()) {
XMMRegister src = cgen_->ToDoubleRegister(source);
if (destination->IsDoubleRegister()) {
- __ movsd(cgen_->ToDoubleRegister(destination), src);
+ __ movaps(cgen_->ToDoubleRegister(destination), src);
} else {
ASSERT(destination->IsDoubleStackSlot());
__ movsd(cgen_->ToOperand(destination), src);
@@ -273,9 +273,9 @@ void LGapResolver::EmitSwap(int index) {
// Swap two double registers.
XMMRegister source_reg = cgen_->ToDoubleRegister(source);
XMMRegister destination_reg = cgen_->ToDoubleRegister(destination);
- __ movsd(xmm0, source_reg);
- __ movsd(source_reg, destination_reg);
- __ movsd(destination_reg, xmm0);
+ __ movaps(xmm0, source_reg);
+ __ movaps(source_reg, destination_reg);
+ __ movaps(destination_reg, xmm0);
} else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
// Swap a double register and a double stack slot.
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index 4601cd9f..569fa3e9 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -71,22 +71,21 @@ void LOsrEntry::MarkSpilledDoubleRegister(int allocation_index,
#ifdef DEBUG
void LInstruction::VerifyCall() {
- // Call instructions can use only fixed registers as
- // temporaries and outputs because all registers
- // are blocked by the calling convention.
- // Inputs must use a fixed register.
+ // Call instructions can use only fixed registers as temporaries and
+ // outputs because all registers are blocked by the calling convention.
+ // Inputs operands must use a fixed register or use-at-start policy or
+ // a non-register policy.
ASSERT(Output() == NULL ||
LUnallocated::cast(Output())->HasFixedPolicy() ||
!LUnallocated::cast(Output())->HasRegisterPolicy());
for (UseIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||
+ operand->IsUsedAtStart());
}
for (TempIterator it(this); it.HasNext(); it.Advance()) {
- LOperand* operand = it.Next();
- ASSERT(LUnallocated::cast(operand)->HasFixedPolicy() ||
- !LUnallocated::cast(operand)->HasRegisterPolicy());
+ LUnallocated* operand = LUnallocated::cast(it.Next());
+ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
}
}
#endif
@@ -240,6 +239,13 @@ void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
}
+void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
+ stream->Add("if is_undetectable(");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
+}
+
+
void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
stream->Add("if has_instance_type(");
InputAt(0)->PrintTo(stream);
@@ -303,6 +309,13 @@ void LStoreContextSlot::PrintDataTo(StringStream* stream) {
}
+void LInvokeFunction::PrintDataTo(StringStream* stream) {
+ stream->Add("= ");
+ InputAt(0)->PrintTo(stream);
+ stream->Add(" #%d / ", arity());
+}
+
+
void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[rcx] #%d / ", arity());
}
@@ -442,7 +455,7 @@ void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
- LGap* gap = new LGap(block);
+ LInstructionGap* gap = new LInstructionGap(block);
int index = -1;
if (instr->IsControl()) {
instructions_.Add(gap);
@@ -845,24 +858,22 @@ LInstruction* LChunkBuilder::DoShift(Token::Value op,
right = UseFixed(right_value, rcx);
}
- // Shift operations can only deoptimize if we do a logical shift
- // by 0 and the result cannot be truncated to int32.
- bool can_deopt = (op == Token::SHR && constant_value == 0);
- if (can_deopt) {
- bool can_truncate = true;
- for (int i = 0; i < instr->uses()->length(); i++) {
- if (!instr->uses()->at(i)->CheckFlag(HValue::kTruncatingToInt32)) {
- can_truncate = false;
+ // Shift operations can only deoptimize if we do a logical shift by 0 and
+ // the result cannot be truncated to int32.
+ bool may_deopt = (op == Token::SHR && constant_value == 0);
+ bool does_deopt = false;
+ if (may_deopt) {
+ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
+ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
+ does_deopt = true;
break;
}
}
- can_deopt = !can_truncate;
}
- LShiftI* result = new LShiftI(op, left, right, can_deopt);
- return can_deopt
- ? AssignEnvironment(DefineSameAsFirst(result))
- : DefineSameAsFirst(result);
+ LInstruction* result =
+ DefineSameAsFirst(new LShiftI(op, left, right, does_deopt));
+ return does_deopt ? AssignEnvironment(result) : result;
}
@@ -1006,6 +1017,8 @@ LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) {
outer);
int argument_index = 0;
for (int i = 0; i < value_count; ++i) {
+ if (hydrogen_env->is_special_index(i)) continue;
+
HValue* value = hydrogen_env->values()->at(i);
LOperand* op = NULL;
if (value->IsArgumentsObject()) {
@@ -1033,98 +1046,94 @@ LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
LInstruction* LChunkBuilder::DoTest(HTest* instr) {
HValue* v = instr->value();
- if (v->EmitAtUses()) {
- if (v->IsClassOfTest()) {
- HClassOfTest* compare = HClassOfTest::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
- TempRegister());
- } else if (v->IsCompare()) {
- HCompare* compare = HCompare::cast(v);
- Token::Value op = compare->token();
- HValue* left = compare->left();
- HValue* right = compare->right();
- Representation r = compare->GetInputRepresentation();
- if (r.IsInteger32()) {
- ASSERT(left->representation().IsInteger32());
- ASSERT(right->representation().IsInteger32());
-
- return new LCmpIDAndBranch(UseRegisterAtStart(left),
- UseOrConstantAtStart(right));
- } else if (r.IsDouble()) {
- ASSERT(left->representation().IsDouble());
- ASSERT(right->representation().IsDouble());
-
- return new LCmpIDAndBranch(UseRegisterAtStart(left),
- UseRegisterAtStart(right));
- } else {
- ASSERT(left->representation().IsTagged());
- ASSERT(right->representation().IsTagged());
- bool reversed = op == Token::GT || op == Token::LTE;
- LOperand* left_operand = UseFixed(left, reversed ? rax : rdx);
- LOperand* right_operand = UseFixed(right, reversed ? rdx : rax);
- LCmpTAndBranch* result = new LCmpTAndBranch(left_operand,
- right_operand);
- return MarkAsCall(result, instr);
- }
- } else if (v->IsIsSmi()) {
- HIsSmi* compare = HIsSmi::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LIsSmiAndBranch(Use(compare->value()));
- } else if (v->IsHasInstanceType()) {
- HHasInstanceType* compare = HHasInstanceType::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LHasInstanceTypeAndBranch(
- UseRegisterAtStart(compare->value()));
- } else if (v->IsHasCachedArrayIndex()) {
- HHasCachedArrayIndex* compare = HHasCachedArrayIndex::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- return new LHasCachedArrayIndexAndBranch(
- UseRegisterAtStart(compare->value()));
- } else if (v->IsIsNull()) {
- HIsNull* compare = HIsNull::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
-
- // We only need a temp register for non-strict compare.
- LOperand* temp = compare->is_strict() ? NULL : TempRegister();
- return new LIsNullAndBranch(UseRegisterAtStart(compare->value()),
- temp);
- } else if (v->IsIsObject()) {
- HIsObject* compare = HIsObject::cast(v);
- ASSERT(compare->value()->representation().IsTagged());
- return new LIsObjectAndBranch(UseRegisterAtStart(compare->value()));
- } else if (v->IsCompareJSObjectEq()) {
- HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
- return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
- UseRegisterAtStart(compare->right()));
- } else if (v->IsInstanceOf()) {
- HInstanceOf* instance_of = HInstanceOf::cast(v);
- LInstanceOfAndBranch* result =
- new LInstanceOfAndBranch(UseFixed(instance_of->left(), rax),
- UseFixed(instance_of->right(), rdx));
- return MarkAsCall(result, instr);
- } else if (v->IsTypeofIs()) {
- HTypeofIs* typeof_is = HTypeofIs::cast(v);
- return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
- } else if (v->IsIsConstructCall()) {
- return new LIsConstructCallAndBranch(TempRegister());
+ if (!v->EmitAtUses()) {
+ return new LBranch(UseRegisterAtStart(v));
+ } else if (v->IsClassOfTest()) {
+ HClassOfTest* compare = HClassOfTest::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LClassOfTestAndBranch(UseTempRegister(compare->value()),
+ TempRegister());
+ } else if (v->IsCompare()) {
+ HCompare* compare = HCompare::cast(v);
+ Token::Value op = compare->token();
+ HValue* left = compare->left();
+ HValue* right = compare->right();
+ Representation r = compare->GetInputRepresentation();
+ if (r.IsInteger32()) {
+ ASSERT(left->representation().IsInteger32());
+ ASSERT(right->representation().IsInteger32());
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
+ UseOrConstantAtStart(right));
+ } else if (r.IsDouble()) {
+ ASSERT(left->representation().IsDouble());
+ ASSERT(right->representation().IsDouble());
+ return new LCmpIDAndBranch(UseRegisterAtStart(left),
+ UseRegisterAtStart(right));
} else {
- if (v->IsConstant()) {
- if (HConstant::cast(v)->ToBoolean()) {
- return new LGoto(instr->FirstSuccessor()->block_id());
- } else {
- return new LGoto(instr->SecondSuccessor()->block_id());
- }
- }
- Abort("Undefined compare before branch");
- return NULL;
+ ASSERT(left->representation().IsTagged());
+ ASSERT(right->representation().IsTagged());
+ bool reversed = op == Token::GT || op == Token::LTE;
+ LOperand* left_operand = UseFixed(left, reversed ? rax : rdx);
+ LOperand* right_operand = UseFixed(right, reversed ? rdx : rax);
+ LCmpTAndBranch* result = new LCmpTAndBranch(left_operand, right_operand);
+ return MarkAsCall(result, instr);
}
+ } else if (v->IsIsSmi()) {
+ HIsSmi* compare = HIsSmi::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsSmiAndBranch(Use(compare->value()));
+ } else if (v->IsIsUndetectable()) {
+ HIsUndetectable* compare = HIsUndetectable::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsUndetectableAndBranch(UseRegisterAtStart(compare->value()),
+ TempRegister());
+ } else if (v->IsHasInstanceType()) {
+ HHasInstanceType* compare = HHasInstanceType::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LHasInstanceTypeAndBranch(UseRegisterAtStart(compare->value()));
+ } else if (v->IsHasCachedArrayIndex()) {
+ HHasCachedArrayIndex* compare = HHasCachedArrayIndex::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LHasCachedArrayIndexAndBranch(
+ UseRegisterAtStart(compare->value()));
+ } else if (v->IsIsNull()) {
+ HIsNull* compare = HIsNull::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ // We only need a temp register for non-strict compare.
+ LOperand* temp = compare->is_strict() ? NULL : TempRegister();
+ return new LIsNullAndBranch(UseRegisterAtStart(compare->value()), temp);
+ } else if (v->IsIsObject()) {
+ HIsObject* compare = HIsObject::cast(v);
+ ASSERT(compare->value()->representation().IsTagged());
+ return new LIsObjectAndBranch(UseRegisterAtStart(compare->value()));
+ } else if (v->IsCompareJSObjectEq()) {
+ HCompareJSObjectEq* compare = HCompareJSObjectEq::cast(v);
+ return new LCmpJSObjectEqAndBranch(UseRegisterAtStart(compare->left()),
+ UseRegisterAtStart(compare->right()));
+ } else if (v->IsCompareSymbolEq()) {
+ HCompareSymbolEq* compare = HCompareSymbolEq::cast(v);
+ return new LCmpSymbolEqAndBranch(UseRegisterAtStart(compare->left()),
+ UseRegisterAtStart(compare->right()));
+ } else if (v->IsInstanceOf()) {
+ HInstanceOf* instance_of = HInstanceOf::cast(v);
+ LInstanceOfAndBranch* result =
+ new LInstanceOfAndBranch(UseFixed(instance_of->left(), rax),
+ UseFixed(instance_of->right(), rdx));
+ return MarkAsCall(result, instr);
+ } else if (v->IsTypeofIs()) {
+ HTypeofIs* typeof_is = HTypeofIs::cast(v);
+ return new LTypeofIsAndBranch(UseTempRegister(typeof_is->value()));
+ } else if (v->IsIsConstructCall()) {
+ return new LIsConstructCallAndBranch(TempRegister());
+ } else if (v->IsConstant()) {
+ HBasicBlock* successor = HConstant::cast(v)->ToBoolean()
+ ? instr->FirstSuccessor()
+ : instr->SecondSuccessor();
+ return new LGoto(successor->block_id());
+ } else {
+ Abort("Undefined compare before branch");
+ return NULL;
}
- return new LBranch(UseRegisterAtStart(v));
}
@@ -1183,7 +1192,7 @@ LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
LInstruction* LChunkBuilder::DoContext(HContext* instr) {
- return DefineAsRegister(new LContext);
+ return instr->HasNoUses() ? NULL : DefineAsRegister(new LContext);
}
@@ -1211,6 +1220,14 @@ LInstruction* LChunkBuilder::DoCallConstantFunction(
}
+LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
+ LOperand* function = UseFixed(instr->function(), rdi);
+ argument_count_ -= instr->argument_count();
+ LInvokeFunction* result = new LInvokeFunction(function);
+ return MarkAsCall(DefineFixed(result, rax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
+}
+
+
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
BuiltinFunctionId op = instr->op();
if (op == kMathLog || op == kMathSin || op == kMathCos) {
@@ -1500,6 +1517,15 @@ LInstruction* LChunkBuilder::DoCompareJSObjectEq(
}
+LInstruction* LChunkBuilder::DoCompareSymbolEq(
+ HCompareSymbolEq* instr) {
+ LOperand* left = UseRegisterAtStart(instr->left());
+ LOperand* right = UseRegisterAtStart(instr->right());
+ LCmpSymbolEq* result = new LCmpSymbolEq(left, right);
+ return DefineAsRegister(result);
+}
+
+
LInstruction* LChunkBuilder::DoIsNull(HIsNull* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1524,6 +1550,14 @@ LInstruction* LChunkBuilder::DoIsSmi(HIsSmi* instr) {
}
+LInstruction* LChunkBuilder::DoIsUndetectable(HIsUndetectable* instr) {
+ ASSERT(instr->value()->representation().IsTagged());
+ LOperand* value = UseRegisterAtStart(instr->value());
+
+ return DefineAsRegister(new LIsUndetectable(value));
+}
+
+
LInstruction* LChunkBuilder::DoHasInstanceType(HHasInstanceType* instr) {
ASSERT(instr->value()->representation().IsTagged());
LOperand* value = UseRegisterAtStart(instr->value());
@@ -1600,6 +1634,14 @@ LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
}
+LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
+ // All HForceRepresentation instructions should be eliminated in the
+ // representation change phase of Hydrogen.
+ UNREACHABLE();
+ return NULL;
+}
+
+
LInstruction* LChunkBuilder::DoChange(HChange* instr) {
Representation from = instr->from();
Representation to = instr->to();
@@ -1694,6 +1736,27 @@ LInstruction* LChunkBuilder::DoCheckMap(HCheckMap* instr) {
}
+LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
+ HValue* value = instr->value();
+ Representation input_rep = value->representation();
+ LOperand* reg = UseRegister(value);
+ if (input_rep.IsDouble()) {
+ return DefineAsRegister(new LClampDToUint8(reg,
+ TempRegister()));
+ } else if (input_rep.IsInteger32()) {
+ return DefineSameAsFirst(new LClampIToUint8(reg));
+ } else {
+ ASSERT(input_rep.IsTagged());
+ // Register allocator doesn't (yet) support allocation of double
+ // temps. Reserve xmm1 explicitly.
+ LClampTToUint8* result = new LClampTToUint8(reg,
+ TempRegister(),
+ FixedTemp(xmm1));
+ return AssignEnvironment(DefineSameAsFirst(result));
+ }
+}
+
+
LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
return new LReturn(UseFixed(instr->value(), rax));
}
@@ -1832,11 +1895,14 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
HLoadKeyedSpecializedArrayElement* instr) {
ExternalArrayType array_type = instr->array_type();
Representation representation(instr->representation());
- ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
- (representation.IsDouble() && array_type == kExternalFloatArray));
+ ASSERT(
+ (representation.IsInteger32() && (array_type != kExternalFloatArray &&
+ array_type != kExternalDoubleArray)) ||
+ (representation.IsDouble() && (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray)));
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
- LOperand* key = UseRegister(instr->key());
+ LOperand* key = UseRegisterOrConstant(instr->key());
LLoadKeyedSpecializedArrayElement* result =
new LLoadKeyedSpecializedArrayElement(external_pointer, key);
LInstruction* load_instr = DefineAsRegister(result);
@@ -1879,8 +1945,11 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
HStoreKeyedSpecializedArrayElement* instr) {
Representation representation(instr->value()->representation());
ExternalArrayType array_type = instr->array_type();
- ASSERT((representation.IsInteger32() && array_type != kExternalFloatArray) ||
- (representation.IsDouble() && array_type == kExternalFloatArray));
+ ASSERT(
+ (representation.IsInteger32() && (array_type != kExternalFloatArray &&
+ array_type != kExternalDoubleArray)) ||
+ (representation.IsDouble() && (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray)));
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
@@ -1890,7 +1959,7 @@ LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
LOperand* val = val_is_temp_register
? UseTempRegister(instr->value())
: UseRegister(instr->value());
- LOperand* key = UseRegister(instr->key());
+ LOperand* key = UseRegisterOrConstant(instr->key());
return new LStoreKeyedSpecializedArrayElement(external_pointer,
key,
@@ -1941,6 +2010,13 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
}
+LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
+ LOperand* left = UseOrConstantAtStart(instr->left());
+ LOperand* right = UseOrConstantAtStart(instr->right());
+ return MarkAsCall(DefineFixed(new LStringAdd(left, right), rax), instr);
+}
+
+
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
LOperand* string = UseRegister(instr->string());
LOperand* index = UseRegisterOrConstant(instr->index());
@@ -1984,7 +2060,8 @@ LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
LDeleteProperty* result =
- new LDeleteProperty(Use(instr->object()), UseOrConstant(instr->key()));
+ new LDeleteProperty(UseAtStart(instr->object()),
+ UseOrConstantAtStart(instr->key()));
return MarkAsCall(DefineFixed(result, rax), instr);
}
@@ -2100,8 +2177,9 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
instr->function(),
- false,
- undefined);
+ HEnvironment::LITHIUM,
+ undefined,
+ instr->call_kind());
current_block_->UpdateEnvironment(inner);
chunk_->AddInlinedClosure(instr->closure());
return NULL;
@@ -2114,6 +2192,15 @@ LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
return NULL;
}
+
+LInstruction* LChunkBuilder::DoIn(HIn* instr) {
+ LOperand* key = UseOrConstantAtStart(instr->key());
+ LOperand* object = UseOrConstantAtStart(instr->object());
+ LIn* result = new LIn(key, object);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
} } // namespace v8::internal
#endif // V8_TARGET_ARCH_X64
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index 8e122821..1493836d 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -73,6 +73,9 @@ class LCodeGen;
V(CheckNonSmi) \
V(CheckPrototypeMaps) \
V(CheckSmi) \
+ V(ClampDToUint8) \
+ V(ClampIToUint8) \
+ V(ClampTToUint8) \
V(ClassOfTest) \
V(ClassOfTestAndBranch) \
V(CmpID) \
@@ -80,6 +83,8 @@ class LCodeGen;
V(CmpJSObjectEq) \
V(CmpJSObjectEqAndBranch) \
V(CmpMapAndBranch) \
+ V(CmpSymbolEq) \
+ V(CmpSymbolEqAndBranch) \
V(CmpT) \
V(CmpTAndBranch) \
V(ConstantD) \
@@ -93,31 +98,38 @@ class LCodeGen;
V(ExternalArrayLength) \
V(FixedArrayLength) \
V(FunctionLiteral) \
- V(Gap) \
V(GetCachedArrayIndex) \
V(GlobalObject) \
V(GlobalReceiver) \
V(Goto) \
- V(HasInstanceType) \
- V(HasInstanceTypeAndBranch) \
V(HasCachedArrayIndex) \
V(HasCachedArrayIndexAndBranch) \
+ V(HasInstanceType) \
+ V(HasInstanceTypeAndBranch) \
+ V(In) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
V(InstanceOfKnownGlobal) \
+ V(InstructionGap) \
V(Integer32ToDouble) \
+ V(InvokeFunction) \
+ V(IsConstructCall) \
+ V(IsConstructCallAndBranch) \
V(IsNull) \
V(IsNullAndBranch) \
V(IsObject) \
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
+ V(IsUndetectable) \
+ V(IsUndetectableAndBranch) \
V(JSArrayLength) \
V(Label) \
V(LazyBailout) \
V(LoadContextSlot) \
V(LoadElements) \
V(LoadExternalArrayPointer) \
+ V(LoadFunctionPrototype) \
V(LoadGlobalCell) \
V(LoadGlobalGeneric) \
V(LoadKeyedFastElement) \
@@ -126,7 +138,6 @@ class LCodeGen;
V(LoadNamedField) \
V(LoadNamedFieldPolymorphic) \
V(LoadNamedGeneric) \
- V(LoadFunctionPrototype) \
V(ModI) \
V(MulI) \
V(NumberTagD) \
@@ -152,37 +163,32 @@ class LCodeGen;
V(StoreKeyedSpecializedArrayElement) \
V(StoreNamedField) \
V(StoreNamedGeneric) \
+ V(StringAdd) \
V(StringCharCodeAt) \
V(StringCharFromCode) \
V(StringLength) \
V(SubI) \
V(TaggedToI) \
- V(ToFastProperties) \
V(Throw) \
+ V(ToFastProperties) \
V(Typeof) \
V(TypeofIs) \
V(TypeofIsAndBranch) \
- V(IsConstructCall) \
- V(IsConstructCallAndBranch) \
V(UnaryMathOperation) \
V(UnknownOSRValue) \
V(ValueOf)
-#define DECLARE_INSTRUCTION(type) \
- virtual bool Is##type() const { return true; } \
- static L##type* cast(LInstruction* instr) { \
- ASSERT(instr->Is##type()); \
- return reinterpret_cast<L##type*>(instr); \
+#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
+ virtual Opcode opcode() const { return LInstruction::k##type; } \
+ virtual void CompileToNative(LCodeGen* generator); \
+ virtual const char* Mnemonic() const { return mnemonic; } \
+ static L##type* cast(LInstruction* instr) { \
+ ASSERT(instr->Is##type()); \
+ return reinterpret_cast<L##type*>(instr); \
}
-#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
- virtual void CompileToNative(LCodeGen* generator); \
- virtual const char* Mnemonic() const { return mnemonic; } \
- DECLARE_INSTRUCTION(type)
-
-
#define DECLARE_HYDROGEN_ACCESSOR(type) \
H##type* hydrogen() const { \
return H##type::cast(hydrogen_value()); \
@@ -205,10 +211,25 @@ class LInstruction: public ZoneObject {
virtual void PrintDataTo(StringStream* stream) = 0;
virtual void PrintOutputOperandTo(StringStream* stream) = 0;
- // Declare virtual type testers.
-#define DECLARE_DO(type) virtual bool Is##type() const { return false; }
- LITHIUM_ALL_INSTRUCTION_LIST(DECLARE_DO)
-#undef DECLARE_DO
+ enum Opcode {
+ // Declare a unique enum value for each instruction.
+#define DECLARE_OPCODE(type) k##type,
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE)
+ kNumberOfInstructions
+#undef DECLARE_OPCODE
+ };
+
+ virtual Opcode opcode() const = 0;
+
+ // Declare non-virtual type testers for all leaf IR classes.
+#define DECLARE_PREDICATE(type) \
+ bool Is##type() const { return opcode() == k##type; }
+ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE)
+#undef DECLARE_PREDICATE
+
+ // Declare virtual predicates for instructions that don't have
+ // an opcode.
+ virtual bool IsGap() const { return false; }
virtual bool IsControl() const { return false; }
virtual void SetBranchTargets(int true_block_id, int false_block_id) { }
@@ -335,8 +356,13 @@ class LGap: public LTemplateInstruction<0, 0, 0> {
parallel_moves_[AFTER] = NULL;
}
- DECLARE_CONCRETE_INSTRUCTION(Gap, "gap")
+ // Can't use the DECLARE-macro here because of sub-classes.
+ virtual bool IsGap() const { return true; }
virtual void PrintDataTo(StringStream* stream);
+ static LGap* cast(LInstruction* instr) {
+ ASSERT(instr->IsGap());
+ return reinterpret_cast<LGap*>(instr);
+ }
bool IsRedundant() const;
@@ -366,6 +392,14 @@ class LGap: public LTemplateInstruction<0, 0, 0> {
};
+class LInstructionGap: public LGap {
+ public:
+ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
+
+ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
+};
+
+
class LGoto: public LTemplateInstruction<0, 0, 0> {
public:
LGoto(int block_id, bool include_stack_check = false)
@@ -454,7 +488,6 @@ class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
template<int I, int T>
class LControlInstruction: public LTemplateInstruction<0, I, T> {
public:
- DECLARE_INSTRUCTION(ControlInstruction)
virtual bool IsControl() const { return true; }
int true_block_id() const { return true_block_id_; }
@@ -638,6 +671,28 @@ class LCmpJSObjectEqAndBranch: public LControlInstruction<2, 0> {
};
+class LCmpSymbolEq: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LCmpSymbolEq(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpSymbolEq, "cmp-symbol-eq")
+};
+
+
+class LCmpSymbolEqAndBranch: public LControlInstruction<2, 0> {
+ public:
+ LCmpSymbolEqAndBranch(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(CmpSymbolEqAndBranch, "cmp-symbol-eq-and-branch")
+};
+
+
class LIsNull: public LTemplateInstruction<1, 1, 0> {
public:
explicit LIsNull(LOperand* value) {
@@ -712,6 +767,31 @@ class LIsSmiAndBranch: public LControlInstruction<1, 0> {
};
+class LIsUndetectable: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LIsUndetectable(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectable, "is-undetectable")
+ DECLARE_HYDROGEN_ACCESSOR(IsUndetectable)
+};
+
+
+class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
+ public:
+ explicit LIsUndetectableAndBranch(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch,
+ "is-undetectable-and-branch")
+
+ virtual void PrintDataTo(StringStream* stream);
+};
+
+
class LHasInstanceType: public LTemplateInstruction<1, 1, 0> {
public:
explicit LHasInstanceType(LOperand* value) {
@@ -828,6 +908,20 @@ class LCmpTAndBranch: public LControlInstruction<2, 0> {
};
+class LIn: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LIn(LOperand* key, LOperand* object) {
+ inputs_[0] = key;
+ inputs_[1] = object;
+ }
+
+ LOperand* key() { return inputs_[0]; }
+ LOperand* object() { return inputs_[1]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(In, "in")
+};
+
+
class LInstanceOf: public LTemplateInstruction<1, 2, 0> {
public:
LInstanceOf(LOperand* left, LOperand* right) {
@@ -1090,6 +1184,7 @@ class LArithmeticD: public LTemplateInstruction<1, 2, 0> {
Token::Value op() const { return op_; }
+ virtual Opcode opcode() const { return LInstruction::kArithmeticD; }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1106,6 +1201,7 @@ class LArithmeticT: public LTemplateInstruction<1, 2, 0> {
inputs_[1] = right;
}
+ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
virtual void CompileToNative(LCodeGen* generator);
virtual const char* Mnemonic() const;
@@ -1393,6 +1489,23 @@ class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> {
};
+class LInvokeFunction: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LInvokeFunction(LOperand* function) {
+ inputs_[0] = function;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
+ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
+
+ LOperand* function() { return inputs_[0]; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ int arity() const { return hydrogen()->argument_count() - 1; }
+};
+
+
class LCallKeyed: public LTemplateInstruction<1, 1, 0> {
public:
explicit LCallKeyed(LOperand* key) {
@@ -1685,6 +1798,21 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
};
+class LStringAdd: public LTemplateInstruction<1, 2, 0> {
+ public:
+ LStringAdd(LOperand* left, LOperand* right) {
+ inputs_[0] = left;
+ inputs_[1] = right;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
+ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
+
+ LOperand* left() { return inputs_[0]; }
+ LOperand* right() { return inputs_[1]; }
+};
+
+
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
@@ -1783,6 +1911,47 @@ class LCheckSmi: public LTemplateInstruction<0, 1, 0> {
};
+class LClampDToUint8: public LTemplateInstruction<1, 1, 1> {
+ public:
+ LClampDToUint8(LOperand* value, LOperand* temp) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
+};
+
+
+class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LClampIToUint8(LOperand* value) {
+ inputs_[0] = value;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
+};
+
+
+class LClampTToUint8: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LClampTToUint8(LOperand* value,
+ LOperand* temp,
+ LOperand* temp2) {
+ inputs_[0] = value;
+ temps_[0] = temp;
+ temps_[1] = temp2;
+ }
+
+ LOperand* unclamped() { return inputs_[0]; }
+
+ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
+};
+
+
class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
public:
explicit LCheckNonSmi(LOperand* value) {
@@ -2158,7 +2327,6 @@ class LChunkBuilder BASE_EMBEDDED {
};
#undef DECLARE_HYDROGEN_ACCESSOR
-#undef DECLARE_INSTRUCTION
#undef DECLARE_CONCRETE_INSTRUCTION
} } // namespace v8::int
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 7f027f70..2d285795 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -201,8 +201,8 @@ void MacroAssembler::RecordWriteHelper(Register object,
Register scratch) {
if (emit_debug_code()) {
// Check that the object is not in new space.
- NearLabel not_in_new_space;
- InNewSpace(object, scratch, not_equal, &not_in_new_space);
+ Label not_in_new_space;
+ InNewSpace(object, scratch, not_equal, &not_in_new_space, Label::kNear);
Abort("new-space object passed to RecordWriteHelper");
bind(&not_in_new_space);
}
@@ -221,6 +221,42 @@ void MacroAssembler::RecordWriteHelper(Register object,
}
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cc,
+ Label* branch,
+ Label::Distance near_jump) {
+ if (Serializer::enabled()) {
+ // Can't do arithmetic on external references if it might get serialized.
+ // The mask isn't really an address. We load it as an external reference in
+ // case the size of the new space is different between the snapshot maker
+ // and the running system.
+ if (scratch.is(object)) {
+ movq(kScratchRegister, ExternalReference::new_space_mask(isolate()));
+ and_(scratch, kScratchRegister);
+ } else {
+ movq(scratch, ExternalReference::new_space_mask(isolate()));
+ and_(scratch, object);
+ }
+ movq(kScratchRegister, ExternalReference::new_space_start(isolate()));
+ cmpq(scratch, kScratchRegister);
+ j(cc, branch, near_jump);
+ } else {
+ ASSERT(is_int32(static_cast<int64_t>(HEAP->NewSpaceMask())));
+ intptr_t new_space_start =
+ reinterpret_cast<intptr_t>(HEAP->NewSpaceStart());
+ movq(kScratchRegister, -new_space_start, RelocInfo::NONE);
+ if (scratch.is(object)) {
+ addq(scratch, kScratchRegister);
+ } else {
+ lea(scratch, Operand(object, kScratchRegister, times_1, 0));
+ }
+ and_(scratch, Immediate(static_cast<int32_t>(HEAP->NewSpaceMask())));
+ j(cc, branch, near_jump);
+ }
+}
+
+
void MacroAssembler::RecordWrite(Register object,
int offset,
Register value,
@@ -287,8 +323,8 @@ void MacroAssembler::RecordWriteNonSmi(Register object,
Label done;
if (emit_debug_code()) {
- NearLabel okay;
- JumpIfNotSmi(object, &okay);
+ Label okay;
+ JumpIfNotSmi(object, &okay, Label::kNear);
Abort("MacroAssembler::RecordWriteNonSmi cannot deal with smis");
bind(&okay);
@@ -344,13 +380,13 @@ void MacroAssembler::Assert(Condition cc, const char* msg) {
void MacroAssembler::AssertFastElements(Register elements) {
if (emit_debug_code()) {
- NearLabel ok;
+ Label ok;
CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
Heap::kFixedArrayMapRootIndex);
- j(equal, &ok);
+ j(equal, &ok, Label::kNear);
CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
Heap::kFixedCOWArrayMapRootIndex);
- j(equal, &ok);
+ j(equal, &ok, Label::kNear);
Abort("JSObject with fast elements map has slow elements");
bind(&ok);
}
@@ -358,8 +394,8 @@ void MacroAssembler::AssertFastElements(Register elements) {
void MacroAssembler::Check(Condition cc, const char* msg) {
- NearLabel L;
- j(cc, &L);
+ Label L;
+ j(cc, &L, Label::kNear);
Abort(msg);
// will not return here
bind(&L);
@@ -371,9 +407,9 @@ void MacroAssembler::CheckStackAlignment() {
int frame_alignment_mask = frame_alignment - 1;
if (frame_alignment > kPointerSize) {
ASSERT(IsPowerOf2(frame_alignment));
- NearLabel alignment_as_expected;
+ Label alignment_as_expected;
testq(rsp, Immediate(frame_alignment_mask));
- j(zero, &alignment_as_expected);
+ j(zero, &alignment_as_expected, Label::kNear);
// Abort if stack is not aligned.
int3();
bind(&alignment_as_expected);
@@ -384,9 +420,9 @@ void MacroAssembler::CheckStackAlignment() {
void MacroAssembler::NegativeZeroTest(Register result,
Register op,
Label* then_label) {
- NearLabel ok;
+ Label ok;
testl(result, result);
- j(not_zero, &ok);
+ j(not_zero, &ok, Label::kNear);
testl(op, op);
j(sign, then_label);
bind(&ok);
@@ -425,9 +461,9 @@ void MacroAssembler::Abort(const char* msg) {
}
-void MacroAssembler::CallStub(CodeStub* stub) {
+void MacroAssembler::CallStub(CodeStub* stub, unsigned ast_id) {
ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
- Call(stub->GetCode(), RelocInfo::CODE_TARGET);
+ Call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id);
}
@@ -650,6 +686,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(
Label leave_exit_frame;
Label write_back;
+ Factory* factory = isolate()->factory();
ExternalReference next_address =
ExternalReference::handle_scope_next_address();
const int kNextOffset = 0;
@@ -697,7 +734,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(
// Check if the function scheduled an exception.
movq(rsi, scheduled_exception_address);
- Cmp(Operand(rsi, 0), FACTORY->the_hole_value());
+ Cmp(Operand(rsi, 0), factory->the_hole_value());
j(not_equal, &promote_scheduled_exception);
LeaveApiExitFrame();
@@ -712,7 +749,7 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(
bind(&empty_result);
// It was zero; the result is undefined.
- Move(rax, FACTORY->undefined_value());
+ Move(rax, factory->undefined_value());
jmp(&prologue);
// HandleScope limit has changed. Delete allocated extensions.
@@ -754,7 +791,7 @@ MaybeObject* MacroAssembler::TryJumpToExternalReference(
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
+ const CallWrapper& call_wrapper) {
// Calls are not allowed in some stubs.
ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
@@ -763,7 +800,7 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
// parameter count to avoid emitting code to do the check.
ParameterCount expected(0);
GetBuiltinEntry(rdx, id);
- InvokeCode(rdx, expected, expected, flag, call_wrapper);
+ InvokeCode(rdx, expected, expected, flag, call_wrapper, CALL_AS_METHOD);
}
@@ -831,8 +868,8 @@ void MacroAssembler::LoadSmiConstant(Register dst, Smi* source) {
if (allow_stub_calls()) {
Assert(equal, "Uninitialized kSmiConstantRegister");
} else {
- NearLabel ok;
- j(equal, &ok);
+ Label ok;
+ j(equal, &ok, Label::kNear);
int3();
bind(&ok);
}
@@ -894,8 +931,8 @@ void MacroAssembler::Integer32ToSmi(Register dst, Register src) {
void MacroAssembler::Integer32ToSmiField(const Operand& dst, Register src) {
if (emit_debug_code()) {
testb(dst, Immediate(0x01));
- NearLabel ok;
- j(zero, &ok);
+ Label ok;
+ j(zero, &ok, Label::kNear);
if (allow_stub_calls()) {
Abort("Integer32ToSmiField writing to non-smi location");
} else {
@@ -1052,6 +1089,24 @@ void MacroAssembler::PositiveSmiDivPowerOfTwoToInteger32(Register dst,
}
+void MacroAssembler::SmiOrIfSmis(Register dst, Register src1, Register src2,
+ Label* on_not_smis,
+ Label::Distance near_jump) {
+ if (dst.is(src1) || dst.is(src2)) {
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ movq(kScratchRegister, src1);
+ or_(kScratchRegister, src2);
+ JumpIfNotSmi(kScratchRegister, on_not_smis, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ or_(dst, src2);
+ JumpIfNotSmi(dst, on_not_smis, near_jump);
+ }
+}
+
+
Condition MacroAssembler::CheckSmi(Register src) {
ASSERT_EQ(0, kSmiTag);
testb(src, Immediate(kSmiTagMask));
@@ -1162,6 +1217,95 @@ void MacroAssembler::CheckSmiToIndicator(Register dst, const Operand& src) {
}
+void MacroAssembler::JumpIfNotValidSmiValue(Register src,
+ Label* on_invalid,
+ Label::Distance near_jump) {
+ Condition is_valid = CheckInteger32ValidSmiValue(src);
+ j(NegateCondition(is_valid), on_invalid, near_jump);
+}
+
+
+void MacroAssembler::JumpIfUIntNotValidSmiValue(Register src,
+ Label* on_invalid,
+ Label::Distance near_jump) {
+ Condition is_valid = CheckUInteger32ValidSmiValue(src);
+ j(NegateCondition(is_valid), on_invalid, near_jump);
+}
+
+
+void MacroAssembler::JumpIfSmi(Register src,
+ Label* on_smi,
+ Label::Distance near_jump) {
+ Condition smi = CheckSmi(src);
+ j(smi, on_smi, near_jump);
+}
+
+
+void MacroAssembler::JumpIfNotSmi(Register src,
+ Label* on_not_smi,
+ Label::Distance near_jump) {
+ Condition smi = CheckSmi(src);
+ j(NegateCondition(smi), on_not_smi, near_jump);
+}
+
+
+void MacroAssembler::JumpUnlessNonNegativeSmi(
+ Register src, Label* on_not_smi_or_negative,
+ Label::Distance near_jump) {
+ Condition non_negative_smi = CheckNonNegativeSmi(src);
+ j(NegateCondition(non_negative_smi), on_not_smi_or_negative, near_jump);
+}
+
+
+void MacroAssembler::JumpIfSmiEqualsConstant(Register src,
+ Smi* constant,
+ Label* on_equals,
+ Label::Distance near_jump) {
+ SmiCompare(src, constant);
+ j(equal, on_equals, near_jump);
+}
+
+
+void MacroAssembler::JumpIfNotBothSmi(Register src1,
+ Register src2,
+ Label* on_not_both_smi,
+ Label::Distance near_jump) {
+ Condition both_smi = CheckBothSmi(src1, src2);
+ j(NegateCondition(both_smi), on_not_both_smi, near_jump);
+}
+
+
+void MacroAssembler::JumpUnlessBothNonNegativeSmi(Register src1,
+ Register src2,
+ Label* on_not_both_smi,
+ Label::Distance near_jump) {
+ Condition both_smi = CheckBothNonNegativeSmi(src1, src2);
+ j(NegateCondition(both_smi), on_not_both_smi, near_jump);
+}
+
+
+void MacroAssembler::SmiTryAddConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ // Does not assume that src is a smi.
+ ASSERT_EQ(static_cast<int>(1), static_cast<int>(kSmiTagMask));
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src.is(kScratchRegister));
+
+ JumpIfNotSmi(src, on_not_smi_result, near_jump);
+ Register tmp = (dst.is(src) ? kScratchRegister : dst);
+ LoadSmiConstant(tmp, constant);
+ addq(tmp, src);
+ j(overflow, on_not_smi_result, near_jump);
+ if (dst.is(src)) {
+ movq(dst, tmp);
+ }
+}
+
+
void MacroAssembler::SmiAddConstant(Register dst, Register src, Smi* constant) {
if (constant->value() == 0) {
if (!dst.is(src)) {
@@ -1218,6 +1362,30 @@ void MacroAssembler::SmiAddConstant(const Operand& dst, Smi* constant) {
}
+void MacroAssembler::SmiAddConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+
+ LoadSmiConstant(kScratchRegister, constant);
+ addq(kScratchRegister, src);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ LoadSmiConstant(dst, constant);
+ addq(dst, src);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) {
if (constant->value() == 0) {
if (!dst.is(src)) {
@@ -1242,17 +1410,148 @@ void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) {
}
+void MacroAssembler::SmiSubConstant(Register dst,
+ Register src,
+ Smi* constant,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ if (constant->value() == 0) {
+ if (!dst.is(src)) {
+ movq(dst, src);
+ }
+ } else if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ if (constant->value() == Smi::kMinValue) {
+ // Subtracting min-value from any non-negative value will overflow.
+ // We test the non-negativeness before doing the subtraction.
+ testq(src, src);
+ j(not_sign, on_not_smi_result, near_jump);
+ LoadSmiConstant(kScratchRegister, constant);
+ subq(dst, kScratchRegister);
+ } else {
+ // Subtract by adding the negation.
+ LoadSmiConstant(kScratchRegister, Smi::FromInt(-constant->value()));
+ addq(kScratchRegister, dst);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ }
+ } else {
+ if (constant->value() == Smi::kMinValue) {
+ // Subtracting min-value from any non-negative value will overflow.
+ // We test the non-negativeness before doing the subtraction.
+ testq(src, src);
+ j(not_sign, on_not_smi_result, near_jump);
+ LoadSmiConstant(dst, constant);
+ // Adding and subtracting the min-value gives the same result, it only
+ // differs on the overflow bit, which we don't check here.
+ addq(dst, src);
+ } else {
+ // Subtract by adding the negation.
+ LoadSmiConstant(dst, Smi::FromInt(-(constant->value())));
+ addq(dst, src);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+ }
+}
+
+
+void MacroAssembler::SmiNeg(Register dst,
+ Register src,
+ Label* on_smi_result,
+ Label::Distance near_jump) {
+ if (dst.is(src)) {
+ ASSERT(!dst.is(kScratchRegister));
+ movq(kScratchRegister, src);
+ neg(dst); // Low 32 bits are retained as zero by negation.
+ // Test if result is zero or Smi::kMinValue.
+ cmpq(dst, kScratchRegister);
+ j(not_equal, on_smi_result, near_jump);
+ movq(src, kScratchRegister);
+ } else {
+ movq(dst, src);
+ neg(dst);
+ cmpq(dst, src);
+ // If the result is zero or Smi::kMinValue, negation failed to create a smi.
+ j(not_equal, on_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiAdd(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ ASSERT(!dst.is(src2));
+ if (dst.is(src1)) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ addq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiAdd(Register dst,
+ Register src1,
+ const Operand& src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ if (dst.is(src1)) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ movq(dst, kScratchRegister);
+ } else {
+ ASSERT(!src2.AddressUsesRegister(dst));
+ movq(dst, src1);
+ addq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
void MacroAssembler::SmiAdd(Register dst,
Register src1,
Register src2) {
// No overflow checking. Use only when it's known that
// overflowing is impossible.
- ASSERT(!dst.is(src2));
if (!dst.is(src1)) {
+ if (emit_debug_code()) {
+ movq(kScratchRegister, src1);
+ addq(kScratchRegister, src2);
+ Check(no_overflow, "Smi addition overflow");
+ }
+ lea(dst, Operand(src1, src2, times_1, 0));
+ } else {
+ addq(dst, src2);
+ Assert(no_overflow, "Smi addition overflow");
+ }
+}
+
+
+void MacroAssembler::SmiSub(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ ASSERT(!dst.is(src2));
+ if (dst.is(src1)) {
+ cmpq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ subq(dst, src2);
+ } else {
movq(dst, src1);
+ subq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
}
- addq(dst, src2);
- Assert(no_overflow, "Smi addition overflow");
}
@@ -1270,6 +1569,25 @@ void MacroAssembler::SmiSub(Register dst, Register src1, Register src2) {
void MacroAssembler::SmiSub(Register dst,
Register src1,
+ const Operand& src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT_NOT_NULL(on_not_smi_result);
+ if (dst.is(src1)) {
+ movq(kScratchRegister, src2);
+ cmpq(src1, kScratchRegister);
+ j(overflow, on_not_smi_result, near_jump);
+ subq(src1, kScratchRegister);
+ } else {
+ movq(dst, src1);
+ subq(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ }
+}
+
+
+void MacroAssembler::SmiSub(Register dst,
+ Register src1,
const Operand& src2) {
// No overflow checking. Use only when it's known that
// overflowing is impossible (e.g., subtracting two positive smis).
@@ -1281,6 +1599,180 @@ void MacroAssembler::SmiSub(Register dst,
}
+void MacroAssembler::SmiMul(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(src2));
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+
+ if (dst.is(src1)) {
+ Label failure, zero_correct_result;
+ movq(kScratchRegister, src1); // Create backup for later testing.
+ SmiToInteger64(dst, src1);
+ imul(dst, src2);
+ j(overflow, &failure, Label::kNear);
+
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case.
+ Label correct_result;
+ testq(dst, dst);
+ j(not_zero, &correct_result, Label::kNear);
+
+ movq(dst, kScratchRegister);
+ xor_(dst, src2);
+ // Result was positive zero.
+ j(positive, &zero_correct_result, Label::kNear);
+
+ bind(&failure); // Reused failure exit, restores src1.
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result, near_jump);
+
+ bind(&zero_correct_result);
+ Set(dst, 0);
+
+ bind(&correct_result);
+ } else {
+ SmiToInteger64(dst, src1);
+ imul(dst, src2);
+ j(overflow, on_not_smi_result, near_jump);
+ // Check for negative zero result. If product is zero, and one
+ // argument is negative, go to slow case.
+ Label correct_result;
+ testq(dst, dst);
+ j(not_zero, &correct_result, Label::kNear);
+ // One of src1 and src2 is zero, the check whether the other is
+ // negative.
+ movq(kScratchRegister, src1);
+ xor_(kScratchRegister, src2);
+ j(negative, on_not_smi_result, near_jump);
+ bind(&correct_result);
+ }
+}
+
+
+void MacroAssembler::SmiDiv(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src2.is(rax));
+ ASSERT(!src2.is(rdx));
+ ASSERT(!src1.is(rdx));
+
+ // Check for 0 divisor (result is +/-Infinity).
+ testq(src2, src2);
+ j(zero, on_not_smi_result, near_jump);
+
+ if (src1.is(rax)) {
+ movq(kScratchRegister, src1);
+ }
+ SmiToInteger32(rax, src1);
+ // We need to rule out dividing Smi::kMinValue by -1, since that would
+ // overflow in idiv and raise an exception.
+ // We combine this with negative zero test (negative zero only happens
+ // when dividing zero by a negative number).
+
+ // We overshoot a little and go to slow case if we divide min-value
+ // by any negative value, not just -1.
+ Label safe_div;
+ testl(rax, Immediate(0x7fffffff));
+ j(not_zero, &safe_div, Label::kNear);
+ testq(src2, src2);
+ if (src1.is(rax)) {
+ j(positive, &safe_div, Label::kNear);
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result, near_jump);
+ } else {
+ j(negative, on_not_smi_result, near_jump);
+ }
+ bind(&safe_div);
+
+ SmiToInteger32(src2, src2);
+ // Sign extend src1 into edx:eax.
+ cdq();
+ idivl(src2);
+ Integer32ToSmi(src2, src2);
+ // Check that the remainder is zero.
+ testl(rdx, rdx);
+ if (src1.is(rax)) {
+ Label smi_result;
+ j(zero, &smi_result, Label::kNear);
+ movq(src1, kScratchRegister);
+ jmp(on_not_smi_result, near_jump);
+ bind(&smi_result);
+ } else {
+ j(not_zero, on_not_smi_result, near_jump);
+ }
+ if (!dst.is(src1) && src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ Integer32ToSmi(dst, rax);
+}
+
+
+void MacroAssembler::SmiMod(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!src2.is(rax));
+ ASSERT(!src2.is(rdx));
+ ASSERT(!src1.is(rdx));
+ ASSERT(!src1.is(src2));
+
+ testq(src2, src2);
+ j(zero, on_not_smi_result, near_jump);
+
+ if (src1.is(rax)) {
+ movq(kScratchRegister, src1);
+ }
+ SmiToInteger32(rax, src1);
+ SmiToInteger32(src2, src2);
+
+ // Test for the edge case of dividing Smi::kMinValue by -1 (will overflow).
+ Label safe_div;
+ cmpl(rax, Immediate(Smi::kMinValue));
+ j(not_equal, &safe_div, Label::kNear);
+ cmpl(src2, Immediate(-1));
+ j(not_equal, &safe_div, Label::kNear);
+ // Retag inputs and go slow case.
+ Integer32ToSmi(src2, src2);
+ if (src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ jmp(on_not_smi_result, near_jump);
+ bind(&safe_div);
+
+ // Sign extend eax into edx:eax.
+ cdq();
+ idivl(src2);
+ // Restore smi tags on inputs.
+ Integer32ToSmi(src2, src2);
+ if (src1.is(rax)) {
+ movq(src1, kScratchRegister);
+ }
+ // Check for a negative zero result. If the result is zero, and the
+ // dividend is negative, go slow to return a floating point negative zero.
+ Label smi_result;
+ testl(rdx, rdx);
+ j(not_zero, &smi_result, Label::kNear);
+ testq(src1, src1);
+ j(negative, on_not_smi_result, near_jump);
+ bind(&smi_result);
+ Integer32ToSmi(dst, rdx);
+}
+
+
void MacroAssembler::SmiNot(Register dst, Register src) {
ASSERT(!dst.is(kScratchRegister));
ASSERT(!src.is(kScratchRegister));
@@ -1387,11 +1879,28 @@ void MacroAssembler::SmiShiftLeftConstant(Register dst,
}
+void MacroAssembler::SmiShiftLogicalRightConstant(
+ Register dst, Register src, int shift_value,
+ Label* on_not_smi_result, Label::Distance near_jump) {
+ // Logic right shift interprets its result as an *unsigned* number.
+ if (dst.is(src)) {
+ UNIMPLEMENTED(); // Not used.
+ } else {
+ movq(dst, src);
+ if (shift_value == 0) {
+ testq(dst, dst);
+ j(negative, on_not_smi_result, near_jump);
+ }
+ shr(dst, Immediate(shift_value + kSmiShift));
+ shl(dst, Immediate(kSmiShift));
+ }
+}
+
+
void MacroAssembler::SmiShiftLeft(Register dst,
Register src1,
Register src2) {
ASSERT(!dst.is(rcx));
- NearLabel result_ok;
// Untag shift amount.
if (!dst.is(src1)) {
movq(dst, src1);
@@ -1403,6 +1912,45 @@ void MacroAssembler::SmiShiftLeft(Register dst,
}
+void MacroAssembler::SmiShiftLogicalRight(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smi_result,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(rcx));
+ // dst and src1 can be the same, because the one case that bails out
+ // is a shift by 0, which leaves dst, and therefore src1, unchanged.
+ if (src1.is(rcx) || src2.is(rcx)) {
+ movq(kScratchRegister, rcx);
+ }
+ if (!dst.is(src1)) {
+ movq(dst, src1);
+ }
+ SmiToInteger32(rcx, src2);
+ orl(rcx, Immediate(kSmiShift));
+ shr_cl(dst); // Shift is rcx modulo 0x1f + 32.
+ shl(dst, Immediate(kSmiShift));
+ testq(dst, dst);
+ if (src1.is(rcx) || src2.is(rcx)) {
+ Label positive_result;
+ j(positive, &positive_result, Label::kNear);
+ if (src1.is(rcx)) {
+ movq(src1, kScratchRegister);
+ } else {
+ movq(src2, kScratchRegister);
+ }
+ jmp(on_not_smi_result, near_jump);
+ bind(&positive_result);
+ } else {
+ // src2 was zero and src1 negative.
+ j(negative, on_not_smi_result, near_jump);
+ }
+}
+
+
void MacroAssembler::SmiShiftArithmeticRight(Register dst,
Register src1,
Register src2) {
@@ -1430,6 +1978,45 @@ void MacroAssembler::SmiShiftArithmeticRight(Register dst,
}
+void MacroAssembler::SelectNonSmi(Register dst,
+ Register src1,
+ Register src2,
+ Label* on_not_smis,
+ Label::Distance near_jump) {
+ ASSERT(!dst.is(kScratchRegister));
+ ASSERT(!src1.is(kScratchRegister));
+ ASSERT(!src2.is(kScratchRegister));
+ ASSERT(!dst.is(src1));
+ ASSERT(!dst.is(src2));
+ // Both operands must not be smis.
+#ifdef DEBUG
+ if (allow_stub_calls()) { // Check contains a stub call.
+ Condition not_both_smis = NegateCondition(CheckBothSmi(src1, src2));
+ Check(not_both_smis, "Both registers were smis in SelectNonSmi.");
+ }
+#endif
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ movl(kScratchRegister, Immediate(kSmiTagMask));
+ and_(kScratchRegister, src1);
+ testl(kScratchRegister, src2);
+ // If non-zero then both are smis.
+ j(not_zero, on_not_smis, near_jump);
+
+ // Exactly one operand is a smi.
+ ASSERT_EQ(1, static_cast<int>(kSmiTagMask));
+ // kScratchRegister still holds src1 & kSmiTag, which is either zero or one.
+ subq(kScratchRegister, Immediate(1));
+ // If src1 is a smi, then scratch register all 1s, else it is all 0s.
+ movq(dst, src1);
+ xor_(dst, src2);
+ and_(dst, kScratchRegister);
+ // If src1 is a smi, dst holds src1 ^ src2, else it is zero.
+ xor_(dst, src1);
+ // If src1 is a smi, dst is src2, else it is src1, i.e., the non-smi.
+}
+
+
SmiIndex MacroAssembler::SmiToIndex(Register dst,
Register src,
int shift) {
@@ -1471,6 +2058,97 @@ void MacroAssembler::AddSmiField(Register dst, const Operand& src) {
}
+void MacroAssembler::JumpIfNotString(Register object,
+ Register object_map,
+ Label* not_string,
+ Label::Distance near_jump) {
+ Condition is_smi = CheckSmi(object);
+ j(is_smi, not_string, near_jump);
+ CmpObjectType(object, FIRST_NONSTRING_TYPE, object_map);
+ j(above_equal, not_string, near_jump);
+}
+
+
+void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(
+ Register first_object,
+ Register second_object,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail,
+ Label::Distance near_jump) {
+ // Check that both objects are not smis.
+ Condition either_smi = CheckEitherSmi(first_object, second_object);
+ j(either_smi, on_fail, near_jump);
+
+ // Load instance type for both strings.
+ movq(scratch1, FieldOperand(first_object, HeapObject::kMapOffset));
+ movq(scratch2, FieldOperand(second_object, HeapObject::kMapOffset));
+ movzxbl(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset));
+ movzxbl(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
+
+ // Check that both are flat ascii strings.
+ ASSERT(kNotStringTag != 0);
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
+
+ andl(scratch1, Immediate(kFlatAsciiStringMask));
+ andl(scratch2, Immediate(kFlatAsciiStringMask));
+ // Interleave the bits to check both scratch1 and scratch2 in one test.
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmpl(scratch1,
+ Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
+ j(not_equal, on_fail, near_jump);
+}
+
+
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
+ Register instance_type,
+ Register scratch,
+ Label* failure,
+ Label::Distance near_jump) {
+ if (!scratch.is(instance_type)) {
+ movl(scratch, instance_type);
+ }
+
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+
+ andl(scratch, Immediate(kFlatAsciiStringMask));
+ cmpl(scratch, Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
+ j(not_equal, failure, near_jump);
+}
+
+
+void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail,
+ Label::Distance near_jump) {
+ // Load instance type for both strings.
+ movq(scratch1, first_object_instance_type);
+ movq(scratch2, second_object_instance_type);
+
+ // Check that both are flat ascii strings.
+ ASSERT(kNotStringTag != 0);
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
+
+ andl(scratch1, Immediate(kFlatAsciiStringMask));
+ andl(scratch2, Immediate(kFlatAsciiStringMask));
+ // Interleave the bits to check both scratch1 and scratch2 in one test.
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmpl(scratch1,
+ Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
+ j(not_equal, on_fail, near_jump);
+}
+
+
void MacroAssembler::Move(Register dst, Register src) {
if (!dst.is(src)) {
@@ -1604,12 +2282,14 @@ void MacroAssembler::Call(Address destination, RelocInfo::Mode rmode) {
}
-void MacroAssembler::Call(Handle<Code> code_object, RelocInfo::Mode rmode) {
+void MacroAssembler::Call(Handle<Code> code_object,
+ RelocInfo::Mode rmode,
+ unsigned ast_id) {
#ifdef DEBUG
int end_position = pc_offset() + CallSize(code_object);
#endif
ASSERT(RelocInfo::IsCodeTarget(rmode));
- call(code_object, rmode);
+ call(code_object, rmode, ast_id);
#ifdef DEBUG
CHECK_EQ(end_position, pc_offset());
#endif
@@ -1774,9 +2454,9 @@ void MacroAssembler::Throw(Register value) {
// Before returning we restore the context from the frame pointer if not NULL.
// The frame pointer is NULL in the exception handler of a JS entry frame.
Set(rsi, 0); // Tentatively set context pointer to NULL
- NearLabel skip;
+ Label skip;
cmpq(rbp, Immediate(0));
- j(equal, &skip);
+ j(equal, &skip, Label::kNear);
movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
bind(&skip);
ret(0);
@@ -1794,12 +2474,12 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
Load(rsp, handler_address);
// Unwind the handlers until the ENTRY handler is found.
- NearLabel loop, done;
+ Label loop, done;
bind(&loop);
// Load the type of the current stack handler.
const int kStateOffset = StackHandlerConstants::kStateOffset;
cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY));
- j(equal, &done);
+ j(equal, &done, Label::kNear);
// Fetch the next handler in the list.
const int kNextOffset = StackHandlerConstants::kNextOffset;
movq(rsp, Operand(rsp, kNextOffset));
@@ -1881,8 +2561,8 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
void MacroAssembler::CheckMap(Register obj,
Handle<Map> map,
Label* fail,
- bool is_heap_object) {
- if (!is_heap_object) {
+ SmiCheckType smi_check_type) {
+ if (smi_check_type == DO_SMI_CHECK) {
JumpIfSmi(obj, fail);
}
Cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
@@ -1890,19 +2570,75 @@ void MacroAssembler::CheckMap(Register obj,
}
+void MacroAssembler::ClampUint8(Register reg) {
+ Label done;
+ testl(reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ setcc(negative, reg); // 1 if negative, 0 if positive.
+ decb(reg); // 0 if negative, 255 if positive.
+ bind(&done);
+}
+
+
+void MacroAssembler::ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister temp_xmm_reg,
+ Register result_reg,
+ Register temp_reg) {
+ Label done;
+ Set(result_reg, 0);
+ xorps(temp_xmm_reg, temp_xmm_reg);
+ ucomisd(input_reg, temp_xmm_reg);
+ j(below, &done, Label::kNear);
+ uint64_t one_half = BitCast<uint64_t, double>(0.5);
+ Set(temp_reg, one_half);
+ movq(temp_xmm_reg, temp_reg);
+ addsd(temp_xmm_reg, input_reg);
+ cvttsd2si(result_reg, temp_xmm_reg);
+ testl(result_reg, Immediate(0xFFFFFF00));
+ j(zero, &done, Label::kNear);
+ Set(result_reg, 255);
+ bind(&done);
+}
+
+
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ movq(descriptors, FieldOperand(map,
+ Map::kInstanceDescriptorsOrBitField3Offset));
+ Label not_smi;
+ JumpIfNotSmi(descriptors, &not_smi, Label::kNear);
+ Move(descriptors, isolate()->factory()->empty_descriptor_array());
+ bind(&not_smi);
+}
+
+
+void MacroAssembler::DispatchMap(Register obj,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type) {
+ Label fail;
+ if (smi_check_type == DO_SMI_CHECK) {
+ JumpIfSmi(obj, &fail);
+ }
+ Cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
+ j(equal, success, RelocInfo::CODE_TARGET);
+
+ bind(&fail);
+}
+
+
void MacroAssembler::AbortIfNotNumber(Register object) {
- NearLabel ok;
+ Label ok;
Condition is_smi = CheckSmi(object);
- j(is_smi, &ok);
+ j(is_smi, &ok, Label::kNear);
Cmp(FieldOperand(object, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
+ isolate()->factory()->heap_number_map());
Assert(equal, "Operand not a number");
bind(&ok);
}
void MacroAssembler::AbortIfSmi(Register object) {
- NearLabel ok;
Condition is_smi = CheckSmi(object);
Assert(NegateCondition(is_smi), "Operand is a smi");
}
@@ -1965,10 +2701,10 @@ void MacroAssembler::TryGetFunctionPrototype(Register function,
j(not_equal, miss);
// Make sure that the function has an instance prototype.
- NearLabel non_instance;
+ Label non_instance;
testb(FieldOperand(result, Map::kBitFieldOffset),
Immediate(1 << Map::kHasNonInstancePrototype));
- j(not_zero, &non_instance);
+ j(not_zero, &non_instance, Label::kNear);
// Get the prototype or initial map from the function.
movq(result,
@@ -1981,13 +2717,13 @@ void MacroAssembler::TryGetFunctionPrototype(Register function,
j(equal, miss);
// If the function does not have an initial map, we're done.
- NearLabel done;
+ Label done;
CmpObjectType(result, MAP_TYPE, kScratchRegister);
- j(not_equal, &done);
+ j(not_equal, &done, Label::kNear);
// Get the prototype from the initial map.
movq(result, FieldOperand(result, Map::kPrototypeOffset));
- jmp(&done);
+ jmp(&done, Label::kNear);
// Non-instance prototype: Fetch prototype from constructor field
// in initial map.
@@ -2044,25 +2780,44 @@ void MacroAssembler::DebugBreak() {
#endif // ENABLE_DEBUGGER_SUPPORT
+void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) {
+ // This macro takes the dst register to make the code more readable
+ // at the call sites. However, the dst register has to be rcx to
+ // follow the calling convention which requires the call type to be
+ // in rcx.
+ ASSERT(dst.is(rcx));
+ if (call_kind == CALL_AS_FUNCTION) {
+ LoadSmiConstant(dst, Smi::FromInt(1));
+ } else {
+ LoadSmiConstant(dst, Smi::FromInt(0));
+ }
+}
+
+
void MacroAssembler::InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
- NearLabel done;
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
+ Label done;
InvokePrologue(expected,
actual,
Handle<Code>::null(),
code,
&done,
flag,
- call_wrapper);
+ Label::kNear,
+ call_wrapper,
+ call_kind);
if (flag == CALL_FUNCTION) {
- if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
+ call_wrapper.BeforeCall(CallSize(code));
+ SetCallKind(rcx, call_kind);
call(code);
- if (call_wrapper != NULL) call_wrapper->AfterCall();
+ call_wrapper.AfterCall();
} else {
ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(rcx, call_kind);
jmp(code);
}
bind(&done);
@@ -2074,8 +2829,9 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
const ParameterCount& actual,
RelocInfo::Mode rmode,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
- NearLabel done;
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
+ Label done;
Register dummy = rax;
InvokePrologue(expected,
actual,
@@ -2083,13 +2839,17 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
dummy,
&done,
flag,
- call_wrapper);
+ Label::kNear,
+ call_wrapper,
+ call_kind);
if (flag == CALL_FUNCTION) {
- if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
+ call_wrapper.BeforeCall(CallSize(code));
+ SetCallKind(rcx, call_kind);
Call(code, rmode);
- if (call_wrapper != NULL) call_wrapper->AfterCall();
+ call_wrapper.AfterCall();
} else {
ASSERT(flag == JUMP_FUNCTION);
+ SetCallKind(rcx, call_kind);
Jump(code, rmode);
}
bind(&done);
@@ -2099,7 +2859,8 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
void MacroAssembler::InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
ASSERT(function.is(rdi));
movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
movq(rsi, FieldOperand(function, JSFunction::kContextOffset));
@@ -2110,14 +2871,15 @@ void MacroAssembler::InvokeFunction(Register function,
movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
ParameterCount expected(rbx);
- InvokeCode(rdx, expected, actual, flag, call_wrapper);
+ InvokeCode(rdx, expected, actual, flag, call_wrapper, call_kind);
}
void MacroAssembler::InvokeFunction(JSFunction* function,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper) {
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
ASSERT(function->is_compiled());
// Get the function and setup the context.
Move(rdi, Handle<JSFunction>(function));
@@ -2128,7 +2890,7 @@ void MacroAssembler::InvokeFunction(JSFunction* function,
// the Code object every time we call the function.
movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
ParameterCount expected(function->shared()->formal_parameter_count());
- InvokeCode(rdx, expected, actual, flag, call_wrapper);
+ InvokeCode(rdx, expected, actual, flag, call_wrapper, call_kind);
} else {
// Invoke the cached code.
Handle<Code> code(function->code());
@@ -2138,7 +2900,79 @@ void MacroAssembler::InvokeFunction(JSFunction* function,
actual,
RelocInfo::CODE_TARGET,
flag,
- call_wrapper);
+ call_wrapper,
+ call_kind);
+ }
+}
+
+
+void MacroAssembler::InvokePrologue(const ParameterCount& expected,
+ const ParameterCount& actual,
+ Handle<Code> code_constant,
+ Register code_register,
+ Label* done,
+ InvokeFlag flag,
+ Label::Distance near_jump,
+ const CallWrapper& call_wrapper,
+ CallKind call_kind) {
+ bool definitely_matches = false;
+ Label invoke;
+ if (expected.is_immediate()) {
+ ASSERT(actual.is_immediate());
+ if (expected.immediate() == actual.immediate()) {
+ definitely_matches = true;
+ } else {
+ Set(rax, actual.immediate());
+ if (expected.immediate() ==
+ SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
+ // Don't worry about adapting arguments for built-ins that
+ // don't want that done. Skip adaption code by making it look
+ // like we have a match between expected and actual number of
+ // arguments.
+ definitely_matches = true;
+ } else {
+ Set(rbx, expected.immediate());
+ }
+ }
+ } else {
+ if (actual.is_immediate()) {
+ // Expected is in register, actual is immediate. This is the
+ // case when we invoke function values without going through the
+ // IC mechanism.
+ cmpq(expected.reg(), Immediate(actual.immediate()));
+ j(equal, &invoke, Label::kNear);
+ ASSERT(expected.reg().is(rbx));
+ Set(rax, actual.immediate());
+ } else if (!expected.reg().is(actual.reg())) {
+ // Both expected and actual are in (different) registers. This
+ // is the case when we invoke functions using call and apply.
+ cmpq(expected.reg(), actual.reg());
+ j(equal, &invoke, Label::kNear);
+ ASSERT(actual.reg().is(rax));
+ ASSERT(expected.reg().is(rbx));
+ }
+ }
+
+ if (!definitely_matches) {
+ Handle<Code> adaptor = isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ if (!code_constant.is_null()) {
+ movq(rdx, code_constant, RelocInfo::EMBEDDED_OBJECT);
+ addq(rdx, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ } else if (!code_register.is(rdx)) {
+ movq(rdx, code_register);
+ }
+
+ if (flag == CALL_FUNCTION) {
+ call_wrapper.BeforeCall(CallSize(adaptor));
+ SetCallKind(rcx, call_kind);
+ Call(adaptor, RelocInfo::CODE_TARGET);
+ call_wrapper.AfterCall();
+ jmp(done, near_jump);
+ } else {
+ SetCallKind(rcx, call_kind);
+ Jump(adaptor, RelocInfo::CODE_TARGET);
+ }
+ bind(&invoke);
}
}
@@ -2152,7 +2986,7 @@ void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(kScratchRegister);
if (emit_debug_code()) {
movq(kScratchRegister,
- FACTORY->undefined_value(),
+ isolate()->factory()->undefined_value(),
RelocInfo::EMBEDDED_OBJECT);
cmpq(Operand(rsp, 0), kScratchRegister);
Check(not_equal, "code object not properly patched");
@@ -2320,7 +3154,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
// Check the context is a global context.
if (emit_debug_code()) {
Cmp(FieldOperand(scratch, HeapObject::kMapOffset),
- FACTORY->global_context_map());
+ isolate()->factory()->global_context_map());
Check(equal, "JSGlobalObject::global_context should be a global context.");
}
@@ -2822,7 +3656,7 @@ void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
movq(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
if (emit_debug_code()) {
Label ok, fail;
- CheckMap(map, FACTORY->meta_map(), &fail, false);
+ CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK);
jmp(&ok);
bind(&fail);
Abort("Global functions must have initial map");
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 4c177205..16f6d8d3 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -29,6 +29,7 @@
#define V8_X64_MACRO_ASSEMBLER_X64_H_
#include "assembler.h"
+#include "v8globals.h"
namespace v8 {
namespace internal {
@@ -44,6 +45,7 @@ enum AllocationFlags {
RESULT_CONTAINS_TOP = 1 << 1
};
+
// Default scratch register used by MacroAssembler (and other code that needs
// a spare register). The register isn't callee save, and not used by the
// function calling convention.
@@ -61,7 +63,6 @@ typedef Operand MemOperand;
// Forward declaration.
class JumpTarget;
-class CallWrapper;
struct SmiIndex {
SmiIndex(Register index_register, ScaleFactor scale)
@@ -146,11 +147,11 @@ class MacroAssembler: public Assembler {
// Check if object is in new space. The condition cc can be equal or
// not_equal. If it is equal a jump will be done if the object is on new
// space. The register scratch can be object itself, but it will be clobbered.
- template <typename LabelType>
void InNewSpace(Register object,
Register scratch,
Condition cc,
- LabelType* branch);
+ Label* branch,
+ Label::Distance near_jump = Label::kFar);
// For page containing |object| mark region covering [object+offset]
// dirty. |object| is the object being stored into, |value| is the
@@ -240,37 +241,46 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// JavaScript invokes
+ // Setup call kind marking in rcx. The method takes rcx as an
+ // explicit first parameter to make the code more readable at the
+ // call sites.
+ void SetCallKind(Register dst, CallKind kind);
+
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
void InvokeCode(Handle<Code> code,
const ParameterCount& expected,
const ParameterCount& actual,
RelocInfo::Mode rmode,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
void InvokeFunction(Register function,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
void InvokeFunction(JSFunction* function,
const ParameterCount& actual,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper,
+ CallKind call_kind);
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
- CallWrapper* call_wrapper = NULL);
+ const CallWrapper& call_wrapper = NullCallWrapper());
// Store the function for the given builtin in the target register.
void GetBuiltinFunction(Register target, Builtins::JavaScript id);
@@ -327,11 +337,11 @@ class MacroAssembler: public Assembler {
// If either argument is not a smi, jump to on_not_smis and retain
// the original values of source registers. The destination register
// may be changed if it's not one of the source registers.
- template <typename LabelType>
void SmiOrIfSmis(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smis);
+ Label* on_not_smis,
+ Label::Distance near_jump = Label::kFar);
// Simple comparison of smis. Both sides must be known smis to use these,
@@ -389,42 +399,45 @@ class MacroAssembler: public Assembler {
// above with a conditional jump.
// Jump if the value cannot be represented by a smi.
- template <typename LabelType>
- void JumpIfNotValidSmiValue(Register src, LabelType* on_invalid);
+ void JumpIfNotValidSmiValue(Register src, Label* on_invalid,
+ Label::Distance near_jump = Label::kFar);
// Jump if the unsigned integer value cannot be represented by a smi.
- template <typename LabelType>
- void JumpIfUIntNotValidSmiValue(Register src, LabelType* on_invalid);
+ void JumpIfUIntNotValidSmiValue(Register src, Label* on_invalid,
+ Label::Distance near_jump = Label::kFar);
// Jump to label if the value is a tagged smi.
- template <typename LabelType>
- void JumpIfSmi(Register src, LabelType* on_smi);
+ void JumpIfSmi(Register src,
+ Label* on_smi,
+ Label::Distance near_jump = Label::kFar);
// Jump to label if the value is not a tagged smi.
- template <typename LabelType>
- void JumpIfNotSmi(Register src, LabelType* on_not_smi);
+ void JumpIfNotSmi(Register src,
+ Label* on_not_smi,
+ Label::Distance near_jump = Label::kFar);
// Jump to label if the value is not a non-negative tagged smi.
- template <typename LabelType>
- void JumpUnlessNonNegativeSmi(Register src, LabelType* on_not_smi);
+ void JumpUnlessNonNegativeSmi(Register src,
+ Label* on_not_smi,
+ Label::Distance near_jump = Label::kFar);
// Jump to label if the value, which must be a tagged smi, has value equal
// to the constant.
- template <typename LabelType>
void JumpIfSmiEqualsConstant(Register src,
Smi* constant,
- LabelType* on_equals);
+ Label* on_equals,
+ Label::Distance near_jump = Label::kFar);
// Jump if either or both register are not smi values.
- template <typename LabelType>
void JumpIfNotBothSmi(Register src1,
Register src2,
- LabelType* on_not_both_smi);
+ Label* on_not_both_smi,
+ Label::Distance near_jump = Label::kFar);
// Jump if either or both register are not non-negative smi values.
- template <typename LabelType>
void JumpUnlessBothNonNegativeSmi(Register src1, Register src2,
- LabelType* on_not_both_smi);
+ Label* on_not_both_smi,
+ Label::Distance near_jump = Label::kFar);
// Operations on tagged smi values.
@@ -434,11 +447,11 @@ class MacroAssembler: public Assembler {
// Optimistically adds an integer constant to a supposed smi.
// If the src is not a smi, or the result is not a smi, jump to
// the label.
- template <typename LabelType>
void SmiTryAddConstant(Register dst,
Register src,
Smi* constant,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Add an integer constant to a tagged smi, giving a tagged smi as result.
// No overflow testing on the result is done.
@@ -450,11 +463,11 @@ class MacroAssembler: public Assembler {
// Add an integer constant to a tagged smi, giving a tagged smi as result,
// or jumping to a label if the result cannot be represented by a smi.
- template <typename LabelType>
void SmiAddConstant(Register dst,
Register src,
Smi* constant,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Subtract an integer constant from a tagged smi, giving a tagged smi as
// result. No testing on the result is done. Sets the N and Z flags
@@ -463,32 +476,32 @@ class MacroAssembler: public Assembler {
// Subtract an integer constant from a tagged smi, giving a tagged smi as
// result, or jumping to a label if the result cannot be represented by a smi.
- template <typename LabelType>
void SmiSubConstant(Register dst,
Register src,
Smi* constant,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Negating a smi can give a negative zero or too large positive value.
// NOTICE: This operation jumps on success, not failure!
- template <typename LabelType>
void SmiNeg(Register dst,
Register src,
- LabelType* on_smi_result);
+ Label* on_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Adds smi values and return the result as a smi.
// If dst is src1, then src1 will be destroyed, even if
// the operation is unsuccessful.
- template <typename LabelType>
void SmiAdd(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smi_result);
- template <typename LabelType>
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
void SmiAdd(Register dst,
Register src1,
const Operand& src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
void SmiAdd(Register dst,
Register src1,
@@ -497,21 +510,21 @@ class MacroAssembler: public Assembler {
// Subtracts smi values and return the result as a smi.
// If dst is src1, then src1 will be destroyed, even if
// the operation is unsuccessful.
- template <typename LabelType>
void SmiSub(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
void SmiSub(Register dst,
Register src1,
Register src2);
- template <typename LabelType>
void SmiSub(Register dst,
Register src1,
const Operand& src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
void SmiSub(Register dst,
Register src1,
@@ -521,27 +534,27 @@ class MacroAssembler: public Assembler {
// if possible.
// If dst is src1, then src1 will be destroyed, even if
// the operation is unsuccessful.
- template <typename LabelType>
void SmiMul(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Divides one smi by another and returns the quotient.
// Clobbers rax and rdx registers.
- template <typename LabelType>
void SmiDiv(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Divides one smi by another and returns the remainder.
// Clobbers rax and rdx registers.
- template <typename LabelType>
void SmiMod(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Bitwise operations.
void SmiNot(Register dst, Register src);
@@ -555,11 +568,11 @@ class MacroAssembler: public Assembler {
void SmiShiftLeftConstant(Register dst,
Register src,
int shift_value);
- template <typename LabelType>
void SmiShiftLogicalRightConstant(Register dst,
Register src,
int shift_value,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
void SmiShiftArithmeticRightConstant(Register dst,
Register src,
int shift_value);
@@ -572,11 +585,11 @@ class MacroAssembler: public Assembler {
// Shifts a smi value to the right, shifting in zero bits at the top, and
// returns the unsigned intepretation of the result if that is a smi.
// Uses and clobbers rcx, so dst may not be rcx.
- template <typename LabelType>
void SmiShiftLogicalRight(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smi_result);
+ Label* on_not_smi_result,
+ Label::Distance near_jump = Label::kFar);
// Shifts a smi value to the right, sign extending the top, and
// returns the signed intepretation of the result. That will always
// be a valid smi value, since it's numerically smaller than the
@@ -590,11 +603,11 @@ class MacroAssembler: public Assembler {
// Select the non-smi register of two registers where exactly one is a
// smi. If neither are smis, jump to the failure label.
- template <typename LabelType>
void SelectNonSmi(Register dst,
Register src1,
Register src2,
- LabelType* on_not_smis);
+ Label* on_not_smis,
+ Label::Distance near_jump = Label::kFar);
// Converts, if necessary, a smi to a combination of number and
// multiplier to be used as a scaled index.
@@ -630,35 +643,36 @@ class MacroAssembler: public Assembler {
// String macros.
// If object is a string, its map is loaded into object_map.
- template <typename LabelType>
void JumpIfNotString(Register object,
Register object_map,
- LabelType* not_string);
+ Label* not_string,
+ Label::Distance near_jump = Label::kFar);
- template <typename LabelType>
- void JumpIfNotBothSequentialAsciiStrings(Register first_object,
- Register second_object,
- Register scratch1,
- Register scratch2,
- LabelType* on_not_both_flat_ascii);
+ void JumpIfNotBothSequentialAsciiStrings(
+ Register first_object,
+ Register second_object,
+ Register scratch1,
+ Register scratch2,
+ Label* on_not_both_flat_ascii,
+ Label::Distance near_jump = Label::kFar);
// Check whether the instance type represents a flat ascii string. Jump to the
// label if not. If the instance type can be scratched specify same register
// for both instance type and scratch.
- template <typename LabelType>
void JumpIfInstanceTypeIsNotSequentialAscii(
Register instance_type,
Register scratch,
- LabelType *on_not_flat_ascii_string);
+ Label*on_not_flat_ascii_string,
+ Label::Distance near_jump = Label::kFar);
- template <typename LabelType>
void JumpIfBothInstanceTypesAreNotSequentialAscii(
Register first_object_instance_type,
Register second_object_instance_type,
Register scratch1,
Register scratch2,
- LabelType* on_fail);
+ Label* on_fail,
+ Label::Distance near_jump = Label::kFar);
// ---------------------------------------------------------------------------
// Macro instructions.
@@ -692,7 +706,9 @@ class MacroAssembler: public Assembler {
void Call(Address destination, RelocInfo::Mode rmode);
void Call(ExternalReference ext);
- void Call(Handle<Code> code_object, RelocInfo::Mode rmode);
+ void Call(Handle<Code> code_object,
+ RelocInfo::Mode rmode,
+ unsigned ast_id = kNoASTId);
// The size of the code generated for different call instructions.
int CallSize(Address destination, RelocInfo::Mode rmode) {
@@ -744,7 +760,15 @@ class MacroAssembler: public Assembler {
void CheckMap(Register obj,
Handle<Map> map,
Label* fail,
- bool is_heap_object);
+ SmiCheckType smi_check_type);
+
+ // Check if the map of an object is equal to a specified map and branch to a
+ // specified target if equal. Skip the smi check if not required (object is
+ // known to be a heap object)
+ void DispatchMap(Register obj,
+ Handle<Map> map,
+ Handle<Code> success,
+ SmiCheckType smi_check_type);
// Check if the object in register heap_object is a string. Afterwards the
// register map contains the object map and the register instance_type
@@ -760,6 +784,15 @@ class MacroAssembler: public Assembler {
// jcc instructions (je, ja, jae, jb, jbe, je, and jz).
void FCmp();
+ void ClampUint8(Register reg);
+
+ void ClampDoubleToUint8(XMMRegister input_reg,
+ XMMRegister temp_xmm_reg,
+ Register result_reg,
+ Register temp_reg);
+
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+
// Abort execution if argument is not a number. Used in debug code.
void AbortIfNotNumber(Register object);
@@ -932,7 +965,7 @@ class MacroAssembler: public Assembler {
// Runtime calls
// Call a code stub.
- void CallStub(CodeStub* stub);
+ void CallStub(CodeStub* stub, unsigned ast_id = kNoASTId);
// Call a code stub and return the code object called. Try to generate
// the code if necessary. Do not perform a GC but instead return a retry
@@ -1102,6 +1135,7 @@ class MacroAssembler: public Assembler {
// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
static int kSafepointPushRegisterIndices[Register::kNumRegisters];
static const int kNumSafepointSavedRegisters = 11;
+ static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
bool generating_stub_;
bool allow_stub_calls_;
@@ -1118,14 +1152,15 @@ class MacroAssembler: public Assembler {
Handle<Object> code_object_;
// Helper functions for generating invokes.
- template <typename LabelType>
void InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual,
Handle<Code> code_constant,
Register code_register,
- LabelType* done,
+ Label* done,
InvokeFlag flag,
- CallWrapper* call_wrapper);
+ Label::Distance near_jump = Label::kFar,
+ const CallWrapper& call_wrapper = NullCallWrapper(),
+ CallKind call_kind = CALL_AS_METHOD);
// Activation support.
void EnterFrame(StackFrame::Type type);
@@ -1190,21 +1225,6 @@ class CodePatcher {
};
-// Helper class for generating code or data associated with the code
-// right before or after a call instruction. As an example this can be used to
-// generate safepoint data after calls for crankshaft.
-class CallWrapper {
- public:
- CallWrapper() { }
- virtual ~CallWrapper() { }
- // Called just before emitting a call. Argument is the size of the generated
- // call code.
- virtual void BeforeCall(int call_size) = 0;
- // Called just after emitting a call, i.e., at the return site for the call.
- virtual void AfterCall() = 0;
-};
-
-
// -----------------------------------------------------------------------------
// Static helper functions.
@@ -1266,751 +1286,6 @@ extern void LogGeneratedCodeCoverage(const char* file_line);
#define ACCESS_MASM(masm) masm->
#endif
-// -----------------------------------------------------------------------------
-// Template implementations.
-
-static int kSmiShift = kSmiTagSize + kSmiShiftSize;
-
-
-template <typename LabelType>
-void MacroAssembler::SmiNeg(Register dst,
- Register src,
- LabelType* on_smi_result) {
- if (dst.is(src)) {
- ASSERT(!dst.is(kScratchRegister));
- movq(kScratchRegister, src);
- neg(dst); // Low 32 bits are retained as zero by negation.
- // Test if result is zero or Smi::kMinValue.
- cmpq(dst, kScratchRegister);
- j(not_equal, on_smi_result);
- movq(src, kScratchRegister);
- } else {
- movq(dst, src);
- neg(dst);
- cmpq(dst, src);
- // If the result is zero or Smi::kMinValue, negation failed to create a smi.
- j(not_equal, on_smi_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiAdd(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smi_result) {
- ASSERT_NOT_NULL(on_not_smi_result);
- ASSERT(!dst.is(src2));
- if (dst.is(src1)) {
- movq(kScratchRegister, src1);
- addq(kScratchRegister, src2);
- j(overflow, on_not_smi_result);
- movq(dst, kScratchRegister);
- } else {
- movq(dst, src1);
- addq(dst, src2);
- j(overflow, on_not_smi_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiAdd(Register dst,
- Register src1,
- const Operand& src2,
- LabelType* on_not_smi_result) {
- ASSERT_NOT_NULL(on_not_smi_result);
- if (dst.is(src1)) {
- movq(kScratchRegister, src1);
- addq(kScratchRegister, src2);
- j(overflow, on_not_smi_result);
- movq(dst, kScratchRegister);
- } else {
- ASSERT(!src2.AddressUsesRegister(dst));
- movq(dst, src1);
- addq(dst, src2);
- j(overflow, on_not_smi_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiSub(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smi_result) {
- ASSERT_NOT_NULL(on_not_smi_result);
- ASSERT(!dst.is(src2));
- if (dst.is(src1)) {
- cmpq(dst, src2);
- j(overflow, on_not_smi_result);
- subq(dst, src2);
- } else {
- movq(dst, src1);
- subq(dst, src2);
- j(overflow, on_not_smi_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiSub(Register dst,
- Register src1,
- const Operand& src2,
- LabelType* on_not_smi_result) {
- ASSERT_NOT_NULL(on_not_smi_result);
- if (dst.is(src1)) {
- movq(kScratchRegister, src2);
- cmpq(src1, kScratchRegister);
- j(overflow, on_not_smi_result);
- subq(src1, kScratchRegister);
- } else {
- movq(dst, src1);
- subq(dst, src2);
- j(overflow, on_not_smi_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiMul(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smi_result) {
- ASSERT(!dst.is(src2));
- ASSERT(!dst.is(kScratchRegister));
- ASSERT(!src1.is(kScratchRegister));
- ASSERT(!src2.is(kScratchRegister));
-
- if (dst.is(src1)) {
- NearLabel failure, zero_correct_result;
- movq(kScratchRegister, src1); // Create backup for later testing.
- SmiToInteger64(dst, src1);
- imul(dst, src2);
- j(overflow, &failure);
-
- // Check for negative zero result. If product is zero, and one
- // argument is negative, go to slow case.
- NearLabel correct_result;
- testq(dst, dst);
- j(not_zero, &correct_result);
-
- movq(dst, kScratchRegister);
- xor_(dst, src2);
- j(positive, &zero_correct_result); // Result was positive zero.
-
- bind(&failure); // Reused failure exit, restores src1.
- movq(src1, kScratchRegister);
- jmp(on_not_smi_result);
-
- bind(&zero_correct_result);
- Set(dst, 0);
-
- bind(&correct_result);
- } else {
- SmiToInteger64(dst, src1);
- imul(dst, src2);
- j(overflow, on_not_smi_result);
- // Check for negative zero result. If product is zero, and one
- // argument is negative, go to slow case.
- NearLabel correct_result;
- testq(dst, dst);
- j(not_zero, &correct_result);
- // One of src1 and src2 is zero, the check whether the other is
- // negative.
- movq(kScratchRegister, src1);
- xor_(kScratchRegister, src2);
- j(negative, on_not_smi_result);
- bind(&correct_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiTryAddConstant(Register dst,
- Register src,
- Smi* constant,
- LabelType* on_not_smi_result) {
- // Does not assume that src is a smi.
- ASSERT_EQ(static_cast<int>(1), static_cast<int>(kSmiTagMask));
- ASSERT_EQ(0, kSmiTag);
- ASSERT(!dst.is(kScratchRegister));
- ASSERT(!src.is(kScratchRegister));
-
- JumpIfNotSmi(src, on_not_smi_result);
- Register tmp = (dst.is(src) ? kScratchRegister : dst);
- LoadSmiConstant(tmp, constant);
- addq(tmp, src);
- j(overflow, on_not_smi_result);
- if (dst.is(src)) {
- movq(dst, tmp);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiAddConstant(Register dst,
- Register src,
- Smi* constant,
- LabelType* on_not_smi_result) {
- if (constant->value() == 0) {
- if (!dst.is(src)) {
- movq(dst, src);
- }
- } else if (dst.is(src)) {
- ASSERT(!dst.is(kScratchRegister));
-
- LoadSmiConstant(kScratchRegister, constant);
- addq(kScratchRegister, src);
- j(overflow, on_not_smi_result);
- movq(dst, kScratchRegister);
- } else {
- LoadSmiConstant(dst, constant);
- addq(dst, src);
- j(overflow, on_not_smi_result);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiSubConstant(Register dst,
- Register src,
- Smi* constant,
- LabelType* on_not_smi_result) {
- if (constant->value() == 0) {
- if (!dst.is(src)) {
- movq(dst, src);
- }
- } else if (dst.is(src)) {
- ASSERT(!dst.is(kScratchRegister));
- if (constant->value() == Smi::kMinValue) {
- // Subtracting min-value from any non-negative value will overflow.
- // We test the non-negativeness before doing the subtraction.
- testq(src, src);
- j(not_sign, on_not_smi_result);
- LoadSmiConstant(kScratchRegister, constant);
- subq(dst, kScratchRegister);
- } else {
- // Subtract by adding the negation.
- LoadSmiConstant(kScratchRegister, Smi::FromInt(-constant->value()));
- addq(kScratchRegister, dst);
- j(overflow, on_not_smi_result);
- movq(dst, kScratchRegister);
- }
- } else {
- if (constant->value() == Smi::kMinValue) {
- // Subtracting min-value from any non-negative value will overflow.
- // We test the non-negativeness before doing the subtraction.
- testq(src, src);
- j(not_sign, on_not_smi_result);
- LoadSmiConstant(dst, constant);
- // Adding and subtracting the min-value gives the same result, it only
- // differs on the overflow bit, which we don't check here.
- addq(dst, src);
- } else {
- // Subtract by adding the negation.
- LoadSmiConstant(dst, Smi::FromInt(-(constant->value())));
- addq(dst, src);
- j(overflow, on_not_smi_result);
- }
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiDiv(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smi_result) {
- ASSERT(!src1.is(kScratchRegister));
- ASSERT(!src2.is(kScratchRegister));
- ASSERT(!dst.is(kScratchRegister));
- ASSERT(!src2.is(rax));
- ASSERT(!src2.is(rdx));
- ASSERT(!src1.is(rdx));
-
- // Check for 0 divisor (result is +/-Infinity).
- NearLabel positive_divisor;
- testq(src2, src2);
- j(zero, on_not_smi_result);
-
- if (src1.is(rax)) {
- movq(kScratchRegister, src1);
- }
- SmiToInteger32(rax, src1);
- // We need to rule out dividing Smi::kMinValue by -1, since that would
- // overflow in idiv and raise an exception.
- // We combine this with negative zero test (negative zero only happens
- // when dividing zero by a negative number).
-
- // We overshoot a little and go to slow case if we divide min-value
- // by any negative value, not just -1.
- NearLabel safe_div;
- testl(rax, Immediate(0x7fffffff));
- j(not_zero, &safe_div);
- testq(src2, src2);
- if (src1.is(rax)) {
- j(positive, &safe_div);
- movq(src1, kScratchRegister);
- jmp(on_not_smi_result);
- } else {
- j(negative, on_not_smi_result);
- }
- bind(&safe_div);
-
- SmiToInteger32(src2, src2);
- // Sign extend src1 into edx:eax.
- cdq();
- idivl(src2);
- Integer32ToSmi(src2, src2);
- // Check that the remainder is zero.
- testl(rdx, rdx);
- if (src1.is(rax)) {
- NearLabel smi_result;
- j(zero, &smi_result);
- movq(src1, kScratchRegister);
- jmp(on_not_smi_result);
- bind(&smi_result);
- } else {
- j(not_zero, on_not_smi_result);
- }
- if (!dst.is(src1) && src1.is(rax)) {
- movq(src1, kScratchRegister);
- }
- Integer32ToSmi(dst, rax);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiMod(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smi_result) {
- ASSERT(!dst.is(kScratchRegister));
- ASSERT(!src1.is(kScratchRegister));
- ASSERT(!src2.is(kScratchRegister));
- ASSERT(!src2.is(rax));
- ASSERT(!src2.is(rdx));
- ASSERT(!src1.is(rdx));
- ASSERT(!src1.is(src2));
-
- testq(src2, src2);
- j(zero, on_not_smi_result);
-
- if (src1.is(rax)) {
- movq(kScratchRegister, src1);
- }
- SmiToInteger32(rax, src1);
- SmiToInteger32(src2, src2);
-
- // Test for the edge case of dividing Smi::kMinValue by -1 (will overflow).
- NearLabel safe_div;
- cmpl(rax, Immediate(Smi::kMinValue));
- j(not_equal, &safe_div);
- cmpl(src2, Immediate(-1));
- j(not_equal, &safe_div);
- // Retag inputs and go slow case.
- Integer32ToSmi(src2, src2);
- if (src1.is(rax)) {
- movq(src1, kScratchRegister);
- }
- jmp(on_not_smi_result);
- bind(&safe_div);
-
- // Sign extend eax into edx:eax.
- cdq();
- idivl(src2);
- // Restore smi tags on inputs.
- Integer32ToSmi(src2, src2);
- if (src1.is(rax)) {
- movq(src1, kScratchRegister);
- }
- // Check for a negative zero result. If the result is zero, and the
- // dividend is negative, go slow to return a floating point negative zero.
- NearLabel smi_result;
- testl(rdx, rdx);
- j(not_zero, &smi_result);
- testq(src1, src1);
- j(negative, on_not_smi_result);
- bind(&smi_result);
- Integer32ToSmi(dst, rdx);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiShiftLogicalRightConstant(
- Register dst, Register src, int shift_value, LabelType* on_not_smi_result) {
- // Logic right shift interprets its result as an *unsigned* number.
- if (dst.is(src)) {
- UNIMPLEMENTED(); // Not used.
- } else {
- movq(dst, src);
- if (shift_value == 0) {
- testq(dst, dst);
- j(negative, on_not_smi_result);
- }
- shr(dst, Immediate(shift_value + kSmiShift));
- shl(dst, Immediate(kSmiShift));
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiShiftLogicalRight(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smi_result) {
- ASSERT(!dst.is(kScratchRegister));
- ASSERT(!src1.is(kScratchRegister));
- ASSERT(!src2.is(kScratchRegister));
- ASSERT(!dst.is(rcx));
- // dst and src1 can be the same, because the one case that bails out
- // is a shift by 0, which leaves dst, and therefore src1, unchanged.
- NearLabel result_ok;
- if (src1.is(rcx) || src2.is(rcx)) {
- movq(kScratchRegister, rcx);
- }
- if (!dst.is(src1)) {
- movq(dst, src1);
- }
- SmiToInteger32(rcx, src2);
- orl(rcx, Immediate(kSmiShift));
- shr_cl(dst); // Shift is rcx modulo 0x1f + 32.
- shl(dst, Immediate(kSmiShift));
- testq(dst, dst);
- if (src1.is(rcx) || src2.is(rcx)) {
- NearLabel positive_result;
- j(positive, &positive_result);
- if (src1.is(rcx)) {
- movq(src1, kScratchRegister);
- } else {
- movq(src2, kScratchRegister);
- }
- jmp(on_not_smi_result);
- bind(&positive_result);
- } else {
- j(negative, on_not_smi_result); // src2 was zero and src1 negative.
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SelectNonSmi(Register dst,
- Register src1,
- Register src2,
- LabelType* on_not_smis) {
- ASSERT(!dst.is(kScratchRegister));
- ASSERT(!src1.is(kScratchRegister));
- ASSERT(!src2.is(kScratchRegister));
- ASSERT(!dst.is(src1));
- ASSERT(!dst.is(src2));
- // Both operands must not be smis.
-#ifdef DEBUG
- if (allow_stub_calls()) { // Check contains a stub call.
- Condition not_both_smis = NegateCondition(CheckBothSmi(src1, src2));
- Check(not_both_smis, "Both registers were smis in SelectNonSmi.");
- }
-#endif
- ASSERT_EQ(0, kSmiTag);
- ASSERT_EQ(0, Smi::FromInt(0));
- movl(kScratchRegister, Immediate(kSmiTagMask));
- and_(kScratchRegister, src1);
- testl(kScratchRegister, src2);
- // If non-zero then both are smis.
- j(not_zero, on_not_smis);
-
- // Exactly one operand is a smi.
- ASSERT_EQ(1, static_cast<int>(kSmiTagMask));
- // kScratchRegister still holds src1 & kSmiTag, which is either zero or one.
- subq(kScratchRegister, Immediate(1));
- // If src1 is a smi, then scratch register all 1s, else it is all 0s.
- movq(dst, src1);
- xor_(dst, src2);
- and_(dst, kScratchRegister);
- // If src1 is a smi, dst holds src1 ^ src2, else it is zero.
- xor_(dst, src1);
- // If src1 is a smi, dst is src2, else it is src1, i.e., the non-smi.
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfSmi(Register src, LabelType* on_smi) {
- ASSERT_EQ(0, kSmiTag);
- Condition smi = CheckSmi(src);
- j(smi, on_smi);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfNotSmi(Register src, LabelType* on_not_smi) {
- Condition smi = CheckSmi(src);
- j(NegateCondition(smi), on_not_smi);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpUnlessNonNegativeSmi(
- Register src, LabelType* on_not_smi_or_negative) {
- Condition non_negative_smi = CheckNonNegativeSmi(src);
- j(NegateCondition(non_negative_smi), on_not_smi_or_negative);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfSmiEqualsConstant(Register src,
- Smi* constant,
- LabelType* on_equals) {
- SmiCompare(src, constant);
- j(equal, on_equals);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfNotValidSmiValue(Register src,
- LabelType* on_invalid) {
- Condition is_valid = CheckInteger32ValidSmiValue(src);
- j(NegateCondition(is_valid), on_invalid);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfUIntNotValidSmiValue(Register src,
- LabelType* on_invalid) {
- Condition is_valid = CheckUInteger32ValidSmiValue(src);
- j(NegateCondition(is_valid), on_invalid);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfNotBothSmi(Register src1,
- Register src2,
- LabelType* on_not_both_smi) {
- Condition both_smi = CheckBothSmi(src1, src2);
- j(NegateCondition(both_smi), on_not_both_smi);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpUnlessBothNonNegativeSmi(Register src1,
- Register src2,
- LabelType* on_not_both_smi) {
- Condition both_smi = CheckBothNonNegativeSmi(src1, src2);
- j(NegateCondition(both_smi), on_not_both_smi);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::SmiOrIfSmis(Register dst, Register src1, Register src2,
- LabelType* on_not_smis) {
- if (dst.is(src1) || dst.is(src2)) {
- ASSERT(!src1.is(kScratchRegister));
- ASSERT(!src2.is(kScratchRegister));
- movq(kScratchRegister, src1);
- or_(kScratchRegister, src2);
- JumpIfNotSmi(kScratchRegister, on_not_smis);
- movq(dst, kScratchRegister);
- } else {
- movq(dst, src1);
- or_(dst, src2);
- JumpIfNotSmi(dst, on_not_smis);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfNotString(Register object,
- Register object_map,
- LabelType* not_string) {
- Condition is_smi = CheckSmi(object);
- j(is_smi, not_string);
- CmpObjectType(object, FIRST_NONSTRING_TYPE, object_map);
- j(above_equal, not_string);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first_object,
- Register second_object,
- Register scratch1,
- Register scratch2,
- LabelType* on_fail) {
- // Check that both objects are not smis.
- Condition either_smi = CheckEitherSmi(first_object, second_object);
- j(either_smi, on_fail);
-
- // Load instance type for both strings.
- movq(scratch1, FieldOperand(first_object, HeapObject::kMapOffset));
- movq(scratch2, FieldOperand(second_object, HeapObject::kMapOffset));
- movzxbl(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset));
- movzxbl(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
-
- // Check that both are flat ascii strings.
- ASSERT(kNotStringTag != 0);
- const int kFlatAsciiStringMask =
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
- const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
-
- andl(scratch1, Immediate(kFlatAsciiStringMask));
- andl(scratch2, Immediate(kFlatAsciiStringMask));
- // Interleave the bits to check both scratch1 and scratch2 in one test.
- ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
- lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
- cmpl(scratch1,
- Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
- j(not_equal, on_fail);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
- Register instance_type,
- Register scratch,
- LabelType *failure) {
- if (!scratch.is(instance_type)) {
- movl(scratch, instance_type);
- }
-
- const int kFlatAsciiStringMask =
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
-
- andl(scratch, Immediate(kFlatAsciiStringMask));
- cmpl(scratch, Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
- j(not_equal, failure);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
- Register first_object_instance_type,
- Register second_object_instance_type,
- Register scratch1,
- Register scratch2,
- LabelType* on_fail) {
- // Load instance type for both strings.
- movq(scratch1, first_object_instance_type);
- movq(scratch2, second_object_instance_type);
-
- // Check that both are flat ascii strings.
- ASSERT(kNotStringTag != 0);
- const int kFlatAsciiStringMask =
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
- const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
-
- andl(scratch1, Immediate(kFlatAsciiStringMask));
- andl(scratch2, Immediate(kFlatAsciiStringMask));
- // Interleave the bits to check both scratch1 and scratch2 in one test.
- ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
- lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
- cmpl(scratch1,
- Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
- j(not_equal, on_fail);
-}
-
-
-template <typename LabelType>
-void MacroAssembler::InNewSpace(Register object,
- Register scratch,
- Condition cc,
- LabelType* branch) {
- if (Serializer::enabled()) {
- // Can't do arithmetic on external references if it might get serialized.
- // The mask isn't really an address. We load it as an external reference in
- // case the size of the new space is different between the snapshot maker
- // and the running system.
- if (scratch.is(object)) {
- movq(kScratchRegister, ExternalReference::new_space_mask(isolate()));
- and_(scratch, kScratchRegister);
- } else {
- movq(scratch, ExternalReference::new_space_mask(isolate()));
- and_(scratch, object);
- }
- movq(kScratchRegister, ExternalReference::new_space_start(isolate()));
- cmpq(scratch, kScratchRegister);
- j(cc, branch);
- } else {
- ASSERT(is_int32(static_cast<int64_t>(HEAP->NewSpaceMask())));
- intptr_t new_space_start =
- reinterpret_cast<intptr_t>(HEAP->NewSpaceStart());
- movq(kScratchRegister, -new_space_start, RelocInfo::NONE);
- if (scratch.is(object)) {
- addq(scratch, kScratchRegister);
- } else {
- lea(scratch, Operand(object, kScratchRegister, times_1, 0));
- }
- and_(scratch, Immediate(static_cast<int32_t>(HEAP->NewSpaceMask())));
- j(cc, branch);
- }
-}
-
-
-template <typename LabelType>
-void MacroAssembler::InvokePrologue(const ParameterCount& expected,
- const ParameterCount& actual,
- Handle<Code> code_constant,
- Register code_register,
- LabelType* done,
- InvokeFlag flag,
- CallWrapper* call_wrapper) {
- bool definitely_matches = false;
- NearLabel invoke;
- if (expected.is_immediate()) {
- ASSERT(actual.is_immediate());
- if (expected.immediate() == actual.immediate()) {
- definitely_matches = true;
- } else {
- Set(rax, actual.immediate());
- if (expected.immediate() ==
- SharedFunctionInfo::kDontAdaptArgumentsSentinel) {
- // Don't worry about adapting arguments for built-ins that
- // don't want that done. Skip adaption code by making it look
- // like we have a match between expected and actual number of
- // arguments.
- definitely_matches = true;
- } else {
- Set(rbx, expected.immediate());
- }
- }
- } else {
- if (actual.is_immediate()) {
- // Expected is in register, actual is immediate. This is the
- // case when we invoke function values without going through the
- // IC mechanism.
- cmpq(expected.reg(), Immediate(actual.immediate()));
- j(equal, &invoke);
- ASSERT(expected.reg().is(rbx));
- Set(rax, actual.immediate());
- } else if (!expected.reg().is(actual.reg())) {
- // Both expected and actual are in (different) registers. This
- // is the case when we invoke functions using call and apply.
- cmpq(expected.reg(), actual.reg());
- j(equal, &invoke);
- ASSERT(actual.reg().is(rax));
- ASSERT(expected.reg().is(rbx));
- }
- }
-
- if (!definitely_matches) {
- Handle<Code> adaptor = isolate()->builtins()->ArgumentsAdaptorTrampoline();
- if (!code_constant.is_null()) {
- movq(rdx, code_constant, RelocInfo::EMBEDDED_OBJECT);
- addq(rdx, Immediate(Code::kHeaderSize - kHeapObjectTag));
- } else if (!code_register.is(rdx)) {
- movq(rdx, code_register);
- }
-
- if (flag == CALL_FUNCTION) {
- if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(adaptor));
- Call(adaptor, RelocInfo::CODE_TARGET);
- if (call_wrapper != NULL) call_wrapper->AfterCall();
- jmp(done);
- } else {
- Jump(adaptor, RelocInfo::CODE_TARGET);
- }
- bind(&invoke);
- }
-}
-
-
} } // namespace v8::internal
#endif // V8_X64_MACRO_ASSEMBLER_X64_H_
diff --git a/src/x64/regexp-macro-assembler-x64.cc b/src/x64/regexp-macro-assembler-x64.cc
index c16da940..2ea17f0e 100644
--- a/src/x64/regexp-macro-assembler-x64.cc
+++ b/src/x64/regexp-macro-assembler-x64.cc
@@ -1065,9 +1065,9 @@ void RegExpMacroAssemblerX64::ReadStackPointerFromRegister(int reg) {
void RegExpMacroAssemblerX64::SetCurrentPositionFromEnd(int by) {
- NearLabel after_position;
+ Label after_position;
__ cmpq(rdi, Immediate(-by * char_size()));
- __ j(greater_equal, &after_position);
+ __ j(greater_equal, &after_position, Label::kNear);
__ movq(rdi, Immediate(-by * char_size()));
// On RegExp code entry (where this operation is used), the character before
// the current position is expected to be already loaded.
diff --git a/src/x64/simulator-x64.h b/src/x64/simulator-x64.h
index cfaa5b8c..df8423a6 100644
--- a/src/x64/simulator-x64.h
+++ b/src/x64/simulator-x64.h
@@ -55,7 +55,8 @@ typedef int (*regexp_matcher)(String*, int, const byte*,
// just use the C stack limit.
class SimulatorStack : public v8::internal::AllStatic {
public:
- static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+ static inline uintptr_t JsLimitFromCLimit(Isolate* isolate,
+ uintptr_t c_limit) {
return c_limit;
}
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index c19d29d0..dae4a55d 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -82,18 +82,18 @@ static void ProbeTable(Isolate* isolate,
// must always call a backup property check that is complete.
// This function is safe to call if the receiver has fast properties.
// Name must be a symbol and receiver must be a heap object.
-static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
- Label* miss_label,
- Register receiver,
- String* name,
- Register r0,
- Register r1) {
+MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ String* name,
+ Register r0,
+ Register r1) {
ASSERT(name->IsSymbol());
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->negative_lookups(), 1);
__ IncrementCounter(counters->negative_lookups_miss(), 1);
- Label done;
__ movq(r0, FieldOperand(receiver, HeapObject::kMapOffset));
const int kInterceptorOrAccessCheckNeededMask =
@@ -117,64 +117,20 @@ static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
Heap::kHashTableMapRootIndex);
__ j(not_equal, miss_label);
- // Compute the capacity mask.
- const int kCapacityOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kCapacityIndex * kPointerSize;
-
- // Generate an unrolled loop that performs a few probes before
- // giving up.
- static const int kProbes = 4;
- const int kElementsStartOffset =
- StringDictionary::kHeaderSize +
- StringDictionary::kElementsStartIndex * kPointerSize;
-
- // If names of slots in range from 1 to kProbes - 1 for the hash value are
- // not equal to the name and kProbes-th slot is not used (its name is the
- // undefined value), it guarantees the hash table doesn't contain the
- // property. It's true even if some slots represent deleted properties
- // (their names are the null value).
- for (int i = 0; i < kProbes; i++) {
- // r0 points to properties hash.
- // Compute the masked index: (hash + i + i * i) & mask.
- Register index = r1;
- // Capacity is smi 2^n.
- __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset));
- __ decl(index);
- __ and_(index,
- Immediate(name->Hash() + StringDictionary::GetProbeOffset(i)));
-
- // Scale the index by multiplying by the entry size.
- ASSERT(StringDictionary::kEntrySize == 3);
- __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
-
- Register entity_name = r1;
- // Having undefined at this place means the name is not contained.
- ASSERT_EQ(kSmiTagSize, 1);
- __ movq(entity_name, Operand(properties, index, times_pointer_size,
- kElementsStartOffset - kHeapObjectTag));
- __ Cmp(entity_name, masm->isolate()->factory()->undefined_value());
- // __ jmp(miss_label);
- if (i != kProbes - 1) {
- __ j(equal, &done);
-
- // Stop if found the property.
- __ Cmp(entity_name, Handle<String>(name));
- __ j(equal, miss_label);
-
- // Check if the entry name is not a symbol.
- __ movq(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
- __ testb(FieldOperand(entity_name, Map::kInstanceTypeOffset),
- Immediate(kIsSymbolMask));
- __ j(zero, miss_label);
- } else {
- // Give up probing if still not found the undefined value.
- __ j(not_equal, miss_label);
- }
- }
+ Label done;
+ MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup(
+ masm,
+ miss_label,
+ &done,
+ properties,
+ name,
+ r1);
+ if (result->IsFailure()) return result;
__ bind(&done);
__ DecrementCounter(counters->negative_lookups_miss(), 1);
+
+ return result;
}
@@ -522,10 +478,12 @@ class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
const ParameterCount& arguments,
- Register name)
+ Register name,
+ Code::ExtraICState extra_ic_state)
: stub_compiler_(stub_compiler),
arguments_(arguments),
- name_(name) {}
+ name_(name),
+ extra_ic_state_(extra_ic_state) {}
MaybeObject* Compile(MacroAssembler* masm,
JSObject* object,
@@ -650,8 +608,11 @@ class CallInterceptorCompiler BASE_EMBEDDED {
arguments_.immediate());
if (result->IsFailure()) return result;
} else {
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
__ InvokeFunction(optimization.constant_function(), arguments_,
- JUMP_FUNCTION);
+ JUMP_FUNCTION, NullCallWrapper(), call_kind);
}
// Deferred code for fast API call case---clean preallocated space.
@@ -730,6 +691,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
StubCompiler* stub_compiler_;
const ParameterCount& arguments_;
Register name_;
+ Code::ExtraICState extra_ic_state_;
};
@@ -747,6 +709,14 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
}
+void StubCompiler::GenerateKeyedLoadMissForceGeneric(MacroAssembler* masm) {
+ Code* code = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ Handle<Code> ic(code);
+ __ Jump(ic, RelocInfo::CODE_TARGET);
+}
+
+
// Both name_reg and receiver_reg are preserved on jumps to miss_label,
// but may be destroyed if store is successful.
void StubCompiler::GenerateStoreField(MacroAssembler* masm,
@@ -907,12 +877,17 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
ASSERT(current->property_dictionary()->FindEntry(name) ==
StringDictionary::kNotFound);
- GenerateDictionaryNegativeLookup(masm(),
- miss,
- reg,
- name,
- scratch1,
- scratch2);
+ MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch1,
+ scratch2);
+ if (negative_lookup->IsFailure()) {
+ set_failure(Failure::cast(negative_lookup));
+ return reg;
+ }
+
__ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
reg = holder_reg; // from now the object is in holder_reg
__ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset));
@@ -1325,8 +1300,10 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
MaybeObject* CallStubCompiler::GenerateMissBranch() {
- MaybeObject* maybe_obj = isolate()->stub_cache()->ComputeCallMiss(
- arguments().immediate(), kind_);
+ MaybeObject* maybe_obj =
+ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
+ kind_,
+ extra_ic_state_);
Object* obj;
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
__ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
@@ -1377,7 +1354,11 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object,
}
// Invoke the function.
- __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
// Handle call cache miss.
__ bind(&miss);
@@ -1657,7 +1638,9 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
Label index_out_of_range;
Label* index_out_of_range_label = &index_out_of_range;
- if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
index_out_of_range_label = &miss;
}
@@ -1739,7 +1722,9 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
Label index_out_of_range;
Label* index_out_of_range_label = &index_out_of_range;
- if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
+ if (kind_ == Code::CALL_IC &&
+ (CallICBase::StringStubState::decode(extra_ic_state_) ==
+ DEFAULT_STRING_STUB)) {
index_out_of_range_label = &miss;
}
@@ -1856,7 +1841,11 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall(
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
__ bind(&miss);
// rcx: function name.
@@ -1944,7 +1933,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
// Check if the argument is a heap number and load its value.
__ bind(&not_smi);
- __ CheckMap(rax, factory()->heap_number_map(), &slow, true);
+ __ CheckMap(rax, factory()->heap_number_map(), &slow, DONT_DO_SMI_CHECK);
__ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset));
// Check the sign of the argument. If the argument is positive,
@@ -1969,7 +1958,11 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object,
// Tail call the full function. We do not have to patch the receiver
// because the function makes no use of it.
__ bind(&slow);
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
__ bind(&miss);
// rcx: function name.
@@ -1993,6 +1986,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall(
// repatch it to global receiver.
if (object->IsGlobalObject()) return heap()->undefined_value();
if (cell != NULL) return heap()->undefined_value();
+ if (!object->IsJSObject()) return heap()->undefined_value();
int depth = optimization.GetPrototypeDepthOfExpectedType(
JSObject::cast(object), holder);
if (depth == kInvalidProtoDepth) return heap()->undefined_value();
@@ -2162,7 +2156,11 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
UNREACHABLE();
}
- __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
// Handle call cache miss.
__ bind(&miss);
@@ -2199,7 +2197,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Get the receiver from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- CallInterceptorCompiler compiler(this, arguments(), rcx);
+ CallInterceptorCompiler compiler(this, arguments(), rcx, extra_ic_state_);
MaybeObject* result = compiler.Compile(masm(),
object,
holder,
@@ -2229,7 +2227,11 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object,
// Invoke the function.
__ movq(rdi, rax);
- __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION);
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
+ __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
// Handle load cache miss.
__ bind(&miss);
@@ -2290,16 +2292,21 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object,
__ IncrementCounter(counters->call_global_inline(), 1);
ASSERT(function->is_compiled());
ParameterCount expected(function->shared()->formal_parameter_count());
+ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
+ ? CALL_AS_FUNCTION
+ : CALL_AS_METHOD;
if (V8::UseCrankshaft()) {
// TODO(kasperl): For now, we always call indirectly through the
// code field in the function to allow recompilation to take effect
// without changing any of the call sites.
__ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
- __ InvokeCode(rdx, expected, arguments(), JUMP_FUNCTION);
+ __ InvokeCode(rdx, expected, arguments(), JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
} else {
Handle<Code> code(function->code());
__ InvokeCode(code, expected, arguments(),
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ RelocInfo::CODE_TARGET, JUMP_FUNCTION,
+ NullCallWrapper(), call_kind);
}
// Handle call cache miss.
__ bind(&miss);
@@ -2523,60 +2530,62 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
}
-MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized(
- JSObject* receiver) {
+MaybeObject* KeyedStoreStubCompiler::CompileStoreFastElement(
+ Map* receiver_map) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
- Label miss;
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ MaybeObject* maybe_stub =
+ KeyedStoreFastElementStub(is_js_array).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(rdx,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(rdx, &miss);
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Check that the map matches.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Handle<Map>(receiver->map()));
- __ j(not_equal, &miss);
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
- // Check that the key is a smi.
- __ JumpIfNotSmi(rcx, &miss);
- // Get the elements array and make sure it is a fast element array, not 'cow'.
- __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
- __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset),
- factory()->fixed_array_map());
- __ j(not_equal, &miss);
+MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(rdx, &miss);
- // Check that the key is within bounds.
- if (receiver->IsJSArray()) {
- __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
- __ j(above_equal, &miss);
- } else {
- __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
- __ j(above_equal, &miss);
+ Register map_reg = rbx;
+ __ movq(map_reg, FieldOperand(rdx, HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ for (int current = 0; current < receiver_count; ++current) {
+ // Check map and tail call if there's a match
+ Handle<Map> map(receiver_maps->at(current));
+ __ Cmp(map_reg, map);
+ __ j(equal,
+ Handle<Code>(handler_ics->at(current)),
+ RelocInfo::CODE_TARGET);
}
- // Do the store and update the write barrier. Make sure to preserve
- // the value in register eax.
- __ movq(rdx, rax);
- __ SmiToInteger32(rcx, rcx);
- __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize),
- rax);
- __ RecordWrite(rdi, 0, rdx, rcx);
-
- // Done.
- __ ret(0);
-
- // Handle store cache miss.
__ bind(&miss);
Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
- return GetCode(NORMAL, NULL);
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -2590,7 +2599,7 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name,
// -----------------------------------
Label miss;
- // Chech that receiver is not a smi.
+ // Check that receiver is not a smi.
__ JumpIfSmi(rax, &miss);
// Check the maps of the full prototype chain. Also check that
@@ -2981,49 +2990,56 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
}
-MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) {
+MaybeObject* KeyedLoadStubCompiler::CompileLoadFastElement(Map* receiver_map) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
- Label miss;
-
- // Check that the receiver isn't a smi.
- __ JumpIfSmi(rdx, &miss);
-
- // Check that the map matches.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Handle<Map>(receiver->map()));
- __ j(not_equal, &miss);
+ MaybeObject* maybe_stub = KeyedLoadFastElementStub().TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(rdx,
+ Handle<Map>(receiver_map),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Check that the key is a smi.
- __ JumpIfNotSmi(rax, &miss);
+ // Return the generated code.
+ return GetCode(NORMAL, NULL);
+}
- // Get the elements array.
- __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
- __ AssertFastElements(rcx);
- // Check that the key is within bounds.
- __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset));
- __ j(above_equal, &miss);
+MaybeObject* KeyedLoadStubCompiler::CompileLoadMegamorphic(
+ MapList* receiver_maps,
+ CodeList* handler_ics) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss;
+ __ JumpIfSmi(rdx, &miss);
- // Load the result and make sure it's not the hole.
- SmiIndex index = masm()->SmiToIndex(rbx, rax, kPointerSizeLog2);
- __ movq(rbx, FieldOperand(rcx,
- index.reg,
- index.scale,
- FixedArray::kHeaderSize));
- __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
- __ j(equal, &miss);
- __ movq(rax, rbx);
- __ ret(0);
+ Register map_reg = rbx;
+ __ movq(map_reg, FieldOperand(rdx, HeapObject::kMapOffset));
+ int receiver_count = receiver_maps->length();
+ for (int current = 0; current < receiver_count; ++current) {
+ // Check map and tail call if there's a match
+ Handle<Map> map(receiver_maps->at(current));
+ __ Cmp(map_reg, map);
+ __ j(equal,
+ Handle<Code>(handler_ics->at(current)),
+ RelocInfo::CODE_TARGET);
+ }
- __ bind(&miss);
+ __ bind(&miss);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
// Return the generated code.
- return GetCode(NORMAL, NULL);
+ return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
@@ -3160,30 +3176,79 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) {
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
- JSObject* receiver, ExternalArrayType array_type, Code::Flags flags) {
+MaybeObject* ExternalArrayLoadStubCompiler::CompileLoad(
+ JSObject*receiver, ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- rax : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
- Label slow;
+ MaybeObject* maybe_stub =
+ KeyedLoadExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(rdx,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
+
+ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Check that the object isn't a smi.
- __ JumpIfSmi(rdx, &slow);
+ // Return the generated code.
+ return GetCode();
+}
- // Check that the key is a smi.
- __ JumpIfNotSmi(rax, &slow);
+MaybeObject* ExternalArrayStoreStubCompiler::CompileStore(
+ JSObject* receiver, ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ MaybeObject* maybe_stub =
+ KeyedStoreExternalArrayStub(array_type).TryGetCode();
+ Code* stub;
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+ __ DispatchMap(rdx,
+ Handle<Map>(receiver->map()),
+ Handle<Code>(stub),
+ DO_SMI_CHECK);
- // Check that the map matches.
- __ CheckMap(rdx, Handle<Map>(receiver->map()), &slow, false);
- __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ return GetCode();
+}
+
+
+#undef __
+#define __ ACCESS_MASM(masm)
+
+
+void KeyedLoadStubCompiler::GenerateLoadExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rax, &miss_force_generic);
// Check that the index is in range.
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ SmiToInteger32(rcx, rax);
__ cmpl(rcx, FieldOperand(rbx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
- __ j(above_equal, &slow);
+ __ j(above_equal, &miss_force_generic);
// rax: index (as a smi)
// rdx: receiver (JSObject)
@@ -3214,6 +3279,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
case kExternalFloatArray:
__ cvtss2sd(xmm0, Operand(rbx, rcx, times_4, 0));
break;
+ case kExternalDoubleArray:
+ __ movsd(xmm0, Operand(rbx, rcx, times_8, 0));
+ break;
default:
UNREACHABLE();
break;
@@ -3231,9 +3299,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
// For the UnsignedInt array type, we need to see whether
// the value can be represented in a Smi. If not, we need to convert
// it to a HeapNumber.
- NearLabel box_int;
+ Label box_int;
- __ JumpIfUIntNotValidSmiValue(rcx, &box_int);
+ __ JumpIfUIntNotValidSmiValue(rcx, &box_int, Label::kNear);
__ Integer32ToSmi(rax, rcx);
__ ret(0);
@@ -3251,7 +3319,8 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
__ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0);
__ movq(rax, rcx);
__ ret(0);
- } else if (array_type == kExternalFloatArray) {
+ } else if (array_type == kExternalFloatArray ||
+ array_type == kExternalDoubleArray) {
// For the floating-point array type, we need to always allocate a
// HeapNumber.
__ AllocateHeapNumber(rcx, rbx, &slow);
@@ -3266,7 +3335,7 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
// Slow case: Jump to runtime.
__ bind(&slow);
- Counters* counters = isolate()->counters();
+ Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->keyed_load_external_array_slow(), 1);
// ----------- S t a t e -------------
@@ -3275,44 +3344,46 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedLoadStub(
// -- rsp[0] : return address
// -----------------------------------
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rax); // name
- __ push(rbx); // return address
+ Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Slow();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
- // Perform tail call to the entry.
- __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
+ // Miss case: Jump to runtime.
+ __ bind(&miss_force_generic);
- // Return the generated code.
- return GetCode(flags);
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
+ __ jmp(miss_ic, RelocInfo::CODE_TARGET);
}
-MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
- JSObject* receiver, ExternalArrayType array_type, Code::Flags flags) {
+void KeyedStoreStubCompiler::GenerateStoreExternalArray(
+ MacroAssembler* masm,
+ ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
- Label slow;
-
- // Check that the object isn't a smi.
- __ JumpIfSmi(rdx, &slow);
+ Label slow, miss_force_generic;
- // Check that the map matches.
- __ CheckMap(rdx, Handle<Map>(receiver->map()), &slow, false);
- __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
// Check that the key is a smi.
- __ JumpIfNotSmi(rcx, &slow);
+ __ JumpIfNotSmi(rcx, &miss_force_generic);
// Check that the index is in range.
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ SmiToInteger32(rdi, rcx); // Untag the index.
__ cmpl(rdi, FieldOperand(rbx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
- __ j(above_equal, &slow);
+ __ j(above_equal, &miss_force_generic);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
@@ -3321,12 +3392,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
// rdx: receiver (a JSObject)
// rbx: elements array
// rdi: untagged key
- NearLabel check_heap_number;
+ Label check_heap_number;
if (array_type == kExternalPixelArray) {
// Float to pixel conversion is only implemented in the runtime for now.
__ JumpIfNotSmi(rax, &slow);
} else {
- __ JumpIfNotSmi(rax, &check_heap_number);
+ __ JumpIfNotSmi(rax, &check_heap_number, Label::kNear);
}
// No more branches to slow case on this path. Key and receiver not needed.
__ SmiToInteger32(rdx, rax);
@@ -3335,9 +3406,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
switch (array_type) {
case kExternalPixelArray:
{ // Clamp the value to [0..255].
- NearLabel done;
+ Label done;
__ testl(rdx, Immediate(0xFFFFFF00));
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
__ setcc(negative, rdx); // 1 if negative, 0 if positive.
__ decb(rdx); // 0 if negative, 255 if positive.
__ bind(&done);
@@ -3361,6 +3432,11 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
__ cvtlsi2ss(xmm0, rdx);
__ movss(Operand(rbx, rdi, times_4, 0), xmm0);
break;
+ case kExternalDoubleArray:
+ // Need to perform int-to-float conversion.
+ __ cvtlsi2sd(xmm0, rdx);
+ __ movsd(Operand(rbx, rdi, times_8, 0), xmm0);
+ break;
default:
UNREACHABLE();
break;
@@ -3391,6 +3467,9 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
__ cvtsd2ss(xmm0, xmm0);
__ movss(Operand(rbx, rdi, times_4, 0), xmm0);
__ ret(0);
+ } else if (array_type == kExternalDoubleArray) {
+ __ movsd(Operand(rbx, rdi, times_8, 0), xmm0);
+ __ ret(0);
} else {
// Perform float-to-int conversion with truncation (round-to-zero)
// behavior.
@@ -3438,21 +3517,116 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub(
// -- rsp[0] : return address
// -----------------------------------
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rcx); // key
- __ push(rax); // value
- __ Push(Smi::FromInt(NONE)); // PropertyAttributes
- __ Push(Smi::FromInt(
- Code::ExtractExtraICStateFromFlags(flags) & kStrictMode));
- __ push(rbx); // return address
+ Handle<Code> ic = masm->isolate()->builtins()->KeyedStoreIC_Slow();
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Miss case: call runtime.
+ __ bind(&miss_force_generic);
+
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ Handle<Code> miss_ic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ jmp(miss_ic, RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic;
- // Do tail-call to runtime routine.
- __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
- return GetCode(flags);
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rax, &miss_force_generic);
+
+ // Get the elements array.
+ __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ AssertFastElements(rcx);
+
+ // Check that the key is within bounds.
+ __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset));
+ __ j(above_equal, &miss_force_generic);
+
+ // Load the result and make sure it's not the hole.
+ SmiIndex index = masm->SmiToIndex(rbx, rax, kPointerSizeLog2);
+ __ movq(rbx, FieldOperand(rcx,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &miss_force_generic);
+ __ movq(rax, rbx);
+ __ ret(0);
+
+ __ bind(&miss_force_generic);
+ Code* code = masm->isolate()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_MissForceGeneric);
+ Handle<Code> ic(code);
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+}
+
+
+void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
+ bool is_js_array) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label miss_force_generic;
+
+ // This stub is meant to be tail-jumped to, the receiver must already
+ // have been verified by the caller to not be a smi.
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rcx, &miss_force_generic);
+
+ // Get the elements array and make sure it is a fast element array, not 'cow'.
+ __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset));
+ __ CompareRoot(FieldOperand(rdi, HeapObject::kMapOffset),
+ Heap::kFixedArrayMapRootIndex);
+ __ j(not_equal, &miss_force_generic);
+
+ // Check that the key is within bounds.
+ if (is_js_array) {
+ __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
+ __ j(above_equal, &miss_force_generic);
+ } else {
+ __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset));
+ __ j(above_equal, &miss_force_generic);
+ }
+
+ // Do the store and update the write barrier. Make sure to preserve
+ // the value in register eax.
+ __ movq(rdx, rax);
+ __ SmiToInteger32(rcx, rcx);
+ __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize),
+ rax);
+ __ RecordWrite(rdi, 0, rdx, rcx);
+
+ // Done.
+ __ ret(0);
+
+ // Handle store cache miss.
+ __ bind(&miss_force_generic);
+ Handle<Code> ic_force_generic =
+ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
+ __ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
}
+
#undef __
} } // namespace v8::internal
diff --git a/src/zone-inl.h b/src/zone-inl.h
index 17e83dc5..6e2d558a 100644
--- a/src/zone-inl.h
+++ b/src/zone-inl.h
@@ -107,9 +107,20 @@ inline void* ZoneListAllocationPolicy::New(int size) {
}
-ZoneScope::ZoneScope(ZoneScopeMode mode)
- : isolate_(Isolate::Current()),
- mode_(mode) {
+template <typename T>
+void* ZoneList<T>::operator new(size_t size) {
+ return ZONE->New(static_cast<int>(size));
+}
+
+
+template <typename T>
+void* ZoneList<T>::operator new(size_t size, Zone* zone) {
+ return zone->New(static_cast<int>(size));
+}
+
+
+ZoneScope::ZoneScope(Isolate* isolate, ZoneScopeMode mode)
+ : isolate_(isolate), mode_(mode) {
isolate_->zone()->scope_nesting_++;
}
diff --git a/src/zone.h b/src/zone.h
index 9efe4f5d..a5e437f7 100644
--- a/src/zone.h
+++ b/src/zone.h
@@ -28,6 +28,8 @@
#ifndef V8_ZONE_H_
#define V8_ZONE_H_
+#include "allocation.h"
+
namespace v8 {
namespace internal {
@@ -132,8 +134,8 @@ class Zone {
class ZoneObject {
public:
// Allocate a new ZoneObject of 'size' bytes in the Zone.
- inline void* operator new(size_t size);
- inline void* operator new(size_t size, Zone* zone);
+ INLINE(void* operator new(size_t size));
+ INLINE(void* operator new(size_t size, Zone* zone));
// Ideally, the delete operator should be private instead of
// public, but unfortunately the compiler sometimes synthesizes
@@ -162,7 +164,7 @@ class AssertNoZoneAllocation {
class ZoneListAllocationPolicy {
public:
// Allocate 'size' bytes of memory in the zone.
- static inline void* New(int size);
+ INLINE(static void* New(int size));
// De-allocation attempts are silently ignored.
static void Delete(void* p) { }
@@ -176,6 +178,9 @@ class ZoneListAllocationPolicy {
template<typename T>
class ZoneList: public List<T, ZoneListAllocationPolicy> {
public:
+ INLINE(void* operator new(size_t size));
+ INLINE(void* operator new(size_t size, Zone* zone));
+
// Construct a new ZoneList with the given capacity; the length is
// always zero. The capacity must be non-negative.
explicit ZoneList(int capacity)
@@ -198,8 +203,7 @@ typedef ZoneList<Handle<Map> > ZoneMapList;
// outer-most scope.
class ZoneScope BASE_EMBEDDED {
public:
- // TODO(isolates): pass isolate pointer here.
- inline explicit ZoneScope(ZoneScopeMode mode);
+ INLINE(ZoneScope(Isolate* isolate, ZoneScopeMode mode));
virtual ~ZoneScope();