Push version 2.1.3 to trunk.
Added API method for context-disposal notifications.
Added API method for accessing elements by integer index.
Added missing implementation of Uint32::Value and Value::IsUint32 API methods.
Added IsExecutionTerminating API method.
Disabled strict aliasing for GCC 4.4.
Fixed string-concatenation bug (issue 636).
Performance improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@4079 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/SConscript b/src/SConscript
index 73de193..d61da3e 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -97,7 +97,6 @@
token.cc
top.cc
unicode.cc
- usage-analyzer.cc
utils.cc
v8-counters.cc
v8.cc
@@ -249,6 +248,7 @@
messages.js
apinatives.js
debug-delay.js
+liveedit-delay.js
mirror-delay.js
date-delay.js
regexp-delay.js
diff --git a/src/accessors.cc b/src/accessors.cc
index b05719e..e41db94 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -32,7 +32,6 @@
#include "factory.h"
#include "scopeinfo.h"
#include "top.h"
-#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/api.cc b/src/api.cc
index 22d2f4b..93fce79 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -34,9 +34,11 @@
#include "debug.h"
#include "execution.h"
#include "global-handles.h"
+#include "messages.h"
#include "platform.h"
#include "serialize.h"
#include "snapshot.h"
+#include "top.h"
#include "utils.h"
#include "v8threads.h"
#include "version.h"
@@ -1569,6 +1571,18 @@
}
+bool Value::IsUint32() const {
+ if (IsDeadCheck("v8::Value::IsUint32()")) return false;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) return i::Smi::cast(*obj)->value() >= 0;
+ if (obj->IsNumber()) {
+ double value = obj->Number();
+ return i::FastUI2D(i::FastD2UI(value)) == value;
+ }
+ return false;
+}
+
+
bool Value::IsDate() const {
if (IsDeadCheck("v8::Value::IsDate()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1974,6 +1988,23 @@
}
+bool v8::Object::Set(uint32_t index, v8::Handle<Value> value) {
+ ON_BAILOUT("v8::Object::Set()", return false);
+ ENTER_V8;
+ HandleScope scope;
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::Object> obj = i::SetElement(
+ self,
+ index,
+ value_obj);
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(false);
+ return true;
+}
+
+
bool v8::Object::ForceSet(v8::Handle<Value> key,
v8::Handle<Value> value,
v8::PropertyAttribute attribs) {
@@ -2022,6 +2053,18 @@
}
+Local<Value> v8::Object::Get(uint32_t index) {
+ ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>());
+ ENTER_V8;
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::Object> result = i::GetElement(self, index);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(Local<Value>());
+ return Utils::ToLocal(result);
+}
+
+
Local<Value> v8::Object::GetPrototype() {
ON_BAILOUT("v8::Object::GetPrototype()", return Local<v8::Value>());
ENTER_V8;
@@ -2614,7 +2657,7 @@
StringTracker::RecordWrite(str);
// Flatten the string for efficiency. This applies whether we are
// using StringInputBuffer or Get(i) to access the characters.
- str->TryFlattenIfNotFlat();
+ str->TryFlatten();
int end = length;
if ( (length == -1) || (length > str->length() - start) )
end = str->length() - start;
@@ -2727,6 +2770,17 @@
}
+uint32_t Uint32::Value() const {
+ if (IsDeadCheck("v8::Uint32::Value()")) return 0;
+ i::Handle<i::Object> obj = Utils::OpenHandle(this);
+ if (obj->IsSmi()) {
+ return i::Smi::cast(*obj)->value();
+ } else {
+ return static_cast<uint32_t>(obj->Number());
+ }
+}
+
+
int v8::Object::InternalFieldCount() {
if (IsDeadCheck("v8::Object::InternalFieldCount()")) return 0;
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
@@ -2820,9 +2874,9 @@
}
-void v8::V8::ContextDisposedNotification() {
- if (!i::V8::IsRunning()) return;
- i::Heap::NotifyContextDisposed();
+int v8::V8::ContextDisposedNotification() {
+ if (!i::V8::IsRunning()) return 0;
+ return i::Heap::NotifyContextDisposed();
}
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index c79aac6..6b226fd 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -81,100 +81,6 @@
// -----------------------------------------------------------------------------
-// Implementation of Register and CRegister
-
-Register no_reg = { -1 };
-
-Register r0 = { 0 };
-Register r1 = { 1 };
-Register r2 = { 2 };
-Register r3 = { 3 };
-Register r4 = { 4 };
-Register r5 = { 5 };
-Register r6 = { 6 };
-Register r7 = { 7 };
-Register r8 = { 8 }; // Used as context register.
-Register r9 = { 9 };
-Register r10 = { 10 }; // Used as roots register.
-Register fp = { 11 };
-Register ip = { 12 };
-Register sp = { 13 };
-Register lr = { 14 };
-Register pc = { 15 };
-
-
-CRegister no_creg = { -1 };
-
-CRegister cr0 = { 0 };
-CRegister cr1 = { 1 };
-CRegister cr2 = { 2 };
-CRegister cr3 = { 3 };
-CRegister cr4 = { 4 };
-CRegister cr5 = { 5 };
-CRegister cr6 = { 6 };
-CRegister cr7 = { 7 };
-CRegister cr8 = { 8 };
-CRegister cr9 = { 9 };
-CRegister cr10 = { 10 };
-CRegister cr11 = { 11 };
-CRegister cr12 = { 12 };
-CRegister cr13 = { 13 };
-CRegister cr14 = { 14 };
-CRegister cr15 = { 15 };
-
-// Support for the VFP registers s0 to s31 (d0 to d15).
-// Note that "sN:sM" is the same as "dN/2".
-SwVfpRegister s0 = { 0 };
-SwVfpRegister s1 = { 1 };
-SwVfpRegister s2 = { 2 };
-SwVfpRegister s3 = { 3 };
-SwVfpRegister s4 = { 4 };
-SwVfpRegister s5 = { 5 };
-SwVfpRegister s6 = { 6 };
-SwVfpRegister s7 = { 7 };
-SwVfpRegister s8 = { 8 };
-SwVfpRegister s9 = { 9 };
-SwVfpRegister s10 = { 10 };
-SwVfpRegister s11 = { 11 };
-SwVfpRegister s12 = { 12 };
-SwVfpRegister s13 = { 13 };
-SwVfpRegister s14 = { 14 };
-SwVfpRegister s15 = { 15 };
-SwVfpRegister s16 = { 16 };
-SwVfpRegister s17 = { 17 };
-SwVfpRegister s18 = { 18 };
-SwVfpRegister s19 = { 19 };
-SwVfpRegister s20 = { 20 };
-SwVfpRegister s21 = { 21 };
-SwVfpRegister s22 = { 22 };
-SwVfpRegister s23 = { 23 };
-SwVfpRegister s24 = { 24 };
-SwVfpRegister s25 = { 25 };
-SwVfpRegister s26 = { 26 };
-SwVfpRegister s27 = { 27 };
-SwVfpRegister s28 = { 28 };
-SwVfpRegister s29 = { 29 };
-SwVfpRegister s30 = { 30 };
-SwVfpRegister s31 = { 31 };
-
-DwVfpRegister d0 = { 0 };
-DwVfpRegister d1 = { 1 };
-DwVfpRegister d2 = { 2 };
-DwVfpRegister d3 = { 3 };
-DwVfpRegister d4 = { 4 };
-DwVfpRegister d5 = { 5 };
-DwVfpRegister d6 = { 6 };
-DwVfpRegister d7 = { 7 };
-DwVfpRegister d8 = { 8 };
-DwVfpRegister d9 = { 9 };
-DwVfpRegister d10 = { 10 };
-DwVfpRegister d11 = { 11 };
-DwVfpRegister d12 = { 12 };
-DwVfpRegister d13 = { 13 };
-DwVfpRegister d14 = { 14 };
-DwVfpRegister d15 = { 15 };
-
-// -----------------------------------------------------------------------------
// Implementation of RelocInfo
const int RelocInfo::kApplyMask = 0;
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index f6b7a06..c972c57 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -84,25 +84,24 @@
int code_;
};
+const Register no_reg = { -1 };
-extern Register no_reg;
-extern Register r0;
-extern Register r1;
-extern Register r2;
-extern Register r3;
-extern Register r4;
-extern Register r5;
-extern Register r6;
-extern Register r7;
-extern Register r8;
-extern Register r9;
-extern Register r10;
-extern Register fp;
-extern Register ip;
-extern Register sp;
-extern Register lr;
-extern Register pc;
-
+const Register r0 = { 0 };
+const Register r1 = { 1 };
+const Register r2 = { 2 };
+const Register r3 = { 3 };
+const Register r4 = { 4 };
+const Register r5 = { 5 };
+const Register r6 = { 6 };
+const Register r7 = { 7 };
+const Register r8 = { 8 }; // Used as context register.
+const Register r9 = { 9 };
+const Register r10 = { 10 }; // Used as roots register.
+const Register fp = { 11 };
+const Register ip = { 12 };
+const Register sp = { 13 };
+const Register lr = { 14 };
+const Register pc = { 15 };
// Single word VFP register.
struct SwVfpRegister {
@@ -139,57 +138,57 @@
};
-// Support for VFP registers s0 to s31 (d0 to d15).
+// Support for the VFP registers s0 to s31 (d0 to d15).
// Note that "s(N):s(N+1)" is the same as "d(N/2)".
-extern SwVfpRegister s0;
-extern SwVfpRegister s1;
-extern SwVfpRegister s2;
-extern SwVfpRegister s3;
-extern SwVfpRegister s4;
-extern SwVfpRegister s5;
-extern SwVfpRegister s6;
-extern SwVfpRegister s7;
-extern SwVfpRegister s8;
-extern SwVfpRegister s9;
-extern SwVfpRegister s10;
-extern SwVfpRegister s11;
-extern SwVfpRegister s12;
-extern SwVfpRegister s13;
-extern SwVfpRegister s14;
-extern SwVfpRegister s15;
-extern SwVfpRegister s16;
-extern SwVfpRegister s17;
-extern SwVfpRegister s18;
-extern SwVfpRegister s19;
-extern SwVfpRegister s20;
-extern SwVfpRegister s21;
-extern SwVfpRegister s22;
-extern SwVfpRegister s23;
-extern SwVfpRegister s24;
-extern SwVfpRegister s25;
-extern SwVfpRegister s26;
-extern SwVfpRegister s27;
-extern SwVfpRegister s28;
-extern SwVfpRegister s29;
-extern SwVfpRegister s30;
-extern SwVfpRegister s31;
+const SwVfpRegister s0 = { 0 };
+const SwVfpRegister s1 = { 1 };
+const SwVfpRegister s2 = { 2 };
+const SwVfpRegister s3 = { 3 };
+const SwVfpRegister s4 = { 4 };
+const SwVfpRegister s5 = { 5 };
+const SwVfpRegister s6 = { 6 };
+const SwVfpRegister s7 = { 7 };
+const SwVfpRegister s8 = { 8 };
+const SwVfpRegister s9 = { 9 };
+const SwVfpRegister s10 = { 10 };
+const SwVfpRegister s11 = { 11 };
+const SwVfpRegister s12 = { 12 };
+const SwVfpRegister s13 = { 13 };
+const SwVfpRegister s14 = { 14 };
+const SwVfpRegister s15 = { 15 };
+const SwVfpRegister s16 = { 16 };
+const SwVfpRegister s17 = { 17 };
+const SwVfpRegister s18 = { 18 };
+const SwVfpRegister s19 = { 19 };
+const SwVfpRegister s20 = { 20 };
+const SwVfpRegister s21 = { 21 };
+const SwVfpRegister s22 = { 22 };
+const SwVfpRegister s23 = { 23 };
+const SwVfpRegister s24 = { 24 };
+const SwVfpRegister s25 = { 25 };
+const SwVfpRegister s26 = { 26 };
+const SwVfpRegister s27 = { 27 };
+const SwVfpRegister s28 = { 28 };
+const SwVfpRegister s29 = { 29 };
+const SwVfpRegister s30 = { 30 };
+const SwVfpRegister s31 = { 31 };
-extern DwVfpRegister d0;
-extern DwVfpRegister d1;
-extern DwVfpRegister d2;
-extern DwVfpRegister d3;
-extern DwVfpRegister d4;
-extern DwVfpRegister d5;
-extern DwVfpRegister d6;
-extern DwVfpRegister d7;
-extern DwVfpRegister d8;
-extern DwVfpRegister d9;
-extern DwVfpRegister d10;
-extern DwVfpRegister d11;
-extern DwVfpRegister d12;
-extern DwVfpRegister d13;
-extern DwVfpRegister d14;
-extern DwVfpRegister d15;
+const DwVfpRegister d0 = { 0 };
+const DwVfpRegister d1 = { 1 };
+const DwVfpRegister d2 = { 2 };
+const DwVfpRegister d3 = { 3 };
+const DwVfpRegister d4 = { 4 };
+const DwVfpRegister d5 = { 5 };
+const DwVfpRegister d6 = { 6 };
+const DwVfpRegister d7 = { 7 };
+const DwVfpRegister d8 = { 8 };
+const DwVfpRegister d9 = { 9 };
+const DwVfpRegister d10 = { 10 };
+const DwVfpRegister d11 = { 11 };
+const DwVfpRegister d12 = { 12 };
+const DwVfpRegister d13 = { 13 };
+const DwVfpRegister d14 = { 14 };
+const DwVfpRegister d15 = { 15 };
// Coprocessor register
@@ -210,23 +209,24 @@
};
-extern CRegister no_creg;
-extern CRegister cr0;
-extern CRegister cr1;
-extern CRegister cr2;
-extern CRegister cr3;
-extern CRegister cr4;
-extern CRegister cr5;
-extern CRegister cr6;
-extern CRegister cr7;
-extern CRegister cr8;
-extern CRegister cr9;
-extern CRegister cr10;
-extern CRegister cr11;
-extern CRegister cr12;
-extern CRegister cr13;
-extern CRegister cr14;
-extern CRegister cr15;
+const CRegister no_creg = { -1 };
+
+const CRegister cr0 = { 0 };
+const CRegister cr1 = { 1 };
+const CRegister cr2 = { 2 };
+const CRegister cr3 = { 3 };
+const CRegister cr4 = { 4 };
+const CRegister cr5 = { 5 };
+const CRegister cr6 = { 6 };
+const CRegister cr7 = { 7 };
+const CRegister cr8 = { 8 };
+const CRegister cr9 = { 9 };
+const CRegister cr10 = { 10 };
+const CRegister cr11 = { 11 };
+const CRegister cr12 = { 12 };
+const CRegister cr13 = { 13 };
+const CRegister cr14 = { 14 };
+const CRegister cr15 = { 15 };
// Coprocessor number
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index edb1b0a..91e896d 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -61,10 +61,10 @@
ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
}
- // JumpToRuntime expects r0 to contain the number of arguments
+ // JumpToExternalReference expects r0 to contain the number of arguments
// including the receiver and the extra arguments.
__ add(r0, r0, Operand(num_extra_args + 1));
- __ JumpToRuntime(ExternalReference(id));
+ __ JumpToExternalReference(ExternalReference(id));
}
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index 6644d02..9e59582 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -31,6 +31,7 @@
#include "codegen-inl.h"
#include "compiler.h"
#include "debug.h"
+#include "ic-inl.h"
#include "parser.h"
#include "register-allocator-inl.h"
#include "runtime.h"
@@ -142,6 +143,7 @@
void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
+ Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
// Initialize state.
info_ = info;
@@ -3321,6 +3323,25 @@
}
+// Generates the Math.pow method - currently just calls runtime.
+void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 2);
+ Load(args->at(0));
+ Load(args->at(1));
+ frame_->CallRuntime(Runtime::kMath_pow, 2);
+ frame_->EmitPush(r0);
+}
+
+
+// Generates the Math.sqrt method - currently just calls runtime.
+void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ frame_->CallRuntime(Runtime::kMath_sqrt, 1);
+ frame_->EmitPush(r0);
+}
+
+
// This should generate code that performs a charCodeAt() call or returns
// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
// It is not yet implemented on ARM, so it always goes to the slow case.
@@ -3404,6 +3425,44 @@
}
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateCharFromCode");
+ ASSERT(args->length() == 1);
+
+ LoadAndSpill(args->at(0));
+ frame_->EmitPop(r0);
+
+ JumpTarget slow_case;
+ JumpTarget exit;
+
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ ASSERT(kSmiTag == 0);
+ ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
+ __ tst(r0, Operand(kSmiTagMask |
+ ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+ slow_case.Branch(nz);
+
+ ASSERT(kSmiTag == 0);
+ __ mov(r1, Operand(Factory::single_character_string_cache()));
+ __ add(r1, r1, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ ldr(r1, MemOperand(r1, FixedArray::kHeaderSize - kHeapObjectTag));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r1, ip);
+ slow_case.Branch(eq);
+
+ frame_->EmitPush(r1);
+ exit.Jump();
+
+ slow_case.Bind();
+ frame_->EmitPush(r0);
+ frame_->CallRuntime(Runtime::kCharFromCode, 1);
+ frame_->EmitPush(r0);
+
+ exit.Bind();
+}
+
+
void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 1);
@@ -3625,6 +3684,24 @@
}
+void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ frame_->CallRuntime(Runtime::kMath_sin, 1);
+ frame_->EmitPush(r0);
+}
+
+
+void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ frame_->CallRuntime(Runtime::kMath_cos, 1);
+ frame_->EmitPush(r0);
+}
+
+
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2);
@@ -4489,7 +4566,7 @@
__ bind(&gc);
__ push(cp);
__ push(r3);
- __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+ __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
}
@@ -4539,7 +4616,7 @@
// Need to collect. Call into runtime system.
__ bind(&gc);
- __ TailCallRuntime(ExternalReference(Runtime::kNewContext), 1, 1);
+ __ TailCallRuntime(Runtime::kNewContext, 1, 1);
}
@@ -4601,8 +4678,7 @@
__ Ret();
__ bind(&slow_case);
- ExternalReference runtime(Runtime::kCreateArrayLiteralShallow);
- __ TailCallRuntime(runtime, 3, 1);
+ __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
}
@@ -6170,12 +6246,17 @@
}
+Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
+ return Handle<Code>::null();
+}
+
+
void StackCheckStub::Generate(MacroAssembler* masm) {
// Do tail-call to runtime routine. Runtime routines expect at least one
// argument, so give it a Smi.
__ mov(r0, Operand(Smi::FromInt(0)));
__ push(r0);
- __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1, 1);
+ __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
__ StubReturn(1);
}
@@ -6784,7 +6865,7 @@
// by calling the runtime system.
__ bind(&slow);
__ push(r1);
- __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1, 1);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
}
@@ -6887,7 +6968,7 @@
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
}
@@ -7178,6 +7259,170 @@
}
+void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ Label* not_found) {
+ // 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;
+ __ sub(scratch, c1, Operand(static_cast<int>('0')));
+ __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
+ __ b(hi, ¬_array_index);
+ __ sub(scratch, c2, Operand(static_cast<int>('0')));
+ __ cmp(scratch, Operand(static_cast<int>('9' - '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
+ __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
+ __ b(ls, not_found);
+
+ __ bind(¬_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ GenerateHashInit(masm, hash, c1);
+ GenerateHashAddCharacter(masm, hash, c2);
+ GenerateHashGetHash(masm, hash);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
+
+ // 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);
+
+ // Load undefined value
+ Register undefined = scratch4;
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ // Calculate capacity mask from the symbol table capacity.
+ Register mask = scratch2;
+ __ ldr(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
+ __ mov(mask, Operand(mask, ASR, 1));
+ __ sub(mask, mask, Operand(1));
+
+ // Calculate untagged address of the first element of the symbol table.
+ Register first_symbol_table_element = symbol_table;
+ __ add(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
+ // scratch: -
+
+ // Perform a number of probes in the symbol table.
+ static const int kProbes = 4;
+ Label found_in_symbol_table;
+ Label next_probe[kProbes];
+ for (int i = 0; i < kProbes; i++) {
+ Register candidate = scratch5; // Scratch register contains candidate.
+
+ // Calculate entry in symbol table.
+ if (i > 0) {
+ __ add(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
+ } else {
+ __ mov(candidate, hash);
+ }
+
+ __ and_(candidate, candidate, Operand(mask));
+
+ // Load the entry from the symble table.
+ ASSERT_EQ(1, SymbolTable::kEntrySize);
+ __ ldr(candidate,
+ MemOperand(first_symbol_table_element,
+ candidate,
+ LSL,
+ kPointerSizeLog2));
+
+ // If entry is undefined no string with this hash can be found.
+ __ cmp(candidate, undefined);
+ __ b(eq, not_found);
+
+ // If length is not 2 the string is not a candidate.
+ __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
+ __ cmp(scratch, Operand(2));
+ __ b(ne, &next_probe[i]);
+
+ // Check that the candidate is a non-external ascii string.
+ __ ldr(scratch, FieldMemOperand(candidate, HeapObject::kMapOffset));
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch,
+ &next_probe[i]);
+
+ // Check if the two characters match.
+ // Assumes that word load is little endian.
+ __ ldrh(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
+ __ cmp(chars, scratch);
+ __ b(eq, &found_in_symbol_table);
+ __ 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 = scratch;
+ __ bind(&found_in_symbol_table);
+ if (!result.is(r0)) {
+ __ mov(r0, result);
+ }
+}
+
+
+void StringStubBase::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character) {
+ // hash = character + (character << 10);
+ __ add(hash, character, Operand(character, LSL, 10));
+ // hash ^= hash >> 6;
+ __ eor(hash, hash, Operand(hash, ASR, 6));
+}
+
+
+void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character) {
+ // hash += character;
+ __ add(hash, hash, Operand(character));
+ // hash += hash << 10;
+ __ add(hash, hash, Operand(hash, LSL, 10));
+ // hash ^= hash >> 6;
+ __ eor(hash, hash, Operand(hash, ASR, 6));
+}
+
+
+void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash) {
+ // hash += hash << 3;
+ __ add(hash, hash, Operand(hash, LSL, 3));
+ // hash ^= hash >> 11;
+ __ eor(hash, hash, Operand(hash, ASR, 11));
+ // hash += hash << 15;
+ __ add(hash, hash, Operand(hash, LSL, 15), SetCC);
+
+ // if (hash == 0) hash = 27;
+ __ mov(hash, Operand(27), LeaveCC, nz);
+}
+
+
void SubStringStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -7213,11 +7458,14 @@
__ sub(r2, r2, Operand(r3), SetCC);
__ b(mi, &runtime); // Fail if from > to.
- // Handle sub-strings of length 2 and less in the runtime system.
+ // 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.
__ cmp(r2, Operand(2));
- __ b(le, &runtime);
+ __ b(lt, &runtime);
// r2: length
+ // r3: from index (untaged smi)
// r6: from (smi)
// r7: to (smi)
@@ -7231,6 +7479,7 @@
// r1: instance type
// r2: length
+ // r3: from index (untaged smi)
// r5: string
// r6: from (smi)
// r7: to (smi)
@@ -7257,6 +7506,7 @@
// r1: instance type.
// r2: length
+ // r3: from index (untaged smi)
// r5: string
// r6: from (smi)
// r7: to (smi)
@@ -7266,6 +7516,7 @@
// r1: instance type.
// r2: result string length.
+ // r3: from index (untaged smi)
// r5: string.
// r6: from offset (smi)
// Check for flat ascii string.
@@ -7274,6 +7525,35 @@
ASSERT_EQ(0, kTwoByteStringTag);
__ b(eq, &non_ascii_flat);
+ Label result_longer_than_two;
+ __ cmp(r2, Operand(2));
+ __ b(gt, &result_longer_than_two);
+
+ // Sub string of length 2 requested.
+ // Get the two characters forming the sub string.
+ __ add(r5, r5, Operand(r3));
+ __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize));
+ __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1));
+
+ // Try to lookup two character string in symbol table.
+ Label make_two_character_string;
+ GenerateTwoCharacterSymbolTableProbe(masm, r3, r4, r1, r5, r6, r7, r9,
+ &make_two_character_string);
+ __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ // r2: result string length.
+ // r3: two characters combined into halfword in little endian byte order.
+ __ bind(&make_two_character_string);
+ __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime);
+ __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
+ __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&result_longer_than_two);
+
// Allocate the result.
__ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
@@ -7331,7 +7611,7 @@
// Just jump to runtime to create the sub string.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
}
@@ -7422,7 +7702,7 @@
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
// tagged as a small integer.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
}
@@ -7482,14 +7762,52 @@
// r4: first string instance type (if string_check_)
// r5: second string instance type (if string_check_)
// Look at the length of the result of adding the two strings.
- Label string_add_flat_result;
+ Label string_add_flat_result, longer_than_two;
// Adding two lengths can't overflow.
ASSERT(String::kMaxLength * 2 > String::kMaxLength);
__ add(r6, r2, Operand(r3));
// Use the runtime system when adding two one character strings, as it
// contains optimizations for this specific case using the symbol table.
__ cmp(r6, Operand(2));
- __ b(eq, &string_add_runtime);
+ __ b(ne, &longer_than_two);
+
+ // Check that both strings are non-external ascii strings.
+ if (!string_check_) {
+ __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
+ __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
+ }
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
+ &string_add_runtime);
+
+ // Get the two characters forming the sub string.
+ __ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
+ __ ldrb(r3, FieldMemOperand(r1, 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;
+ GenerateTwoCharacterSymbolTableProbe(masm, r2, r3, r6, r7, r4, r5, r9,
+ &make_two_character_string);
+ __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
+ __ add(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 r2 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)
+ __ mov(r6, Operand(2));
+ __ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
+ __ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
+ __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
+ __ add(sp, sp, Operand(2 * kPointerSize));
+ __ Ret();
+
+ __ bind(&longer_than_two);
// Check if resulting string will be flat.
__ cmp(r6, Operand(String::kMinNonFlatLength));
__ b(lt, &string_add_flat_result);
@@ -7568,6 +7886,7 @@
// Both strings are sequential ASCII strings. We also know that they are
// short (since the sum of the lengths is less than kMinNonFlatLength).
+ // r6: length of resulting flat string
__ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
// Locate first character of result.
__ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
@@ -7636,7 +7955,7 @@
// Just jump to runtime to add the two strings.
__ bind(&string_add_runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
}
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 2bc482e..bea98b6 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -370,6 +370,9 @@
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
+ // Fast support for string.charAt(n) and string[n].
+ void GenerateCharFromCode(ZoneList<Expression*>* args);
+
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
@@ -393,6 +396,16 @@
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
+ // Fast support for Math.pow().
+ void GenerateMathPow(ZoneList<Expression*>* args);
+
+ // Fast call to sine function.
+ void GenerateMathSin(ZoneList<Expression*>* args);
+ void GenerateMathCos(ZoneList<Expression*>* args);
+
+ // Fast support for Math.pow().
+ void GenerateMathSqrt(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
@@ -554,6 +567,36 @@
Register scratch4,
Register scratch5,
int flags);
+
+
+ // Probe the symbol table for a two character string. If the string is
+ // not found by probing a jump to the label not_found is performed. This jump
+ // does not guarantee that the string is not in the symbol table. If the
+ // string is found the code falls through with the string in register r0.
+ // Contents of both c1 and c2 registers are modified. At the exit c1 is
+ // guaranteed to contain halfword with low and high bytes equal to
+ // initial contents of c1 and c2 respectively.
+ void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Register scratch5,
+ Label* not_found);
+
+ // Generate string hash.
+ void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character);
+
+ void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character);
+
+ void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash);
};
diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc
index 0d322d1..5dedc29 100644
--- a/src/arm/fast-codegen-arm.cc
+++ b/src/arm/fast-codegen-arm.cc
@@ -40,6 +40,7 @@
Register FastCodeGenerator::accumulator1() { return r1; }
Register FastCodeGenerator::scratch0() { return r3; }
Register FastCodeGenerator::scratch1() { return r4; }
+Register FastCodeGenerator::scratch2() { return r5; }
Register FastCodeGenerator::receiver_reg() { return r2; }
Register FastCodeGenerator::context_reg() { return cp; }
@@ -100,7 +101,7 @@
if (needs_write_barrier) {
__ mov(scratch1(), Operand(offset));
- __ RecordWrite(scratch0(), scratch1(), ip);
+ __ RecordWrite(scratch0(), scratch1(), scratch2());
}
if (destination().is(accumulator1())) {
@@ -180,6 +181,7 @@
void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
ASSERT(info_ == NULL);
info_ = compilation_info;
+ Comment cmnt(masm_, "[ function compiled by fast code generator");
// Save the caller's frame pointer and set up our own.
Comment prologue_cmnt(masm(), ";; Prologue");
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index c37e29f..230818f 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -57,6 +57,7 @@
ASSERT(info_ == NULL);
info_ = info;
SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
if (mode == PRIMARY) {
int locals_count = scope()->num_stack_slots();
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 7ddb338..e68a77a 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -494,7 +494,8 @@
__ stm(db_w, sp, r2.bit() | r3.bit());
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -531,7 +532,8 @@
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r2.bit() | r3.bit());
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -545,7 +547,7 @@
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r2.bit() | r3.bit());
- __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2, 1);
+ __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
}
@@ -662,7 +664,7 @@
__ push(r0); // key
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(
+ __ TailCallExternalReference(ExternalReference(
IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
__ bind(&slow);
@@ -681,7 +683,8 @@
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
@@ -695,7 +698,7 @@
__ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object
__ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit());
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
+ __ TailCallRuntime(Runtime::kSetProperty, 3, 1);
}
@@ -854,7 +857,8 @@
__ stm(db_w, sp, r2.bit() | r0.bit());
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
@@ -897,7 +901,8 @@
__ push(receiver);
__ push(value);
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_ArrayLength)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength));
+ __ TailCallExternalReference(ref, 2, 1);
__ bind(&miss);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index b249d69..36bebdf 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -220,7 +220,7 @@
// remembered set bits in the new space.
// object: heap object pointer (with tag)
// offset: offset to store location from the object
- and_(scratch, object, Operand(Heap::NewSpaceMask()));
+ and_(scratch, object, Operand(ExternalReference::new_space_mask()));
cmp(scratch, Operand(ExternalReference::new_space_start()));
b(eq, &done);
@@ -1234,19 +1234,26 @@
}
-void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
- int num_arguments,
- int result_size) {
+void MacroAssembler::TailCallExternalReference(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.
mov(r0, Operand(num_arguments));
- JumpToRuntime(ext);
+ JumpToExternalReference(ext);
}
-void MacroAssembler::JumpToRuntime(const ExternalReference& builtin) {
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid), num_arguments, result_size);
+}
+
+
+void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) {
#if defined(__thumb__)
// Thumb mode builtin.
ASSERT((reinterpret_cast<intptr_t>(builtin.address()) & 1) == 1);
@@ -1410,15 +1417,12 @@
ldr(scratch2, FieldMemOperand(second, HeapObject::kMapOffset));
ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
ldrb(scratch2, FieldMemOperand(scratch2, Map::kInstanceTypeOffset));
- int kFlatAsciiStringMask =
- kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
- int kFlatAsciiStringTag = ASCII_STRING_TYPE;
- and_(scratch1, scratch1, Operand(kFlatAsciiStringMask));
- and_(scratch2, scratch2, Operand(kFlatAsciiStringMask));
- cmp(scratch1, Operand(kFlatAsciiStringTag));
- // Ignore second test if first test failed.
- cmp(scratch2, Operand(kFlatAsciiStringTag), eq);
- b(ne, failure);
+
+ JumpIfBothInstanceTypesAreNotSequentialAscii(scratch1,
+ scratch2,
+ scratch1,
+ scratch2,
+ failure);
}
void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first,
@@ -1439,6 +1443,36 @@
}
+void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first,
+ Register second,
+ Register scratch1,
+ Register scratch2,
+ Label* failure) {
+ int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
+ int kFlatAsciiStringTag = ASCII_STRING_TYPE;
+ and_(scratch1, first, Operand(kFlatAsciiStringMask));
+ and_(scratch2, second, Operand(kFlatAsciiStringMask));
+ cmp(scratch1, Operand(kFlatAsciiStringTag));
+ // Ignore second test if first test failed.
+ cmp(scratch2, Operand(kFlatAsciiStringTag), eq);
+ b(ne, failure);
+}
+
+
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type,
+ Register scratch,
+ Label* failure) {
+ int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
+ int kFlatAsciiStringTag = ASCII_STRING_TYPE;
+ and_(scratch, type, Operand(kFlatAsciiStringMask));
+ cmp(scratch, Operand(kFlatAsciiStringTag));
+ b(ne, failure);
+}
+
+
#ifdef ENABLE_DEBUGGER_SUPPORT
CodePatcher::CodePatcher(byte* address, int instructions)
: address_(address),
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 98cea16..5d9e513 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -333,7 +333,6 @@
void StubReturn(int argc);
// Call a runtime routine.
- // Eventually this should be used for all C calls.
void CallRuntime(Runtime::Function* f, int num_arguments);
// Convenience function: Same as above, but takes the fid instead.
@@ -344,14 +343,19 @@
int num_arguments);
// Tail call of a runtime routine (jump).
- // Like JumpToRuntime, but also takes care of passing the number
+ // Like JumpToExternalReference, but also takes care of passing the number
// of parameters.
- void TailCallRuntime(const ExternalReference& ext,
+ void TailCallExternalReference(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,
int result_size);
// Jump to a runtime routine.
- void JumpToRuntime(const ExternalReference& builtin);
+ void JumpToExternalReference(const ExternalReference& builtin);
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
@@ -421,6 +425,22 @@
Register scratch2,
Label* not_flat_ascii_strings);
+ // Checks if both instance types are sequential ASCII strings and jumps to
+ // label if either is not.
+ void JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* failure);
+
+ // Check if instance type is sequential ASCII string and jump to label if
+ // it is not.
+ void JumpIfInstanceTypeIsNotSequentialAscii(Register type,
+ Register scratch,
+ Label* failure);
+
+
private:
void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
void Call(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
diff --git a/src/arm/regexp-macro-assembler-arm.cc b/src/arm/regexp-macro-assembler-arm.cc
index 9dd3b93..f621be4 100644
--- a/src/arm/regexp-macro-assembler-arm.cc
+++ b/src/arm/regexp-macro-assembler-arm.cc
@@ -765,7 +765,7 @@
Label grow_failed;
// Call GrowStack(backtrack_stackpointer())
- int num_arguments = 2;
+ static const int num_arguments = 2;
FrameAlign(num_arguments, r0);
__ mov(r0, backtrack_stackpointer());
__ add(r1, frame_pointer(), Operand(kStackHighEnd));
@@ -966,7 +966,7 @@
// Private methods:
void RegExpMacroAssemblerARM::CallCheckStackGuardState(Register scratch) {
- int num_arguments = 3;
+ static const int num_arguments = 3;
FrameAlign(num_arguments, scratch);
// RegExp code frame pointer.
__ mov(r2, frame_pointer());
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index da73942..5d5b2a5 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -297,7 +297,7 @@
__ push(receiver_reg);
__ mov(r2, Operand(Handle<Map>(transition)));
__ stm(db_w, sp, r2.bit() | r0.bit());
- __ TailCallRuntime(
+ __ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)),
3, 1);
return;
@@ -529,7 +529,7 @@
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
__ bind(&cleanup);
__ pop(scratch1);
@@ -549,7 +549,7 @@
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
}
private:
@@ -719,7 +719,7 @@
// Do tail-call to the runtime system.
ExternalReference load_callback_property =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(load_callback_property, 5, 1);
+ __ TailCallExternalReference(load_callback_property, 5, 1);
return true;
}
@@ -1204,7 +1204,7 @@
// Do tail-call to the runtime system.
ExternalReference store_callback_property =
ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
- __ TailCallRuntime(store_callback_property, 4, 1);
+ __ TailCallExternalReference(store_callback_property, 4, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1251,7 +1251,7 @@
// Do tail-call to the runtime system.
ExternalReference store_ic_property =
ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
- __ TailCallRuntime(store_ic_property, 3, 1);
+ __ TailCallExternalReference(store_ic_property, 3, 1);
// Handle store cache miss.
__ bind(&miss);
diff --git a/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc
index 6e1a47f..ab6e5f8 100644
--- a/src/arm/virtual-frame-arm.cc
+++ b/src/arm/virtual-frame-arm.cc
@@ -35,27 +35,8 @@
namespace v8 {
namespace internal {
-// -------------------------------------------------------------------------
-// VirtualFrame implementation.
-
#define __ ACCESS_MASM(masm())
-
-// On entry to a function, the virtual frame already contains the
-// receiver and the parameters. All initial frame elements are in
-// memory.
-VirtualFrame::VirtualFrame()
- : elements_(parameter_count() + local_count() + kPreallocatedElements),
- stack_pointer_(parameter_count()) { // 0-based index of TOS.
- for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
- }
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- register_locations_[i] = kIllegalIndex;
- }
-}
-
-
void VirtualFrame::SyncElementBelowStackPointer(int index) {
UNREACHABLE();
}
@@ -314,7 +295,7 @@
void VirtualFrame::EmitPush(Register reg) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::Unknown()));
stack_pointer_++;
__ push(reg);
}
diff --git a/src/arm/virtual-frame-arm.h b/src/arm/virtual-frame-arm.h
index f69bddf..6eb0811 100644
--- a/src/arm/virtual-frame-arm.h
+++ b/src/arm/virtual-frame-arm.h
@@ -59,7 +59,7 @@
static const int kIllegalIndex = -1;
// Construct an initial virtual frame on entry to a JS function.
- VirtualFrame();
+ inline VirtualFrame();
// Construct a virtual frame as a clone of an existing one.
explicit inline VirtualFrame(VirtualFrame* original);
@@ -69,7 +69,7 @@
// Create a duplicate of an existing valid frame element.
FrameElement CopyElementAt(int index,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -344,7 +344,7 @@
void EmitPushMultiple(int count, int src_regs);
// Push an element on the virtual frame.
- inline void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
+ inline void Push(Register reg, NumberInfo info = NumberInfo::Unknown());
inline void Push(Handle<Object> value);
inline void Push(Smi* value);
diff --git a/src/array.js b/src/array.js
index c28a662..e33c280 100644
--- a/src/array.js
+++ b/src/array.js
@@ -1149,6 +1149,8 @@
ArrayReduce: 1,
ArrayReduceRight: 1
});
+
+ %FinishArrayPrototypeSetup($Array.prototype);
}
diff --git a/src/assembler.cc b/src/assembler.cc
index 96d516f..aaf10ef 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -579,6 +579,11 @@
}
+ExternalReference ExternalReference::transcendental_cache_array_address() {
+ return ExternalReference(TranscendentalCache::cache_array_address());
+}
+
+
ExternalReference ExternalReference::keyed_lookup_cache_keys() {
return ExternalReference(KeyedLookupCache::keys_address());
}
@@ -619,6 +624,11 @@
}
+ExternalReference ExternalReference::new_space_mask() {
+ return ExternalReference(reinterpret_cast<Address>(Heap::NewSpaceMask()));
+}
+
+
ExternalReference ExternalReference::new_space_allocation_top_address() {
return ExternalReference(Heap::NewSpaceAllocationTopAddress());
}
diff --git a/src/assembler.h b/src/assembler.h
index f401306..cde7d69 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -37,7 +37,6 @@
#include "runtime.h"
#include "top.h"
-#include "zone-inl.h"
#include "token.h"
namespace v8 {
@@ -400,6 +399,7 @@
static ExternalReference perform_gc_function();
static ExternalReference random_positive_smi_function();
+ static ExternalReference transcendental_cache_array_address();
// Static data in the keyed lookup cache.
static ExternalReference keyed_lookup_cache_keys();
@@ -427,6 +427,7 @@
// Static variable Heap::NewSpaceStart()
static ExternalReference new_space_start();
+ static ExternalReference new_space_mask();
static ExternalReference heap_always_allocate_scope_depth();
// Used for fast allocation in generated code.
diff --git a/src/ast.cc b/src/ast.cc
index 7cb5578..062a5c6 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -67,8 +67,6 @@
inside_with_(inside_with) {
// names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol());
- // at least one access, otherwise no need for a VariableProxy
- var_uses_.RecordRead(1);
}
@@ -87,8 +85,7 @@
// eval() etc. Const-ness and variable declarations are a complete mess
// in JS. Sigh...
var_ = var;
- var->var_uses()->RecordUses(&var_uses_);
- var->obj_uses()->RecordUses(&obj_uses_);
+ var->set_is_used(true);
}
diff --git a/src/ast.h b/src/ast.h
index f2171cc..13502dc 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -117,6 +117,9 @@
class AstNode: public ZoneObject {
public:
+ static const int kNoNumber = -1;
+
+ AstNode() : num_(kNoNumber) {}
virtual ~AstNode() { }
virtual void Accept(AstVisitor* v) = 0;
@@ -141,6 +144,13 @@
virtual ObjectLiteral* AsObjectLiteral() { return NULL; }
virtual ArrayLiteral* AsArrayLiteral() { return NULL; }
virtual CompareOperation* AsCompareOperation() { return NULL; }
+
+ int num() { return num_; }
+ void set_num(int n) { num_ = n; }
+
+ private:
+ // Support for ast node numbering.
+ int num_;
};
@@ -181,9 +191,10 @@
kTestValue
};
- static const int kNoLabel = -1;
-
- Expression() : num_(kNoLabel), def_(NULL), defined_vars_(NULL) {}
+ Expression()
+ : bitfields_(0),
+ def_(NULL),
+ defined_vars_(NULL) {}
virtual Expression* AsExpression() { return this; }
@@ -211,11 +222,6 @@
// Static type information for this expression.
StaticType* type() { return &type_; }
- int num() { return num_; }
-
- // AST node numbering ordered by evaluation order.
- void set_num(int n) { num_ = n; }
-
// Data flow information.
DefinitionInfo* var_def() { return def_; }
void set_var_def(DefinitionInfo* def) { def_ = def; }
@@ -225,11 +231,36 @@
defined_vars_ = defined_vars;
}
+ // AST analysis results
+
+ // True if the expression rooted at this node can be compiled by the
+ // side-effect free compiler.
+ bool side_effect_free() { return SideEffectFreeField::decode(bitfields_); }
+ void set_side_effect_free(bool is_side_effect_free) {
+ bitfields_ &= ~SideEffectFreeField::mask();
+ bitfields_ |= SideEffectFreeField::encode(is_side_effect_free);
+ }
+
+ // Will ToInt32 (ECMA 262-3 9.5) or ToUint32 (ECMA 262-3 9.6)
+ // be applied to the value of this expression?
+ // If so, we may be able to optimize the calculation of the value.
+ bool to_int32() { return ToInt32Field::decode(bitfields_); }
+ void set_to_int32(bool to_int32) {
+ bitfields_ &= ~ToInt32Field::mask();
+ bitfields_ |= ToInt32Field::encode(to_int32);
+ }
+
+
private:
+ uint32_t bitfields_;
StaticType type_;
- int num_;
+
DefinitionInfo* def_;
ZoneList<DefinitionInfo*>* defined_vars_;
+
+ // Using template BitField<type, start, size>.
+ class SideEffectFreeField : public BitField<bool, 0, 1> {};
+ class ToInt32Field : public BitField<bool, 1, 1> {};
};
@@ -931,6 +962,10 @@
return var()->is_global() || var()->rewrite()->IsLeaf();
}
+ // Reading from a mutable variable is a side effect, but 'this' is
+ // immutable.
+ virtual bool IsTrivial() { return is_this(); }
+
bool IsVariable(Handle<String> n) {
return !is_this() && name().is_identical_to(n);
}
@@ -942,8 +977,6 @@
Handle<String> name() const { return name_; }
Variable* var() const { return var_; }
- UseCount* var_uses() { return &var_uses_; }
- UseCount* obj_uses() { return &obj_uses_; }
bool is_this() const { return is_this_; }
bool inside_with() const { return inside_with_; }
@@ -956,10 +989,6 @@
bool is_this_;
bool inside_with_;
- // VariableProxy usage info.
- UseCount var_uses_; // uses of the variable value
- UseCount obj_uses_; // uses of the object the variable points to
-
VariableProxy(Handle<String> name, bool is_this, bool inside_with);
explicit VariableProxy(bool is_this);
@@ -1018,6 +1047,8 @@
virtual bool IsLeaf() { return true; }
+ bool IsStackAllocated() { return type_ == PARAMETER || type_ == LOCAL; }
+
// Accessors
Variable* var() const { return var_; }
Type type() const { return type_; }
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index a7cf421..12efbc1 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1050,6 +1050,19 @@
script->set_type(Smi::FromInt(Script::TYPE_NATIVE));
global_context()->set_empty_script(*script);
}
+ {
+ // Builtin function for OpaqueReference -- a JSValue-based object,
+ // that keeps its field isolated from JavaScript code. It may store
+ // objects, that JavaScript code may not access.
+ Handle<JSFunction> opaque_reference_fun =
+ InstallFunction(builtins, "OpaqueReference", JS_VALUE_TYPE,
+ JSValue::kSize, Top::initial_object_prototype(),
+ Builtins::Illegal, false);
+ Handle<JSObject> prototype =
+ Factory::NewJSObject(Top::object_function(), TENURED);
+ SetPrototype(opaque_reference_fun, prototype);
+ global_context()->set_opaque_reference_function(*opaque_reference_fun);
+ }
if (FLAG_natives_file == NULL) {
// Without natives file, install default natives.
diff --git a/src/builtins.cc b/src/builtins.cc
index 8e88c28..a8ba818 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -242,6 +242,109 @@
}
+static Object* AllocateJSArray() {
+ JSFunction* array_function =
+ Top::context()->global_context()->array_function();
+ Object* result = Heap::AllocateJSObject(array_function);
+ if (result->IsFailure()) return result;
+ return result;
+}
+
+
+static Object* AllocateEmptyJSArray() {
+ Object* result = AllocateJSArray();
+ if (result->IsFailure()) return result;
+ JSArray* result_array = JSArray::cast(result);
+ result_array->set_length(Smi::FromInt(0));
+ result_array->set_elements(Heap::empty_fixed_array());
+ return result_array;
+}
+
+
+static void CopyElements(AssertNoAllocation* no_gc,
+ FixedArray* dst,
+ int dst_index,
+ FixedArray* src,
+ int src_index,
+ int len) {
+ ASSERT(dst != src); // Use MoveElements instead.
+ memcpy(dst->data_start() + dst_index,
+ src->data_start() + src_index,
+ len * kPointerSize);
+ WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
+ if (mode == UPDATE_WRITE_BARRIER) {
+ Heap::RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
+ }
+}
+
+
+static void MoveElements(AssertNoAllocation* no_gc,
+ FixedArray* dst,
+ int dst_index,
+ FixedArray* src,
+ int src_index,
+ int len) {
+ memmove(dst->data_start() + dst_index,
+ src->data_start() + src_index,
+ len * kPointerSize);
+ WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
+ if (mode == UPDATE_WRITE_BARRIER) {
+ Heap::RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
+ }
+}
+
+
+static void FillWithHoles(FixedArray* dst, int from, int to) {
+ MemsetPointer(dst->data_start() + from, Heap::the_hole_value(), to - from);
+}
+
+
+static bool ArrayPrototypeHasNoElements() {
+ // This method depends on non writability of Object and Array prototype
+ // fields.
+ Context* global_context = Top::context()->global_context();
+ // Array.prototype
+ JSObject* proto =
+ JSObject::cast(global_context->array_function()->prototype());
+ if (proto->elements() != Heap::empty_fixed_array()) return false;
+ // Hidden prototype
+ proto = JSObject::cast(proto->GetPrototype());
+ ASSERT(proto->elements() == Heap::empty_fixed_array());
+ // Object.prototype
+ proto = JSObject::cast(proto->GetPrototype());
+ if (proto != global_context->initial_object_prototype()) return false;
+ if (proto->elements() != Heap::empty_fixed_array()) return false;
+ ASSERT(proto->GetPrototype()->IsNull());
+ return true;
+}
+
+
+static Object* CallJsBuiltin(const char* name,
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
+ HandleScope handleScope;
+
+ Handle<Object> js_builtin =
+ GetProperty(Handle<JSObject>(Top::global_context()->builtins()),
+ name);
+ ASSERT(js_builtin->IsJSFunction());
+ Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin));
+ Vector<Object**> argv(Vector<Object**>::New(args.length() - 1));
+ int n_args = args.length() - 1;
+ for (int i = 0; i < n_args; i++) {
+ argv[i] = &args[i + 1];
+ }
+ bool pending_exception = false;
+ Handle<Object> result = Execution::Call(function,
+ args.receiver(),
+ n_args,
+ argv.start(),
+ &pending_exception);
+ argv.Dispose();
+ if (pending_exception) return Failure::Exception();
+ return *result;
+}
+
+
BUILTIN(ArrayPush) {
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
@@ -261,22 +364,21 @@
if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
- Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
+ Object* obj = Heap::AllocateUninitializedFixedArray(capacity);
if (obj->IsFailure()) return obj;
+ FixedArray* new_elms = FixedArray::cast(obj);
AssertNoAllocation no_gc;
- FixedArray* new_elms = FixedArray::cast(obj);
- WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
- // Fill out the new array with old elements.
- for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode);
+ CopyElements(&no_gc, new_elms, 0, elms, 0, len);
+ FillWithHoles(new_elms, new_length, capacity);
+
elms = new_elms;
array->set_elements(elms);
}
+ // Add the provided values.
AssertNoAllocation no_gc;
WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
-
- // Add the provided values.
for (int index = 0; index < to_add; index++) {
elms->set(index + len, args[index + 1], mode);
}
@@ -290,10 +392,9 @@
BUILTIN(ArrayPop) {
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
- Object* undefined = Heap::undefined_value();
int len = Smi::cast(array->length())->value();
- if (len == 0) return undefined;
+ if (len == 0) return Heap::undefined_value();
// Get top element
FixedArray* elms = FixedArray::cast(array->elements());
@@ -318,41 +419,28 @@
}
-static Object* GetElementToMove(uint32_t index,
- FixedArray* elms,
- JSObject* prototype) {
- Object* e = elms->get(index);
- if (e->IsTheHole() && prototype->HasElement(index)) {
- e = prototype->GetElement(index);
- }
- return e;
-}
-
-
BUILTIN(ArrayShift) {
+ if (!ArrayPrototypeHasNoElements()) {
+ return CallJsBuiltin("ArrayShift", args);
+ }
+
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
int len = Smi::cast(array->length())->value();
if (len == 0) return Heap::undefined_value();
- // Fetch the prototype.
- JSFunction* array_function =
- Top::context()->global_context()->array_function();
- JSObject* prototype = JSObject::cast(array_function->prototype());
-
FixedArray* elms = FixedArray::cast(array->elements());
// Get first element
Object* first = elms->get(0);
if (first->IsTheHole()) {
- first = prototype->GetElement(0);
+ first = Heap::undefined_value();
}
// Shift the elements.
- for (int i = 0; i < len - 1; i++) {
- elms->set(i, GetElementToMove(i + 1, elms, prototype));
- }
+ AssertNoAllocation no_gc;
+ MoveElements(&no_gc, elms, 0, elms, 1, len - 1);
elms->set(len - 1, Heap::the_hole_value());
// Set the length.
@@ -363,6 +451,10 @@
BUILTIN(ArrayUnshift) {
+ if (!ArrayPrototypeHasNoElements()) {
+ return CallJsBuiltin("ArrayUnshift", args);
+ }
+
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
@@ -379,38 +471,22 @@
FixedArray* elms = FixedArray::cast(array->elements());
- // Fetch the prototype.
- JSFunction* array_function =
- Top::context()->global_context()->array_function();
- JSObject* prototype = JSObject::cast(array_function->prototype());
-
if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
- Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
+ Object* obj = Heap::AllocateUninitializedFixedArray(capacity);
if (obj->IsFailure()) return obj;
+ FixedArray* new_elms = FixedArray::cast(obj);
AssertNoAllocation no_gc;
- FixedArray* new_elms = FixedArray::cast(obj);
- WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
- // Fill out the new array with old elements.
- for (int i = 0; i < len; i++)
- new_elms->set(to_add + i,
- GetElementToMove(i, elms, prototype),
- mode);
+ CopyElements(&no_gc, new_elms, to_add, elms, 0, len);
+ FillWithHoles(new_elms, new_length, capacity);
elms = new_elms;
array->set_elements(elms);
} else {
AssertNoAllocation no_gc;
- WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
-
- // Move elements to the right
- for (int i = 0; i < len; i++) {
- elms->set(new_length - i - 1,
- GetElementToMove(len - i - 1, elms, prototype),
- mode);
- }
+ MoveElements(&no_gc, elms, to_add, elms, 0, len);
}
// Add the provided values.
@@ -426,32 +502,11 @@
}
-static Object* CallJsBuiltin(const char* name,
- BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
- HandleScope handleScope;
-
- Handle<Object> js_builtin =
- GetProperty(Handle<JSObject>(Top::global_context()->builtins()),
- name);
- ASSERT(js_builtin->IsJSFunction());
- Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin));
- Vector<Object**> argv(Vector<Object**>::New(args.length() - 1));
- int n_args = args.length() - 1;
- for (int i = 0; i < n_args; i++) {
- argv[i] = &args[i + 1];
- }
- bool pending_exception = false;
- Handle<Object> result = Execution::Call(function,
- args.receiver(),
- n_args,
- argv.start(),
- &pending_exception);
- if (pending_exception) return Failure::Exception();
- return *result;
-}
-
-
BUILTIN(ArraySlice) {
+ if (!ArrayPrototypeHasNoElements()) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
+
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
@@ -460,21 +515,21 @@
int n_arguments = args.length() - 1;
// Note carefully choosen defaults---if argument is missing,
- // it's undefined which gets converted to 0 for relativeStart
- // and to len for relativeEnd.
- int relativeStart = 0;
- int relativeEnd = len;
+ // it's undefined which gets converted to 0 for relative_start
+ // and to len for relative_end.
+ int relative_start = 0;
+ int relative_end = len;
if (n_arguments > 0) {
Object* arg1 = args[1];
if (arg1->IsSmi()) {
- relativeStart = Smi::cast(arg1)->value();
+ relative_start = Smi::cast(arg1)->value();
} else if (!arg1->IsUndefined()) {
return CallJsBuiltin("ArraySlice", args);
}
if (n_arguments > 1) {
Object* arg2 = args[2];
if (arg2->IsSmi()) {
- relativeEnd = Smi::cast(arg2)->value();
+ relative_end = Smi::cast(arg2)->value();
} else if (!arg2->IsUndefined()) {
return CallJsBuiltin("ArraySlice", args);
}
@@ -482,43 +537,31 @@
}
// ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
- int k = (relativeStart < 0) ? Max(len + relativeStart, 0)
- : Min(relativeStart, len);
+ int k = (relative_start < 0) ? Max(len + relative_start, 0)
+ : Min(relative_start, len);
// ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
- int final = (relativeEnd < 0) ? Max(len + relativeEnd, 0)
- : Min(relativeEnd, len);
+ int final = (relative_end < 0) ? Max(len + relative_end, 0)
+ : Min(relative_end, len);
// Calculate the length of result array.
int result_len = final - k;
- if (result_len < 0) {
- result_len = 0;
+ if (result_len <= 0) {
+ return AllocateEmptyJSArray();
}
- JSFunction* array_function =
- Top::context()->global_context()->array_function();
- Object* result = Heap::AllocateJSObject(array_function);
+ Object* result = AllocateJSArray();
if (result->IsFailure()) return result;
JSArray* result_array = JSArray::cast(result);
- result = Heap::AllocateFixedArrayWithHoles(result_len);
+ result = Heap::AllocateUninitializedFixedArray(result_len);
if (result->IsFailure()) return result;
FixedArray* result_elms = FixedArray::cast(result);
FixedArray* elms = FixedArray::cast(array->elements());
- // Fetch the prototype.
- JSObject* prototype = JSObject::cast(array_function->prototype());
-
AssertNoAllocation no_gc;
- WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
-
- // Fill newly created array.
- for (int i = 0; i < result_len; i++) {
- result_elms->set(i,
- GetElementToMove(k + i, elms, prototype),
- mode);
- }
+ CopyElements(&no_gc, result_elms, 0, elms, k, result_len);
// Set elements.
result_array->set_elements(result_elms);
@@ -530,6 +573,10 @@
BUILTIN(ArraySplice) {
+ if (!ArrayPrototypeHasNoElements()) {
+ return CallJsBuiltin("ArraySplice", args);
+ }
+
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
@@ -546,118 +593,111 @@
return Heap::undefined_value();
}
- int relativeStart = 0;
+ int relative_start = 0;
Object* arg1 = args[1];
if (arg1->IsSmi()) {
- relativeStart = Smi::cast(arg1)->value();
+ relative_start = Smi::cast(arg1)->value();
} else if (!arg1->IsUndefined()) {
return CallJsBuiltin("ArraySplice", args);
}
- int actualStart = (relativeStart < 0) ? Max(len + relativeStart, 0)
- : Min(relativeStart, len);
+ int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
+ : Min(relative_start, len);
// SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
// given differently from when an undefined delete count is given.
// This does not follow ECMA-262, but we do the same for
// compatibility.
- int deleteCount = len;
+ int delete_count = len;
if (n_arguments > 1) {
Object* arg2 = args[2];
if (arg2->IsSmi()) {
- deleteCount = Smi::cast(arg2)->value();
+ delete_count = Smi::cast(arg2)->value();
} else {
return CallJsBuiltin("ArraySplice", args);
}
}
- int actualDeleteCount = Min(Max(deleteCount, 0), len - actualStart);
-
- JSFunction* array_function =
- Top::context()->global_context()->array_function();
-
- // Allocate result array.
- Object* result = Heap::AllocateJSObject(array_function);
- if (result->IsFailure()) return result;
- JSArray* result_array = JSArray::cast(result);
-
- result = Heap::AllocateFixedArrayWithHoles(actualDeleteCount);
- if (result->IsFailure()) return result;
- FixedArray* result_elms = FixedArray::cast(result);
+ int actual_delete_count = Min(Max(delete_count, 0), len - actual_start);
FixedArray* elms = FixedArray::cast(array->elements());
- // Fetch the prototype.
- JSObject* prototype = JSObject::cast(array_function->prototype());
+ JSArray* result_array = NULL;
+ if (actual_delete_count == 0) {
+ Object* result = AllocateEmptyJSArray();
+ if (result->IsFailure()) return result;
+ result_array = JSArray::cast(result);
+ } else {
+ // Allocate result array.
+ Object* result = AllocateJSArray();
+ if (result->IsFailure()) return result;
+ result_array = JSArray::cast(result);
- AssertNoAllocation no_gc;
- WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
+ result = Heap::AllocateUninitializedFixedArray(actual_delete_count);
+ if (result->IsFailure()) return result;
+ FixedArray* result_elms = FixedArray::cast(result);
- // Fill newly created array.
- for (int k = 0; k < actualDeleteCount; k++) {
- result_elms->set(k,
- GetElementToMove(actualStart + k, elms, prototype),
- mode);
+ AssertNoAllocation no_gc;
+ // Fill newly created array.
+ CopyElements(&no_gc,
+ result_elms, 0,
+ elms, actual_start,
+ actual_delete_count);
+
+ // Set elements.
+ result_array->set_elements(result_elms);
+
+ // Set the length.
+ result_array->set_length(Smi::FromInt(actual_delete_count));
}
- // Set elements.
- result_array->set_elements(result_elms);
+ int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
- // Set the length.
- result_array->set_length(Smi::FromInt(actualDeleteCount));
+ int new_length = len - actual_delete_count + item_count;
- int itemCount = (n_arguments > 1) ? (n_arguments - 2) : 0;
-
- int new_length = len - actualDeleteCount + itemCount;
-
- mode = elms->GetWriteBarrierMode(no_gc);
- if (itemCount < actualDeleteCount) {
+ if (item_count < actual_delete_count) {
// Shrink the array.
- for (int k = actualStart; k < (len - actualDeleteCount); k++) {
- elms->set(k + itemCount,
- GetElementToMove(k + actualDeleteCount, elms, prototype),
- mode);
- }
-
- for (int k = len; k > new_length; k--) {
- elms->set(k - 1, Heap::the_hole_value());
- }
- } else if (itemCount > actualDeleteCount) {
+ AssertNoAllocation no_gc;
+ MoveElements(&no_gc,
+ elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ FillWithHoles(elms, new_length, len);
+ } else if (item_count > actual_delete_count) {
// Currently fixed arrays cannot grow too big, so
// we should never hit this case.
- ASSERT((itemCount - actualDeleteCount) <= (Smi::kMaxValue - len));
-
- FixedArray* source_elms = elms;
+ ASSERT((item_count - actual_delete_count) <= (Smi::kMaxValue - len));
// Check if array need to grow.
if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
- Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
+ Object* obj = Heap::AllocateUninitializedFixedArray(capacity);
if (obj->IsFailure()) return obj;
-
FixedArray* new_elms = FixedArray::cast(obj);
- mode = new_elms->GetWriteBarrierMode(no_gc);
- // Copy the part before actualStart as is.
- for (int k = 0; k < actualStart; k++) {
- new_elms->set(k, elms->get(k), mode);
- }
+ AssertNoAllocation no_gc;
+ // Copy the part before actual_start as is.
+ CopyElements(&no_gc, new_elms, 0, elms, 0, actual_start);
+ CopyElements(&no_gc,
+ new_elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
+ FillWithHoles(new_elms, new_length, capacity);
- source_elms = elms;
elms = new_elms;
array->set_elements(elms);
- }
-
- for (int k = len - actualDeleteCount; k > actualStart; k--) {
- elms->set(k + itemCount - 1,
- GetElementToMove(k + actualDeleteCount - 1,
- source_elms,
- prototype),
- mode);
+ } else {
+ AssertNoAllocation no_gc;
+ MoveElements(&no_gc,
+ elms, actual_start + item_count,
+ elms, actual_start + actual_delete_count,
+ (len - actual_delete_count - actual_start));
}
}
- for (int k = actualStart; k < actualStart + itemCount; k++) {
- elms->set(k, args[3 + k - actualStart], mode);
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int k = actual_start; k < actual_start + item_count; k++) {
+ elms->set(k, args[3 + k - actual_start], mode);
}
// Set the length.
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 4d0fd29..e42f758 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -83,6 +83,11 @@
}
+int CodeStub::GetCodeKind() {
+ return Code::STUB;
+}
+
+
Handle<Code> CodeStub::GetCode() {
Code* code;
if (!FindCodeInCache(&code)) {
@@ -97,7 +102,10 @@
masm.GetCode(&desc);
// Copy the generated code into a heap object.
- Code::Flags flags = Code::ComputeFlags(Code::STUB, InLoop());
+ Code::Flags flags = Code::ComputeFlags(
+ static_cast<Code::Kind>(GetCodeKind()),
+ InLoop(),
+ GetICState());
Handle<Code> new_object =
Factory::NewCode(desc, NULL, flags, masm.CodeObject());
RecordCodeGeneration(*new_object, &masm);
@@ -132,7 +140,10 @@
masm.GetCode(&desc);
// Try to copy the generated code into a heap object.
- Code::Flags flags = Code::ComputeFlags(Code::STUB, InLoop());
+ Code::Flags flags = Code::ComputeFlags(
+ static_cast<Code::Kind>(GetCodeKind()),
+ InLoop(),
+ GetICState());
Object* new_object =
Heap::CreateCode(desc, NULL, flags, masm.CodeObject());
if (new_object->IsFailure()) return new_object;
diff --git a/src/code-stubs.h b/src/code-stubs.h
index 3901a64..de2ad56 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -28,6 +28,8 @@
#ifndef V8_CODE_STUBS_H_
#define V8_CODE_STUBS_H_
+#include "globals.h"
+
namespace v8 {
namespace internal {
@@ -48,6 +50,7 @@
V(FastNewClosure) \
V(FastNewContext) \
V(FastCloneShallowArray) \
+ V(TranscendentalCache) \
V(GenericUnaryOp) \
V(RevertToNumber) \
V(ToBoolean) \
@@ -138,6 +141,14 @@
// lazily generated function should be fully optimized or not.
virtual InLoopFlag InLoop() { return NOT_IN_LOOP; }
+ // GenericBinaryOpStub needs to override this.
+ virtual int GetCodeKind();
+
+ // GenericBinaryOpStub needs to override this.
+ virtual InlineCacheState GetICState() {
+ return UNINITIALIZED;
+ }
+
// Returns a name for logging/debugging purposes.
virtual const char* GetName() { return MajorName(MajorKey(), false); }
diff --git a/src/codegen.cc b/src/codegen.cc
index 5e25f69..6841c21 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -369,6 +369,7 @@
{&CodeGenerator::GenerateValueOf, "_ValueOf"},
{&CodeGenerator::GenerateSetValueOf, "_SetValueOf"},
{&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"},
+ {&CodeGenerator::GenerateCharFromCode, "_CharFromCode"},
{&CodeGenerator::GenerateObjectEquals, "_ObjectEquals"},
{&CodeGenerator::GenerateLog, "_Log"},
{&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"},
@@ -380,6 +381,10 @@
{&CodeGenerator::GenerateStringCompare, "_StringCompare"},
{&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
{&CodeGenerator::GenerateNumberToString, "_NumberToString"},
+ {&CodeGenerator::GenerateMathPow, "_Math_pow"},
+ {&CodeGenerator::GenerateMathSin, "_Math_sin"},
+ {&CodeGenerator::GenerateMathCos, "_Math_cos"},
+ {&CodeGenerator::GenerateMathSqrt, "_Math_sqrt"},
};
diff --git a/src/compilation-cache.cc b/src/compilation-cache.cc
index 5427367..9dcbeb5 100644
--- a/src/compilation-cache.cc
+++ b/src/compilation-cache.cc
@@ -32,28 +32,23 @@
namespace v8 {
namespace internal {
-
// The number of sub caches covering the different types to cache.
static const int kSubCacheCount = 4;
// The number of generations for each sub cache.
-#if defined(ANDROID)
-static const int kScriptGenerations = 1;
-static const int kEvalGlobalGenerations = 1;
-static const int kEvalContextualGenerations = 1;
-static const int kRegExpGenerations = 1;
-#else
// The number of ScriptGenerations is carefully chosen based on histograms.
// See issue 458: http://code.google.com/p/v8/issues/detail?id=458
static const int kScriptGenerations = 5;
static const int kEvalGlobalGenerations = 2;
static const int kEvalContextualGenerations = 2;
static const int kRegExpGenerations = 2;
-#endif
// Initial size of each compilation cache table allocated.
static const int kInitialCacheSize = 64;
+// Index for the first generation in the cache.
+static const int kFirstGeneration = 0;
+
// The compilation cache consists of several generational sub-caches which uses
// this class as a base class. A sub-cache contains a compilation cache tables
// for each generation of the sub-cache. Since the same source code string has
@@ -70,6 +65,15 @@
// Get the compilation cache tables for a specific generation.
Handle<CompilationCacheTable> GetTable(int generation);
+ // Accessors for first generation.
+ Handle<CompilationCacheTable> GetFirstTable() {
+ return GetTable(kFirstGeneration);
+ }
+ void SetFirstTable(Handle<CompilationCacheTable> value) {
+ ASSERT(kFirstGeneration < generations_);
+ tables_[kFirstGeneration] = *value;
+ }
+
// Age the sub-cache by evicting the oldest generation and creating a new
// young generation.
void Age();
@@ -104,6 +108,10 @@
void Put(Handle<String> source, Handle<JSFunction> boilerplate);
private:
+ // Note: Returns a new hash table if operation results in expansion.
+ Handle<CompilationCacheTable> TablePut(Handle<String> source,
+ Handle<JSFunction> boilerplate);
+
bool HasOrigin(Handle<JSFunction> boilerplate,
Handle<Object> name,
int line_offset,
@@ -125,6 +133,12 @@
Handle<Context> context,
Handle<JSFunction> boilerplate);
+ private:
+ // Note: Returns a new hash table if operation results in expansion.
+ Handle<CompilationCacheTable> TablePut(Handle<String> source,
+ Handle<Context> context,
+ Handle<JSFunction> boilerplate);
+
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval);
};
@@ -140,6 +154,11 @@
void Put(Handle<String> source,
JSRegExp::Flags flags,
Handle<FixedArray> data);
+ private:
+ // Note: Returns a new hash table if operation results in expansion.
+ Handle<CompilationCacheTable> TablePut(Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data);
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp);
};
@@ -287,12 +306,19 @@
}
+Handle<CompilationCacheTable> CompilationCacheScript::TablePut(
+ Handle<String> source,
+ Handle<JSFunction> boilerplate) {
+ CALL_HEAP_FUNCTION(GetFirstTable()->Put(*source, *boilerplate),
+ CompilationCacheTable);
+}
+
+
void CompilationCacheScript::Put(Handle<String> source,
Handle<JSFunction> boilerplate) {
HandleScope scope;
ASSERT(boilerplate->IsBoilerplate());
- Handle<CompilationCacheTable> table = GetTable(0);
- CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
+ SetFirstTable(TablePut(source, boilerplate));
}
@@ -326,13 +352,21 @@
}
+Handle<CompilationCacheTable> CompilationCacheEval::TablePut(
+ Handle<String> source,
+ Handle<Context> context,
+ Handle<JSFunction> boilerplate) {
+ CALL_HEAP_FUNCTION(GetFirstTable()->PutEval(*source, *context, *boilerplate),
+ CompilationCacheTable);
+}
+
+
void CompilationCacheEval::Put(Handle<String> source,
Handle<Context> context,
Handle<JSFunction> boilerplate) {
HandleScope scope;
ASSERT(boilerplate->IsBoilerplate());
- Handle<CompilationCacheTable> table = GetTable(0);
- CALL_HEAP_FUNCTION_VOID(table->PutEval(*source, *context, *boilerplate));
+ SetFirstTable(TablePut(source, context, boilerplate));
}
@@ -366,12 +400,20 @@
}
+Handle<CompilationCacheTable> CompilationCacheRegExp::TablePut(
+ Handle<String> source,
+ JSRegExp::Flags flags,
+ Handle<FixedArray> data) {
+ CALL_HEAP_FUNCTION(GetFirstTable()->PutRegExp(*source, flags, *data),
+ CompilationCacheTable);
+}
+
+
void CompilationCacheRegExp::Put(Handle<String> source,
JSRegExp::Flags flags,
Handle<FixedArray> data) {
HandleScope scope;
- Handle<CompilationCacheTable> table = GetTable(0);
- CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data));
+ SetFirstTable(TablePut(source, flags, data));
}
diff --git a/src/compiler.cc b/src/compiler.cc
index 557a91e..ebb62f1 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -31,14 +31,14 @@
#include "codegen-inl.h"
#include "compilation-cache.h"
#include "compiler.h"
+#include "data-flow.h"
#include "debug.h"
#include "fast-codegen.h"
#include "full-codegen.h"
+#include "liveedit.h"
#include "oprofile-agent.h"
#include "rewriter.h"
#include "scopes.h"
-#include "usage-analyzer.h"
-#include "liveedit.h"
namespace v8 {
namespace internal {
@@ -48,7 +48,7 @@
FunctionLiteral* function = info->function();
ASSERT(function != NULL);
// Rewrite the AST by introducing .result assignments where needed.
- if (!Rewriter::Process(function) || !AnalyzeVariableUsage(function)) {
+ if (!Rewriter::Process(function)) {
// Signal a stack overflow by returning a null handle. The stack
// overflow exception will be thrown by the caller.
return Handle<Code>::null();
@@ -79,6 +79,17 @@
return Handle<Code>::null();
}
+ if (FLAG_use_flow_graph) {
+ FlowGraphBuilder builder;
+ builder.Build(function);
+
+#ifdef DEBUG
+ if (FLAG_print_graph_text) {
+ builder.graph()->PrintText(builder.postorder());
+ }
+#endif
+ }
+
// Generate code and return it. Code generator selection is governed by
// which backends are enabled and whether the function is considered
// run-once code or not:
@@ -117,6 +128,14 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+Handle<Code> MakeCodeForLiveEdit(CompilationInfo* info) {
+ Handle<Context> context = Handle<Context>::null();
+ return MakeCode(context, info);
+}
+#endif
+
+
static Handle<JSFunction> MakeFunction(bool is_global,
bool is_eval,
Compiler::ValidationState validate,
@@ -224,7 +243,7 @@
#ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger
- Debugger::OnAfterCompile(script, fun);
+ Debugger::OnAfterCompile(script, Debugger::NO_AFTER_COMPILE_FLAGS);
#endif
return fun;
@@ -444,6 +463,17 @@
return Handle<JSFunction>::null();
}
+ if (FLAG_use_flow_graph) {
+ FlowGraphBuilder builder;
+ builder.Build(literal);
+
+#ifdef DEBUG
+ if (FLAG_print_graph_text) {
+ builder.graph()->PrintText(builder.postorder());
+ }
+#endif
+ }
+
// Generate code and return it. The way that the compilation mode
// is controlled by the command-line flags is described in
// the static helper function MakeCode.
diff --git a/src/compiler.h b/src/compiler.h
index f01889d..8e220e6 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -276,6 +276,13 @@
};
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+Handle<Code> MakeCodeForLiveEdit(CompilationInfo* info);
+
+#endif
+
+
// During compilation we need a global list of handles to constants
// for frame elements. When the zone gets deleted, we make sure to
// clear this list of handles as well.
diff --git a/src/contexts.h b/src/contexts.h
index 9baf072..98ebc47 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -95,6 +95,7 @@
call_as_constructor_delegate) \
V(EMPTY_SCRIPT_INDEX, Script, empty_script) \
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
+ V(OPAQUE_REFERENCE_FUNCTION_INDEX, JSFunction, opaque_reference_function) \
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) \
@@ -216,6 +217,7 @@
CALL_AS_CONSTRUCTOR_DELEGATE_INDEX,
EMPTY_SCRIPT_INDEX,
SCRIPT_FUNCTION_INDEX,
+ OPAQUE_REFERENCE_FUNCTION_INDEX,
CONTEXT_EXTENSION_FUNCTION_INDEX,
OUT_OF_MEMORY_INDEX,
MAP_CACHE_INDEX,
diff --git a/src/conversions-inl.h b/src/conversions-inl.h
index ba7220a..f7210d5 100644
--- a/src/conversions-inl.h
+++ b/src/conversions-inl.h
@@ -59,6 +59,32 @@
}
+// The fast double-to-unsigned-int conversion routine does not guarantee
+// rounding towards zero.
+static inline unsigned int FastD2UI(double x) {
+ // There is no unsigned version of lrint, so there is no fast path
+ // in this function as there is in FastD2I. Using lrint doesn't work
+ // for values of 2^31 and above.
+
+ // Convert "small enough" doubles to uint32_t by fixing the 32
+ // least significant non-fractional bits in the low 32 bits of the
+ // double, and reading them from there.
+ const double k2Pow52 = 4503599627370496.0;
+ bool negative = x < 0;
+ if (negative) {
+ x = -x;
+ }
+ if (x < k2Pow52) {
+ x += k2Pow52;
+ uint32_t result;
+ memcpy(&result, &x, sizeof(result)); // Copy low 32 bits.
+ return negative ? ~result + 1 : result;
+ }
+ // Large number (outside uint32 range), Infinity or NaN.
+ return 0x80000000u; // Return integer indefinite.
+}
+
+
static inline double DoubleToInteger(double x) {
if (isnan(x)) return 0;
if (!isfinite(x) || x == 0) return x;
diff --git a/src/conversions.h b/src/conversions.h
index 67f7d53..bdc7e44 100644
--- a/src/conversions.h
+++ b/src/conversions.h
@@ -32,11 +32,12 @@
namespace internal {
-// The fast double-to-int conversion routine does not guarantee
+// The fast double-to-(unsigned-)int conversion routine does not guarantee
// rounding towards zero.
// The result is unspecified if x is infinite or NaN, or if the rounded
// integer value is outside the range of type int.
static inline int FastD2I(double x);
+static inline unsigned int FastD2UI(double x);
static inline double FastI2D(int x) {
diff --git a/src/data-flow.cc b/src/data-flow.cc
index 5e9d217..6b45da0 100644
--- a/src/data-flow.cc
+++ b/src/data-flow.cc
@@ -33,6 +33,540 @@
namespace internal {
+void FlowGraph::AppendInstruction(AstNode* instruction) {
+ ASSERT(instruction != NULL);
+ if (is_empty() || !exit()->IsBlockNode()) {
+ AppendNode(new BlockNode());
+ }
+ BlockNode::cast(exit())->AddInstruction(instruction);
+}
+
+
+void FlowGraph::AppendNode(Node* node) {
+ ASSERT(node != NULL);
+ if (is_empty()) {
+ entry_ = exit_ = node;
+ } else {
+ exit()->AddSuccessor(node);
+ node->AddPredecessor(exit());
+ exit_ = node;
+ }
+}
+
+
+void FlowGraph::AppendGraph(FlowGraph* graph) {
+ ASSERT(!graph->is_empty());
+ if (is_empty()) {
+ entry_ = graph->entry();
+ exit_ = graph->exit();
+ } else {
+ exit()->AddSuccessor(graph->entry());
+ graph->entry()->AddPredecessor(exit());
+ exit_ = graph->exit();
+ }
+}
+
+
+void FlowGraph::Split(BranchNode* branch,
+ FlowGraph* left,
+ FlowGraph* right,
+ JoinNode* merge) {
+ // Graphs are in edge split form. Add empty blocks if necessary.
+ if (left->is_empty()) left->AppendNode(new BlockNode());
+ if (right->is_empty()) right->AppendNode(new BlockNode());
+
+ // Add the branch, left flowgraph and merge.
+ AppendNode(branch);
+ AppendGraph(left);
+ AppendNode(merge);
+
+ // Splice in the right flowgraph.
+ right->AppendNode(merge);
+ branch->AddSuccessor(right->entry());
+ right->entry()->AddPredecessor(branch);
+}
+
+
+void FlowGraph::Loop(JoinNode* merge,
+ FlowGraph* condition,
+ BranchNode* branch,
+ FlowGraph* body) {
+ // Add the merge, condition and branch. Add merge's predecessors in
+ // left-to-right order.
+ AppendNode(merge);
+ body->AppendNode(merge);
+ AppendGraph(condition);
+ AppendNode(branch);
+
+ // Splice in the body flowgraph.
+ branch->AddSuccessor(body->entry());
+ body->entry()->AddPredecessor(branch);
+}
+
+
+void EntryNode::Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder) {
+ ASSERT(successor_ != NULL);
+ preorder->Add(this);
+ if (!successor_->IsMarkedWith(mark)) {
+ successor_->MarkWith(mark);
+ successor_->Traverse(mark, preorder, postorder);
+ }
+ postorder->Add(this);
+}
+
+
+void ExitNode::Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder) {
+ preorder->Add(this);
+ postorder->Add(this);
+}
+
+
+void BlockNode::Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder) {
+ ASSERT(successor_ != NULL);
+ preorder->Add(this);
+ if (!successor_->IsMarkedWith(mark)) {
+ successor_->MarkWith(mark);
+ successor_->Traverse(mark, preorder, postorder);
+ }
+ postorder->Add(this);
+}
+
+
+void BranchNode::Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder) {
+ ASSERT(successor0_ != NULL && successor1_ != NULL);
+ preorder->Add(this);
+ if (!successor0_->IsMarkedWith(mark)) {
+ successor0_->MarkWith(mark);
+ successor0_->Traverse(mark, preorder, postorder);
+ }
+ if (!successor1_->IsMarkedWith(mark)) {
+ successor1_->MarkWith(mark);
+ successor1_->Traverse(mark, preorder, postorder);
+ }
+ postorder->Add(this);
+}
+
+
+void JoinNode::Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder) {
+ ASSERT(successor_ != NULL);
+ preorder->Add(this);
+ if (!successor_->IsMarkedWith(mark)) {
+ successor_->MarkWith(mark);
+ successor_->Traverse(mark, preorder, postorder);
+ }
+ postorder->Add(this);
+}
+
+
+void FlowGraphBuilder::Build(FunctionLiteral* lit) {
+ graph_ = FlowGraph::Empty();
+ graph_.AppendNode(new EntryNode());
+ global_exit_ = new ExitNode();
+ VisitStatements(lit->body());
+
+ if (HasStackOverflow()) {
+ graph_ = FlowGraph::Empty();
+ return;
+ }
+
+ graph_.AppendNode(global_exit_);
+
+ // Build preorder and postorder traversal orders. All the nodes in
+ // the graph have the same mark flag. For the traversal, use that
+ // flag's negation. Traversal will flip all the flags.
+ bool mark = graph_.entry()->IsMarkedWith(false);
+ graph_.entry()->MarkWith(mark);
+ graph_.entry()->Traverse(mark, &preorder_, &postorder_);
+}
+
+
+void FlowGraphBuilder::VisitDeclaration(Declaration* decl) {
+ UNREACHABLE();
+}
+
+
+void FlowGraphBuilder::VisitBlock(Block* stmt) {
+ VisitStatements(stmt->statements());
+}
+
+
+void FlowGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void FlowGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
+ // Nothing to do.
+}
+
+
+void FlowGraphBuilder::VisitIfStatement(IfStatement* stmt) {
+ Visit(stmt->condition());
+
+ BranchNode* branch = new BranchNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->then_statement());
+
+ FlowGraph left = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->else_statement());
+
+ JoinNode* join = new JoinNode();
+ original.Split(branch, &left, &graph_, join);
+ graph_ = original;
+}
+
+
+void FlowGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
+ SetStackOverflow();
+}
+
+
+void FlowGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
+ SetStackOverflow();
+}
+
+
+void FlowGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
+ Visit(stmt->expression());
+ graph_.AppendInstruction(stmt);
+ graph_.AppendNode(global_exit());
+}
+
+
+void FlowGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
+ Visit(stmt->expression());
+ graph_.AppendInstruction(stmt);
+}
+
+
+void FlowGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
+ graph_.AppendInstruction(stmt);
+}
+
+
+void FlowGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
+ SetStackOverflow();
+}
+
+
+void FlowGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ JoinNode* join = new JoinNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->body());
+
+ FlowGraph body = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->cond());
+
+ BranchNode* branch = new BranchNode();
+
+ // Add body, condition and branch.
+ original.AppendNode(join);
+ original.AppendGraph(&body);
+ original.AppendGraph(&graph_); // The condition.
+ original.AppendNode(branch);
+
+ // Tie the knot.
+ branch->AddSuccessor(join);
+ join->AddPredecessor(branch);
+
+ graph_ = original;
+}
+
+
+void FlowGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
+ JoinNode* join = new JoinNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->cond());
+
+ BranchNode* branch = new BranchNode();
+ FlowGraph condition = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->body());
+
+ original.Loop(join, &condition, branch, &graph_);
+ graph_ = original;
+}
+
+
+void FlowGraphBuilder::VisitForStatement(ForStatement* stmt) {
+ if (stmt->init() != NULL) Visit(stmt->init());
+
+ JoinNode* join = new JoinNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ if (stmt->cond() != NULL) Visit(stmt->cond());
+
+ BranchNode* branch = new BranchNode();
+ FlowGraph condition = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->body());
+
+ if (stmt->next() != NULL) Visit(stmt->next());
+
+ original.Loop(join, &condition, branch, &graph_);
+ graph_ = original;
+}
+
+
+void FlowGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
+ Visit(stmt->enumerable());
+
+ JoinNode* join = new JoinNode();
+ FlowGraph empty;
+ BranchNode* branch = new BranchNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(stmt->body());
+
+ original.Loop(join, &empty, branch, &graph_);
+ graph_ = original;
+}
+
+
+void FlowGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ SetStackOverflow();
+}
+
+
+void FlowGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ SetStackOverflow();
+}
+
+
+void FlowGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ graph_.AppendInstruction(stmt);
+}
+
+
+void FlowGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitConditional(Conditional* expr) {
+ Visit(expr->condition());
+
+ BranchNode* branch = new BranchNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(expr->then_expression());
+
+ FlowGraph left = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(expr->else_expression());
+
+ JoinNode* join = new JoinNode();
+ original.Split(branch, &left, &graph_, join);
+ graph_ = original;
+}
+
+
+void FlowGraphBuilder::VisitSlot(Slot* expr) {
+ UNREACHABLE();
+}
+
+
+void FlowGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitLiteral(Literal* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
+ ZoneList<ObjectLiteral::Property*>* properties = expr->properties();
+ for (int i = 0, len = properties->length(); i < len; i++) {
+ Visit(properties->at(i)->value());
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
+ ZoneList<Expression*>* values = expr->values();
+ for (int i = 0, len = values->length(); i < len; i++) {
+ Visit(values->at(i));
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitAssignment(Assignment* expr) {
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ Property* prop = expr->target()->AsProperty();
+ // Left-hand side can be a variable or property (or reference error) but
+ // not both.
+ ASSERT(var == NULL || prop == NULL);
+ if (var != NULL) {
+ Visit(expr->value());
+ if (var->IsStackAllocated()) definitions_.Add(expr);
+
+ } else if (prop != NULL) {
+ Visit(prop->obj());
+ if (!prop->key()->IsPropertyName()) Visit(prop->key());
+ Visit(expr->value());
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitThrow(Throw* expr) {
+ Visit(expr->exception());
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitProperty(Property* expr) {
+ Visit(expr->obj());
+ if (!expr->key()->IsPropertyName()) Visit(expr->key());
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitCall(Call* expr) {
+ Visit(expr->expression());
+ ZoneList<Expression*>* arguments = expr->arguments();
+ for (int i = 0, len = arguments->length(); i < len; i++) {
+ Visit(arguments->at(i));
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitCallNew(CallNew* expr) {
+ Visit(expr->expression());
+ ZoneList<Expression*>* arguments = expr->arguments();
+ for (int i = 0, len = arguments->length(); i < len; i++) {
+ Visit(arguments->at(i));
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
+ ZoneList<Expression*>* arguments = expr->arguments();
+ for (int i = 0, len = arguments->length(); i < len; i++) {
+ Visit(arguments->at(i));
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
+ Visit(expr->expression());
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitCountOperation(CountOperation* expr) {
+ Visit(expr->expression());
+ Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
+ if (var != NULL && var->IsStackAllocated()) {
+ definitions_.Add(expr);
+ }
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
+ Visit(expr->left());
+
+ switch (expr->op()) {
+ case Token::COMMA:
+ Visit(expr->right());
+ break;
+
+ case Token::OR: {
+ BranchNode* branch = new BranchNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(expr->right());
+ FlowGraph empty;
+ JoinNode* join = new JoinNode();
+ original.Split(branch, &empty, &graph_, join);
+ graph_ = original;
+ break;
+ }
+
+ case Token::AND: {
+ BranchNode* branch = new BranchNode();
+ FlowGraph original = graph_;
+ graph_ = FlowGraph::Empty();
+ Visit(expr->right());
+ FlowGraph empty;
+ JoinNode* join = new JoinNode();
+ original.Split(branch, &graph_, &empty, join);
+ graph_ = original;
+ break;
+ }
+
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SHL:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ Visit(expr->right());
+ graph_.AppendInstruction(expr);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FlowGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
+ Visit(expr->left());
+ Visit(expr->right());
+ graph_.AppendInstruction(expr);
+}
+
+
+void FlowGraphBuilder::VisitThisFunction(ThisFunction* expr) {
+ graph_.AppendInstruction(expr);
+}
+
+
void AstLabeler::Label(CompilationInfo* info) {
info_ = info;
VisitStatements(info_->function()->body());
@@ -204,6 +738,9 @@
USE(proxy);
ASSERT(proxy != NULL && proxy->var()->is_this());
info()->set_has_this_properties(true);
+
+ prop->obj()->set_num(AstNode::kNoNumber);
+ prop->key()->set_num(AstNode::kNoNumber);
Visit(expr->value());
expr->set_num(next_number_++);
}
@@ -220,6 +757,9 @@
USE(proxy);
ASSERT(proxy != NULL && proxy->var()->is_this());
info()->set_has_this_properties(true);
+
+ expr->obj()->set_num(AstNode::kNoNumber);
+ expr->key()->set_num(AstNode::kNoNumber);
expr->set_num(next_number_++);
}
@@ -558,4 +1098,370 @@
}
+#ifdef DEBUG
+
+// Print a textual representation of an instruction in a flow graph. Using
+// the AstVisitor is overkill because there is no recursion here. It is
+// only used for printing in debug mode.
+class TextInstructionPrinter: public AstVisitor {
+ public:
+ TextInstructionPrinter() {}
+
+ private:
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ DISALLOW_COPY_AND_ASSIGN(TextInstructionPrinter);
+};
+
+
+void TextInstructionPrinter::VisitDeclaration(Declaration* decl) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitBlock(Block* stmt) {
+ PrintF("Block");
+}
+
+
+void TextInstructionPrinter::VisitExpressionStatement(
+ ExpressionStatement* stmt) {
+ PrintF("ExpressionStatement");
+}
+
+
+void TextInstructionPrinter::VisitEmptyStatement(EmptyStatement* stmt) {
+ PrintF("EmptyStatement");
+}
+
+
+void TextInstructionPrinter::VisitIfStatement(IfStatement* stmt) {
+ PrintF("IfStatement");
+}
+
+
+void TextInstructionPrinter::VisitContinueStatement(ContinueStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitBreakStatement(BreakStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitReturnStatement(ReturnStatement* stmt) {
+ PrintF("return @%d", stmt->expression()->num());
+}
+
+
+void TextInstructionPrinter::VisitWithEnterStatement(WithEnterStatement* stmt) {
+ PrintF("WithEnterStatement");
+}
+
+
+void TextInstructionPrinter::VisitWithExitStatement(WithExitStatement* stmt) {
+ PrintF("WithExitStatement");
+}
+
+
+void TextInstructionPrinter::VisitSwitchStatement(SwitchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ PrintF("DoWhileStatement");
+}
+
+
+void TextInstructionPrinter::VisitWhileStatement(WhileStatement* stmt) {
+ PrintF("WhileStatement");
+}
+
+
+void TextInstructionPrinter::VisitForStatement(ForStatement* stmt) {
+ PrintF("ForStatement");
+}
+
+
+void TextInstructionPrinter::VisitForInStatement(ForInStatement* stmt) {
+ PrintF("ForInStatement");
+}
+
+
+void TextInstructionPrinter::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitTryFinallyStatement(
+ TryFinallyStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ PrintF("DebuggerStatement");
+}
+
+
+void TextInstructionPrinter::VisitFunctionLiteral(FunctionLiteral* expr) {
+ PrintF("FunctionLiteral");
+}
+
+
+void TextInstructionPrinter::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ PrintF("FunctionBoilerplateLiteral");
+}
+
+
+void TextInstructionPrinter::VisitConditional(Conditional* expr) {
+ PrintF("Conditional");
+}
+
+
+void TextInstructionPrinter::VisitSlot(Slot* expr) {
+ UNREACHABLE();
+}
+
+
+void TextInstructionPrinter::VisitVariableProxy(VariableProxy* expr) {
+ Variable* var = expr->AsVariable();
+ if (var != NULL) {
+ SmartPointer<char> name = var->name()->ToCString();
+ PrintF("%s", *name);
+ } else {
+ ASSERT(expr->AsProperty() != NULL);
+ VisitProperty(expr->AsProperty());
+ }
+}
+
+
+void TextInstructionPrinter::VisitLiteral(Literal* expr) {
+ expr->handle()->ShortPrint();
+}
+
+
+void TextInstructionPrinter::VisitRegExpLiteral(RegExpLiteral* expr) {
+ PrintF("RegExpLiteral");
+}
+
+
+void TextInstructionPrinter::VisitObjectLiteral(ObjectLiteral* expr) {
+ PrintF("ObjectLiteral");
+}
+
+
+void TextInstructionPrinter::VisitArrayLiteral(ArrayLiteral* expr) {
+ PrintF("ArrayLiteral");
+}
+
+
+void TextInstructionPrinter::VisitCatchExtensionObject(
+ CatchExtensionObject* expr) {
+ PrintF("CatchExtensionObject");
+}
+
+
+void TextInstructionPrinter::VisitAssignment(Assignment* expr) {
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ Property* prop = expr->target()->AsProperty();
+
+ if (var != NULL) {
+ SmartPointer<char> name = var->name()->ToCString();
+ PrintF("%s %s @%d",
+ *name,
+ Token::String(expr->op()),
+ expr->value()->num());
+ } else if (prop != NULL) {
+ if (prop->key()->IsPropertyName()) {
+ PrintF("@%d.", prop->obj()->num());
+ ASSERT(prop->key()->AsLiteral() != NULL);
+ prop->key()->AsLiteral()->handle()->Print();
+ PrintF(" %s @%d",
+ Token::String(expr->op()),
+ expr->value()->num());
+ } else {
+ PrintF("@%d[@%d] %s @%d",
+ prop->obj()->num(),
+ prop->key()->num(),
+ Token::String(expr->op()),
+ expr->value()->num());
+ }
+ } else {
+ // Throw reference error.
+ Visit(expr->target());
+ }
+}
+
+
+void TextInstructionPrinter::VisitThrow(Throw* expr) {
+ PrintF("throw @%d", expr->exception()->num());
+}
+
+
+void TextInstructionPrinter::VisitProperty(Property* expr) {
+ if (expr->key()->IsPropertyName()) {
+ PrintF("@%d.", expr->obj()->num());
+ ASSERT(expr->key()->AsLiteral() != NULL);
+ expr->key()->AsLiteral()->handle()->Print();
+ } else {
+ PrintF("@%d[@%d]", expr->obj()->num(), expr->key()->num());
+ }
+}
+
+
+void TextInstructionPrinter::VisitCall(Call* expr) {
+ PrintF("@%d(", expr->expression()->num());
+ ZoneList<Expression*>* arguments = expr->arguments();
+ for (int i = 0, len = arguments->length(); i < len; i++) {
+ if (i != 0) PrintF(", ");
+ PrintF("@%d", arguments->at(i)->num());
+ }
+ PrintF(")");
+}
+
+
+void TextInstructionPrinter::VisitCallNew(CallNew* expr) {
+ PrintF("new @%d(", expr->expression()->num());
+ ZoneList<Expression*>* arguments = expr->arguments();
+ for (int i = 0, len = arguments->length(); i < len; i++) {
+ if (i != 0) PrintF(", ");
+ PrintF("@%d", arguments->at(i)->num());
+ }
+ PrintF(")");
+}
+
+
+void TextInstructionPrinter::VisitCallRuntime(CallRuntime* expr) {
+ SmartPointer<char> name = expr->name()->ToCString();
+ PrintF("%s(", *name);
+ ZoneList<Expression*>* arguments = expr->arguments();
+ for (int i = 0, len = arguments->length(); i < len; i++) {
+ if (i != 0) PrintF(", ");
+ PrintF("@%d", arguments->at(i)->num());
+ }
+ PrintF(")");
+}
+
+
+void TextInstructionPrinter::VisitUnaryOperation(UnaryOperation* expr) {
+ PrintF("%s(@%d)", Token::String(expr->op()), expr->expression()->num());
+}
+
+
+void TextInstructionPrinter::VisitCountOperation(CountOperation* expr) {
+ if (expr->is_prefix()) {
+ PrintF("%s@%d", Token::String(expr->op()), expr->expression()->num());
+ } else {
+ PrintF("@%d%s", expr->expression()->num(), Token::String(expr->op()));
+ }
+}
+
+
+void TextInstructionPrinter::VisitBinaryOperation(BinaryOperation* expr) {
+ ASSERT(expr->op() != Token::COMMA);
+ ASSERT(expr->op() != Token::OR);
+ ASSERT(expr->op() != Token::AND);
+ PrintF("@%d %s @%d",
+ expr->left()->num(),
+ Token::String(expr->op()),
+ expr->right()->num());
+}
+
+
+void TextInstructionPrinter::VisitCompareOperation(CompareOperation* expr) {
+ PrintF("@%d %s @%d",
+ expr->left()->num(),
+ Token::String(expr->op()),
+ expr->right()->num());
+}
+
+
+void TextInstructionPrinter::VisitThisFunction(ThisFunction* expr) {
+ PrintF("ThisFunction");
+}
+
+
+static int node_count = 0;
+static int instruction_count = 0;
+
+
+void Node::AssignNumbers() {
+ set_number(node_count++);
+}
+
+
+void BlockNode::AssignNumbers() {
+ set_number(node_count++);
+ for (int i = 0, len = instructions_.length(); i < len; i++) {
+ instructions_[i]->set_num(instruction_count++);
+ }
+}
+
+
+void EntryNode::PrintText() {
+ PrintF("L%d: Entry\n", number());
+ PrintF("goto L%d\n\n", successor_->number());
+}
+
+void ExitNode::PrintText() {
+ PrintF("L%d: Exit\n\n", number());
+}
+
+
+void BlockNode::PrintText() {
+ // Print the instructions in the block.
+ PrintF("L%d: Block\n", number());
+ TextInstructionPrinter printer;
+ for (int i = 0, len = instructions_.length(); i < len; i++) {
+ PrintF("%d ", instructions_[i]->num());
+ printer.Visit(instructions_[i]);
+ PrintF("\n");
+ }
+ PrintF("goto L%d\n\n", successor_->number());
+}
+
+
+void BranchNode::PrintText() {
+ PrintF("L%d: Branch\n", number());
+ PrintF("goto (L%d, L%d)\n\n", successor0_->number(), successor1_->number());
+}
+
+
+void JoinNode::PrintText() {
+ PrintF("L%d: Join(", number());
+ for (int i = 0, len = predecessors_.length(); i < len; i++) {
+ if (i != 0) PrintF(", ");
+ PrintF("L%d", predecessors_[i]->number());
+ }
+ PrintF(")\ngoto L%d\n\n", successor_->number());
+}
+
+
+void FlowGraph::PrintText(ZoneList<Node*>* postorder) {
+ PrintF("\n========\n");
+
+ // Number nodes and instructions in reverse postorder.
+ node_count = 0;
+ instruction_count = 0;
+ for (int i = postorder->length() - 1; i >= 0; i--) {
+ postorder->at(i)->AssignNumbers();
+ }
+
+ // Print basic blocks in reverse postorder.
+ for (int i = postorder->length() - 1; i >= 0; i--) {
+ postorder->at(i)->PrintText();
+ }
+}
+
+
+#endif // defined(DEBUG)
+
+
} } // namespace v8::internal
diff --git a/src/data-flow.h b/src/data-flow.h
index 2331944..2dc2d73 100644
--- a/src/data-flow.h
+++ b/src/data-flow.h
@@ -28,12 +28,403 @@
#ifndef V8_DATAFLOW_H_
#define V8_DATAFLOW_H_
+#include "v8.h"
+
#include "ast.h"
#include "compiler.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
+class BitVector: public ZoneObject {
+ public:
+ explicit BitVector(int length)
+ : length_(length),
+ data_length_(SizeFor(length)),
+ data_(Zone::NewArray<uint32_t>(data_length_)) {
+ ASSERT(length > 0);
+ Clear();
+ }
+
+ BitVector(const BitVector& other)
+ : length_(other.length()),
+ data_length_(SizeFor(length_)),
+ data_(Zone::NewArray<uint32_t>(data_length_)) {
+ CopyFrom(other);
+ }
+
+ static int SizeFor(int length) {
+ return 1 + ((length - 1) / 32);
+ }
+
+ BitVector& operator=(const BitVector& rhs) {
+ if (this != &rhs) CopyFrom(rhs);
+ return *this;
+ }
+
+ void CopyFrom(const BitVector& other) {
+ ASSERT(other.length() == length());
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] = other.data_[i];
+ }
+ }
+
+ bool Contains(int i) {
+ ASSERT(i >= 0 && i < length());
+ uint32_t block = data_[i / 32];
+ return (block & (1U << (i % 32))) != 0;
+ }
+
+ void Add(int i) {
+ ASSERT(i >= 0 && i < length());
+ data_[i / 32] |= (1U << (i % 32));
+ }
+
+ void Remove(int i) {
+ ASSERT(i >= 0 && i < length());
+ data_[i / 32] &= ~(1U << (i % 32));
+ }
+
+ void Union(const BitVector& other) {
+ ASSERT(other.length() == length());
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] |= other.data_[i];
+ }
+ }
+
+ void Intersect(const BitVector& other) {
+ ASSERT(other.length() == length());
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] &= other.data_[i];
+ }
+ }
+
+ void Clear() {
+ for (int i = 0; i < data_length_; i++) {
+ data_[i] = 0;
+ }
+ }
+
+ bool IsEmpty() const {
+ for (int i = 0; i < data_length_; i++) {
+ if (data_[i] != 0) return false;
+ }
+ return true;
+ }
+
+ int length() const { return length_; }
+
+ private:
+ int length_;
+ int data_length_;
+ uint32_t* data_;
+};
+
+
+// Forward declarations of Node types.
+class Node;
+class BranchNode;
+class JoinNode;
+
+// Flow graphs have a single entry and single exit. The empty flowgraph is
+// represented by both entry and exit being NULL.
+class FlowGraph BASE_EMBEDDED {
+ public:
+ FlowGraph() : entry_(NULL), exit_(NULL) {}
+
+ static FlowGraph Empty() { return FlowGraph(); }
+
+ bool is_empty() const { return entry_ == NULL; }
+ Node* entry() const { return entry_; }
+ Node* exit() const { return exit_; }
+
+ // Add a single instruction to the end of this flowgraph.
+ void AppendInstruction(AstNode* instruction);
+
+ // Add a single node to the end of this flow graph.
+ void AppendNode(Node* node);
+
+ // Add a flow graph fragment to the end of this one.
+ void AppendGraph(FlowGraph* graph);
+
+ // Concatenate an if-then-else flow-graph to this one. Control is split
+ // and merged, so the graph remains single-entry, single-exit.
+ void Split(BranchNode* branch,
+ FlowGraph* left,
+ FlowGraph* right,
+ JoinNode* merge);
+
+ // Concatenate a forward loop (e.g., while or for loop) flow-graph to this
+ // one. Control is split by the condition and merged back from the back
+ // edge at end of the body to the beginning of the condition. The single
+ // (free) exit of the result graph is the right (false) arm of the branch
+ // node.
+ void Loop(JoinNode* merge,
+ FlowGraph* condition,
+ BranchNode* branch,
+ FlowGraph* body);
+
+#ifdef DEBUG
+ void PrintText(ZoneList<Node*>* postorder);
+#endif
+
+ private:
+ Node* entry_;
+ Node* exit_;
+};
+
+
+// Flow-graph nodes.
+class Node: public ZoneObject {
+ public:
+ Node() : number_(-1), mark_(false) {}
+
+ virtual ~Node() {}
+
+ virtual bool IsBlockNode() { return false; }
+ virtual bool IsJoinNode() { return false; }
+
+ virtual void AddPredecessor(Node* predecessor) = 0;
+ virtual void AddSuccessor(Node* successor) = 0;
+
+ bool IsMarkedWith(bool mark) { return mark_ == mark; }
+ void MarkWith(bool mark) { mark_ = mark; }
+
+ // Perform a depth first search and record preorder and postorder
+ // traversal orders.
+ virtual void Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder) = 0;
+
+ int number() { return number_; }
+ void set_number(int number) { number_ = number; }
+
+#ifdef DEBUG
+ virtual void AssignNumbers();
+ virtual void PrintText() = 0;
+#endif
+
+ private:
+ int number_;
+ bool mark_;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+};
+
+
+// An entry node has no predecessors and a single successor.
+class EntryNode: public Node {
+ public:
+ EntryNode() : successor_(NULL) {}
+
+ void AddPredecessor(Node* predecessor) { UNREACHABLE(); }
+
+ void AddSuccessor(Node* successor) {
+ ASSERT(successor_ == NULL && successor != NULL);
+ successor_ = successor;
+ }
+
+ void Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder);
+
+#ifdef DEBUG
+ void PrintText();
+#endif
+
+ private:
+ Node* successor_;
+
+ DISALLOW_COPY_AND_ASSIGN(EntryNode);
+};
+
+
+// An exit node has a arbitrarily many predecessors and no successors.
+class ExitNode: public Node {
+ public:
+ ExitNode() : predecessors_(4) {}
+
+ void AddPredecessor(Node* predecessor) {
+ ASSERT(predecessor != NULL);
+ predecessors_.Add(predecessor);
+ }
+
+ void AddSuccessor(Node* successor) { /* Do nothing. */ }
+
+ void Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder);
+
+#ifdef DEBUG
+ void PrintText();
+#endif
+
+ private:
+ ZoneList<Node*> predecessors_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExitNode);
+};
+
+
+// Block nodes have a single successor and predecessor and a list of
+// instructions.
+class BlockNode: public Node {
+ public:
+ BlockNode() : predecessor_(NULL), successor_(NULL), instructions_(4) {}
+
+ static BlockNode* cast(Node* node) {
+ ASSERT(node->IsBlockNode());
+ return reinterpret_cast<BlockNode*>(node);
+ }
+
+ bool IsBlockNode() { return true; }
+
+ void AddPredecessor(Node* predecessor) {
+ ASSERT(predecessor_ == NULL && predecessor != NULL);
+ predecessor_ = predecessor;
+ }
+
+ void AddSuccessor(Node* successor) {
+ ASSERT(successor_ == NULL && successor != NULL);
+ successor_ = successor;
+ }
+
+ void AddInstruction(AstNode* instruction) {
+ instructions_.Add(instruction);
+ }
+
+ void Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder);
+
+#ifdef DEBUG
+ void AssignNumbers();
+ void PrintText();
+#endif
+
+ private:
+ Node* predecessor_;
+ Node* successor_;
+ ZoneList<AstNode*> instructions_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlockNode);
+};
+
+
+// Branch nodes have a single predecessor and a pair of successors.
+class BranchNode: public Node {
+ public:
+ BranchNode() : predecessor_(NULL), successor0_(NULL), successor1_(NULL) {}
+
+ void AddPredecessor(Node* predecessor) {
+ ASSERT(predecessor_ == NULL && predecessor != NULL);
+ predecessor_ = predecessor;
+ }
+
+ void AddSuccessor(Node* successor) {
+ ASSERT(successor1_ == NULL && successor != NULL);
+ if (successor0_ == NULL) {
+ successor0_ = successor;
+ } else {
+ successor1_ = successor;
+ }
+ }
+
+ void Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder);
+
+#ifdef DEBUG
+ void PrintText();
+#endif
+
+ private:
+ Node* predecessor_;
+ Node* successor0_;
+ Node* successor1_;
+
+ DISALLOW_COPY_AND_ASSIGN(BranchNode);
+};
+
+
+// Join nodes have arbitrarily many predecessors and a single successor.
+class JoinNode: public Node {
+ public:
+ JoinNode() : predecessors_(2), successor_(NULL) {}
+
+ static JoinNode* cast(Node* node) {
+ ASSERT(node->IsJoinNode());
+ return reinterpret_cast<JoinNode*>(node);
+ }
+
+ bool IsJoinNode() { return true; }
+
+ void AddPredecessor(Node* predecessor) {
+ ASSERT(predecessor != NULL);
+ predecessors_.Add(predecessor);
+ }
+
+ void AddSuccessor(Node* successor) {
+ ASSERT(successor_ == NULL && successor != NULL);
+ successor_ = successor;
+ }
+
+ void Traverse(bool mark,
+ ZoneList<Node*>* preorder,
+ ZoneList<Node*>* postorder);
+
+#ifdef DEBUG
+ void PrintText();
+#endif
+
+ private:
+ ZoneList<Node*> predecessors_;
+ Node* successor_;
+
+ DISALLOW_COPY_AND_ASSIGN(JoinNode);
+};
+
+
+// Construct a flow graph from a function literal. Build pre- and postorder
+// traversal orders as a byproduct.
+class FlowGraphBuilder: public AstVisitor {
+ public:
+ FlowGraphBuilder()
+ : global_exit_(NULL),
+ preorder_(4),
+ postorder_(4),
+ definitions_(4) {
+ }
+
+ void Build(FunctionLiteral* lit);
+
+ FlowGraph* graph() { return &graph_; }
+
+ ZoneList<Node*>* postorder() { return &postorder_; }
+
+ private:
+ ExitNode* global_exit() { return global_exit_; }
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ FlowGraph graph_;
+ ExitNode* global_exit_;
+ ZoneList<Node*> preorder_;
+ ZoneList<Node*> postorder_;
+
+ // The flow graph builder collects a list of definitions (assignments and
+ // count operations) to stack-allocated variables to use for reaching
+ // definitions analysis.
+ ZoneList<AstNode*> definitions_;
+
+ DISALLOW_COPY_AND_ASSIGN(FlowGraphBuilder);
+};
+
+
// This class is used to number all expressions in the AST according to
// their evaluation order (post-order left-to-right traversal).
class AstLabeler: public AstVisitor {
diff --git a/src/date-delay.js b/src/date-delay.js
index 7d8f458..c0180c2 100644
--- a/src/date-delay.js
+++ b/src/date-delay.js
@@ -113,8 +113,11 @@
// we must do this, but for compatibility with other browsers, we use
// the actual year if it is in the range 1970..2037
if (t >= 0 && t <= 2.1e12) return t;
- var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)), MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
- return TimeClip(MakeDate(day, TimeWithinDay(t)));
+
+ var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)),
+ MONTH_FROM_TIME(t),
+ DATE_FROM_TIME(t));
+ return MakeDate(day, TimeWithinDay(t));
}
@@ -257,14 +260,6 @@
}
-// Compute modified Julian day from year, month, date.
-function ToJulianDay(year, month, date) {
- var jy = (month > 1) ? year : year - 1;
- var jm = (month > 1) ? month + 2 : month + 14;
- var ja = FLOOR(jy / 100);
- return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja);
-}
-
var four_year_cycle_table = CalculateDateTable();
@@ -359,20 +354,18 @@
function MakeDay(year, month, date) {
if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
- // Conversion to integers.
year = TO_INTEGER(year);
month = TO_INTEGER(month);
date = TO_INTEGER(date);
- // Overflow months into year.
- year = year + FLOOR(month/12);
- month = month % 12;
- if (month < 0) {
- month += 12;
+ if (year < kMinYear || year > kMaxYear ||
+ month < kMinMonth || month > kMaxMonth ||
+ date < kMinDate || date > kMaxDate) {
+ return $NaN;
}
- // Return days relative to Jan 1 1970.
- return ToJulianDay(year, month, date) - kDayZeroInJulianDay;
+ // Now we rely on year, month and date being SMIs.
+ return %DateMakeDay(year, month, date);
}
diff --git a/src/debug-delay.js b/src/debug-delay.js
index 55c25a9..5ba5a3b 100644
--- a/src/debug-delay.js
+++ b/src/debug-delay.js
@@ -1251,7 +1251,9 @@
} else if (request.command == 'version') {
this.versionRequest_(request, response);
} else if (request.command == 'profile') {
- this.profileRequest_(request, response);
+ this.profileRequest_(request, response);
+ } else if (request.command == 'changelive') {
+ this.changeLiveRequest_(request, response);
} else {
throw new Error('Unknown command "' + request.command + '" in request');
}
@@ -1954,6 +1956,52 @@
};
+DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response) {
+ if (!Debug.LiveEditChangeScript) {
+ return response.failed('LiveEdit feature is not supported');
+ }
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+ var script_id = request.arguments.script_id;
+ var change_pos = parseInt(request.arguments.change_pos);
+ var change_len = parseInt(request.arguments.change_len);
+ var new_string = request.arguments.new_string;
+ if (!IS_STRING(new_string)) {
+ response.failed('Argument "new_string" is not a string value');
+ return;
+ }
+
+ var scripts = %DebugGetLoadedScripts();
+
+ var the_script = null;
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i].id == script_id) {
+ the_script = scripts[i];
+ }
+ }
+ if (!the_script) {
+ response.failed('Script not found');
+ return;
+ }
+
+ var change_log = new Array();
+ try {
+ Debug.LiveEditChangeScript(the_script, change_pos, change_len, new_string,
+ change_log);
+ } catch (e) {
+ if (e instanceof Debug.LiveEditChangeScript.Failure) {
+ // Let's treat it as a "success" so that body with change_log will be
+ // sent back. "change_log" will have "failure" field set.
+ change_log.push( { failure: true } );
+ } else {
+ throw e;
+ }
+ }
+ response.body = {change_log: change_log};
+};
+
+
// Check whether the previously processed command caused the VM to become
// running.
DebugCommandProcessor.prototype.isRunning = function() {
diff --git a/src/debug.cc b/src/debug.cc
index 8c4f51d..959bea1 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -39,6 +39,7 @@
#include "global-handles.h"
#include "ic.h"
#include "ic-inl.h"
+#include "messages.h"
#include "natives.h"
#include "stub-cache.h"
#include "log.h"
@@ -123,7 +124,9 @@
if (RelocInfo::IsCodeTarget(rmode())) {
Address target = original_rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
- if (code->is_inline_cache_stub() || RelocInfo::IsConstructCall(rmode())) {
+ if ((code->is_inline_cache_stub() &&
+ code->kind() != Code::BINARY_OP_IC) ||
+ RelocInfo::IsConstructCall(rmode())) {
break_point_++;
return;
}
@@ -755,6 +758,12 @@
bool caught_exception =
!CompileDebuggerScript(Natives::GetIndex("mirror")) ||
!CompileDebuggerScript(Natives::GetIndex("debug"));
+
+ if (FLAG_enable_liveedit) {
+ caught_exception = caught_exception ||
+ !CompileDebuggerScript(Natives::GetIndex("liveedit"));
+ }
+
Debugger::set_compiling_natives(false);
// Make sure we mark the debugger as not loading before we might
@@ -1337,24 +1346,26 @@
// Find the builtin debug break function matching the calling convention
// used by the call site.
if (code->is_inline_cache_stub()) {
- if (code->is_call_stub()) {
- return ComputeCallDebugBreak(code->arguments_count());
- }
- if (code->is_load_stub()) {
- return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
- }
- if (code->is_store_stub()) {
- return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
- }
- if (code->is_keyed_load_stub()) {
- Handle<Code> result =
- Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
- return result;
- }
- if (code->is_keyed_store_stub()) {
- Handle<Code> result =
- Handle<Code>(Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
- return result;
+ switch (code->kind()) {
+ case Code::CALL_IC:
+ return ComputeCallDebugBreak(code->arguments_count());
+
+ case Code::LOAD_IC:
+ return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
+
+ case Code::STORE_IC:
+ return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
+
+ case Code::KEYED_LOAD_IC:
+ return Handle<Code>(
+ Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
+
+ case Code::KEYED_STORE_IC:
+ return Handle<Code>(
+ Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
+
+ default:
+ UNREACHABLE();
}
}
if (RelocInfo::IsConstructCall(mode)) {
@@ -1959,7 +1970,8 @@
// Handle debugger actions when a new script is compiled.
-void Debugger::OnAfterCompile(Handle<Script> script, Handle<JSFunction> fun) {
+void Debugger::OnAfterCompile(Handle<Script> script,
+ AfterCompileFlags after_compile_flags) {
HandleScope scope;
// Add the newly compiled script to the script cache.
@@ -2006,7 +2018,7 @@
return;
}
// Bail out based on state or if there is no listener for this event
- if (in_debugger) return;
+ if (in_debugger && (after_compile_flags & SEND_WHEN_DEBUGGING) == 0) return;
if (!Debugger::EventActive(v8::AfterCompile)) return;
// Create the compile state object.
diff --git a/src/debug.h b/src/debug.h
index cab9e8e..546512b 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -604,8 +604,13 @@
static void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
static void OnException(Handle<Object> exception, bool uncaught);
static void OnBeforeCompile(Handle<Script> script);
+
+ enum AfterCompileFlags {
+ NO_AFTER_COMPILE_FLAGS,
+ SEND_WHEN_DEBUGGING
+ };
static void OnAfterCompile(Handle<Script> script,
- Handle<JSFunction> fun);
+ AfterCompileFlags after_compile_flags);
static void OnNewFunction(Handle<JSFunction> fun);
static void OnScriptCollected(int id);
static void ProcessDebugEvent(v8::DebugEvent event,
diff --git a/src/factory.h b/src/factory.h
index 2a347cd..36911da 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -30,11 +30,12 @@
#include "globals.h"
#include "heap.h"
-#include "zone-inl.h"
namespace v8 {
namespace internal {
+// Forward declarations.
+class ZoneScopeInfo;
// Interface for handle based allocation.
diff --git a/src/fast-codegen.h b/src/fast-codegen.h
index e96daf6..a0282bb 100644
--- a/src/fast-codegen.h
+++ b/src/fast-codegen.h
@@ -93,6 +93,7 @@
Register accumulator1();
Register scratch0();
Register scratch1();
+ Register scratch2();
Register receiver_reg();
Register context_reg();
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 6e22d5b..573d893 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -153,6 +153,9 @@
"try to use the speculative optimizing backend for all code")
DEFINE_bool(trace_bailout, false,
"print reasons for falling back to using the classic V8 backend")
+DEFINE_bool(safe_int32_compiler, false,
+ "enable optimized side-effect-free int32 expressions.")
+DEFINE_bool(use_flow_graph, false, "perform flow-graph based optimizations")
// compilation-cache.cc
DEFINE_bool(compilation_cache, true, "enable compilation cache")
@@ -163,6 +166,7 @@
DEFINE_bool(debugger_auto_break, true,
"automatically set the debug break flag when debugger commands are "
"in the queue")
+DEFINE_bool(enable_liveedit, true, "enable liveedit experimental feature")
// frames.cc
DEFINE_int(max_stack_trace_source_length, 300,
@@ -230,9 +234,6 @@
DEFINE_bool(preallocate_message_memory, false,
"preallocate some memory to build stack traces.")
-// usage-analyzer.cc
-DEFINE_bool(usage_computation, true, "compute variable usage counts")
-
// v8.cc
DEFINE_bool(preemption, false,
"activate a 100ms timer that switches between V8 threads")
@@ -304,6 +305,8 @@
DEFINE_bool(print_builtin_scopes, false, "print scopes for builtins")
DEFINE_bool(print_scopes, false, "print scopes")
DEFINE_bool(print_ir, false, "print the AST as seen by the backend")
+DEFINE_bool(print_graph_text, false,
+ "print a text representation of the flow graph")
// contexts.cc
DEFINE_bool(trace_contexts, false, "trace contexts operations")
diff --git a/src/frame-element.cc b/src/frame-element.cc
index 1455559..ee7be95 100644
--- a/src/frame-element.cc
+++ b/src/frame-element.cc
@@ -28,6 +28,7 @@
#include "v8.h"
#include "frame-element.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/frame-element.h b/src/frame-element.h
index 5762814..3c5a389 100644
--- a/src/frame-element.h
+++ b/src/frame-element.h
@@ -30,6 +30,7 @@
#include "number-info.h"
#include "macro-assembler.h"
+#include "zone.h"
namespace v8 {
namespace internal {
@@ -53,23 +54,25 @@
SYNCED
};
- inline NumberInfo::Type number_info() {
+ inline NumberInfo number_info() {
// Copied elements do not have number info. Instead
// we have to inspect their backing element in the frame.
ASSERT(!is_copy());
- if (!is_constant()) return NumberInfoField::decode(value_);
+ if (!is_constant()) {
+ return NumberInfo::FromInt(NumberInfoField::decode(value_));
+ }
Handle<Object> value = handle();
- if (value->IsSmi()) return NumberInfo::kSmi;
- if (value->IsHeapNumber()) return NumberInfo::kHeapNumber;
- return NumberInfo::kUnknown;
+ if (value->IsSmi()) return NumberInfo::Smi();
+ if (value->IsHeapNumber()) return NumberInfo::HeapNumber();
+ return NumberInfo::Unknown();
}
- inline void set_number_info(NumberInfo::Type info) {
+ inline void set_number_info(NumberInfo info) {
// Copied elements do not have number info. Instead
// we have to inspect their backing element in the frame.
ASSERT(!is_copy());
value_ = value_ & ~NumberInfoField::mask();
- value_ = value_ | NumberInfoField::encode(info);
+ value_ = value_ | NumberInfoField::encode(info.ToInt());
}
// The default constructor creates an invalid frame element.
@@ -77,7 +80,7 @@
value_ = TypeField::encode(INVALID)
| CopiedField::encode(false)
| SyncedField::encode(false)
- | NumberInfoField::encode(NumberInfo::kUninitialized)
+ | NumberInfoField::encode(NumberInfo::Uninitialized().ToInt())
| DataField::encode(0);
}
@@ -88,7 +91,7 @@
}
// Factory function to construct an in-memory frame element.
- static FrameElement MemoryElement(NumberInfo::Type info) {
+ static FrameElement MemoryElement(NumberInfo info) {
FrameElement result(MEMORY, no_reg, SYNCED, info);
return result;
}
@@ -96,7 +99,7 @@
// Factory function to construct an in-register frame element.
static FrameElement RegisterElement(Register reg,
SyncFlag is_synced,
- NumberInfo::Type info) {
+ NumberInfo info) {
return FrameElement(REGISTER, reg, is_synced, info);
}
@@ -210,11 +213,11 @@
FrameElement(Type type,
Register reg,
SyncFlag is_synced,
- NumberInfo::Type info) {
+ NumberInfo info) {
value_ = TypeField::encode(type)
| CopiedField::encode(false)
| SyncedField::encode(is_synced != NOT_SYNCED)
- | NumberInfoField::encode(info)
+ | NumberInfoField::encode(info.ToInt())
| DataField::encode(reg.code_ > 0 ? reg.code_ : 0);
}
@@ -223,7 +226,7 @@
value_ = TypeField::encode(CONSTANT)
| CopiedField::encode(false)
| SyncedField::encode(is_synced != NOT_SYNCED)
- | NumberInfoField::encode(NumberInfo::kUninitialized)
+ | NumberInfoField::encode(NumberInfo::Uninitialized().ToInt())
| DataField::encode(ConstantList()->length());
ConstantList()->Add(value);
}
@@ -252,8 +255,8 @@
class TypeField: public BitField<Type, 0, 3> {};
class CopiedField: public BitField<bool, 3, 1> {};
class SyncedField: public BitField<bool, 4, 1> {};
- class NumberInfoField: public BitField<NumberInfo::Type, 5, 3> {};
- class DataField: public BitField<uint32_t, 8, 32 - 8> {};
+ class NumberInfoField: public BitField<int, 5, 4> {};
+ class DataField: public BitField<uint32_t, 9, 32 - 9> {};
friend class VirtualFrame;
};
diff --git a/src/frames.cc b/src/frames.cc
index 5d88265..3bf4c93 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -32,7 +32,6 @@
#include "scopeinfo.h"
#include "string-stream.h"
#include "top.h"
-#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/globals.h b/src/globals.h
index 8f6f47c..3840fee 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -261,6 +261,8 @@
class Script;
class Slot;
class Smi;
+template <typename Config, class Allocator = FreeStoreAllocationPolicy>
+ class SplayTree;
class Statement;
class String;
class Struct;
diff --git a/src/handles.cc b/src/handles.cc
index 971c916..c71d92b 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -203,7 +203,7 @@
void FlattenString(Handle<String> string) {
- CALL_HEAP_FUNCTION_VOID(string->TryFlattenIfNotFlat());
+ CALL_HEAP_FUNCTION_VOID(string->TryFlatten());
ASSERT(string->IsFlat());
}
@@ -283,6 +283,12 @@
}
+Handle<Object> GetElement(Handle<Object> obj,
+ uint32_t index) {
+ CALL_HEAP_FUNCTION(Runtime::GetElement(obj, index), Object);
+}
+
+
Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver,
Handle<JSObject> holder,
Handle<String> name,
@@ -362,8 +368,11 @@
}
-Handle<String> SubString(Handle<String> str, int start, int end) {
- CALL_HEAP_FUNCTION(str->SubString(start, end), String);
+Handle<String> SubString(Handle<String> str,
+ int start,
+ int end,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(str->SubString(start, end, pretenure), String);
}
diff --git a/src/handles.h b/src/handles.h
index 90e51fa..0c137a4 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -233,6 +233,9 @@
Handle<Object> GetProperty(Handle<Object> obj,
Handle<Object> key);
+Handle<Object> GetElement(Handle<Object> obj,
+ uint32_t index);
+
Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver,
Handle<JSObject> holder,
Handle<String> name,
@@ -287,7 +290,10 @@
Handle<FixedArray> UnionOfKeys(Handle<FixedArray> first,
Handle<FixedArray> second);
-Handle<String> SubString(Handle<String> str, int start, int end);
+Handle<String> SubString(Handle<String> str,
+ int start,
+ int end,
+ PretenureFlag pretenure = NOT_TENURED);
// Sets the expected number of properties for the function's instances.
diff --git a/src/heap-inl.h b/src/heap-inl.h
index f18bf0f..1acdb2a 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -187,6 +187,18 @@
}
+void Heap::RecordWrites(Address address, int start, int len) {
+ if (new_space_.Contains(address)) return;
+ ASSERT(!new_space_.FromSpaceContains(address));
+ for (int offset = start;
+ offset < start + len * kPointerSize;
+ offset += kPointerSize) {
+ SLOW_ASSERT(Contains(address + offset));
+ Page::SetRSet(address, offset);
+ }
+}
+
+
OldSpace* Heap::TargetSpace(HeapObject* object) {
InstanceType type = object->map()->instance_type();
AllocationSpace space = TargetSpaceId(type);
diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
index 3cb65ee..90544f1 100644
--- a/src/heap-profiler.cc
+++ b/src/heap-profiler.cc
@@ -31,6 +31,7 @@
#include "frames-inl.h"
#include "global-handles.h"
#include "string-stream.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/heap-profiler.h b/src/heap-profiler.h
index c615942..d6f2650 100644
--- a/src/heap-profiler.h
+++ b/src/heap-profiler.h
@@ -28,6 +28,8 @@
#ifndef V8_HEAP_PROFILER_H_
#define V8_HEAP_PROFILER_H_
+#include "zone.h"
+
namespace v8 {
namespace internal {
diff --git a/src/heap.cc b/src/heap.cc
index 9ff57d2..fbe0464 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -46,6 +46,7 @@
#include "arm/regexp-macro-assembler-arm.h"
#endif
+
namespace v8 {
namespace internal {
@@ -371,11 +372,6 @@
}
-void Heap::NotifyContextDisposed() {
- contexts_disposed_++;
-}
-
-
bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {
// The VM is in the GC state until exiting this function.
VMState state(GC);
@@ -545,12 +541,22 @@
VerifySymbolTable();
if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) {
ASSERT(!allocation_allowed_);
+ GCTracer::ExternalScope scope(tracer);
global_gc_prologue_callback_();
}
EnsureFromSpaceIsCommitted();
+
+ // Perform mark-sweep with optional compaction.
if (collector == MARK_COMPACTOR) {
MarkCompact(tracer);
+ }
+ // Always perform a scavenge to make room in new space.
+ Scavenge();
+
+ // Update the old space promotion limits after the scavenge due to
+ // promotions during scavenge.
+ if (collector == MARK_COMPACTOR) {
int old_gen_size = PromotedSpaceSize();
old_gen_promotion_limit_ =
old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3);
@@ -558,12 +564,12 @@
old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2);
old_gen_exhausted_ = false;
}
- Scavenge();
Counters::objs_since_last_young.Set(0);
if (collector == MARK_COMPACTOR) {
DisableAssertNoAllocation allow_allocation;
+ GCTracer::ExternalScope scope(tracer);
GlobalHandles::PostGarbageCollectionProcessing();
}
@@ -578,6 +584,7 @@
if (collector == MARK_COMPACTOR && global_gc_epilogue_callback_) {
ASSERT(!allocation_allowed_);
+ GCTracer::ExternalScope scope(tracer);
global_gc_epilogue_callback_();
}
VerifySymbolTable();
@@ -1209,6 +1216,16 @@
}
+Object* Heap::AllocateCodeCache() {
+ Object* result = AllocateStruct(CODE_CACHE_TYPE);
+ if (result->IsFailure()) return result;
+ CodeCache* code_cache = CodeCache::cast(result);
+ code_cache->set_default_cache(empty_fixed_array());
+ code_cache->set_normal_type_cache(undefined_value());
+ return code_cache;
+}
+
+
const Heap::StringTypeTable Heap::string_type_table[] = {
#define STRING_TYPE_ELEMENT(type, size, name, camel_name) \
{type, size, k##camel_name##MapRootIndex},
@@ -1625,7 +1642,7 @@
if (InitializeNumberStringCache()->IsFailure()) return false;
// Allocate cache for single character strings.
- obj = AllocateFixedArray(String::kMaxAsciiCharCode+1);
+ obj = AllocateFixedArray(String::kMaxAsciiCharCode+1, TENURED);
if (obj->IsFailure()) return false;
set_single_character_string_cache(FixedArray::cast(obj));
@@ -1659,7 +1676,7 @@
// max_semispace_size_ == 8 MB => number_string_cache_size = 16KB.
int number_string_cache_size = max_semispace_size_ / 512;
number_string_cache_size = Max(32, Min(16*KB, number_string_cache_size));
- Object* obj = AllocateFixedArray(number_string_cache_size * 2);
+ Object* obj = AllocateFixedArray(number_string_cache_size * 2, TENURED);
if (!obj->IsFailure()) set_number_string_cache(FixedArray::cast(obj));
return obj;
}
@@ -1982,7 +1999,8 @@
Object* Heap::AllocateSubString(String* buffer,
int start,
- int end) {
+ int end,
+ PretenureFlag pretenure) {
int length = end - start;
if (length == 1) {
@@ -1998,16 +2016,13 @@
}
// Make an attempt to flatten the buffer to reduce access time.
- if (!buffer->IsFlat()) {
- buffer->TryFlatten();
- }
+ buffer->TryFlatten();
Object* result = buffer->IsAsciiRepresentation()
- ? AllocateRawAsciiString(length)
- : AllocateRawTwoByteString(length);
+ ? AllocateRawAsciiString(length, pretenure )
+ : AllocateRawTwoByteString(length, pretenure);
if (result->IsFailure()) return result;
String* string_result = String::cast(result);
-
// Copy the characters into the new object.
if (buffer->IsAsciiRepresentation()) {
ASSERT(string_result->IsAsciiRepresentation());
@@ -2957,6 +2972,18 @@
}
+Object* Heap::AllocateUninitializedFixedArray(int length) {
+ if (length == 0) return empty_fixed_array();
+
+ Object* obj = AllocateRawFixedArray(length);
+ if (obj->IsFailure()) return obj;
+
+ reinterpret_cast<FixedArray*>(obj)->set_map(fixed_array_map());
+ FixedArray::cast(obj)->set_length(length);
+ return obj;
+}
+
+
Object* Heap::AllocateFixedArrayWithHoles(int length) {
if (length == 0) return empty_fixed_array();
Object* result = AllocateRawFixedArray(length);
@@ -2966,18 +2993,17 @@
FixedArray* array = FixedArray::cast(result);
array->set_length(length);
// Initialize body.
- Object* value = the_hole_value();
- for (int index = 0; index < length; index++) {
- ASSERT(!Heap::InNewSpace(value)); // value = the hole
- array->set(index, value, SKIP_WRITE_BARRIER);
- }
+ ASSERT(!Heap::InNewSpace(the_hole_value()));
+ MemsetPointer(HeapObject::RawField(array, FixedArray::kHeaderSize),
+ the_hole_value(),
+ length);
}
return result;
}
-Object* Heap::AllocateHashTable(int length) {
- Object* result = Heap::AllocateFixedArray(length);
+Object* Heap::AllocateHashTable(int length, PretenureFlag pretenure) {
+ Object* result = Heap::AllocateFixedArray(length, pretenure);
if (result->IsFailure()) return result;
reinterpret_cast<Array*>(result)->set_map(hash_table_map());
ASSERT(result->IsHashTable());
@@ -3060,13 +3086,7 @@
static int number_idle_notifications = 0;
static int last_gc_count = gc_count_;
- if (!FLAG_expose_gc && (contexts_disposed_ > 0)) {
- HistogramTimerScope scope(&Counters::gc_context);
- CollectAllGarbage(false);
- ASSERT(contexts_disposed_ == 0);
- return false;
- }
-
+ bool uncommit = true;
bool finished = false;
if (last_gc_count == gc_count_) {
@@ -3077,7 +3097,12 @@
}
if (number_idle_notifications == kIdlesBeforeScavenge) {
- CollectGarbage(0, NEW_SPACE);
+ if (contexts_disposed_ > 0) {
+ HistogramTimerScope scope(&Counters::gc_context);
+ CollectAllGarbage(false);
+ } else {
+ CollectGarbage(0, NEW_SPACE);
+ }
new_space_.Shrink();
last_gc_count = gc_count_;
@@ -3097,10 +3122,29 @@
last_gc_count = gc_count_;
number_idle_notifications = 0;
finished = true;
+
+ } else if (contexts_disposed_ > 0) {
+ if (FLAG_expose_gc) {
+ contexts_disposed_ = 0;
+ } else {
+ HistogramTimerScope scope(&Counters::gc_context);
+ CollectAllGarbage(false);
+ last_gc_count = gc_count_;
+ }
+ // If this is the first idle notification, we reset the
+ // notification count to avoid letting idle notifications for
+ // context disposal garbage collections start a potentially too
+ // aggressive idle GC cycle.
+ if (number_idle_notifications <= 1) {
+ number_idle_notifications = 0;
+ uncommit = false;
+ }
}
- // Uncommit unused memory in new space.
- Heap::UncommitFromSpace();
+ // Make sure that we have no pending context disposals and
+ // conditionally uncommit from space.
+ ASSERT(contexts_disposed_ == 0);
+ if (uncommit) Heap::UncommitFromSpace();
return finished;
}
@@ -4062,6 +4106,7 @@
GCTracer::GCTracer()
: start_time_(0.0),
start_size_(0.0),
+ external_time_(0.0),
gc_count_(0),
full_gc_count_(0),
is_compacting_(false),
@@ -4079,10 +4124,12 @@
GCTracer::~GCTracer() {
if (!FLAG_trace_gc) return;
// Printf ONE line iff flag is set.
- PrintF("%s %.1f -> %.1f MB, %d ms.\n",
- CollectorString(),
- start_size_, SizeOfHeapObjects(),
- static_cast<int>(OS::TimeCurrentMillis() - start_time_));
+ int time = static_cast<int>(OS::TimeCurrentMillis() - start_time_);
+ int external_time = static_cast<int>(external_time_);
+ PrintF("%s %.1f -> %.1f MB, ",
+ CollectorString(), start_size_, SizeOfHeapObjects());
+ if (external_time > 0) PrintF("%d / ", external_time);
+ PrintF("%d ms.\n", time);
#if defined(ENABLE_LOGGING_AND_PROFILING)
Heap::PrintShortHeapStatistics();
diff --git a/src/heap.h b/src/heap.h
index 5084649..eee5a05 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -30,12 +30,15 @@
#include <math.h>
-#include "zone-inl.h"
-
+#include "splay-tree-inl.h"
+#include "v8-counters.h"
namespace v8 {
namespace internal {
+// Forward declarations.
+class ZoneScopeInfo;
+
// Defines all the roots in Heap.
#define UNCONDITIONAL_STRONG_ROOT_LIST(V) \
/* Put the byte array map early. We need it to be in place by the time */ \
@@ -345,6 +348,9 @@
// Allocate a map for the specified function
static Object* AllocateInitialMap(JSFunction* fun);
+ // Allocates an empty code cache.
+ static Object* AllocateCodeCache();
+
// Allocates and fully initializes a String. There are two String
// encodings: ASCII and two byte. One should choose between the three string
// allocation functions based on the encoding of the string buffer used to
@@ -449,9 +455,16 @@
// failed.
// Please note this does not perform a garbage collection.
static Object* AllocateFixedArray(int length, PretenureFlag pretenure);
- // Allocate uninitialized, non-tenured fixed array with length elements.
+ // Allocates a fixed array initialized with undefined values
static Object* AllocateFixedArray(int length);
+ // Allocates an uninitialized fixed array. It must be filled by the caller.
+ //
+ // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+ // failed.
+ // Please note this does not perform a garbage collection.
+ static Object* AllocateUninitializedFixedArray(int length);
+
// Make a copy of src and return it. Returns
// Failure::RetryAfterGC(requested_bytes, space) if the allocation failed.
static Object* CopyFixedArray(FixedArray* src);
@@ -464,7 +477,8 @@
// AllocateHashTable is identical to AllocateFixedArray except
// that the resulting object has hash_table_map as map.
- static Object* AllocateHashTable(int length);
+ static Object* AllocateHashTable(int length,
+ PretenureFlag pretenure = NOT_TENURED);
// Allocate a global (but otherwise uninitialized) context.
static Object* AllocateGlobalContext();
@@ -556,7 +570,8 @@
// Please note this does not perform a garbage collection.
static Object* AllocateSubString(String* buffer,
int start,
- int end);
+ int end,
+ PretenureFlag pretenure = NOT_TENURED);
// Allocate a new external string object, which is backed by a string
// resource that resides outside the V8 heap.
@@ -633,7 +648,7 @@
static void CollectAllGarbage(bool force_compaction);
// Notify the heap that a context has been disposed.
- static void NotifyContextDisposed();
+ static int NotifyContextDisposed() { return ++contexts_disposed_; }
// Utility to invoke the scavenger. This is needed in test code to
// ensure correct callback for weak global handles.
@@ -765,6 +780,9 @@
// Write barrier support for address[offset] = o.
static inline void RecordWrite(Address address, int offset);
+ // Write barrier support for address[start : start + len[ = o.
+ static inline void RecordWrites(Address address, int start, int len);
+
// Given an address occupied by a live code object, return that object.
static Object* FindCodeObject(Address a);
@@ -1518,8 +1536,23 @@
class GCTracer BASE_EMBEDDED {
public:
- GCTracer();
+ // Time spent while in the external scope counts towards the
+ // external time in the tracer and will be reported separately.
+ class ExternalScope BASE_EMBEDDED {
+ public:
+ explicit ExternalScope(GCTracer* tracer) : tracer_(tracer) {
+ start_time_ = OS::TimeCurrentMillis();
+ }
+ ~ExternalScope() {
+ tracer_->external_time_ += OS::TimeCurrentMillis() - start_time_;
+ }
+ private:
+ GCTracer* tracer_;
+ double start_time_;
+ };
+
+ GCTracer();
~GCTracer();
// Sets the collector.
@@ -1553,6 +1586,9 @@
double start_size_; // Size of objects in heap set in constructor.
GarbageCollector collector_; // Type of collector.
+ // Keep track of the amount of time spent in external callbacks.
+ double external_time_;
+
// A count (including this one, eg, the first collection is 1) of the
// number of garbage collections.
int gc_count_;
@@ -1608,6 +1644,7 @@
if (e.in[0] == c.integers[0] &&
e.in[1] == c.integers[1]) {
ASSERT(e.output != NULL);
+ Counters::transcendental_cache_hit.Increment();
return e.output;
}
double answer = Calculate(input);
@@ -1617,6 +1654,7 @@
elements_[hash].in[1] = c.integers[1];
elements_[hash].output = heap_number;
}
+ Counters::transcendental_cache_miss.Increment();
return heap_number;
}
@@ -1657,6 +1695,17 @@
hash ^= hash >> 8;
return (hash & (kCacheSize - 1));
}
+
+ static Address cache_array_address() {
+ // Used to create an external reference.
+ return reinterpret_cast<Address>(caches_);
+ }
+
+ // Allow access to the caches_ array as an ExternalReference.
+ friend class ExternalReference;
+ // Inline implementation of the caching.
+ friend class TranscendentalCacheStub;
+
static TranscendentalCache* caches_[kNumberOfCaches];
Element elements_[kCacheSize];
Type type_;
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index ffcefe0..f13556b 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -753,6 +753,13 @@
}
+void Assembler::cld() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xFC);
+}
+
+
void Assembler::rep_movs() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -761,6 +768,14 @@
}
+void Assembler::rep_stos() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF3);
+ EMIT(0xAB);
+}
+
+
void Assembler::xchg(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1637,6 +1652,13 @@
}
+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_;
@@ -1645,6 +1667,14 @@
}
+void Assembler::fldpi() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xD9);
+ EMIT(0xEB);
+}
+
+
void Assembler::fldz() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1685,6 +1715,14 @@
}
+void Assembler::fst_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xDD);
+ emit_operand(edx, adr);
+}
+
+
void Assembler::fild_s(const Operand& adr) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -2012,6 +2050,17 @@
}
+void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::addsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
@@ -2067,6 +2116,16 @@
}
+void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x51);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::comisd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
@@ -2078,6 +2137,17 @@
}
+void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x2E);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::movdqa(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
@@ -2157,6 +2227,50 @@
emit_sse_operand(dst, 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);
+ emit_sse_operand(dst, 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);
+ emit_sse_operand(dst, 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);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ptest(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x38);
+ EMIT(0x17);
+ emit_sse_operand(dst, src);
+}
+
void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
Register ireg = { reg.code() };
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index 3d7af82..4497e2a 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -93,7 +93,7 @@
struct XMMRegister {
- bool is_valid() const { return 0 <= code_ && code_ < 2; } // currently
+ bool is_valid() const { return 0 <= code_ && code_ < 8; }
int code() const {
ASSERT(is_valid());
return code_;
@@ -542,8 +542,12 @@
void cmov(Condition cc, Register dst, Handle<Object> handle);
void cmov(Condition cc, Register dst, const Operand& src);
+ // Flag management.
+ void cld();
+
// Repetitive string instructions.
void rep_movs();
+ void rep_stos();
// Exchange two registers
void xchg(Register dst, Register src);
@@ -668,6 +672,7 @@
void call(Label* L);
void call(byte* entry, RelocInfo::Mode rmode);
void call(const Operand& adr);
+ void call(const ExternalReference& target);
void call(Handle<Code> code, RelocInfo::Mode rmode);
// Jumps
@@ -683,15 +688,18 @@
// Floating-point operations
void fld(int i);
+ void fstp(int i);
void fld1();
void fldz();
+ void fldpi();
void fld_s(const Operand& adr);
void fld_d(const Operand& adr);
void fstp_s(const Operand& adr);
void fstp_d(const Operand& adr);
+ void fst_d(const Operand& adr);
void fild_s(const Operand& adr);
void fild_d(const Operand& adr);
@@ -750,14 +758,17 @@
void cvttsd2si(Register dst, const Operand& src);
void cvtsi2sd(XMMRegister dst, const Operand& src);
+ void cvtss2sd(XMMRegister dst, XMMRegister src);
void addsd(XMMRegister dst, XMMRegister src);
void subsd(XMMRegister dst, XMMRegister src);
void mulsd(XMMRegister dst, XMMRegister src);
void divsd(XMMRegister dst, XMMRegister src);
void xorpd(XMMRegister dst, XMMRegister src);
+ void sqrtsd(XMMRegister dst, XMMRegister src);
void comisd(XMMRegister dst, XMMRegister src);
+ void ucomisd(XMMRegister dst, XMMRegister src);
void movdqa(XMMRegister dst, const Operand& src);
void movdqa(const Operand& dst, XMMRegister src);
@@ -768,6 +779,12 @@
void movdbl(XMMRegister dst, const Operand& src);
void movdbl(const Operand& dst, XMMRegister src);
+ void movd(XMMRegister dst, const Operand& src);
+ void movsd(XMMRegister dst, XMMRegister src);
+
+ void pxor(XMMRegister dst, XMMRegister src);
+ void ptest(XMMRegister dst, XMMRegister src);
+
// Debugging
void Print();
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 54ef382..80e421b 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -63,10 +63,10 @@
ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
}
- // JumpToRuntime expects eax to contain the number of arguments
+ // JumpToExternalReference expects eax to contain the number of arguments
// including the receiver and the extra arguments.
__ add(Operand(eax), Immediate(num_extra_args + 1));
- __ JumpToRuntime(ExternalReference(id));
+ __ JumpToExternalReference(ExternalReference(id));
}
@@ -797,38 +797,23 @@
// register elements_array is scratched.
static void AllocateJSArray(MacroAssembler* masm,
Register array_function, // Array function.
- Register array_size, // As a smi.
+ Register array_size, // As a smi, cannot be 0.
Register result,
Register elements_array,
Register elements_array_end,
Register scratch,
bool fill_with_hole,
Label* gc_required) {
- Label not_empty, allocated;
+ ASSERT(scratch.is(edi)); // rep stos destination
+ ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count
// Load the initial map from the array function.
__ mov(elements_array,
FieldOperand(array_function,
JSFunction::kPrototypeOrInitialMapOffset));
- // Check whether an empty sized array is requested.
- __ test(array_size, Operand(array_size));
- __ j(not_zero, ¬_empty);
-
- // 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(kPreallocatedArrayElements);
- __ AllocateInNewSpace(size,
- result,
- elements_array_end,
- scratch,
- gc_required,
- TAG_OBJECT);
- __ jmp(&allocated);
-
// Allocate the JSArray object together with space for a FixedArray with the
// requested elements.
- __ bind(¬_empty);
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
times_half_pointer_size, // array_size is a smi.
@@ -845,7 +830,6 @@
// elements_array: initial map
// elements_array_end: start of next object
// array_size: size of array (smi)
- __ bind(&allocated);
__ mov(FieldOperand(result, JSObject::kMapOffset), elements_array);
__ mov(elements_array, Factory::empty_fixed_array());
__ mov(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
@@ -869,15 +853,6 @@
__ SmiUntag(array_size); // Convert from smi to value.
__ mov(FieldOperand(elements_array, JSObject::kMapOffset),
Factory::fixed_array_map());
- Label not_empty_2, fill_array;
- __ test(array_size, Operand(array_size));
- __ j(not_zero, ¬_empty_2);
- // Length of the FixedArray is the number of pre-allocated elements even
- // though the actual JSArray has length 0.
- __ mov(FieldOperand(elements_array, Array::kLengthOffset),
- Immediate(kPreallocatedArrayElements));
- __ jmp(&fill_array);
- __ bind(¬_empty_2);
// For non-empty JSArrays the length of the FixedArray and the JSArray is the
// same.
__ mov(FieldOperand(elements_array, Array::kLengthOffset), array_size);
@@ -885,20 +860,18 @@
// Fill the allocated FixedArray with the hole value if requested.
// result: JSObject
// elements_array: elements array
- // elements_array_end: start of next object
- __ bind(&fill_array);
if (fill_with_hole) {
- Label loop, entry;
- __ mov(scratch, Factory::the_hole_value());
- __ lea(elements_array, Operand(elements_array,
- FixedArray::kHeaderSize - kHeapObjectTag));
- __ jmp(&entry);
- __ bind(&loop);
- __ mov(Operand(elements_array, 0), scratch);
- __ add(Operand(elements_array), Immediate(kPointerSize));
- __ bind(&entry);
- __ cmp(elements_array, Operand(elements_array_end));
- __ j(below, &loop);
+ __ lea(edi, Operand(elements_array,
+ FixedArray::kHeaderSize - kHeapObjectTag));
+
+ __ push(eax);
+ __ mov(eax, Factory::the_hole_value());
+
+ __ cld();
+ __ rep_stos();
+
+ // Restore saved registers.
+ __ pop(eax);
}
}
@@ -920,7 +893,8 @@
static void ArrayNativeCode(MacroAssembler* masm,
bool construct_call,
Label* call_generic_code) {
- Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call;
+ Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call,
+ empty_array, not_empty_array;
// Push the constructor and argc. No need to tag argc as a smi, as there will
// be no garbage collection with this on the stack.
@@ -936,6 +910,7 @@
__ test(eax, Operand(eax));
__ j(not_zero, &argc_one_or_more);
+ __ bind(&empty_array);
// Handle construction of an empty array.
AllocateEmptyJSArray(masm,
edi,
@@ -958,30 +933,46 @@
__ cmp(eax, 1);
__ j(not_equal, &argc_two_or_more);
ASSERT(kSmiTag == 0);
- __ test(Operand(esp, (push_count + 1) * kPointerSize),
- Immediate(kIntptrSignBit | kSmiTagMask));
+ __ mov(ecx, Operand(esp, (push_count + 1) * kPointerSize));
+ __ test(ecx, Operand(ecx));
+ __ j(not_zero, ¬_empty_array);
+
+ // The single argument passed is zero, so we jump to the code above used to
+ // handle the case of no arguments passed. To adapt the stack for that we move
+ // the return address and the pushed constructor (if pushed) one stack slot up
+ // thereby removing the passed argument. Argc is also on the stack - at the
+ // bottom - and it needs to be changed from 1 to 0 to have the call into the
+ // runtime system work in case a GC is required.
+ for (int i = push_count; i > 0; i--) {
+ __ mov(eax, Operand(esp, i * kPointerSize));
+ __ mov(Operand(esp, (i + 1) * kPointerSize), eax);
+ }
+ __ add(Operand(esp), Immediate(2 * kPointerSize)); // Drop two stack slots.
+ __ push(Immediate(0)); // Treat this as a call with argc of zero.
+ __ jmp(&empty_array);
+
+ __ bind(¬_empty_array);
+ __ test(ecx, Immediate(kIntptrSignBit | kSmiTagMask));
__ j(not_zero, &prepare_generic_code_call);
// Handle construction of an empty array of a certain size. Get the size from
// the stack and bail out if size is to large to actually allocate an elements
// array.
- __ mov(edx, Operand(esp, (push_count + 1) * kPointerSize));
- ASSERT(kSmiTag == 0);
- __ cmp(edx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
+ __ cmp(ecx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
__ j(greater_equal, &prepare_generic_code_call);
// edx: array_size (smi)
// edi: constructor
- // esp[0]: argc
+ // esp[0]: argc (cannot be 0 here)
// esp[4]: constructor (only if construct_call)
// esp[8]: return address
// esp[C]: argument
AllocateJSArray(masm,
edi,
- edx,
+ ecx,
eax,
ebx,
- ecx,
+ edx,
edi,
true,
&prepare_generic_code_call);
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index 7d28c51..bdd1c4a 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -126,6 +126,7 @@
void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
+ Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
// Initialize state.
info_ = info;
@@ -732,7 +733,27 @@
Result value = frame_->Pop();
value.ToRegister();
- if (value.is_number()) {
+ if (value.is_integer32()) { // Also takes Smi case.
+ Comment cmnt(masm_, "ONLY_INTEGER_32");
+ if (FLAG_debug_code) {
+ Label ok;
+ __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number.");
+ __ test(value.reg(), Immediate(kSmiTagMask));
+ __ j(zero, &ok);
+ __ fldz();
+ __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
+ __ FCmp();
+ __ j(not_zero, &ok);
+ __ Abort("Smi was wrapped in HeapNumber in output from bitop");
+ __ bind(&ok);
+ }
+ // In the integer32 case there are no Smis hidden in heap numbers, so we
+ // need only test for Smi zero.
+ __ test(value.reg(), Operand(value.reg()));
+ dest->false_target()->Branch(zero);
+ value.Unuse();
+ dest->Split(not_zero);
+ } else if (value.is_number()) {
Comment cmnt(masm_, "ONLY_NUMBER");
// Fast case if NumberInfo indicates only numbers.
if (FLAG_debug_code) {
@@ -816,8 +837,17 @@
// Takes the operands in edx and eax and loads them as integers in eax
// and ecx.
static void LoadAsIntegers(MacroAssembler* masm,
+ NumberInfo number_info,
bool use_sse3,
Label* operand_conversion_failure);
+ static void LoadNumbersAsIntegers(MacroAssembler* masm,
+ NumberInfo number_info,
+ bool use_sse3,
+ Label* operand_conversion_failure);
+ static void LoadUnknownsAsIntegers(MacroAssembler* masm,
+ bool use_sse3,
+ Label* operand_conversion_failure);
+
// Test if operands are smis or heap numbers and load them
// into xmm0 and xmm1 if they are. Operands are in edx and eax.
// Leaves operands unchanged.
@@ -849,13 +879,14 @@
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "GenericBinaryOpStub_%s_%s%s_%s%s_%s",
+ "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s",
op_name,
overwrite_name,
(flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
args_in_registers_ ? "RegArgs" : "StackArgs",
args_reversed_ ? "_R" : "",
- NumberInfo::ToString(operands_type_));
+ static_operands_type_.ToString(),
+ BinaryOpIC::GetName(runtime_operands_type_));
return name_;
}
@@ -867,8 +898,11 @@
Register dst,
Register left,
Register right,
+ NumberInfo left_info,
+ NumberInfo right_info,
OverwriteMode mode)
- : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
+ : op_(op), dst_(dst), left_(left), right_(right),
+ left_info_(left_info), right_info_(right_info), mode_(mode) {
set_comment("[ DeferredInlineBinaryOperation");
}
@@ -879,6 +913,8 @@
Register dst_;
Register left_;
Register right_;
+ NumberInfo left_info_;
+ NumberInfo right_info_;
OverwriteMode mode_;
};
@@ -892,18 +928,22 @@
CpuFeatures::Scope use_sse2(SSE2);
Label call_runtime, after_alloc_failure;
Label left_smi, right_smi, load_right, do_op;
- __ test(left_, Immediate(kSmiTagMask));
- __ j(zero, &left_smi);
- __ cmp(FieldOperand(left_, HeapObject::kMapOffset),
- Factory::heap_number_map());
- __ j(not_equal, &call_runtime);
- __ movdbl(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_LEFT) {
- __ mov(dst_, left_);
- }
- __ jmp(&load_right);
+ if (!left_info_.IsSmi()) {
+ __ test(left_, Immediate(kSmiTagMask));
+ __ j(zero, &left_smi);
+ if (!left_info_.IsNumber()) {
+ __ cmp(FieldOperand(left_, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, &call_runtime);
+ }
+ __ movdbl(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
+ if (mode_ == OVERWRITE_LEFT) {
+ __ mov(dst_, left_);
+ }
+ __ jmp(&load_right);
- __ bind(&left_smi);
+ __ bind(&left_smi);
+ }
__ SmiUntag(left_);
__ cvtsi2sd(xmm0, Operand(left_));
__ SmiTag(left_);
@@ -915,23 +955,27 @@
}
__ bind(&load_right);
- __ test(right_, Immediate(kSmiTagMask));
- __ j(zero, &right_smi);
- __ cmp(FieldOperand(right_, HeapObject::kMapOffset),
- Factory::heap_number_map());
- __ j(not_equal, &call_runtime);
- __ movdbl(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_RIGHT) {
- __ mov(dst_, right_);
- } else if (mode_ == NO_OVERWRITE) {
- Label alloc_failure;
- __ push(left_);
- __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
- __ pop(left_);
- }
- __ jmp(&do_op);
+ if (!right_info_.IsSmi()) {
+ __ test(right_, Immediate(kSmiTagMask));
+ __ j(zero, &right_smi);
+ if (!right_info_.IsNumber()) {
+ __ cmp(FieldOperand(right_, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, &call_runtime);
+ }
+ __ movdbl(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
+ if (mode_ == OVERWRITE_RIGHT) {
+ __ mov(dst_, right_);
+ } else if (mode_ == NO_OVERWRITE) {
+ Label alloc_failure;
+ __ push(left_);
+ __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
+ __ pop(left_);
+ }
+ __ jmp(&do_op);
- __ bind(&right_smi);
+ __ bind(&right_smi);
+ }
__ SmiUntag(right_);
__ cvtsi2sd(xmm1, Operand(right_));
__ SmiTag(right_);
@@ -957,13 +1001,105 @@
__ pop(left_);
__ bind(&call_runtime);
}
- GenericBinaryOpStub stub(op_, mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub stub(op_,
+ mode_,
+ NO_SMI_CODE_IN_STUB,
+ NumberInfo::Combine(left_info_, right_info_));
stub.GenerateCall(masm_, left_, right_);
if (!dst_.is(eax)) __ mov(dst_, eax);
__ bind(&done);
}
+static NumberInfo CalculateNumberInfo(NumberInfo operands_type,
+ Token::Value op,
+ const Result& right,
+ const Result& left) {
+ // Set NumberInfo of result according to the operation performed.
+ // Rely on the fact that smis have a 31 bit payload on ia32.
+ ASSERT(kSmiValueSize == 31);
+ switch (op) {
+ case Token::COMMA:
+ return right.number_info();
+ case Token::OR:
+ case Token::AND:
+ // Result type can be either of the two input types.
+ return operands_type;
+ case Token::BIT_AND: {
+ // Anding with positive Smis will give you a Smi.
+ if (right.is_constant() && right.handle()->IsSmi() &&
+ Smi::cast(*right.handle())->value() >= 0) {
+ return NumberInfo::Smi();
+ } else if (left.is_constant() && left.handle()->IsSmi() &&
+ Smi::cast(*left.handle())->value() >= 0) {
+ return NumberInfo::Smi();
+ }
+ return (operands_type.IsSmi())
+ ? NumberInfo::Smi()
+ : NumberInfo::Integer32();
+ }
+ case Token::BIT_OR: {
+ // Oring with negative Smis will give you a Smi.
+ if (right.is_constant() && right.handle()->IsSmi() &&
+ Smi::cast(*right.handle())->value() < 0) {
+ return NumberInfo::Smi();
+ } else if (left.is_constant() && left.handle()->IsSmi() &&
+ Smi::cast(*left.handle())->value() < 0) {
+ return NumberInfo::Smi();
+ }
+ return (operands_type.IsSmi())
+ ? NumberInfo::Smi()
+ : NumberInfo::Integer32();
+ }
+ case Token::BIT_XOR:
+ // Result is always a 32 bit integer. Smi property of inputs is preserved.
+ return (operands_type.IsSmi())
+ ? NumberInfo::Smi()
+ : NumberInfo::Integer32();
+ case Token::SAR:
+ if (left.is_smi()) return NumberInfo::Smi();
+ // Result is a smi if we shift by a constant >= 1, otherwise an integer32.
+ return (right.is_constant() && right.handle()->IsSmi()
+ && Smi::cast(*right.handle())->value() >= 1)
+ ? NumberInfo::Smi()
+ : NumberInfo::Integer32();
+ case Token::SHR:
+ // Result is a smi if we shift by a constant >= 2, otherwise an integer32.
+ return (right.is_constant() && right.handle()->IsSmi()
+ && Smi::cast(*right.handle())->value() >= 2)
+ ? NumberInfo::Smi()
+ : NumberInfo::Integer32();
+ case Token::ADD:
+ if (operands_type.IsSmi()) {
+ // The Integer32 range is big enough to take the sum of any two Smis.
+ return NumberInfo::Integer32();
+ } else {
+ // Result could be a string or a number. Check types of inputs.
+ return operands_type.IsNumber()
+ ? NumberInfo::Number()
+ : NumberInfo::Unknown();
+ }
+ case Token::SHL:
+ return NumberInfo::Integer32();
+ case Token::SUB:
+ // The Integer32 range is big enough to take the difference of any two
+ // Smis.
+ return (operands_type.IsSmi()) ?
+ NumberInfo::Integer32() :
+ NumberInfo::Number();
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ // Result is always a number.
+ return NumberInfo::Number();
+ default:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+ return NumberInfo::Unknown();
+}
+
+
void CodeGenerator::GenericBinaryOperation(Token::Value op,
StaticType* type,
OverwriteMode overwrite_mode) {
@@ -1019,9 +1155,11 @@
}
// Get number type of left and right sub-expressions.
- NumberInfo::Type operands_type =
+ NumberInfo operands_type =
NumberInfo::Combine(left.number_info(), right.number_info());
+ NumberInfo result_type = CalculateNumberInfo(operands_type, op, right, left);
+
Result answer;
if (left_is_non_smi_constant || right_is_non_smi_constant) {
// Go straight to the slow case, with no smi code.
@@ -1042,7 +1180,10 @@
// generate the inline Smi check code if this operation is part of a loop.
// For all other operations only inline the Smi check code for likely smis
// if the operation is part of a loop.
- if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) {
+ if (loop_nesting() > 0 &&
+ (Token::IsBitOp(op) ||
+ operands_type.IsInteger32() ||
+ type->IsLikelySmi())) {
answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
} else {
GenericBinaryOpStub stub(op,
@@ -1053,58 +1194,6 @@
}
}
- // Set NumberInfo of result according to the operation performed.
- // Rely on the fact that smis have a 31 bit payload on ia32.
- ASSERT(kSmiValueSize == 31);
- NumberInfo::Type result_type = NumberInfo::kUnknown;
- switch (op) {
- case Token::COMMA:
- result_type = right.number_info();
- break;
- case Token::OR:
- case Token::AND:
- // Result type can be either of the two input types.
- result_type = operands_type;
- break;
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- // Result is always a number. Smi property of inputs is preserved.
- result_type = (operands_type == NumberInfo::kSmi)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
- break;
- case Token::SAR:
- // Result is a smi if we shift by a constant >= 1, otherwise a number.
- result_type = (right.is_constant() && right.handle()->IsSmi()
- && Smi::cast(*right.handle())->value() >= 1)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
- break;
- case Token::SHR:
- // Result is a smi if we shift by a constant >= 2, otherwise a number.
- result_type = (right.is_constant() && right.handle()->IsSmi()
- && Smi::cast(*right.handle())->value() >= 2)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
- break;
- case Token::ADD:
- // Result could be a string or a number. Check types of inputs.
- result_type = NumberInfo::IsNumber(operands_type)
- ? NumberInfo::kNumber
- : NumberInfo::kUnknown;
- break;
- case Token::SHL:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- // Result is always a number.
- result_type = NumberInfo::kNumber;
- break;
- default:
- UNREACHABLE();
- }
answer.set_number_info(result_type);
frame_->Push(&answer);
}
@@ -1191,6 +1280,12 @@
}
+static void CheckTwoForSminess(MacroAssembler* masm,
+ Register left, Register right, Register scratch,
+ NumberInfo left_info, NumberInfo right_info,
+ DeferredInlineBinaryOperation* deferred);
+
+
// Implements a binary operation using a deferred code object and some
// inline code to operate on smis quickly.
Result CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
@@ -1271,6 +1366,8 @@
(op == Token::DIV) ? eax : edx,
left->reg(),
right->reg(),
+ left->number_info(),
+ right->number_info(),
overwrite_mode);
if (left->reg().is(right->reg())) {
__ test(left->reg(), Immediate(kSmiTagMask));
@@ -1368,11 +1465,11 @@
answer.reg(),
left->reg(),
ecx,
+ left->number_info(),
+ right->number_info(),
overwrite_mode);
- __ mov(answer.reg(), left->reg());
- __ or_(answer.reg(), Operand(ecx));
- __ test(answer.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(),
+ left->number_info(), right->number_info(), deferred);
// Untag both operands.
__ mov(answer.reg(), left->reg());
@@ -1442,16 +1539,12 @@
answer.reg(),
left->reg(),
right->reg(),
+ left->number_info(),
+ right->number_info(),
overwrite_mode);
- if (left->reg().is(right->reg())) {
- __ test(left->reg(), Immediate(kSmiTagMask));
- } else {
- __ mov(answer.reg(), left->reg());
- __ or_(answer.reg(), Operand(right->reg()));
- ASSERT(kSmiTag == 0); // Adjust test if not the case.
- __ test(answer.reg(), Immediate(kSmiTagMask));
- }
- deferred->Branch(not_zero);
+ CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(),
+ left->number_info(), right->number_info(), deferred);
+
__ mov(answer.reg(), left->reg());
switch (op) {
case Token::ADD:
@@ -1520,13 +1613,16 @@
DeferredInlineSmiOperation(Token::Value op,
Register dst,
Register src,
+ NumberInfo number_info,
Smi* value,
OverwriteMode overwrite_mode)
: op_(op),
dst_(dst),
src_(src),
+ number_info_(number_info),
value_(value),
overwrite_mode_(overwrite_mode) {
+ if (number_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
set_comment("[ DeferredInlineSmiOperation");
}
@@ -1536,6 +1632,7 @@
Token::Value op_;
Register dst_;
Register src_;
+ NumberInfo number_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1546,7 +1643,8 @@
GenericBinaryOpStub stub(
op_,
overwrite_mode_,
- (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB);
+ (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB,
+ NumberInfo::Combine(NumberInfo::Smi(), number_info_));
stub.GenerateCall(masm_, src_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1560,9 +1658,11 @@
Register dst,
Smi* value,
Register src,
+ NumberInfo number_info,
OverwriteMode overwrite_mode)
: op_(op),
dst_(dst),
+ number_info_(number_info),
value_(value),
src_(src),
overwrite_mode_(overwrite_mode) {
@@ -1574,6 +1674,7 @@
private:
Token::Value op_;
Register dst_;
+ NumberInfo number_info_;
Smi* value_;
Register src_;
OverwriteMode overwrite_mode_;
@@ -1581,7 +1682,11 @@
void DeferredInlineSmiOperationReversed::Generate() {
- GenericBinaryOpStub igostub(op_, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ op_,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ NumberInfo::Combine(NumberInfo::Smi(), number_info_));
igostub.GenerateCall(masm_, value_, src_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1593,9 +1698,14 @@
class DeferredInlineSmiAdd: public DeferredCode {
public:
DeferredInlineSmiAdd(Register dst,
+ NumberInfo number_info,
Smi* value,
OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
+ : dst_(dst),
+ number_info_(number_info),
+ value_(value),
+ overwrite_mode_(overwrite_mode) {
+ if (number_info_.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
set_comment("[ DeferredInlineSmiAdd");
}
@@ -1603,6 +1713,7 @@
private:
Register dst_;
+ NumberInfo number_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1611,7 +1722,11 @@
void DeferredInlineSmiAdd::Generate() {
// Undo the optimistic add operation and call the shared stub.
__ sub(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ Token::ADD,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ NumberInfo::Combine(NumberInfo::Smi(), number_info_));
igostub.GenerateCall(masm_, dst_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1623,9 +1738,13 @@
class DeferredInlineSmiAddReversed: public DeferredCode {
public:
DeferredInlineSmiAddReversed(Register dst,
+ NumberInfo number_info,
Smi* value,
OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
+ : dst_(dst),
+ number_info_(number_info),
+ value_(value),
+ overwrite_mode_(overwrite_mode) {
set_comment("[ DeferredInlineSmiAddReversed");
}
@@ -1633,6 +1752,7 @@
private:
Register dst_;
+ NumberInfo number_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1641,7 +1761,11 @@
void DeferredInlineSmiAddReversed::Generate() {
// Undo the optimistic add operation and call the shared stub.
__ sub(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ Token::ADD,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ NumberInfo::Combine(NumberInfo::Smi(), number_info_));
igostub.GenerateCall(masm_, value_, dst_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1654,9 +1778,14 @@
class DeferredInlineSmiSub: public DeferredCode {
public:
DeferredInlineSmiSub(Register dst,
+ NumberInfo number_info,
Smi* value,
OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
+ : dst_(dst),
+ number_info_(number_info),
+ value_(value),
+ overwrite_mode_(overwrite_mode) {
+ if (number_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
set_comment("[ DeferredInlineSmiSub");
}
@@ -1664,6 +1793,7 @@
private:
Register dst_;
+ NumberInfo number_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1672,7 +1802,11 @@
void DeferredInlineSmiSub::Generate() {
// Undo the optimistic sub operation and call the shared stub.
__ add(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ Token::SUB,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ NumberInfo::Combine(NumberInfo::Smi(), number_info_));
igostub.GenerateCall(masm_, dst_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1716,17 +1850,21 @@
DeferredCode* deferred = NULL;
if (reversed) {
deferred = new DeferredInlineSmiAddReversed(operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
} else {
deferred = new DeferredInlineSmiAdd(operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
}
__ add(Operand(operand->reg()), Immediate(value));
deferred->Branch(overflow);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->number_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
deferred->BindExit();
answer = *operand;
break;
@@ -1741,24 +1879,29 @@
answer = allocator()->Allocate();
ASSERT(answer.is_valid());
__ Set(answer.reg(), Immediate(value));
- deferred = new DeferredInlineSmiOperationReversed(op,
- answer.reg(),
- smi_value,
- operand->reg(),
- overwrite_mode);
+ deferred =
+ new DeferredInlineSmiOperationReversed(op,
+ answer.reg(),
+ smi_value,
+ operand->reg(),
+ operand->number_info(),
+ overwrite_mode);
__ sub(answer.reg(), Operand(operand->reg()));
} else {
operand->ToRegister();
frame_->Spill(operand->reg());
answer = *operand;
deferred = new DeferredInlineSmiSub(operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
__ sub(Operand(operand->reg()), Immediate(value));
}
deferred->Branch(overflow);
- __ test(answer.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->number_info().IsSmi()) {
+ __ test(answer.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
deferred->BindExit();
operand->Unuse();
break;
@@ -1775,19 +1918,27 @@
int shift_value = int_value & 0x1f;
operand->ToRegister();
frame_->Spill(operand->reg());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- if (shift_value > 0) {
- __ sar(operand->reg(), shift_value);
- __ and_(operand->reg(), ~kSmiTagMask);
+ if (!operand->number_info().IsSmi()) {
+ DeferredInlineSmiOperation* deferred =
+ new DeferredInlineSmiOperation(op,
+ operand->reg(),
+ operand->reg(),
+ operand->number_info(),
+ smi_value,
+ overwrite_mode);
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ if (shift_value > 0) {
+ __ sar(operand->reg(), shift_value);
+ __ and_(operand->reg(), ~kSmiTagMask);
+ }
+ deferred->BindExit();
+ } else {
+ if (shift_value > 0) {
+ __ sar(operand->reg(), shift_value);
+ __ and_(operand->reg(), ~kSmiTagMask);
+ }
}
- deferred->BindExit();
answer = *operand;
}
break;
@@ -1808,10 +1959,13 @@
new DeferredInlineSmiOperation(op,
answer.reg(),
operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->number_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
__ mov(answer.reg(), operand->reg());
__ SmiUntag(answer.reg());
__ shr(answer.reg(), shift_value);
@@ -1853,10 +2007,13 @@
answer.reg(),
smi_value,
right.reg(),
+ right.number_info(),
overwrite_mode);
__ mov(answer.reg(), Immediate(int_value));
__ sar(ecx, kSmiTagSize);
- deferred->Branch(carry);
+ if (!right.number_info().IsSmi()) {
+ deferred->Branch(carry);
+ }
__ shl_cl(answer.reg());
__ cmp(answer.reg(), 0xc0000000);
deferred->Branch(sign);
@@ -1875,6 +2032,7 @@
new DeferredInlineSmiOperation(op,
operand->reg(),
operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
__ test(operand->reg(), Immediate(kSmiTagMask));
@@ -1889,10 +2047,13 @@
new DeferredInlineSmiOperation(op,
answer.reg(),
operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->number_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
__ mov(answer.reg(), operand->reg());
ASSERT(kSmiTag == 0); // adjust code if not the case
// We do no shifts, only the Smi conversion, if shift_value is 1.
@@ -1916,20 +2077,25 @@
frame_->Spill(operand->reg());
DeferredCode* deferred = NULL;
if (reversed) {
- deferred = new DeferredInlineSmiOperationReversed(op,
- operand->reg(),
- smi_value,
- operand->reg(),
- overwrite_mode);
+ deferred =
+ new DeferredInlineSmiOperationReversed(op,
+ operand->reg(),
+ smi_value,
+ operand->reg(),
+ operand->number_info(),
+ overwrite_mode);
} else {
deferred = new DeferredInlineSmiOperation(op,
operand->reg(),
operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
}
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->number_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
if (op == Token::BIT_AND) {
__ and_(Operand(operand->reg()), Immediate(value));
} else if (op == Token::BIT_XOR) {
@@ -1956,6 +2122,7 @@
new DeferredInlineSmiOperation(op,
operand->reg(),
operand->reg(),
+ operand->number_info(),
smi_value,
overwrite_mode);
// Check that lowest log2(value) bits of operand are zero, and test
@@ -1987,11 +2154,13 @@
(IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
operand->ToRegister();
frame_->Spill(operand->reg());
- DeferredCode* deferred = new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
+ DeferredCode* deferred =
+ new DeferredInlineSmiOperation(op,
+ operand->reg(),
+ operand->reg(),
+ operand->number_info(),
+ smi_value,
+ overwrite_mode);
// Check for negative or non-Smi left hand side.
__ test(operand->reg(), Immediate(kSmiTagMask | 0x80000000));
deferred->Branch(not_zero);
@@ -2025,6 +2194,8 @@
static bool CouldBeNaN(const Result& result) {
+ if (result.number_info().IsSmi()) return false;
+ if (result.number_info().IsInteger32()) return false;
if (!result.is_constant()) return true;
if (!result.handle()->IsHeapNumber()) return false;
return isnan(HeapNumber::cast(*result.handle())->value());
@@ -5420,6 +5591,54 @@
}
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateCharFromCode");
+ ASSERT(args->length() == 1);
+
+ Load(args->at(0));
+ Result code = frame_->Pop();
+ code.ToRegister();
+ ASSERT(code.is_valid());
+
+ Result temp = allocator()->Allocate();
+ ASSERT(temp.is_valid());
+
+ JumpTarget slow_case;
+ JumpTarget exit;
+
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ ASSERT(kSmiTag == 0);
+ ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
+ __ test(code.reg(),
+ Immediate(kSmiTagMask |
+ ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+ slow_case.Branch(not_zero, &code, not_taken);
+
+ __ Set(temp.reg(), Immediate(Factory::single_character_string_cache()));
+ ASSERT(kSmiTag == 0);
+ ASSERT(kSmiTagSize == 1);
+ ASSERT(kSmiShiftSize == 0);
+ // At this point code register contains smi tagged ascii char code.
+ __ mov(temp.reg(), FieldOperand(temp.reg(),
+ code.reg(), times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(temp.reg(), Factory::undefined_value());
+ slow_case.Branch(equal, &code, not_taken);
+ code.Unuse();
+
+ frame_->Push(&temp);
+ exit.Jump();
+
+ slow_case.Bind(&code);
+ frame_->Push(&code);
+ Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
+ frame_->Push(&result);
+
+ exit.Bind();
+}
+
+
void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
@@ -5740,21 +5959,12 @@
ASSERT(args->length() == 0);
frame_->SpillAll();
- // Make sure the frame is aligned like the OS expects.
- static const int kFrameAlignment = OS::ActivationFrameAlignment();
- if (kFrameAlignment > 0) {
- ASSERT(IsPowerOf2(kFrameAlignment));
- __ mov(edi, Operand(esp)); // Save in callee-saved register.
- __ and_(esp, -kFrameAlignment);
- }
+ static const int num_arguments = 0;
+ __ PrepareCallCFunction(num_arguments, eax);
// Call V8::RandomPositiveSmi().
- __ call(FUNCTION_ADDR(V8::RandomPositiveSmi), RelocInfo::RUNTIME_ENTRY);
-
- // Restore stack pointer from callee-saved register edi.
- if (kFrameAlignment > 0) {
- __ mov(esp, Operand(edi));
- }
+ __ CallCFunction(ExternalReference::random_positive_smi_function(),
+ num_arguments);
Result result = allocator_->Allocate(eax);
frame_->Push(&result);
@@ -5823,6 +6033,269 @@
}
+// Generates the Math.pow method - only handles special cases and branches to
+// the runtime system if not.Please note - this function assumes that
+// the callsite has executed ToNumber on both arguments and that the
+// arguments are not the same identifier.
+void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 2);
+ Load(args->at(0));
+ Load(args->at(1));
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
+ frame_->Push(&res);
+ } else {
+ CpuFeatures::Scope use_sse2(SSE2);
+ Label allocate_return;
+ // Load the two operands while leaving the values on the frame.
+ frame()->Dup();
+ Result exponent = frame()->Pop();
+ exponent.ToRegister();
+ frame()->Spill(exponent.reg());
+ frame()->PushElementAt(1);
+ Result base = frame()->Pop();
+ base.ToRegister();
+ frame()->Spill(base.reg());
+
+ Result answer = allocator()->Allocate();
+ ASSERT(answer.is_valid());
+ // We can safely assume that the base and exponent is not in the same
+ // register since we only call this from one callsite (math.js).
+ ASSERT(!exponent.reg().is(base.reg()));
+ JumpTarget call_runtime;
+
+ // Save 1 in xmm3 - we need this several times later on.
+ __ mov(answer.reg(), Immediate(1));
+ __ cvtsi2sd(xmm3, Operand(answer.reg()));
+
+ Label exponent_nonsmi;
+ Label base_nonsmi;
+ // If the exponent is a heap number go to that specific case.
+ __ test(exponent.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &exponent_nonsmi);
+ __ test(base.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &base_nonsmi);
+
+ // Optimized version when y is an integer.
+ Label powi;
+ __ SmiUntag(base.reg());
+ __ cvtsi2sd(xmm0, Operand(base.reg()));
+ __ jmp(&powi);
+ // exponent is smi and base is a heapnumber.
+ __ bind(&base_nonsmi);
+ __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ call_runtime.Branch(not_equal);
+
+ __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
+
+ // Optimized version of pow if y is an integer.
+ __ bind(&powi);
+ __ SmiUntag(exponent.reg());
+
+ // Save exponent in base as we need to check if exponent is negative later.
+ // We know that base and exponent are in different registers.
+ __ mov(base.reg(), exponent.reg());
+
+ // Get absolute value of exponent.
+ Label no_neg;
+ __ cmp(exponent.reg(), 0);
+ __ j(greater_equal, &no_neg);
+ __ neg(exponent.reg());
+ __ bind(&no_neg);
+
+ // Load xmm1 with 1.
+ __ movsd(xmm1, xmm3);
+ Label while_true;
+ Label no_multiply;
+
+ // Label allocate_and_return;
+ __ bind(&while_true);
+ __ shr(exponent.reg(), 1);
+ __ j(not_carry, &no_multiply);
+ __ mulsd(xmm1, xmm0);
+ __ bind(&no_multiply);
+ __ test(exponent.reg(), Operand(exponent.reg()));
+ __ mulsd(xmm0, xmm0);
+ __ j(not_zero, &while_true);
+
+ // x has the original value of y - if y is negative return 1/result.
+ __ test(base.reg(), Operand(base.reg()));
+ __ j(positive, &allocate_return);
+ // Special case if xmm1 has reached infinity.
+ __ mov(answer.reg(), Immediate(0x7FB00000));
+ __ movd(xmm0, Operand(answer.reg()));
+ __ cvtss2sd(xmm0, xmm0);
+ __ ucomisd(xmm0, xmm1);
+ call_runtime.Branch(equal);
+ __ divsd(xmm3, xmm1);
+ __ movsd(xmm1, xmm3);
+ __ jmp(&allocate_return);
+
+ // exponent (or both) is a heapnumber - no matter what we should now work
+ // on doubles.
+ __ bind(&exponent_nonsmi);
+ __ cmp(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ call_runtime.Branch(not_equal);
+ __ movdbl(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
+ // Test if exponent is nan.
+ __ ucomisd(xmm1, xmm1);
+ call_runtime.Branch(parity_even);
+
+ Label base_not_smi;
+ Label handle_special_cases;
+ __ test(base.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &base_not_smi);
+ __ SmiUntag(base.reg());
+ __ cvtsi2sd(xmm0, Operand(base.reg()));
+ __ jmp(&handle_special_cases);
+ __ bind(&base_not_smi);
+ __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ call_runtime.Branch(not_equal);
+ __ mov(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
+ __ and_(answer.reg(), HeapNumber::kExponentMask);
+ __ cmp(Operand(answer.reg()), Immediate(HeapNumber::kExponentMask));
+ // base is NaN or +/-Infinity
+ call_runtime.Branch(greater_equal);
+ __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
+
+ // base is in xmm0 and exponent is in xmm1.
+ __ bind(&handle_special_cases);
+ Label not_minus_half;
+ // Test for -0.5.
+ // Load xmm2 with -0.5.
+ __ mov(answer.reg(), Immediate(0xBF000000));
+ __ movd(xmm2, Operand(answer.reg()));
+ __ cvtss2sd(xmm2, xmm2);
+ // xmm2 now has -0.5.
+ __ ucomisd(xmm2, xmm1);
+ __ j(not_equal, ¬_minus_half);
+
+ // Calculates reciprocal of square root.
+ // Note that 1/sqrt(x) = sqrt(1/x))
+ __ divsd(xmm3, xmm0);
+ __ movsd(xmm1, xmm3);
+ __ sqrtsd(xmm1, xmm1);
+ __ jmp(&allocate_return);
+
+ // Test for 0.5.
+ __ bind(¬_minus_half);
+ // Load xmm2 with 0.5.
+ // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
+ __ addsd(xmm2, xmm3);
+ // xmm2 now has 0.5.
+ __ comisd(xmm2, xmm1);
+ call_runtime.Branch(not_equal);
+ // Calculates square root.
+ __ movsd(xmm1, xmm0);
+ __ sqrtsd(xmm1, xmm1);
+
+ JumpTarget done;
+ Label failure, success;
+ __ bind(&allocate_return);
+ // Make a copy of the frame to enable us to handle allocation
+ // failure after the JumpTarget jump.
+ VirtualFrame* clone = new VirtualFrame(frame());
+ __ AllocateHeapNumber(answer.reg(), exponent.reg(),
+ base.reg(), &failure);
+ __ movdbl(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
+ // Remove the two original values from the frame - we only need those
+ // in the case where we branch to runtime.
+ frame()->Drop(2);
+ exponent.Unuse();
+ base.Unuse();
+ done.Jump(&answer);
+ // Use the copy of the original frame as our current frame.
+ RegisterFile empty_regs;
+ SetFrame(clone, &empty_regs);
+ // If we experience an allocation failure we branch to runtime.
+ __ bind(&failure);
+ call_runtime.Bind();
+ answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
+
+ done.Bind(&answer);
+ frame()->Push(&answer);
+ }
+}
+
+
+void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ Load(args->at(0));
+ TranscendentalCacheStub stub(TranscendentalCache::SIN);
+ Result result = frame_->CallStub(&stub, 1);
+ frame_->Push(&result);
+}
+
+
+void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ Load(args->at(0));
+ TranscendentalCacheStub stub(TranscendentalCache::COS);
+ Result result = frame_->CallStub(&stub, 1);
+ frame_->Push(&result);
+}
+
+
+// Generates the Math.sqrt method. Please note - this function assumes that
+// the callsite has executed ToNumber on the argument.
+void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ Load(args->at(0));
+
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ Result result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
+ frame()->Push(&result);
+ } else {
+ CpuFeatures::Scope use_sse2(SSE2);
+ // Leave original value on the frame if we need to call runtime.
+ frame()->Dup();
+ Result result = frame()->Pop();
+ result.ToRegister();
+ frame()->Spill(result.reg());
+ Label runtime;
+ Label non_smi;
+ Label load_done;
+ JumpTarget end;
+
+ __ test(result.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &non_smi);
+ __ SmiUntag(result.reg());
+ __ cvtsi2sd(xmm0, Operand(result.reg()));
+ __ jmp(&load_done);
+ __ bind(&non_smi);
+ __ cmp(FieldOperand(result.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, &runtime);
+ __ movdbl(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
+
+ __ bind(&load_done);
+ __ sqrtsd(xmm0, xmm0);
+ // A copy of the virtual frame to allow us to go to runtime after the
+ // JumpTarget jump.
+ Result scratch = allocator()->Allocate();
+ VirtualFrame* clone = new VirtualFrame(frame());
+ __ AllocateHeapNumber(result.reg(), scratch.reg(), no_reg, &runtime);
+
+ __ movdbl(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
+ frame()->Drop(1);
+ scratch.Unuse();
+ end.Jump(&result);
+ // We only branch to runtime if we have an allocation error.
+ // Use the copy of the original frame as our current frame.
+ RegisterFile empty_regs;
+ SetFrame(clone, &empty_regs);
+ __ bind(&runtime);
+ result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
+
+ end.Bind(&result);
+ frame()->Push(&result);
+ }
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
@@ -6682,8 +7155,12 @@
public:
DeferredReferenceSetKeyedValue(Register value,
Register key,
- Register receiver)
- : value_(value), key_(key), receiver_(receiver) {
+ Register receiver,
+ Register scratch)
+ : value_(value),
+ key_(key),
+ receiver_(receiver),
+ scratch_(scratch) {
set_comment("[ DeferredReferenceSetKeyedValue");
}
@@ -6695,17 +7172,65 @@
Register value_;
Register key_;
Register receiver_;
+ Register scratch_;
Label patch_site_;
};
void DeferredReferenceSetKeyedValue::Generate() {
__ IncrementCounter(&Counters::keyed_store_inline_miss, 1);
- // Push receiver and key arguments on the stack.
- __ push(receiver_);
- __ push(key_);
- // Move value argument to eax as expected by the IC stub.
- if (!value_.is(eax)) __ mov(eax, value_);
+ // Move value_ to eax, key_ to ecx, and receiver_ to edx.
+ Register old_value = value_;
+
+ // First, move value to eax.
+ if (!value_.is(eax)) {
+ if (key_.is(eax)) {
+ // Move key_ out of eax, preferably to ecx.
+ if (!value_.is(ecx) && !receiver_.is(ecx)) {
+ __ mov(ecx, key_);
+ key_ = ecx;
+ } else {
+ __ mov(scratch_, key_);
+ key_ = scratch_;
+ }
+ }
+ if (receiver_.is(eax)) {
+ // Move receiver_ out of eax, preferably to edx.
+ if (!value_.is(edx) && !key_.is(edx)) {
+ __ mov(edx, receiver_);
+ receiver_ = edx;
+ } else {
+ // Both moves to scratch are from eax, also, no valid path hits both.
+ __ mov(scratch_, receiver_);
+ receiver_ = scratch_;
+ }
+ }
+ __ mov(eax, value_);
+ value_ = eax;
+ }
+
+ // Now value_ is in eax. Move the other two to the right positions.
+ // We do not update the variables key_ and receiver_ to ecx and edx.
+ if (key_.is(ecx)) {
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ } else if (key_.is(edx)) {
+ if (receiver_.is(ecx)) {
+ __ xchg(edx, ecx);
+ } else {
+ __ mov(ecx, key_);
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ }
+ } else { // Key is not in edx or ecx.
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ __ mov(ecx, key_);
+ }
+
// Call the IC stub.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
@@ -6718,11 +7243,8 @@
// Here we use masm_-> instead of the __ macro because this is the
// instruction that gets patched and coverage code gets in the way.
masm_->test(eax, Immediate(-delta_to_patch_site));
- // Restore value (returned from store IC), key and receiver
- // registers.
- if (!value_.is(eax)) __ mov(value_, eax);
- __ pop(key_);
- __ pop(receiver_);
+ // Restore value (returned from store IC) register.
+ if (!old_value.is(eax)) __ mov(old_value, eax);
}
@@ -6846,8 +7368,10 @@
deferred->Branch(not_equal);
// Check that the key is a smi.
- __ test(key.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!key.is_smi()) {
+ __ test(key.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
// Get the elements array from the receiver and check that it
// is not a dictionary.
@@ -6921,7 +7445,8 @@
DeferredReferenceSetKeyedValue* deferred =
new DeferredReferenceSetKeyedValue(result.reg(),
key.reg(),
- receiver.reg());
+ receiver.reg(),
+ tmp.reg());
// Check that the value is a smi if it is not a constant. We can skip
// the write barrier for smis and constants.
@@ -6981,7 +7506,6 @@
// indicate that we have generated an inline version of the
// keyed store.
__ nop();
- frame()->Drop(2);
}
ASSERT(frame()->height() == original_height - 3);
return result;
@@ -6992,6 +7516,34 @@
#define __ ACCESS_MASM(masm)
+static void CheckTwoForSminess(MacroAssembler* masm,
+ Register left, Register right, Register scratch,
+ NumberInfo left_info, NumberInfo right_info,
+ DeferredInlineBinaryOperation* deferred) {
+ if (left.is(right)) {
+ if (!left_info.IsSmi()) {
+ __ test(left, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
+ } else if (!left_info.IsSmi()) {
+ if (!right_info.IsSmi()) {
+ __ mov(scratch, left);
+ __ or_(scratch, Operand(right));
+ __ test(scratch, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else {
+ __ test(left, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
+ } else {
+ if (!right_info.IsSmi()) {
+ __ test(right, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
+ }
+}
+
+
Handle<String> Reference::GetName() {
ASSERT(type_ == NAMED);
Property* property = expression_->AsProperty();
@@ -7123,6 +7675,7 @@
Comment cmnt(masm, "[ Store to keyed Property");
Property* property = expression()->AsProperty();
ASSERT(property != NULL);
+
Result answer = cgen_->EmitKeyedStore(property->key()->type());
cgen_->frame()->Push(&answer);
set_unloaded();
@@ -7175,7 +7728,7 @@
__ push(esi);
__ push(edx);
__ push(ecx); // Restore return address.
- __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+ __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
}
@@ -7219,7 +7772,7 @@
// Need to collect. Call into runtime system.
__ bind(&gc);
- __ TailCallRuntime(ExternalReference(Runtime::kNewContext), 1, 1);
+ __ TailCallRuntime(Runtime::kNewContext, 1, 1);
}
@@ -7274,8 +7827,7 @@
__ ret(3 * kPointerSize);
__ bind(&slow_case);
- ExternalReference runtime(Runtime::kCreateArrayLiteralShallow);
- __ TailCallRuntime(runtime, 3, 1);
+ __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
}
@@ -7492,6 +8044,22 @@
__ mov(left, Operand(esp, 2 * kPointerSize));
}
+ if (static_operands_type_.IsSmi()) {
+ if (op_ == Token::BIT_OR) {
+ __ or_(right, Operand(left));
+ GenerateReturn(masm);
+ return;
+ } else if (op_ == Token::BIT_AND) {
+ __ and_(right, Operand(left));
+ GenerateReturn(masm);
+ return;
+ } else if (op_ == Token::BIT_XOR) {
+ __ xor_(right, Operand(left));
+ GenerateReturn(masm);
+ return;
+ }
+ }
+
// 2. Prepare the smi check of both operands by oring them together.
Comment smi_check_comment(masm, "-- Smi check arguments");
Label not_smis;
@@ -7800,146 +8368,181 @@
// Generate fast case smi code if requested. This flag is set when the fast
// case smi code is not generated by the caller. Generating it here will speed
// up common operations.
- if (HasSmiCodeInStub()) {
+ if (ShouldGenerateSmiCode()) {
GenerateSmiCode(masm, &call_runtime);
} else if (op_ != Token::MOD) { // MOD goes straight to runtime.
- GenerateLoadArguments(masm);
+ if (!HasArgsInRegisters()) {
+ GenerateLoadArguments(masm);
+ }
}
// Floating point case.
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV: {
- if (CpuFeatures::IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- if (NumberInfo::IsNumber(operands_type_)) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(edx,
- "GenericBinaryOpStub operand not a number.");
- __ AbortIfNotNumber(eax,
- "GenericBinaryOpStub operand not a number.");
- }
- FloatingPointHelper::LoadSSE2Operands(masm);
- } else {
- FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
+ if (ShouldGenerateFPCode()) {
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV: {
+ if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
+ HasSmiCodeInStub()) {
+ // Execution reaches this point when the first non-smi argument occurs
+ // (and only if smi code is generated). This is the right moment to
+ // patch to HEAP_NUMBERS state. The transition is attempted only for
+ // the four basic operations. The stub stays in the DEFAULT state
+ // forever for all other operations (also if smi code is skipped).
+ GenerateTypeTransition(masm);
}
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- GenerateHeapResultAllocation(masm, &call_runtime);
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- GenerateReturn(masm);
- } else { // SSE2 not available, use FPU.
- if (NumberInfo::IsNumber(operands_type_)) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(edx,
- "GenericBinaryOpStub operand not a number.");
- __ AbortIfNotNumber(eax,
- "GenericBinaryOpStub operand not a number.");
- }
- } else {
- FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
- }
- FloatingPointHelper::LoadFloatOperands(
- masm,
- ecx,
- FloatingPointHelper::ARGS_IN_REGISTERS);
- switch (op_) {
- case Token::ADD: __ faddp(1); break;
- case Token::SUB: __ fsubp(1); break;
- case Token::MUL: __ fmulp(1); break;
- case Token::DIV: __ fdivp(1); break;
- default: UNREACHABLE();
- }
- Label after_alloc_failure;
- GenerateHeapResultAllocation(masm, &after_alloc_failure);
- __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- GenerateReturn(masm);
- __ bind(&after_alloc_failure);
- __ ffree();
- __ jmp(&call_runtime);
- }
- }
- case Token::MOD: {
- // For MOD we go directly to runtime in the non-smi case.
- break;
- }
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- case Token::SAR:
- case Token::SHL:
- case Token::SHR: {
- Label non_smi_result;
- FloatingPointHelper::LoadAsIntegers(masm, use_sse3_, &call_runtime);
- switch (op_) {
- case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
- case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
- case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
- case Token::SAR: __ sar_cl(eax); break;
- case Token::SHL: __ shl_cl(eax); break;
- case Token::SHR: __ shr_cl(eax); break;
- default: UNREACHABLE();
- }
- if (op_ == Token::SHR) {
- // Check if result is non-negative and fits in a smi.
- __ test(eax, Immediate(0xc0000000));
- __ j(not_zero, &call_runtime);
- } else {
- // Check if result fits in a smi.
- __ cmp(eax, 0xc0000000);
- __ j(negative, &non_smi_result);
- }
- // Tag smi result and return.
- __ SmiTag(eax);
- GenerateReturn(masm);
-
- // All ops except SHR return a signed int32 that we load in a HeapNumber.
- if (op_ != Token::SHR) {
- __ bind(&non_smi_result);
- // Allocate a heap number if needed.
- __ mov(ebx, Operand(eax)); // ebx: result
- Label skip_allocation;
- switch (mode_) {
- case OVERWRITE_LEFT:
- case OVERWRITE_RIGHT:
- // If the operand was an object, we skip the
- // allocation of a heap number.
- __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
- 1 * kPointerSize : 2 * kPointerSize));
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
- // Fall through!
- case NO_OVERWRITE:
- __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
- __ bind(&skip_allocation);
- break;
- default: UNREACHABLE();
- }
- // Store the result in the HeapNumber and return.
+ Label not_floats;
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
- __ cvtsi2sd(xmm0, Operand(ebx));
+ if (static_operands_type_.IsNumber()) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(edx,
+ "GenericBinaryOpStub operand not a number.");
+ __ AbortIfNotNumber(eax,
+ "GenericBinaryOpStub operand not a number.");
+ }
+ if (static_operands_type_.IsSmi()) {
+ FloatingPointHelper::LoadSSE2Smis(masm, ecx);
+ } else {
+ FloatingPointHelper::LoadSSE2Operands(masm);
+ }
+ } else {
+ FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
+ }
+
+ switch (op_) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ GenerateHeapResultAllocation(masm, &call_runtime);
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- } else {
- __ mov(Operand(esp, 1 * kPointerSize), ebx);
- __ fild_s(Operand(esp, 1 * kPointerSize));
+ GenerateReturn(masm);
+ } else { // SSE2 not available, use FPU.
+ if (static_operands_type_.IsNumber()) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(edx,
+ "GenericBinaryOpStub operand not a number.");
+ __ AbortIfNotNumber(eax,
+ "GenericBinaryOpStub operand not a number.");
+ }
+ } else {
+ FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
+ }
+ FloatingPointHelper::LoadFloatOperands(
+ masm,
+ ecx,
+ FloatingPointHelper::ARGS_IN_REGISTERS);
+ switch (op_) {
+ case Token::ADD: __ faddp(1); break;
+ case Token::SUB: __ fsubp(1); break;
+ case Token::MUL: __ fmulp(1); break;
+ case Token::DIV: __ fdivp(1); break;
+ default: UNREACHABLE();
+ }
+ Label after_alloc_failure;
+ GenerateHeapResultAllocation(masm, &after_alloc_failure);
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ GenerateReturn(masm);
+ __ bind(&after_alloc_failure);
+ __ ffree();
+ __ jmp(&call_runtime);
}
- GenerateReturn(masm);
+ __ bind(¬_floats);
+ if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
+ !HasSmiCodeInStub()) {
+ // Execution reaches this point when the first non-number argument
+ // occurs (and only if smi code is skipped from the stub, otherwise
+ // the patching has already been done earlier in this case branch).
+ // Try patching to STRINGS for ADD operation.
+ if (op_ == Token::ADD) {
+ GenerateTypeTransition(masm);
+ }
+ }
+ break;
}
- break;
+ case Token::MOD: {
+ // For MOD we go directly to runtime in the non-smi case.
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ Label non_smi_result;
+ FloatingPointHelper::LoadAsIntegers(masm,
+ static_operands_type_,
+ use_sse3_,
+ &call_runtime);
+ switch (op_) {
+ case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
+ case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
+ case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
+ case Token::SAR: __ sar_cl(eax); break;
+ case Token::SHL: __ shl_cl(eax); break;
+ case Token::SHR: __ shr_cl(eax); break;
+ default: UNREACHABLE();
+ }
+ if (op_ == Token::SHR) {
+ // Check if result is non-negative and fits in a smi.
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, &call_runtime);
+ } else {
+ // Check if result fits in a smi.
+ __ cmp(eax, 0xc0000000);
+ __ j(negative, &non_smi_result);
+ }
+ // Tag smi result and return.
+ __ SmiTag(eax);
+ GenerateReturn(masm);
+
+ // All ops except SHR return a signed int32 that we load in
+ // a HeapNumber.
+ if (op_ != Token::SHR) {
+ __ bind(&non_smi_result);
+ // Allocate a heap number if needed.
+ __ mov(ebx, Operand(eax)); // ebx: result
+ Label skip_allocation;
+ switch (mode_) {
+ case OVERWRITE_LEFT:
+ case OVERWRITE_RIGHT:
+ // If the operand was an object, we skip the
+ // allocation of a heap number.
+ __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
+ 1 * kPointerSize : 2 * kPointerSize));
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &skip_allocation, not_taken);
+ // Fall through!
+ case NO_OVERWRITE:
+ __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+ // Store the result in the HeapNumber and return.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ cvtsi2sd(xmm0, Operand(ebx));
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ GenerateReturn(masm);
+ }
+ break;
+ }
+ default: UNREACHABLE(); break;
}
- default: UNREACHABLE(); break;
}
// If all else fails, use the runtime system to get the correct
@@ -7947,21 +8550,20 @@
// stack in the correct order below the return address.
__ bind(&call_runtime);
if (HasArgsInRegisters()) {
- __ pop(ecx);
- if (HasArgsReversed()) {
- __ push(eax);
- __ push(edx);
- } else {
- __ push(edx);
- __ push(eax);
- }
- __ push(ecx);
+ GenerateRegisterArgsPush(masm);
}
+
switch (op_) {
case Token::ADD: {
// Test for string arguments before calling runtime.
Label not_strings, not_string1, string1, string1_smi2;
- Result answer;
+
+ // If this stub has already generated FP-specific code then the arguments
+ // are already in edx, eax
+ if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) {
+ GenerateLoadArguments(masm);
+ }
+
// Registers containing left and right operands respectively.
Register lhs, rhs;
if (HasArgsReversed()) {
@@ -8051,6 +8653,13 @@
default:
UNREACHABLE();
}
+
+ // Generate an unreachable reference to the DEFAULT stub so that it can be
+ // found at the end of this stub when clearing ICs at GC.
+ if (runtime_operands_type_ != BinaryOpIC::DEFAULT) {
+ GenericBinaryOpStub uninit(MinorKey(), BinaryOpIC::DEFAULT);
+ __ TailCallStub(&uninit);
+ }
}
@@ -8104,10 +8713,9 @@
void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
// If arguments are not passed in registers read them from the stack.
- if (!HasArgsInRegisters()) {
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
- }
+ ASSERT(!HasArgsInRegisters());
+ __ mov(eax, Operand(esp, 1 * kPointerSize));
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
}
@@ -8122,30 +8730,310 @@
}
+void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ ASSERT(HasArgsInRegisters());
+ __ pop(ecx);
+ if (HasArgsReversed()) {
+ __ push(eax);
+ __ push(edx);
+ } else {
+ __ push(edx);
+ __ push(eax);
+ }
+ __ push(ecx);
+}
+
+
+void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ Label get_result;
+
+ // Keep a copy of operands on the stack and make sure they are also in
+ // edx, eax.
+ if (HasArgsInRegisters()) {
+ GenerateRegisterArgsPush(masm);
+ } else {
+ GenerateLoadArguments(masm);
+ }
+
+ // Internal frame is necessary to handle exceptions properly.
+ __ EnterInternalFrame();
+
+ // Push arguments on stack if the stub expects them there.
+ if (!HasArgsInRegisters()) {
+ __ push(edx);
+ __ push(eax);
+ }
+ // Call the stub proper to get the result in eax.
+ __ call(&get_result);
+ __ LeaveInternalFrame();
+
+ __ pop(ecx); // Return address.
+ // Left and right arguments are now on top.
+ // Push the operation result. The tail call to BinaryOp_Patch will
+ // return it to the original caller.
+ __ push(eax);
+ // 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(runtime_operands_type_)));
+
+ __ push(ecx); // Return address.
+
+ // Patch the caller to an appropriate specialized stub
+ // and return the operation result.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
+ 6,
+ 1);
+
+ // The entry point for the result calculation is assumed to be immediately
+ // after this sequence.
+ __ bind(&get_result);
+}
+
+Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
+ GenericBinaryOpStub stub(key, type_info);
+ HandleScope scope;
+ return stub.GetCode();
+}
+
+
+void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
+ // Input on stack:
+ // esp[4]: argument (should be number).
+ // esp[0]: return address.
+ // Test that eax is a number.
+ Label runtime_call;
+ Label runtime_call_clear_stack;
+ Label input_not_smi;
+ Label loaded;
+ __ mov(eax, Operand(esp, kPointerSize));
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &input_not_smi);
+ // 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.
+ ASSERT_EQ(1, kSmiTagSize);
+ __ sar(eax, 1);
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
+ __ mov(Operand(esp, 0), eax);
+ __ fild_s(Operand(esp, 0));
+ __ fst_d(Operand(esp, 0));
+ __ pop(edx);
+ __ pop(ebx);
+ __ jmp(&loaded);
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ cmp(Operand(ebx), Immediate(Factory::heap_number_map()));
+ __ j(not_equal, &runtime_call);
+ // Input is a HeapNumber. Push it on the FPU stack and load its
+ // low and high words into ebx, edx.
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
+ __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
+
+ __ bind(&loaded);
+ // ST[0] == double value
+ // ebx = low 32 bits of double value
+ // edx = high 32 bits of double value
+ // Compute hash:
+ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
+ __ mov(ecx, ebx);
+ __ xor_(ecx, Operand(edx));
+ __ mov(eax, ecx);
+ __ sar(eax, 16);
+ __ xor_(ecx, Operand(eax));
+ __ mov(eax, ecx);
+ __ sar(eax, 8);
+ __ xor_(ecx, Operand(eax));
+ ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
+ __ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1));
+ // ST[0] == double value.
+ // ebx = low 32 bits of double value.
+ // edx = high 32 bits of double value.
+ // ecx = TranscendentalCache::hash(double value).
+ __ mov(eax,
+ Immediate(ExternalReference::transcendental_cache_array_address()));
+ // Eax points to cache array.
+ __ mov(eax, Operand(eax, type_ * sizeof(TranscendentalCache::caches_[0])));
+ // Eax points to the cache for the type type_.
+ // If NULL, the cache hasn't been initialized yet, so go through runtime.
+ __ test(eax, Operand(eax));
+ __ j(zero, &runtime_call_clear_stack);
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { // NOLINT - doesn't like a single brace on a line.
+ TranscendentalCache::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 ecx'th entry in the cache, i.e., &eax[ecx*12].
+ __ 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.
+ Label cache_miss;
+ __ cmp(ebx, Operand(ecx, 0));
+ __ j(not_equal, &cache_miss);
+ __ cmp(edx, Operand(ecx, kIntSize));
+ __ j(not_equal, &cache_miss);
+ // Cache hit!
+ __ mov(eax, Operand(ecx, 2 * kIntSize));
+ __ fstp(0);
+ __ ret(kPointerSize);
+
+ __ bind(&cache_miss);
+ // Update cache with new value.
+ // We are short on registers, so use no_reg as scratch.
+ // This gives slightly larger code.
+ __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
+ GenerateOperation(masm);
+ __ mov(Operand(ecx, 0), ebx);
+ __ mov(Operand(ecx, kIntSize), edx);
+ __ mov(Operand(ecx, 2 * kIntSize), eax);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ ret(kPointerSize);
+
+ __ bind(&runtime_call_clear_stack);
+ __ fstp(0);
+ __ bind(&runtime_call);
+ __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
+}
+
+
+Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
+ switch (type_) {
+ // Add more cases when necessary.
+ case TranscendentalCache::SIN: return Runtime::kMath_sin;
+ case TranscendentalCache::COS: return Runtime::kMath_cos;
+ default:
+ UNIMPLEMENTED();
+ return Runtime::kAbort;
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
+ // Only free register is edi.
+ Label done;
+ ASSERT(type_ == TranscendentalCache::SIN ||
+ type_ == TranscendentalCache::COS);
+ // More transcendental types can be added later.
+
+ // 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.
+ Label in_range;
+ // 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);
+ __ and_(Operand(edi), Immediate(0x7ff00000)); // Exponent only.
+ int supported_exponent_limit =
+ (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
+ __ cmp(Operand(edi), Immediate(supported_exponent_limit));
+ __ j(below, &in_range, taken);
+ // Check for infinity and NaN. Both return NaN for sin.
+ __ cmp(Operand(edi), Immediate(0x7ff00000));
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, taken);
+ // Input is +/-Infinity or NaN. Result is NaN.
+ __ fstp(0);
+ // NaN is represented by 0x7ff8000000000000.
+ __ push(Immediate(0x7ff80000));
+ __ push(Immediate(0));
+ __ fld_d(Operand(esp, 0));
+ __ add(Operand(esp), Immediate(2 * kPointerSize));
+ __ jmp(&done);
+
+ __ bind(&non_nan_result);
+
+ // Use fpmod to restrict argument to the range +/-2*PI.
+ __ mov(edi, eax); // Save eax before using fnstsw_ax.
+ __ fldpi();
+ __ fadd(0);
+ __ fld(1);
+ // FPU Stack: input, 2*pi, input.
+ {
+ 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);
+ __ fnclex();
+ __ bind(&no_exceptions);
+ }
+
+ // Compute st(0) % st(1)
+ {
+ Label partial_remainder_loop;
+ __ bind(&partial_remainder_loop);
+ __ fprem1();
+ __ fwait();
+ __ fnstsw_ax();
+ __ test(Operand(eax), Immediate(0x400 /* C2 */));
+ // If C2 is set, computation only has partial result. Loop to
+ // continue computation.
+ __ j(not_zero, &partial_remainder_loop);
+ }
+ // FPU Stack: input, 2*pi, input % 2*pi
+ __ fstp(2);
+ __ fstp(0);
+ __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
+
+ // FPU Stack: input % 2*pi
+ __ bind(&in_range);
+ switch (type_) {
+ case TranscendentalCache::SIN:
+ __ fsin();
+ break;
+ case TranscendentalCache::COS:
+ __ fcos();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ __ bind(&done);
+}
+
+
// 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,
+ NumberInfo number_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;
- // Get exponent word.
- __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
- // Get exponent alone in scratch2.
- __ mov(scratch2, scratch);
- __ and_(scratch2, HeapNumber::kExponentMask);
+ if (!number_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);
- // 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);
+ if (!number_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.
@@ -8259,16 +9147,66 @@
// 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::LoadAsIntegers(MacroAssembler* masm,
- bool use_sse3,
- Label* conversion_failure) {
+void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm,
+ NumberInfo number_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 (!number_info.IsHeapNumber()) {
+ if (!number_info.IsSmi()) {
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(not_zero, &arg1_is_object);
+ }
+ __ SmiUntag(edx);
+ __ jmp(&load_arg2);
+ }
+
+ __ bind(&arg1_is_object);
+
+ // Get the untagged integer version of the edx heap number in ecx.
+ IntegerConvert(masm, edx, number_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 (!number_info.IsHeapNumber()) {
+ // Test if arg2 is a Smi.
+ if (!number_info.IsSmi()) {
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &arg2_is_object);
+ }
+ __ 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, number_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,
+ 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;
+
+ // Test if arg1 is a Smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(not_zero, &arg1_is_object);
+
__ SmiUntag(edx);
__ jmp(&load_arg2);
@@ -8283,15 +9221,22 @@
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
__ cmp(ebx, Factory::heap_number_map());
__ j(not_equal, &check_undefined_arg1);
+
// Get the untagged integer version of the edx heap number in ecx.
- IntegerConvert(masm, edx, use_sse3, conversion_failure);
+ IntegerConvert(masm,
+ edx,
+ NumberInfo::Unknown(),
+ use_sse3,
+ conversion_failure);
__ mov(edx, ecx);
// Here edx has the untagged integer, eax has a Smi or a heap number.
__ bind(&load_arg2);
+
// Test if arg2 is a Smi.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &arg2_is_object);
+
__ SmiUntag(eax);
__ mov(ecx, eax);
__ jmp(&done);
@@ -8307,13 +9252,30 @@
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
__ cmp(ebx, Factory::heap_number_map());
__ j(not_equal, &check_undefined_arg2);
+
// Get the untagged integer version of the eax heap number in ecx.
- IntegerConvert(masm, eax, use_sse3, conversion_failure);
+ IntegerConvert(masm,
+ eax,
+ NumberInfo::Unknown(),
+ use_sse3,
+ conversion_failure);
__ bind(&done);
__ mov(eax, edx);
}
+void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
+ NumberInfo number_info,
+ bool use_sse3,
+ Label* conversion_failure) {
+ if (number_info.IsNumber()) {
+ LoadNumbersAsIntegers(masm, number_info, use_sse3, conversion_failure);
+ } else {
+ LoadUnknownsAsIntegers(masm, use_sse3, conversion_failure);
+ }
+}
+
+
void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
Register number) {
Label load_smi, done;
@@ -8550,7 +9512,11 @@
__ j(not_equal, &slow, not_taken);
// Convert the heap number in eax to an untagged integer in ecx.
- IntegerConvert(masm, eax, CpuFeatures::IsSupported(SSE3), &slow);
+ IntegerConvert(masm,
+ eax,
+ NumberInfo::Unknown(),
+ CpuFeatures::IsSupported(SSE3),
+ &slow);
// Do the bitwise operation and check if the result fits in a smi.
Label try_float;
@@ -8686,7 +9652,7 @@
__ pop(ebx); // Return address.
__ push(edx);
__ push(ebx);
- __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1, 1);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
}
@@ -8787,7 +9753,7 @@
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
}
@@ -8796,10 +9762,10 @@
// time or if regexp entry in generated code is turned off runtime switch or
// at compilation.
#ifndef V8_NATIVE_REGEXP
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
#else // V8_NATIVE_REGEXP
if (!FLAG_regexp_entry_native) {
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
return;
}
@@ -8982,48 +9948,50 @@
// All checks done. Now push arguments for native regexp code.
__ IncrementCounter(&Counters::regexp_entry_native, 1);
+ static const int kRegExpExecuteArguments = 7;
+ __ PrepareCallCFunction(kRegExpExecuteArguments, ecx);
+
// Argument 7: Indicate that this is a direct call from JavaScript.
- __ push(Immediate(1));
+ __ mov(Operand(esp, 6 * kPointerSize), Immediate(1));
// Argument 6: Start (high end) of backtracking stack memory area.
__ mov(ecx, Operand::StaticVariable(address_of_regexp_stack_memory_address));
__ add(ecx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
- __ push(ecx);
+ __ mov(Operand(esp, 5 * kPointerSize), ecx);
// Argument 5: static offsets vector buffer.
- __ push(Immediate(ExternalReference::address_of_static_offsets_vector()));
+ __ mov(Operand(esp, 4 * kPointerSize),
+ Immediate(ExternalReference::address_of_static_offsets_vector()));
// Argument 4: End of string data
// Argument 3: Start of string data
- Label push_two_byte, push_rest;
+ Label setup_two_byte, setup_rest;
__ test(edi, Operand(edi));
__ mov(edi, FieldOperand(eax, String::kLengthOffset));
- __ j(zero, &push_two_byte);
+ __ j(zero, &setup_two_byte);
__ lea(ecx, FieldOperand(eax, edi, times_1, SeqAsciiString::kHeaderSize));
- __ push(ecx); // Argument 4.
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
__ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize));
- __ push(ecx); // Argument 3.
- __ jmp(&push_rest);
+ __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
+ __ jmp(&setup_rest);
- __ bind(&push_two_byte);
+ __ bind(&setup_two_byte);
__ lea(ecx, FieldOperand(eax, edi, times_2, SeqTwoByteString::kHeaderSize));
- __ push(ecx); // Argument 4.
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
__ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
- __ push(ecx); // Argument 3.
+ __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
- __ bind(&push_rest);
+ __ bind(&setup_rest);
// Argument 2: Previous index.
- __ push(ebx);
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
// Argument 1: Subject string.
- __ push(eax);
+ __ mov(Operand(esp, 0 * kPointerSize), eax);
// Locate the code entry and call it.
__ add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag));
- __ call(Operand(edx));
- // Remove arguments.
- __ add(Operand(esp), Immediate(7 * kPointerSize));
+ __ CallCFunction(edx, kRegExpExecuteArguments);
// Check the result.
Label success;
@@ -9121,7 +10089,7 @@
// Do the runtime call to execute the regexp.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
#endif // V8_NATIVE_REGEXP
}
@@ -9190,7 +10158,7 @@
__ bind(&runtime);
// Handle number to string in the runtime system if not found in the cache.
- __ TailCallRuntime(ExternalReference(Runtime::kNumberToString), 1, 1);
+ __ TailCallRuntime(Runtime::kNumberToString, 1, 1);
}
@@ -9475,7 +10443,7 @@
__ push(eax);
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1, 1);
+ __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
}
@@ -9636,9 +10604,7 @@
__ LeaveExitFrame(ExitFrame::MODE_NORMAL);
__ ret(0);
__ bind(&promote_scheduled_exception);
- __ TailCallRuntime(ExternalReference(Runtime::kPromoteScheduledException),
- 0,
- 1);
+ __ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
}
@@ -10123,6 +11089,7 @@
Label make_two_character_string, make_flat_ascii_string;
GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
&make_two_character_string);
+ __ IncrementCounter(&Counters::string_add_native, 1);
__ ret(2 * kPointerSize);
__ bind(&make_two_character_string);
@@ -10267,7 +11234,7 @@
// Just jump to runtime to add the two strings.
__ bind(&string_add_runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
}
@@ -10331,6 +11298,7 @@
// Copy from edi to esi using rep movs instruction.
__ mov(scratch, count);
__ sar(count, 2); // Number of doublewords to copy.
+ __ cld();
__ rep_movs();
// Find number of bytes left.
@@ -10402,10 +11370,7 @@
// Calculate capacity mask from the symbol table capacity.
Register mask = scratch2;
- static const int kCapacityOffset =
- FixedArray::kHeaderSize +
- SymbolTable::kCapacityIndex * kPointerSize;
- __ mov(mask, FieldOperand(symbol_table, kCapacityOffset));
+ __ mov(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
__ SmiUntag(mask);
__ sub(Operand(mask), Immediate(1));
@@ -10430,16 +11395,12 @@
// Load the entry from the symble table.
Register candidate = scratch; // Scratch register contains candidate.
- ASSERT_EQ(1, SymbolTableShape::kEntrySize);
- static const int kFirstElementOffset =
- FixedArray::kHeaderSize +
- SymbolTable::kPrefixStartIndex * kPointerSize +
- SymbolTableShape::kPrefixSize * kPointerSize;
+ ASSERT_EQ(1, SymbolTable::kEntrySize);
__ mov(candidate,
FieldOperand(symbol_table,
scratch,
times_pointer_size,
- kFirstElementOffset));
+ SymbolTable::kElementsStartOffset));
// If entry is undefined no string with this hash can be found.
__ cmp(candidate, Factory::undefined_value());
@@ -10593,7 +11554,7 @@
Label make_two_character_string;
GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
&make_two_character_string);
- __ ret(2 * kPointerSize);
+ __ ret(3 * kPointerSize);
__ bind(&make_two_character_string);
// Setup registers for allocating the two character string.
@@ -10677,7 +11638,7 @@
// Just jump to runtime to create the sub string.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
}
@@ -10793,7 +11754,7 @@
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
// tagged as a small integer.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
}
#undef __
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index 0fa6919..79cad72 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -28,6 +28,8 @@
#ifndef V8_IA32_CODEGEN_IA32_H_
#define V8_IA32_CODEGEN_IA32_H_
+#include "ic-inl.h"
+
namespace v8 {
namespace internal {
@@ -494,8 +496,8 @@
// To prevent long attacker-controlled byte sequences, integer constants
// from the JavaScript source are loaded in two parts if they are larger
- // than 16 bits.
- static const int kMaxSmiInlinedBits = 16;
+ // than 17 bits.
+ static const int kMaxSmiInlinedBits = 17;
bool IsUnsafeSmi(Handle<Object> value);
// Load an integer constant x into a register target or into the stack using
// at most 16 bits of user-controlled data per assembly operation.
@@ -563,6 +565,9 @@
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
+ // Fast support for string.charAt(n) and string[n].
+ void GenerateCharFromCode(ZoneList<Expression*>* args);
+
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
@@ -588,6 +593,16 @@
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
+ // Fast support for Math.pow().
+ void GenerateMathPow(ZoneList<Expression*>* args);
+
+ // Fast call to transcendental functions.
+ void GenerateMathSin(ZoneList<Expression*>* args);
+ void GenerateMathCos(ZoneList<Expression*>* args);
+
+ // Fast case for sqrt
+ void GenerateMathSqrt(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
@@ -655,6 +670,22 @@
};
+// Compute a transcendental math function natively, or call the
+// TranscendentalCache runtime function.
+class TranscendentalCacheStub: public CodeStub {
+ public:
+ explicit TranscendentalCacheStub(TranscendentalCache::Type type)
+ : type_(type) {}
+ void Generate(MacroAssembler* masm);
+ private:
+ TranscendentalCache::Type type_;
+ Major MajorKey() { return TranscendentalCache; }
+ int MinorKey() { return type_; }
+ Runtime::FunctionId RuntimeFunction();
+ void GenerateOperation(MacroAssembler* masm);
+};
+
+
// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
enum GenericBinaryFlags {
NO_GENERIC_BINARY_FLAGS = 0,
@@ -667,18 +698,35 @@
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags,
- NumberInfo::Type operands_type = NumberInfo::kUnknown)
+ NumberInfo operands_type)
: op_(op),
mode_(mode),
flags_(flags),
args_in_registers_(false),
args_reversed_(false),
- name_(NULL),
- operands_type_(operands_type) {
+ static_operands_type_(operands_type),
+ runtime_operands_type_(BinaryOpIC::DEFAULT),
+ name_(NULL) {
+ if (static_operands_type_.IsSmi()) {
+ mode_ = NO_OVERWRITE;
+ }
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
+ GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ flags_(FlagBits::decode(key)),
+ args_in_registers_(ArgsInRegistersBits::decode(key)),
+ args_reversed_(ArgsReversedBits::decode(key)),
+ use_sse3_(SSE3Bits::decode(key)),
+ static_operands_type_(NumberInfo::ExpandedRepresentation(
+ StaticTypeInfoBits::decode(key))),
+ runtime_operands_type_(runtime_operands_type),
+ name_(NULL) {
+ }
+
// Generate code to call the stub with the supplied arguments. This will add
// code at the call site to prepare arguments either in registers or on the
// stack together with the actual call.
@@ -698,8 +746,14 @@
bool args_in_registers_; // Arguments passed in registers not on the stack.
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
+
+ // Number type information of operands, determined by code generator.
+ NumberInfo static_operands_type_;
+
+ // Operand type information determined at runtime.
+ BinaryOpIC::TypeInfo runtime_operands_type_;
+
char* name_;
- NumberInfo::Type operands_type_; // Number type information of operands.
const char* GetName();
@@ -713,29 +767,32 @@
static_cast<int>(flags_),
static_cast<int>(args_in_registers_),
static_cast<int>(args_reversed_),
- NumberInfo::ToString(operands_type_));
+ static_operands_type_.ToString());
}
#endif
- // Minor key encoding in 16 bits NNNFRASOOOOOOOMM.
+ // Minor key encoding in 18 bits RRNNNFRASOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
class SSE3Bits: public BitField<bool, 9, 1> {};
class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
class ArgsReversedBits: public BitField<bool, 11, 1> {};
class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
- class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
+ class StaticTypeInfoBits: public BitField<int, 13, 3> {};
+ class RuntimeTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 16, 2> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
- // Encode the parameters in a unique 16 bit value.
+ // Encode the parameters in a unique 18 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_)
| SSE3Bits::encode(use_sse3_)
| ArgsInRegistersBits::encode(args_in_registers_)
| ArgsReversedBits::encode(args_reversed_)
- | NumberInfoBits::encode(operands_type_);
+ | StaticTypeInfoBits::encode(
+ static_operands_type_.ThreeBitRepresentation())
+ | RuntimeTypeInfoBits::encode(runtime_operands_type_);
}
void Generate(MacroAssembler* masm);
@@ -743,6 +800,8 @@
void GenerateLoadArguments(MacroAssembler* masm);
void GenerateReturn(MacroAssembler* masm);
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateTypeTransition(MacroAssembler* masm);
bool ArgsInRegistersSupported() {
return op_ == Token::ADD || op_ == Token::SUB
@@ -757,6 +816,22 @@
bool HasSmiCodeInStub() { return (flags_ & NO_SMI_CODE_IN_STUB) == 0; }
bool HasArgsInRegisters() { return args_in_registers_; }
bool HasArgsReversed() { return args_reversed_; }
+
+ bool ShouldGenerateSmiCode() {
+ return HasSmiCodeInStub() &&
+ runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
+ runtime_operands_type_ != BinaryOpIC::STRINGS;
+ }
+
+ bool ShouldGenerateFPCode() {
+ return runtime_operands_type_ != BinaryOpIC::STRINGS;
+ }
+
+ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return BinaryOpIC::ToState(runtime_operands_type_);
+ }
};
diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc
index a9e2626..5d18a03 100644
--- a/src/ia32/debug-ia32.cc
+++ b/src/ia32/debug-ia32.cc
@@ -146,9 +146,10 @@
void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
// Register state for keyed IC load call (from ic-ia32.cc).
// ----------- S t a t e -------------
- // No registers used on entry.
+ // -- edx : receiver
+ // -- eax : key
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, 0, false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | edx.bit(), false);
}
@@ -156,10 +157,12 @@
// Register state for keyed IC load call (from ic-ia32.cc).
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -----------------------------------
// Register eax contains an object that needs to be pushed on the
// expression stack of the fake JS frame.
- Generate_DebugBreakCallHelper(masm, eax.bit(), false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit() | edx.bit(), false);
}
diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc
index a3b7016..0d85b10 100644
--- a/src/ia32/disasm-ia32.cc
+++ b/src/ia32/disasm-ia32.cc
@@ -89,6 +89,7 @@
{0x9E, "sahf", UNSET_OP_ORDER},
{0x99, "cdq", UNSET_OP_ORDER},
{0x9B, "fwait", UNSET_OP_ORDER},
+ {0xFC, "cld", UNSET_OP_ORDER},
{-1, "", UNSET_OP_ORDER}
};
@@ -679,6 +680,7 @@
case 0xDD: switch (regop) {
case 0: mnem = "fld_d"; break;
+ case 2: mnem = "fstp"; break;
case 3: mnem = "fstp_d"; break;
default: UnimplementedInstruction();
}
@@ -720,6 +722,7 @@
case 0xE1: mnem = "fabs"; break;
case 0xE4: mnem = "ftst"; break;
case 0xE8: mnem = "fld1"; break;
+ case 0xEB: mnem = "fldpi"; break;
case 0xEE: mnem = "fldz"; break;
case 0xF5: mnem = "fprem1"; break;
case 0xF7: mnem = "fincstp"; break;
@@ -1053,7 +1056,20 @@
AppendToBuffer(",%s", NameOfCPURegister(regop));
} else if (*data == 0x0F) {
data++;
- if (*data == 0x2F) {
+ if (*data == 0x38) {
+ data++;
+ if (*data == 0x17) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("ptest %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (*data == 0x2F) {
data++;
int mod, regop, rm;
get_modrm(*data, &mod, ®op, &rm);
@@ -1069,6 +1085,12 @@
NameOfXMMRegister(regop),
NameOfXMMRegister(rm));
data++;
+ } else if (*data == 0x6E) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
} else if (*data == 0x6F) {
data++;
int mod, regop, rm;
@@ -1082,6 +1104,14 @@
get_modrm(*data, &mod, ®op, &rm);
data += PrintRightOperand(data);
AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else if (*data == 0xEF) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("pxor %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else {
UnimplementedInstruction();
}
@@ -1168,6 +1198,7 @@
const char* mnem = "?";
switch (b2) {
case 0x2A: mnem = "cvtsi2sd"; break;
+ case 0x51: mnem = "sqrtsd"; break;
case 0x58: mnem = "addsd"; break;
case 0x59: mnem = "mulsd"; break;
case 0x5C: mnem = "subsd"; break;
@@ -1197,6 +1228,14 @@
if (*(data+2) == 0x2C) {
data += 3;
data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
+ } else if (*(data+2) == 0x5A) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("cvtss2sd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else if (*(data+2) == 0x6F) {
data += 3;
int mod, regop, rm;
@@ -1216,6 +1255,9 @@
} else if (*(data+1) == 0xA5) {
data += 2;
AppendToBuffer("rep_movs");
+ } else if (*(data+1) == 0xAB) {
+ data += 2;
+ AppendToBuffer("rep_stos");
} else {
UnimplementedInstruction();
}
diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc
index f1c2507..4dcf232 100644
--- a/src/ia32/fast-codegen-ia32.cc
+++ b/src/ia32/fast-codegen-ia32.cc
@@ -621,6 +621,7 @@
void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
ASSERT(info_ == NULL);
info_ = compilation_info;
+ Comment cmnt(masm_, "[ function compiled by fast code generator");
// Save the caller's frame pointer and set up our own.
Comment prologue_cmnt(masm(), ";; Prologue");
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index e42fcc8..6e3ae10 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -56,6 +56,7 @@
ASSERT(info_ == NULL);
info_ = info;
SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
if (mode == PRIMARY) {
__ push(ebp); // Caller's frame pointer.
@@ -741,23 +742,22 @@
// We are declaring a function or constant that rewrites to a
// property. Use (keyed) IC to set the initial value.
VisitForValue(prop->obj(), kStack);
- VisitForValue(prop->key(), kStack);
-
if (decl->fun() != NULL) {
+ VisitForValue(prop->key(), kStack);
VisitForValue(decl->fun(), kAccumulator);
+ __ pop(ecx);
} else {
+ VisitForValue(prop->key(), kAccumulator);
+ __ mov(ecx, result_register());
__ mov(result_register(), Factory::the_hole_value());
}
+ __ pop(edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// Absence of a test eax instruction following the call
// indicates that none of the load was inlined.
__ nop();
-
- // Value in eax is ignored (declarations are statements). Receiver
- // and key on stack are discarded.
- __ Drop(2);
}
}
}
@@ -1130,7 +1130,8 @@
__ push(result_register());
GenericBinaryOpStub stub(op,
NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS);
+ NO_GENERIC_BINARY_FLAGS,
+ NumberInfo::Unknown());
__ CallStub(&stub);
Apply(context, eax);
}
@@ -1251,6 +1252,12 @@
__ pop(result_register());
}
+ __ pop(ecx);
+ if (expr->ends_initialization_block()) {
+ __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later.
+ } else {
+ __ pop(edx);
+ }
// Record source code position before IC call.
SetSourcePosition(expr->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
@@ -1261,15 +1268,14 @@
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
+ __ pop(edx);
__ push(eax); // Result of assignment, saved even if not needed.
- // Receiver is under the key and value.
- __ push(Operand(esp, 2 * kPointerSize));
+ __ push(edx);
__ CallRuntime(Runtime::kToFastProperties, 1);
__ pop(eax);
}
- // Receiver and key are still on stack.
- DropAndApply(2, context_, eax);
+ Apply(context_, eax);
}
@@ -1739,7 +1745,8 @@
// Call stub for +1/-1.
GenericBinaryOpStub stub(expr->binary_op(),
NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS);
+ NO_GENERIC_BINARY_FLAGS,
+ NumberInfo::Unknown());
stub.GenerateCall(masm(), eax, Smi::FromInt(1));
__ bind(&done);
@@ -1777,18 +1784,20 @@
break;
}
case KEYED_PROPERTY: {
+ __ pop(ecx);
+ __ pop(edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
// site for it to patch.
__ nop();
if (expr->is_postfix()) {
- __ Drop(2); // Result is on the stack under the key and the receiver.
+ // Result is on the stack
if (context_ != Expression::kEffect) {
ApplyTOS(context_);
}
} else {
- DropAndApply(2, context_, eax);
+ Apply(context_, eax);
}
break;
}
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index fcc8271..555cd1b 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -610,8 +610,9 @@
__ push(ecx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(
- IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
+ ExternalReference ref = ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor));
+ __ TailCallExternalReference(ref, 2, 1);
__ bind(&slow);
GenerateMiss(masm);
@@ -621,54 +622,41 @@
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, fast, array, extra, check_pixel_array;
- // Get the receiver from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, key
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow, not_taken);
// Get the map from the receiver.
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ 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.
- __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(edi, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow, not_taken);
- // Get the key from the stack.
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &slow, not_taken);
- // Get the instance type from the map of the receiver.
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // Check if the object is a JS array or not.
- __ cmp(ecx, JS_ARRAY_TYPE);
+ __ CmpInstanceType(edi, JS_ARRAY_TYPE);
__ j(equal, &array);
// Check that the object is some kind of JS object.
- __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
- __ j(less, &slow, not_taken);
+ __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE);
+ __ j(below, &slow, not_taken);
// Object case: Check key against length in the elements array.
// eax: value
// edx: JSObject
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
+ // ecx: key (a smi)
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array, not_taken);
- // Untag the key (for checking against untagged length in the fixed array).
- __ mov(edx, Operand(ebx));
- __ sar(edx, kSmiTagSize); // untag the index and use it for the comparison
- __ cmp(edx, FieldOperand(ecx, Array::kLengthOffset));
- // eax: value
- // ecx: FixedArray
- // ebx: index (as a smi)
+ __ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
+ __ mov(ebx, Operand(ecx));
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
__ j(below, &fast, taken);
// Slow case: call runtime.
@@ -676,52 +664,51 @@
GenerateRuntimeSetProperty(masm);
// Check whether the elements is a pixel array.
- // eax: value
- // ecx: elements array
- // ebx: index (as a smi)
__ bind(&check_pixel_array);
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::pixel_array_map()));
- __ j(not_equal, &slow);
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements array
+ __ CheckMap(edi, Factory::pixel_array_map(), &slow, true);
// Check that the value is a smi. If a conversion is needed call into the
// runtime to convert and clamp.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
- __ sar(ebx, kSmiTagSize); // Untag the index.
- __ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, PixelArray::kLengthOffset));
__ j(above_equal, &slow);
- __ mov(edx, eax); // Save the value.
- __ sar(eax, kSmiTagSize); // Untag the value.
+ __ mov(ecx, eax); // Save the value. Key is not longer needed.
+ __ SmiUntag(ecx);
{ // Clamp the value to [0..255].
Label done;
- __ test(eax, Immediate(0xFFFFFF00));
+ __ test(ecx, Immediate(0xFFFFFF00));
__ j(zero, &done);
- __ setcc(negative, eax); // 1 if negative, 0 if positive.
- __ dec_b(eax); // 0 if negative, 255 if positive.
+ __ setcc(negative, ecx); // 1 if negative, 0 if positive.
+ __ dec_b(ecx); // 0 if negative, 255 if positive.
__ bind(&done);
}
- __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ mov(edi, FieldOperand(edi, PixelArray::kExternalPointerOffset));
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
+ __ ret(0); // Return value in eax.
// 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);
// eax: value
- // edx: JSArray
- // ecx: FixedArray
- // ebx: index (as a smi)
- // flags: compare (ebx, edx.length())
+ // edx: receiver, a JSArray
+ // 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
- __ sar(ebx, kSmiTagSize); // untag
- __ cmp(ebx, FieldOperand(ecx, Array::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx); // untag
+ __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
__ j(above_equal, &slow, not_taken);
- // Restore tag and increment.
- __ lea(ebx, Operand(ebx, times_2, 1 << kSmiTagSize));
- __ mov(FieldOperand(edx, JSArray::kLengthOffset), ebx);
- __ sub(Operand(ebx), Immediate(1 << kSmiTagSize)); // decrement ebx again
+ // Add 1 to receiver->length, and go to fast array write.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(1 << kSmiTagSize));
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
@@ -729,28 +716,26 @@
// length is always a smi.
__ bind(&array);
// eax: value
- // edx: JSArray
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array);
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
// Check the key against the length in the array, compute the
// address to store into and fall through to fast case.
- __ cmp(ebx, FieldOperand(edx, JSArray::kLengthOffset));
+ __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
__ j(above_equal, &extra, not_taken);
// Fast case: Do the store.
__ bind(&fast);
// eax: value
- // ecx: FixedArray
- // ebx: index (as a smi)
- __ mov(Operand(ecx, ebx, times_2, FixedArray::kHeaderSize - kHeapObjectTag),
- eax);
+ // ecx: key (a smi)
+ // edx: receiver
+ // edi: FixedArray receiver->elements
+ __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax);
// Update write barrier for the elements array address.
__ mov(edx, Operand(eax));
- __ RecordWrite(ecx, 0, edx, ebx);
+ __ RecordWrite(edi, 0, edx, ecx);
__ ret(0);
}
@@ -759,92 +744,91 @@
ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, check_heap_number;
- // Get the receiver from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize));
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow);
// Get the map from the receiver.
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ 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.
- __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(edi, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
- // Get the key from the stack.
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
// Get the instance type from the map of the receiver.
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // Check that the object is a JS object.
- __ cmp(ecx, JS_OBJECT_TYPE);
+ __ CmpInstanceType(edi, JS_OBJECT_TYPE);
__ j(not_equal, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// eax: value
- // edx: JSObject
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- Handle<Map> map(Heap::MapForExternalArrayType(array_type));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(map));
- __ j(not_equal, &slow);
+ // edx: receiver, a JSObject
+ // ecx: key, a smi
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ CheckMap(edi, Handle<Map>(Heap::MapForExternalArrayType(array_type)),
+ &slow, true);
// Check that the index is in range.
- __ sar(ebx, kSmiTagSize); // Untag the index.
- __ cmp(ebx, FieldOperand(ecx, ExternalArray::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
// eax: value
- // ecx: elements array
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
// ebx: untagged index
__ test(eax, Immediate(kSmiTagMask));
__ j(not_equal, &check_heap_number);
// smi case
- __ mov(edx, eax); // Save the value.
- __ sar(eax, kSmiTagSize); // Untag the value.
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
+ __ mov(ecx, eax); // Preserve the value in eax. Key is no longer needed.
+ __ SmiUntag(ecx);
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
// ecx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(ecx, ebx, times_4, 0), eax);
+ __ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
case kExternalFloatArray:
// Need to perform int-to-float conversion.
- __ push(eax);
+ __ push(ecx);
__ fild_s(Operand(esp, 0));
- __ pop(eax);
- __ fstp_s(Operand(ecx, ebx, times_4, 0));
+ __ pop(ecx);
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
break;
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return the original value.
__ bind(&check_heap_number);
+ // eax: value
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
+ // ebx: untagged index
__ cmp(FieldOperand(eax, HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
__ j(not_equal, &slow);
@@ -853,14 +837,12 @@
// +/-Infinity into integer arrays basically undefined. For more
// reproducible behavior, convert these to zero.
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
- __ mov(edx, eax); // Save the value.
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
// ebx: untagged index
- // ecx: base pointer of external storage
+ // edi: base pointer of external storage
// top of FPU stack: value
if (array_type == kExternalFloatArray) {
- __ fstp_s(Operand(ecx, ebx, times_4, 0));
- __ mov(eax, edx); // Return the original value.
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
__ ret(0);
} else {
// Need to perform float-to-int conversion.
@@ -870,29 +852,27 @@
__ j(parity_even, &is_nan);
if (array_type != kExternalUnsignedIntArray) {
- __ push(eax); // Make room on stack
+ __ push(ecx); // Make room on stack
__ fistp_s(Operand(esp, 0));
- __ pop(eax);
+ __ pop(ecx);
} else {
// fistp stores values as signed integers.
// To represent the entire range, we need to store as a 64-bit
// int and discard the high 32 bits.
- __ push(eax); // Make room on stack
- __ push(eax); // Make room on stack
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
__ fistp_d(Operand(esp, 0));
- __ pop(eax);
- __ mov(Operand(esp, 0), eax);
- __ pop(eax);
+ __ pop(ecx);
+ __ add(Operand(esp), Immediate(kPointerSize));
}
- // eax: untagged integer value
+ // ecx: untagged integer value
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray: {
@@ -903,21 +883,20 @@
// This test would apparently detect both NaN and Infinity,
// but we've already checked for NaN using the FPU hardware
// above.
- __ mov_w(edi, FieldOperand(edx, HeapNumber::kValueOffset + 6));
- __ and_(edi, 0x7FF0);
- __ cmp(edi, 0x7FF0);
+ __ mov_w(edx, FieldOperand(eax, HeapNumber::kValueOffset + 6));
+ __ and_(edx, 0x7FF0);
+ __ cmp(edx, 0x7FF0);
__ j(not_equal, ¬_infinity);
- __ mov(eax, 0);
+ __ mov(ecx, 0);
__ bind(¬_infinity);
- __ mov(Operand(ecx, ebx, times_4, 0), eax);
+ __ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
}
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return original value.
__ bind(&is_nan);
__ ffree();
@@ -925,23 +904,22 @@
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), 0);
+ __ mov_b(Operand(edi, ebx, times_1, 0), 0);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov(eax, 0);
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ xor_(ecx, Operand(ecx));
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(ecx, ebx, times_4, 0), Immediate(0));
+ __ mov(Operand(edi, ebx, times_4, 0), Immediate(0));
break;
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return the original value.
}
// Slow case: call runtime.
@@ -1262,7 +1240,8 @@
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -1377,7 +1356,8 @@
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -1394,7 +1374,7 @@
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1);
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
}
@@ -1431,7 +1411,8 @@
__ push(ebx);
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
@@ -1478,7 +1459,8 @@
__ push(value);
__ push(scratch); // return address
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_ArrayLength)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength));
+ __ TailCallExternalReference(ref, 2, 1);
__ bind(&miss);
@@ -1492,38 +1474,39 @@
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
- __ pop(ecx);
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(eax);
+ __ pop(ebx);
+ __ push(edx);
__ push(ecx);
+ __ push(eax);
+ __ push(ebx);
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
+ __ TailCallRuntime(Runtime::kSetProperty, 3, 1);
}
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
- __ pop(ecx);
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(eax);
+ __ pop(ebx);
+ __ push(edx);
__ push(ecx);
+ __ push(eax);
+ __ push(ebx);
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
#undef __
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 5ae3fe2..45e24fa 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -164,7 +164,10 @@
if (Serializer::enabled()) {
// Can't do arithmetic on external references if it might get serialized.
mov(value, Operand(object));
- and_(value, Heap::NewSpaceMask());
+ // 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(value), Immediate(ExternalReference::new_space_mask()));
cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
j(equal, &done);
} else {
@@ -1186,15 +1189,22 @@
}
-void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
- int num_arguments,
- int result_size) {
+void MacroAssembler::TailCallExternalReference(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.
Set(eax, Immediate(num_arguments));
- JumpToRuntime(ext);
+ JumpToExternalReference(ext);
+}
+
+
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid), num_arguments, result_size);
}
@@ -1264,7 +1274,7 @@
}
-void MacroAssembler::JumpToRuntime(const ExternalReference& ext) {
+void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) {
// Set the entry point and jump to the C entry runtime stub.
mov(ebx, Immediate(ext));
CEntryStub ces(1);
@@ -1615,6 +1625,41 @@
}
+void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
+ int frameAlignment = OS::ActivationFrameAlignment();
+ if (frameAlignment != 0) {
+ // Make stack end at alignment and make room for num_arguments words
+ // and the original value of esp.
+ mov(scratch, esp);
+ sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
+ ASSERT(IsPowerOf2(frameAlignment));
+ and_(esp, -frameAlignment);
+ mov(Operand(esp, num_arguments * kPointerSize), scratch);
+ } else {
+ sub(Operand(esp), Immediate(num_arguments * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::CallCFunction(ExternalReference function,
+ int num_arguments) {
+ // Trashing eax is ok as it will be the return value.
+ mov(Operand(eax), Immediate(function));
+ CallCFunction(eax, num_arguments);
+}
+
+
+void MacroAssembler::CallCFunction(Register function,
+ int num_arguments) {
+ call(Operand(function));
+ if (OS::ActivationFrameAlignment() != 0) {
+ mov(esp, Operand(esp, num_arguments * kPointerSize));
+ } else {
+ add(Operand(esp), Immediate(num_arguments * sizeof(int32_t)));
+ }
+}
+
+
CodePatcher::CodePatcher(byte* address, int size)
: address_(address), size_(size), masm_(address, size + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index 69dc54c..a284b63 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -349,7 +349,6 @@
void StubReturn(int argc);
// Call a runtime routine.
- // Eventually this should be used for all C calls.
void CallRuntime(Runtime::Function* f, int num_arguments);
// Call a runtime function, returning the CodeStub object called.
@@ -367,12 +366,34 @@
Object* TryCallRuntime(Runtime::FunctionId id, int num_arguments);
// Tail call of a runtime routine (jump).
- // Like JumpToRuntime, but also takes care of passing the number
- // of arguments.
- void TailCallRuntime(const ExternalReference& ext,
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(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,
int result_size);
+ // Before calling a C-function from generated code, align arguments on stack.
+ // After aligning the frame, arguments must be stored in esp[0], esp[4],
+ // etc., not pushed. The argument count assumes all arguments are word sized.
+ // 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);
+
+ // Calls a C function and cleans up the space for arguments allocated
+ // by PrepareCallCFunction. The called function is not allowed to trigger a
+ // garbage collection, since that might move the code and invalidate the
+ // return address (unless this is somehow accounted for by the called
+ // function).
+ void CallCFunction(ExternalReference function, int num_arguments);
+ void CallCFunction(Register function, int num_arguments);
+
void PushHandleScope(Register scratch);
// Pops a handle scope using the specified scratch register and
@@ -384,7 +405,7 @@
Object* TryPopHandleScope(Register saved, Register scratch);
// Jump to a runtime routine.
- void JumpToRuntime(const ExternalReference& ext);
+ void JumpToExternalReference(const ExternalReference& ext);
// ---------------------------------------------------------------------------
diff --git a/src/ia32/regexp-macro-assembler-ia32.cc b/src/ia32/regexp-macro-assembler-ia32.cc
index f6da693..74b9d12 100644
--- a/src/ia32/regexp-macro-assembler-ia32.cc
+++ b/src/ia32/regexp-macro-assembler-ia32.cc
@@ -324,8 +324,8 @@
__ push(backtrack_stackpointer());
__ push(ebx);
- const int argument_count = 3;
- FrameAlign(argument_count, ecx);
+ static const int argument_count = 3;
+ __ PrepareCallCFunction(argument_count, ecx);
// Put arguments into allocated stack area, last argument highest on stack.
// Parameters are
// Address byte_offset1 - Address captured substring's start.
@@ -346,7 +346,7 @@
ExternalReference compare =
ExternalReference::re_case_insensitive_compare_uc16();
- CallCFunction(compare, argument_count);
+ __ CallCFunction(compare, argument_count);
// Pop original values before reacting on result value.
__ pop(ebx);
__ pop(backtrack_stackpointer());
@@ -784,13 +784,13 @@
__ push(edi);
// Call GrowStack(backtrack_stackpointer())
- int num_arguments = 2;
- FrameAlign(num_arguments, ebx);
+ static const int num_arguments = 2;
+ __ PrepareCallCFunction(num_arguments, ebx);
__ lea(eax, Operand(ebp, kStackHighEnd));
__ mov(Operand(esp, 1 * kPointerSize), eax);
__ mov(Operand(esp, 0 * kPointerSize), backtrack_stackpointer());
ExternalReference grow_stack = ExternalReference::re_grow_stack();
- CallCFunction(grow_stack, num_arguments);
+ __ CallCFunction(grow_stack, num_arguments);
// If return NULL, we have failed to grow the stack, and
// must exit with a stack-overflow exception.
__ or_(eax, Operand(eax));
@@ -951,8 +951,8 @@
// Private methods:
void RegExpMacroAssemblerIA32::CallCheckStackGuardState(Register scratch) {
- int num_arguments = 3;
- FrameAlign(num_arguments, scratch);
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, scratch);
// RegExp code frame pointer.
__ mov(Operand(esp, 2 * kPointerSize), ebp);
// Code* of self.
@@ -962,7 +962,7 @@
__ mov(Operand(esp, 0 * kPointerSize), eax);
ExternalReference check_stack_guard =
ExternalReference::re_check_stack_guard_state();
- CallCFunction(check_stack_guard, num_arguments);
+ __ CallCFunction(check_stack_guard, num_arguments);
}
@@ -1153,37 +1153,6 @@
}
-void RegExpMacroAssemblerIA32::FrameAlign(int num_arguments, Register scratch) {
- // TODO(lrn): Since we no longer use the system stack arbitrarily (but we do
- // use it, e.g., for SafeCall), we know the number of elements on the stack
- // since the last frame alignment. We might be able to do this simpler then.
- int frameAlignment = OS::ActivationFrameAlignment();
- if (frameAlignment != 0) {
- // Make stack end at alignment and make room for num_arguments words
- // and the original value of esp.
- __ mov(scratch, esp);
- __ sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
- ASSERT(IsPowerOf2(frameAlignment));
- __ and_(esp, -frameAlignment);
- __ mov(Operand(esp, num_arguments * kPointerSize), scratch);
- } else {
- __ sub(Operand(esp), Immediate(num_arguments * kPointerSize));
- }
-}
-
-
-void RegExpMacroAssemblerIA32::CallCFunction(ExternalReference function,
- int num_arguments) {
- __ mov(Operand(eax), Immediate(function));
- __ call(Operand(eax));
- if (OS::ActivationFrameAlignment() != 0) {
- __ mov(esp, Operand(esp, num_arguments * kPointerSize));
- } else {
- __ add(Operand(esp), Immediate(num_arguments * sizeof(int32_t)));
- }
-}
-
-
void RegExpMacroAssemblerIA32::LoadCurrentCharacterUnchecked(int cp_offset,
int characters) {
if (mode_ == ASCII) {
diff --git a/src/ia32/regexp-macro-assembler-ia32.h b/src/ia32/regexp-macro-assembler-ia32.h
index d9866b7..0d5e272 100644
--- a/src/ia32/regexp-macro-assembler-ia32.h
+++ b/src/ia32/regexp-macro-assembler-ia32.h
@@ -187,21 +187,6 @@
// (ecx) and increments it by a word size.
inline void Pop(Register target);
- // Before calling a C-function from generated code, align arguments on stack.
- // After aligning the frame, arguments must be stored in esp[0], esp[4],
- // etc., not pushed. The argument count assumes all arguments are word sized.
- // 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.
- inline void FrameAlign(int num_arguments, Register scratch);
-
- // Calls a C function and cleans up the space for arguments allocated
- // by FrameAlign. The called function is not allowed to trigger a garbage
- // collection, since that might move the code and invalidate the return
- // address (unless this is somehow accounted for).
- inline void CallCFunction(ExternalReference function, int num_arguments);
-
MacroAssembler* masm_;
// Which mode to generate code for (ASCII or UC16).
diff --git a/src/ia32/register-allocator-ia32.cc b/src/ia32/register-allocator-ia32.cc
index 20539bf..3ccfe8c 100644
--- a/src/ia32/register-allocator-ia32.cc
+++ b/src/ia32/register-allocator-ia32.cc
@@ -49,6 +49,7 @@
Immediate(handle()));
}
// This result becomes a copy of the fresh one.
+ fresh.set_number_info(number_info());
*this = fresh;
}
ASSERT(is_register());
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index ade324b..a7e9a69 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -446,7 +446,7 @@
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
__ bind(&cleanup);
__ pop(scratch1);
@@ -468,7 +468,7 @@
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
}
private:
@@ -907,7 +907,7 @@
__ push(Immediate(Handle<Map>(transition)));
__ push(eax);
__ push(scratch);
- __ TailCallRuntime(
+ __ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
return;
}
@@ -1589,7 +1589,7 @@
// Do tail-call to the runtime system.
ExternalReference store_callback_property =
ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
- __ TailCallRuntime(store_callback_property, 4, 1);
+ __ TailCallExternalReference(store_callback_property, 4, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1638,7 +1638,7 @@
// Do tail-call to the runtime system.
ExternalReference store_ic_property =
ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
- __ TailCallRuntime(store_ic_property, 3, 1);
+ __ TailCallExternalReference(store_ic_property, 3, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1691,23 +1691,18 @@
String* name) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
__ IncrementCounter(&Counters::keyed_store_field, 1);
- // Get the name from the stack.
- __ mov(ecx, Operand(esp, 1 * kPointerSize));
// Check that the name has not changed.
__ cmp(Operand(ecx), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- // Get the object from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize));
-
// Generate store field code. Trashes the name register.
GenerateStoreField(masm(),
object,
diff --git a/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc
index 75ff670..7b03a5b 100644
--- a/src/ia32/virtual-frame-ia32.cc
+++ b/src/ia32/virtual-frame-ia32.cc
@@ -37,23 +37,6 @@
#define __ ACCESS_MASM(masm())
-// -------------------------------------------------------------------------
-// VirtualFrame implementation.
-
-// On entry to a function, the virtual frame already contains the receiver,
-// the parameters, and a return address. All frame elements are in memory.
-VirtualFrame::VirtualFrame()
- : elements_(parameter_count() + local_count() + kPreallocatedElements),
- stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
- for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
- }
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- register_locations_[i] = kIllegalIndex;
- }
-}
-
-
void VirtualFrame::SyncElementBelowStackPointer(int index) {
// Emit code to write elements below the stack pointer to their
// (already allocated) stack address.
@@ -179,7 +162,7 @@
if (element.is_constant() || element.is_copy()) {
if (element.is_synced()) {
// Just spill.
- elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown);
+ elements_[i] = FrameElement::MemoryElement(NumberInfo::Unknown());
} else {
// Allocate to a register.
FrameElement backing_element; // Invalid if not a copy.
@@ -191,7 +174,7 @@
elements_[i] =
FrameElement::RegisterElement(fresh.reg(),
FrameElement::NOT_SYNCED,
- NumberInfo::kUnknown);
+ NumberInfo::Unknown());
Use(fresh.reg(), i);
// Emit a move.
@@ -224,7 +207,7 @@
// The copy flag is not relied on before the end of this loop,
// including when registers are spilled.
elements_[i].clear_copied();
- elements_[i].set_number_info(NumberInfo::kUnknown);
+ elements_[i].set_number_info(NumberInfo::Unknown());
}
}
}
@@ -896,30 +879,39 @@
}
+// This function assumes that the only results that could be in a_reg or b_reg
+// are a and b. Other results can be live, but must not be in a_reg or b_reg.
+void VirtualFrame::MoveResultsToRegisters(Result* a,
+ Result* b,
+ Register a_reg,
+ Register b_reg) {
+ if (a->is_register() && a->reg().is(a_reg)) {
+ b->ToRegister(b_reg);
+ } else if (!cgen()->allocator()->is_used(a_reg)) {
+ a->ToRegister(a_reg);
+ b->ToRegister(b_reg);
+ } else if (cgen()->allocator()->is_used(b_reg)) {
+ // a must be in b_reg, b in a_reg.
+ __ xchg(a_reg, b_reg);
+ // Results a and b will be invalidated, so it is ok if they are switched.
+ } else {
+ b->ToRegister(b_reg);
+ a->ToRegister(a_reg);
+ }
+ a->Unuse();
+ b->Unuse();
+}
+
+
Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
// Name and receiver are on the top of the frame. The IC expects
// name in ecx and receiver in eax.
- Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
Result name = Pop();
Result receiver = Pop();
PrepareForCall(0, 0); // No stack arguments.
- // Move results to the right registers:
- if (name.is_register() && name.reg().is(eax)) {
- if (receiver.is_register() && receiver.reg().is(ecx)) {
- // Wrong registers.
- __ xchg(eax, ecx);
- } else {
- // Register ecx is free for name, which frees eax for receiver.
- name.ToRegister(ecx);
- receiver.ToRegister(eax);
- }
- } else {
- // Register eax is free for receiver, which frees ecx for name.
- receiver.ToRegister(eax);
- name.ToRegister(ecx);
- }
- name.Unuse();
- receiver.Unuse();
+ MoveResultsToRegisters(&name, &receiver, ecx, eax);
+
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
return RawCallCodeObject(ic, mode);
}
@@ -929,20 +921,7 @@
Result key = Pop();
Result receiver = Pop();
PrepareForCall(0, 0);
-
- if (!key.is_register() || !key.reg().is(edx)) {
- // Register edx is available for receiver.
- receiver.ToRegister(edx);
- key.ToRegister(eax);
- } else if (!receiver.is_register() || !receiver.reg().is(eax)) {
- // Register eax is available for key.
- key.ToRegister(eax);
- receiver.ToRegister(edx);
- } else {
- __ xchg(edx, eax);
- }
- key.Unuse();
- receiver.Unuse();
+ MoveResultsToRegisters(&key, &receiver, eax, edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
return RawCallCodeObject(ic, mode);
@@ -958,42 +937,57 @@
PrepareForCall(0, 0);
value.ToRegister(eax);
__ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- __ mov(ecx, name);
+ value.Unuse();
} else {
Result receiver = Pop();
PrepareForCall(0, 0);
-
- if (value.is_register() && value.reg().is(edx)) {
- if (receiver.is_register() && receiver.reg().is(eax)) {
- // Wrong registers.
- __ xchg(eax, edx);
- } else {
- // Register eax is free for value, which frees edx for receiver.
- value.ToRegister(eax);
- receiver.ToRegister(edx);
- }
- } else {
- // Register edx is free for receiver, which guarantees eax is free for
- // value.
- receiver.ToRegister(edx);
- value.ToRegister(eax);
- }
+ MoveResultsToRegisters(&value, &receiver, eax, edx);
}
__ mov(ecx, name);
- value.Unuse();
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}
Result VirtualFrame::CallKeyedStoreIC() {
// Value, key, and receiver are on the top of the frame. The IC
- // expects value in eax and key and receiver on the stack. It does
- // not drop the key and receiver.
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ // expects value in eax, key in ecx, and receiver in edx.
Result value = Pop();
- PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
- value.ToRegister(eax);
- value.Unuse();
+ Result key = Pop();
+ Result receiver = Pop();
+ PrepareForCall(0, 0);
+ if (!cgen()->allocator()->is_used(eax) ||
+ (value.is_register() && value.reg().is(eax))) {
+ value.ToRegister(eax); // No effect if value is in eax already.
+ MoveResultsToRegisters(&key, &receiver, ecx, edx);
+ value.Unuse();
+ } else if (!cgen()->allocator()->is_used(ecx) ||
+ (key.is_register() && key.reg().is(ecx))) {
+ // Receiver and/or key are in eax.
+ key.ToRegister(ecx);
+ MoveResultsToRegisters(&value, &receiver, eax, edx);
+ key.Unuse();
+ } else if (!cgen()->allocator()->is_used(edx) ||
+ (receiver.is_register() && receiver.reg().is(edx))) {
+ receiver.ToRegister(edx);
+ MoveResultsToRegisters(&key, &value, ecx, eax);
+ receiver.Unuse();
+ } else {
+ // All three registers are used, and no value is in the correct place.
+ // We have one of the two circular permutations of eax, ecx, edx.
+ ASSERT(value.is_register());
+ if (value.reg().is(ecx)) {
+ __ xchg(eax, edx);
+ __ xchg(eax, ecx);
+ } else {
+ __ xchg(eax, ecx);
+ __ xchg(eax, edx);
+ }
+ value.Unuse();
+ key.Unuse();
+ receiver.Unuse();
+ }
+
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}
@@ -1068,7 +1062,7 @@
ASSERT(element.is_valid());
// Get number type information of the result.
- NumberInfo::Type info;
+ NumberInfo info;
if (!element.is_copy()) {
info = element.number_info();
} else {
@@ -1143,7 +1137,7 @@
}
-void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Register reg, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -1151,7 +1145,7 @@
}
-void VirtualFrame::EmitPush(Operand operand, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Operand operand, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -1159,7 +1153,7 @@
}
-void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Immediate immediate, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h
index d4cc141..cd2d18f 100644
--- a/src/ia32/virtual-frame-ia32.h
+++ b/src/ia32/virtual-frame-ia32.h
@@ -73,7 +73,7 @@
static const int kIllegalIndex = -1;
// Construct an initial virtual frame on entry to a JS function.
- VirtualFrame();
+ inline VirtualFrame();
// Construct a virtual frame as a clone of an existing one.
explicit inline VirtualFrame(VirtualFrame* original);
@@ -84,7 +84,7 @@
// Create a duplicate of an existing valid frame element.
FrameElement CopyElementAt(int index,
- NumberInfo::Type info = NumberInfo::kUninitialized);
+ NumberInfo info = NumberInfo::Uninitialized());
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -388,14 +388,14 @@
// Push an element on top of the expression stack and emit a
// corresponding push instruction.
void EmitPush(Register reg,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
void EmitPush(Operand operand,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
void EmitPush(Immediate immediate,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
// Push an element on the virtual frame.
- inline void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
+ inline void Push(Register reg, NumberInfo info = NumberInfo::Unknown());
inline void Push(Handle<Object> value);
inline void Push(Smi* value);
@@ -571,6 +571,14 @@
// Register counts are correctly updated.
int InvalidateFrameSlotAt(int index);
+ // This function assumes that a and b are the only results that could be in
+ // the registers a_reg or b_reg. Other results can be live, but must not
+ // be in the registers a_reg or b_reg. The results a and b are invalidated.
+ void MoveResultsToRegisters(Result* a,
+ Result* b,
+ Register a_reg,
+ Register b_reg);
+
// Call a code stub that has already been prepared for calling (via
// PrepareForCall).
Result RawCallStub(CodeStub* stub);
diff --git a/src/ic.cc b/src/ic.cc
index 107c3c0..f82e61e 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -63,7 +63,9 @@
Code* new_target,
const char* extra_info) {
if (FLAG_trace_ic) {
- State new_state = StateFrom(new_target, Heap::undefined_value());
+ State new_state = StateFrom(new_target,
+ Heap::undefined_value(),
+ Heap::undefined_value());
PrintF("[%s (%c->%c)%s", type,
TransitionMarkFromState(old_state),
TransitionMarkFromState(new_state),
@@ -132,7 +134,7 @@
}
#endif
-IC::State IC::StateFrom(Code* target, Object* receiver) {
+IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
IC::State state = target->ic_state();
if (state != MONOMORPHIC) return state;
@@ -148,7 +150,7 @@
// the receiver map's code cache. Therefore, if the current target
// is in the receiver map's code cache, the inline cache failed due
// to prototype check failure.
- int index = map->IndexInCodeCache(target);
+ int index = map->IndexInCodeCache(String::cast(name), target);
if (index >= 0) {
// For keyed load/store, the most likely cause of cache failure is
// that the key has changed. We do not distinguish between
@@ -160,7 +162,7 @@
// Remove the target from the code cache to avoid hitting the same
// invalid stub again.
- map->RemoveFromCodeCache(index);
+ map->RemoveFromCodeCache(String::cast(name), target, index);
return MONOMORPHIC_PROTOTYPE_FAILURE;
}
@@ -222,6 +224,7 @@
case Code::STORE_IC: return StoreIC::Clear(address, target);
case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
case Code::CALL_IC: return CallIC::Clear(address, target);
+ case Code::BINARY_OP_IC: return BinaryOpIC::Clear(address, target);
default: UNREACHABLE();
}
}
@@ -1049,6 +1052,20 @@
return *value;
}
+
+ // Use specialized code for setting the length of arrays.
+ if (receiver->IsJSArray()
+ && name->Equals(Heap::length_symbol())
+ && receiver->AllowsSetElementsLength()) {
+#ifdef DEBUG
+ if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
+#endif
+ Code* target = Builtins::builtin(Builtins::StoreIC_ArrayLength);
+ set_target(target);
+ StubCache::Set(*name, HeapObject::cast(*object)->map(), target);
+ return receiver->SetProperty(*name, *value, NONE);
+ }
+
// Lookup the property locally in the receiver.
if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
LookupResult lookup;
@@ -1285,7 +1302,7 @@
NoHandleAllocation na;
ASSERT(args.length() == 2);
CallIC ic;
- IC::State state = IC::StateFrom(ic.target(), args[0]);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Object* result =
ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
@@ -1318,7 +1335,7 @@
NoHandleAllocation na;
ASSERT(args.length() == 2);
LoadIC ic;
- IC::State state = IC::StateFrom(ic.target(), args[0]);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
return ic.Load(state, args.at<Object>(0), args.at<String>(1));
}
@@ -1328,7 +1345,7 @@
NoHandleAllocation na;
ASSERT(args.length() == 2);
KeyedLoadIC ic;
- IC::State state = IC::StateFrom(ic.target(), args[0]);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
}
@@ -1338,7 +1355,7 @@
NoHandleAllocation na;
ASSERT(args.length() == 3);
StoreIC ic;
- IC::State state = IC::StateFrom(ic.target(), args[0]);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
return ic.Store(state, args.at<Object>(0), args.at<String>(1),
args.at<Object>(2));
}
@@ -1351,7 +1368,9 @@
JSObject* receiver = JSObject::cast(args[0]);
Object* len = args[1];
- return receiver->SetElementsLength(len);
+ Object* result = receiver->SetElementsLength(len);
+ if (result->IsFailure()) return result;
+ return len;
}
@@ -1394,12 +1413,118 @@
NoHandleAllocation na;
ASSERT(args.length() == 3);
KeyedStoreIC ic;
- IC::State state = IC::StateFrom(ic.target(), args[0]);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
return ic.Store(state, args.at<Object>(0), args.at<Object>(1),
args.at<Object>(2));
}
+void BinaryOpIC::patch(Code* code) {
+ set_target(code);
+}
+
+
+void BinaryOpIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+
+ // At the end of a fast case stub there should be a reference to
+ // a corresponding UNINITIALIZED stub, so look for the last reloc info item.
+ RelocInfo* rinfo = NULL;
+ for (RelocIterator it(target, RelocInfo::kCodeTargetMask);
+ !it.done(); it.next()) {
+ rinfo = it.rinfo();
+ }
+
+ ASSERT(rinfo != NULL);
+ Code* uninit_stub = Code::GetCodeFromTargetAddress(rinfo->target_address());
+ ASSERT(uninit_stub->ic_state() == UNINITIALIZED &&
+ uninit_stub->kind() == Code::BINARY_OP_IC);
+ SetTargetAtAddress(address, uninit_stub);
+}
+
+
+const char* BinaryOpIC::GetName(TypeInfo type_info) {
+ switch (type_info) {
+ case DEFAULT: return "Default";
+ case GENERIC: return "Generic";
+ case HEAP_NUMBERS: return "HeapNumbers";
+ case STRINGS: return "Strings";
+ default: return "Invalid";
+ }
+}
+
+
+BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
+ switch (type_info) {
+ // DEFAULT is mapped to UNINITIALIZED so that calls to DEFAULT stubs
+ // are not cleared at GC.
+ case DEFAULT: return UNINITIALIZED;
+
+ // Could have mapped GENERIC to MONOMORPHIC just as well but MEGAMORPHIC is
+ // conceptually closer.
+ case GENERIC: return MEGAMORPHIC;
+
+ default: return MONOMORPHIC;
+ }
+}
+
+
+BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left,
+ Object* right) {
+ // Patching is never requested for the two smis.
+ ASSERT(!left->IsSmi() || !right->IsSmi());
+
+ if (left->IsNumber() && right->IsNumber()) {
+ return HEAP_NUMBERS;
+ }
+
+ if (left->IsString() || right->IsString()) {
+ // Patching for fast string ADD makes sense even if only one of the
+ // arguments is a string.
+ return STRINGS;
+ }
+
+ return GENERIC;
+}
+
+
+// defined in codegen-<arch>.cc
+Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
+
+
+Object* BinaryOp_Patch(Arguments args) {
+ ASSERT(args.length() == 6);
+
+ Handle<Object> left = args.at<Object>(0);
+ Handle<Object> right = args.at<Object>(1);
+ Handle<Object> result = args.at<Object>(2);
+ int key = Smi::cast(args[3])->value();
+#ifdef DEBUG
+ Token::Value op = static_cast<Token::Value>(Smi::cast(args[4])->value());
+ BinaryOpIC::TypeInfo prev_type_info =
+ static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[5])->value());
+#endif // DEBUG
+ { HandleScope scope;
+ BinaryOpIC::TypeInfo type_info = BinaryOpIC::GetTypeInfo(*left, *right);
+ Handle<Code> code = GetBinaryOpStub(key, type_info);
+ if (!code.is_null()) {
+ BinaryOpIC ic;
+ ic.patch(*code);
+#ifdef DEBUG
+ if (FLAG_trace_ic) {
+ PrintF("[BinaryOpIC (%s->%s)#%s]\n",
+ BinaryOpIC::GetName(prev_type_info),
+ BinaryOpIC::GetName(type_info),
+ Token::Name(op));
+ }
+#endif // DEBUG
+ }
+ }
+
+ return *result;
+}
+
+
static Address IC_utilities[] = {
#define ADDR(name) FUNCTION_ADDR(name),
IC_UTIL_LIST(ADDR)
diff --git a/src/ic.h b/src/ic.h
index d545989..7464a57 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -55,7 +55,8 @@
ICU(LoadPropertyWithInterceptorForLoad) \
ICU(LoadPropertyWithInterceptorForCall) \
ICU(KeyedLoadPropertyWithInterceptor) \
- ICU(StoreInterceptorProperty)
+ ICU(StoreInterceptorProperty) \
+ ICU(BinaryOp_Patch)
//
// IC is the base class for LoadIC, StoreIC, CallIC, KeyedLoadIC,
@@ -93,8 +94,8 @@
Code* target() { return GetTargetAtAddress(address()); }
inline Address address();
- // Compute the current IC state based on the target stub and the receiver.
- static State StateFrom(Code* target, Object* receiver);
+ // Compute the current IC state based on the target stub, receiver and name.
+ static State StateFrom(Code* target, Object* receiver, Object* name);
// Clear the inline cache to initial state.
static void Clear(Address address);
@@ -444,6 +445,30 @@
};
+class BinaryOpIC: public IC {
+ public:
+
+ enum TypeInfo {
+ DEFAULT, // Initial state. When first executed, patches to one
+ // of the following states depending on the operands types.
+ HEAP_NUMBERS, // Both arguments are HeapNumbers.
+ STRINGS, // At least one of the arguments is String.
+ GENERIC // Non-specialized case (processes any type combination).
+ };
+
+ BinaryOpIC() : IC(NO_EXTRA_FRAME) { }
+
+ void patch(Code* code);
+
+ static void Clear(Address address, Code* target);
+
+ static const char* GetName(TypeInfo type_info);
+
+ static State ToState(TypeInfo type_info);
+
+ static TypeInfo GetTypeInfo(Object* left, Object* right);
+};
+
} } // namespace v8::internal
#endif // V8_IC_H_
diff --git a/src/jsregexp.h b/src/jsregexp.h
index b99a89e..46d53c2 100644
--- a/src/jsregexp.h
+++ b/src/jsregexp.h
@@ -29,6 +29,7 @@
#define V8_JSREGEXP_H_
#include "macro-assembler.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/jump-target-inl.h b/src/jump-target-inl.h
index 12fabc3..6db0081 100644
--- a/src/jump-target-inl.h
+++ b/src/jump-target-inl.h
@@ -46,7 +46,7 @@
entry_frame_->elements_[target->index()].set_copied();
}
if (direction_ == BIDIRECTIONAL && !target->is_copy()) {
- element->set_number_info(NumberInfo::kUnknown);
+ element->set_number_info(NumberInfo::Unknown());
}
}
diff --git a/src/jump-target.cc b/src/jump-target.cc
index ab09870..7b1ced7 100644
--- a/src/jump-target.cc
+++ b/src/jump-target.cc
@@ -135,7 +135,7 @@
FrameElement* target = elements[index];
if (target == NULL) {
entry_frame_->elements_.Add(
- FrameElement::MemoryElement(NumberInfo::kUninitialized));
+ FrameElement::MemoryElement(NumberInfo::Uninitialized()));
} else {
entry_frame_->elements_.Add(*target);
InitializeEntryElement(index, target);
@@ -152,12 +152,12 @@
RegisterFile candidate_registers;
int best_count = kMinInt;
int best_reg_num = RegisterAllocator::kInvalidRegister;
- NumberInfo::Type info = NumberInfo::kUninitialized;
+ NumberInfo info = NumberInfo::Uninitialized();
for (int j = 0; j < reaching_frames_.length(); j++) {
FrameElement element = reaching_frames_[j]->elements_[i];
if (direction_ == BIDIRECTIONAL) {
- info = NumberInfo::kUnknown;
+ info = NumberInfo::Unknown();
} else if (!element.is_copy()) {
info = NumberInfo::Combine(info, element.number_info());
} else {
@@ -181,7 +181,7 @@
// We must have a number type information now (not for copied elements).
ASSERT(entry_frame_->elements_[i].is_copy()
- || info != NumberInfo::kUninitialized);
+ || !info.IsUninitialized());
// If the value is synced on all frames, put it in memory. This
// costs nothing at the merge code but will incur a
@@ -211,7 +211,7 @@
Register reg = RegisterAllocator::ToRegister(best_reg_num);
entry_frame_->elements_[i] =
FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED,
- NumberInfo::kUninitialized);
+ NumberInfo::Uninitialized());
if (is_copied) entry_frame_->elements_[i].set_copied();
entry_frame_->set_register_location(reg, i);
}
@@ -225,8 +225,7 @@
if (direction_ == BIDIRECTIONAL) {
for (int i = 0; i < length; ++i) {
if (!entry_frame_->elements_[i].is_copy()) {
- ASSERT(entry_frame_->elements_[i].number_info() ==
- NumberInfo::kUnknown);
+ ASSERT(entry_frame_->elements_[i].number_info().IsUnknown());
}
}
}
diff --git a/src/jump-target.h b/src/jump-target.h
index dd291c6..db7c115 100644
--- a/src/jump-target.h
+++ b/src/jump-target.h
@@ -29,6 +29,7 @@
#define V8_JUMP_TARGET_H_
#include "macro-assembler.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/liveedit-delay.js b/src/liveedit-delay.js
new file mode 100644
index 0000000..12479b1
--- /dev/null
+++ b/src/liveedit-delay.js
@@ -0,0 +1,426 @@
+// 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.
+
+// LiveEdit feature implementation. The script should be executed after
+// debug-delay.js.
+
+
+// Changes script text and recompiles all relevant functions if possible.
+// The change is always a substring (change_pos, change_pos + change_len)
+// being replaced with a completely different string new_str.
+//
+// Only one function will have its Code changed in result of this function.
+// All nested functions (should they have any instances at the moment) are left
+// unchanged and re-linked to a newly created script instance representing old
+// version of the source. (Generally speaking,
+// during the change all nested functions are erased and completely different
+// set of nested functions are introduced.) All other functions just have
+// their positions updated.
+//
+// @param {Script} script that is being changed
+// @param {Array} change_log a list that collects engineer-readable description
+// of what happened.
+Debug.LiveEditChangeScript = function(script, change_pos, change_len, new_str,
+ change_log) {
+
+ // So far the function works as namespace.
+ var liveedit = Debug.LiveEditChangeScript;
+ var Assert = liveedit.Assert;
+
+ // Fully compiles source string as a script. Returns Array of
+ // FunctionCompileInfo -- a descriptions of all functions of the script.
+ // Elements of array are ordered by start positions of functions (from top
+ // to bottom) in the source. Fields outer_index and next_sibling_index help
+ // to navigate the nesting structure of functions.
+ //
+ // The script is used for compilation, because it produces code that
+ // needs to be linked with some particular script (for nested functions).
+ function DebugGatherCompileInfo(source) {
+ // Get function info, elements are partially sorted (it is a tree
+ // of nested functions serialized as parent followed by serialized children.
+ var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
+
+ // Sort function infos by start position field.
+ var compile_info = new Array();
+ var old_index_map = new Array();
+ for (var i = 0; i < raw_compile_info.length; i++) {
+ compile_info.push(new liveedit.FunctionCompileInfo(raw_compile_info[i]));
+ old_index_map.push(i);
+ }
+
+ for (var i = 0; i < compile_info.length; i++) {
+ var k = i;
+ for (var j = i + 1; j < compile_info.length; j++) {
+ if (compile_info[k].start_position > compile_info[j].start_position) {
+ k = j;
+ }
+ }
+ if (k != i) {
+ var temp_info = compile_info[k];
+ var temp_index = old_index_map[k];
+ compile_info[k] = compile_info[i];
+ old_index_map[k] = old_index_map[i];
+ compile_info[i] = temp_info;
+ old_index_map[i] = temp_index;
+ }
+ }
+
+ // After sorting update outer_inder field using old_index_map. Also
+ // set next_sibling_index field.
+ var current_index = 0;
+
+ // The recursive function, that goes over all children of a particular
+ // node (i.e. function info).
+ function ResetIndexes(new_parent_index, old_parent_index) {
+ var previous_sibling = -1;
+ while (current_index < compile_info.length &&
+ compile_info[current_index].outer_index == old_parent_index) {
+ var saved_index = current_index;
+ compile_info[saved_index].outer_index = new_parent_index;
+ if (previous_sibling != -1) {
+ compile_info[previous_sibling].next_sibling_index = saved_index;
+ }
+ previous_sibling = saved_index;
+ current_index++;
+ ResetIndexes(saved_index, old_index_map[saved_index]);
+ }
+ if (previous_sibling != -1) {
+ compile_info[previous_sibling].next_sibling_index = -1;
+ }
+ }
+
+ ResetIndexes(-1, -1);
+ Assert(current_index == compile_info.length);
+
+ return compile_info;
+ }
+
+ // Given a positions, finds a function that fully includes the entire change.
+ function FindChangedFunction(compile_info, offset, len) {
+ // First condition: function should start before the change region.
+ // Function #0 (whole-script function) always does, but we want
+ // one, that is later in this list.
+ var index = 0;
+ while (index + 1 < compile_info.length &&
+ compile_info[index + 1].start_position <= offset) {
+ index++;
+ }
+ // Now we are at the last function that begins before the change
+ // region. The function that covers entire change region is either
+ // this function or the enclosing one.
+ for (; compile_info[index].end_position < offset + len;
+ index = compile_info[index].outer_index) {
+ Assert(index != -1);
+ }
+ return index;
+ }
+
+ // Variable forward declarations. Preprocessor "Minifier" needs them.
+ var old_compile_info;
+ var shared_infos;
+ // Finds SharedFunctionInfo that corresponds compile info with index
+ // in old version of the script.
+ function FindFunctionInfo(index) {
+ var old_info = old_compile_info[index];
+ for (var i = 0; i < shared_infos.length; i++) {
+ var info = shared_infos[i];
+ if (info.start_position == old_info.start_position &&
+ info.end_position == old_info.end_position) {
+ return info;
+ }
+ }
+ }
+
+ // Replaces function's Code.
+ function PatchCode(new_info, shared_info) {
+ %LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array);
+
+ change_log.push( {function_patched: new_info.function_name} );
+ }
+
+ var change_len_old;
+ var change_len_new;
+ // Translate position in old version of script into position in new
+ // version of script.
+ function PosTranslator(old_pos) {
+ if (old_pos <= change_pos) {
+ return old_pos;
+ }
+ if (old_pos >= change_pos + change_len_old) {
+ return old_pos + change_len_new - change_len_old;
+ }
+ return -1;
+ }
+
+ var position_change_array;
+ var position_patch_report;
+ function PatchPositions(new_info, shared_info) {
+ if (!shared_info) {
+ // TODO: explain what is happening.
+ return;
+ }
+ %LiveEditPatchFunctionPositions(shared_info.raw_array,
+ position_change_array);
+ position_patch_report.push( { name: new_info.function_name } );
+ }
+
+ var link_to_old_script_report;
+ var old_script;
+ // Makes a function associated with another instance of a script (the
+ // one representing its old version). This way the function still
+ // may access its own text.
+ function LinkToOldScript(shared_info) {
+ %LiveEditRelinkFunctionToScript(shared_info.raw_array, old_script);
+
+ link_to_old_script_report.push( { name: shared_info.function_name } );
+ }
+
+
+
+ var old_source = script.source;
+ var change_len_old = change_len;
+ var change_len_new = new_str.length;
+
+ // Prepare new source string.
+ var new_source = old_source.substring(0, change_pos) +
+ new_str + old_source.substring(change_pos + change_len);
+
+ // Find all SharedFunctionInfo's that are compiled from this script.
+ var shared_raw_list = %LiveEditFindSharedFunctionInfosForScript(script);
+
+ var shared_infos = new Array();
+
+ for (var i = 0; i < shared_raw_list.length; i++) {
+ shared_infos.push(new liveedit.SharedInfoWrapper(shared_raw_list[i]));
+ }
+
+ // Gather compile information about old version of script.
+ var old_compile_info = DebugGatherCompileInfo(old_source);
+
+ // Gather compile information about new version of script.
+ var new_compile_info;
+ try {
+ new_compile_info = DebugGatherCompileInfo(new_source);
+ } catch (e) {
+ throw new liveedit.Failure("Failed to compile new version of script: " + e);
+ }
+
+ // An index of a single function, that is going to have its code replaced.
+ var function_being_patched =
+ FindChangedFunction(old_compile_info, change_pos, change_len_old);
+
+ // In old and new script versions function with a change should have the
+ // same indexes.
+ var function_being_patched2 =
+ FindChangedFunction(new_compile_info, change_pos, change_len_new);
+ Assert(function_being_patched == function_being_patched2,
+ "inconsistent old/new compile info");
+
+ // Check that function being patched has the same expectations in a new
+ // version. Otherwise we cannot safely patch its behavior and should
+ // choose the outer function instead.
+ while (!liveedit.CompareFunctionExpectations(
+ old_compile_info[function_being_patched],
+ new_compile_info[function_being_patched])) {
+
+ Assert(old_compile_info[function_being_patched].outer_index ==
+ new_compile_info[function_being_patched].outer_index);
+ function_being_patched =
+ old_compile_info[function_being_patched].outer_index;
+ Assert(function_being_patched != -1);
+ }
+
+ // Check that function being patched is not currently on stack.
+ liveedit.CheckStackActivations(
+ [ FindFunctionInfo(function_being_patched) ], change_log );
+
+
+ // Committing all changes.
+ var old_script_name = liveedit.CreateNameForOldScript(script);
+
+ // Update the script text and create a new script representing an old
+ // version of the script.
+ var old_script = %LiveEditReplaceScript(script, new_source, old_script_name);
+
+ PatchCode(new_compile_info[function_being_patched],
+ FindFunctionInfo(function_being_patched));
+
+ var position_patch_report = new Array();
+ change_log.push( {position_patched: position_patch_report} );
+
+ var position_change_array = [ change_pos,
+ change_pos + change_len_old,
+ change_pos + change_len_new ];
+
+ // Update positions of all outer functions (i.e. all functions, that
+ // are partially below the function being patched).
+ for (var i = new_compile_info[function_being_patched].outer_index;
+ i != -1;
+ i = new_compile_info[i].outer_index) {
+ PatchPositions(new_compile_info[i], FindFunctionInfo(i));
+ }
+
+ // Update positions of all functions that are fully below the function
+ // being patched.
+ var old_next_sibling =
+ old_compile_info[function_being_patched].next_sibling_index;
+ var new_next_sibling =
+ new_compile_info[function_being_patched].next_sibling_index;
+
+ // We simply go over the tail of both old and new lists. Their tails should
+ // have an identical structure.
+ if (old_next_sibling == -1) {
+ Assert(new_next_sibling == -1);
+ } else {
+ Assert(old_compile_info.length - old_next_sibling ==
+ new_compile_info.length - new_next_sibling);
+
+ for (var i = old_next_sibling, j = new_next_sibling;
+ i < old_compile_info.length; i++, j++) {
+ PatchPositions(new_compile_info[j], FindFunctionInfo(i));
+ }
+ }
+
+ var link_to_old_script_report = new Array();
+ change_log.push( { linked_to_old_script: link_to_old_script_report } );
+
+ // We need to link to old script all former nested functions.
+ for (var i = function_being_patched + 1; i < old_next_sibling; i++) {
+ LinkToOldScript(FindFunctionInfo(i), old_script);
+ }
+}
+
+Debug.LiveEditChangeScript.Assert = function(condition, message) {
+ if (!condition) {
+ if (message) {
+ throw "Assert " + message;
+ } else {
+ throw "Assert";
+ }
+ }
+}
+
+// An object describing function compilation details. Its index fields
+// apply to indexes inside array that stores these objects.
+Debug.LiveEditChangeScript.FunctionCompileInfo = function(raw_array) {
+ this.function_name = raw_array[0];
+ this.start_position = raw_array[1];
+ this.end_position = raw_array[2];
+ this.param_num = raw_array[3];
+ this.code = raw_array[4];
+ this.scope_info = raw_array[5];
+ this.outer_index = raw_array[6];
+ this.next_sibling_index = null;
+ this.raw_array = raw_array;
+}
+
+// A structure describing SharedFunctionInfo.
+Debug.LiveEditChangeScript.SharedInfoWrapper = function(raw_array) {
+ this.function_name = raw_array[0];
+ this.start_position = raw_array[1];
+ this.end_position = raw_array[2];
+ this.info = raw_array[3];
+ this.raw_array = raw_array;
+}
+
+// Adds a suffix to script name to mark that it is old version.
+Debug.LiveEditChangeScript.CreateNameForOldScript = function(script) {
+ // TODO(635): try better than this; support several changes.
+ return script.name + " (old)";
+}
+
+// Compares a function interface old and new version, whether it
+// changed or not.
+Debug.LiveEditChangeScript.CompareFunctionExpectations =
+ function(function_info1, function_info2) {
+ // Check that function has the same number of parameters (there may exist
+ // an adapter, that won't survive function parameter number change).
+ if (function_info1.param_num != function_info2.param_num) {
+ return false;
+ }
+ var scope_info1 = function_info1.scope_info;
+ var scope_info2 = function_info2.scope_info;
+
+ if (!scope_info1) {
+ return !scope_info2;
+ }
+
+ if (scope_info1.length != scope_info2.length) {
+ return false;
+ }
+
+ // Check that outer scope structure is not changed. Otherwise the function
+ // will not properly work with existing scopes.
+ return scope_info1.toString() == scope_info2.toString();
+}
+
+// For array of wrapped shared function infos checks that none of them
+// have activations on stack (of any thread). Throws a Failure exception
+// if this proves to be false.
+Debug.LiveEditChangeScript.CheckStackActivations = function(shared_wrapper_list,
+ change_log) {
+ var liveedit = Debug.LiveEditChangeScript;
+
+ var shared_list = new Array();
+ for (var i = 0; i < shared_wrapper_list.length; i++) {
+ shared_list[i] = shared_wrapper_list[i].info;
+ }
+ var result = %LiveEditCheckStackActivations(shared_list);
+ var problems = new Array();
+ for (var i = 0; i < shared_list.length; i++) {
+ if (result[i] == liveedit.FunctionPatchabilityStatus.FUNCTION_BLOCKED_ON_STACK) {
+ var shared = shared_list[i];
+ var description = {
+ name: shared.function_name,
+ start_pos: shared.start_position,
+ end_pos: shared.end_position
+ };
+ problems.push(description);
+ }
+ }
+ if (problems.length > 0) {
+ change_log.push( { functions_on_stack: problems } );
+ throw new liveedit.Failure("Blocked by functions on stack");
+ }
+}
+
+// A copy of the FunctionPatchabilityStatus enum from liveedit.h
+Debug.LiveEditChangeScript.FunctionPatchabilityStatus = {
+ FUNCTION_AVAILABLE_FOR_PATCH: 0,
+ FUNCTION_BLOCKED_ON_STACK: 1
+}
+
+
+// A logical failure in liveedit process. This means that change_log
+// is valid and consistent description of what happened.
+Debug.LiveEditChangeScript.Failure = function(message) {
+ this.message = message;
+}
+
+Debug.LiveEditChangeScript.Failure.prototype.toString = function() {
+ return "LiveEdit Failure: " + this.message;
+}
diff --git a/src/liveedit.cc b/src/liveedit.cc
index c50e007..513cc02 100644
--- a/src/liveedit.cc
+++ b/src/liveedit.cc
@@ -39,49 +39,445 @@
namespace internal {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+static void CompileScriptForTracker(Handle<Script> script) {
+ const bool is_eval = false;
+ const bool is_global = true;
+ // TODO(635): support extensions.
+ Extension* extension = NULL;
+
+ PostponeInterruptsScope postpone;
+
+ // Only allow non-global compiles for eval.
+ ASSERT(is_eval || is_global);
+
+ // Build AST.
+ ScriptDataImpl* pre_data = NULL;
+ FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data);
+
+ // Check for parse errors.
+ if (lit == NULL) {
+ ASSERT(Top::has_pending_exception());
+ return;
+ }
+
+ // Compile the code.
+ CompilationInfo info(lit, script, is_eval);
+ Handle<Code> code = MakeCodeForLiveEdit(&info);
+
+ // Check for stack-overflow exceptions.
+ if (code.is_null()) {
+ Top::StackOverflow();
+ return;
+ }
+}
+
+// Unwraps JSValue object, returning its field "value"
+static Handle<Object> UnwrapJSValue(Handle<JSValue> jsValue) {
+ return Handle<Object>(jsValue->value());
+}
+
+// Wraps any object into a OpaqueReference, that will hide the object
+// from JavaScript.
+static Handle<JSValue> WrapInJSValue(Object* object) {
+ Handle<JSFunction> constructor = Top::opaque_reference_function();
+ Handle<JSValue> result =
+ Handle<JSValue>::cast(Factory::NewJSObject(constructor));
+ result->set_value(object);
+ return result;
+}
+
+// Simple helper class that creates more or less typed structures over
+// JSArray object. This is an adhoc method of passing structures from C++
+// to JavaScript.
+template<typename S>
+class JSArrayBasedStruct {
+ public:
+ static S Create() {
+ Handle<JSArray> array = Factory::NewJSArray(S::kSize_);
+ return S(array);
+ }
+ static S cast(Object* object) {
+ JSArray* array = JSArray::cast(object);
+ Handle<JSArray> array_handle(array);
+ return S(array_handle);
+ }
+ explicit JSArrayBasedStruct(Handle<JSArray> array) : array_(array) {
+ }
+ Handle<JSArray> GetJSArray() {
+ return array_;
+ }
+ protected:
+ void SetField(int field_position, Handle<Object> value) {
+ SetElement(array_, field_position, value);
+ }
+ void SetSmiValueField(int field_position, int value) {
+ SetElement(array_, field_position, Handle<Smi>(Smi::FromInt(value)));
+ }
+ Object* GetField(int field_position) {
+ return array_->GetElement(field_position);
+ }
+ int GetSmiValueField(int field_position) {
+ Object* res = GetField(field_position);
+ return Smi::cast(res)->value();
+ }
+ private:
+ Handle<JSArray> array_;
+};
+
+
+// Represents some function compilation details. This structure will be used
+// from JavaScript. It contains Code object, which is kept wrapped
+// into a BlindReference for sanitizing reasons.
+class FunctionInfoWrapper : public JSArrayBasedStruct<FunctionInfoWrapper> {
+ public:
+ explicit FunctionInfoWrapper(Handle<JSArray> array)
+ : JSArrayBasedStruct<FunctionInfoWrapper>(array) {
+ }
+ void SetInitialProperties(Handle<String> name, int start_position,
+ int end_position, int param_num, int parent_index) {
+ HandleScope scope;
+ this->SetField(kFunctionNameOffset_, name);
+ this->SetSmiValueField(kStartPositionOffset_, start_position);
+ this->SetSmiValueField(kEndPositionOffset_, end_position);
+ this->SetSmiValueField(kParamNumOffset_, param_num);
+ this->SetSmiValueField(kParentIndexOffset_, parent_index);
+ }
+ void SetFunctionCode(Handle<Code> function_code) {
+ Handle<JSValue> wrapper = WrapInJSValue(*function_code);
+ this->SetField(kCodeOffset_, wrapper);
+ }
+ void SetScopeInfo(Handle<JSArray> scope_info_array) {
+ this->SetField(kScopeInfoOffset_, scope_info_array);
+ }
+ int GetParentIndex() {
+ return this->GetSmiValueField(kParentIndexOffset_);
+ }
+ Handle<Code> GetFunctionCode() {
+ Handle<Object> raw_result = UnwrapJSValue(Handle<JSValue>(
+ JSValue::cast(this->GetField(kCodeOffset_))));
+ return Handle<Code>::cast(raw_result);
+ }
+ int GetStartPosition() {
+ return this->GetSmiValueField(kStartPositionOffset_);
+ }
+ int GetEndPosition() {
+ return this->GetSmiValueField(kEndPositionOffset_);
+ }
+
+ private:
+ static const int kFunctionNameOffset_ = 0;
+ static const int kStartPositionOffset_ = 1;
+ static const int kEndPositionOffset_ = 2;
+ static const int kParamNumOffset_ = 3;
+ static const int kCodeOffset_ = 4;
+ static const int kScopeInfoOffset_ = 5;
+ static const int kParentIndexOffset_ = 6;
+ static const int kSize_ = 7;
+
+ friend class JSArrayBasedStruct<FunctionInfoWrapper>;
+};
+
+// Wraps SharedFunctionInfo along with some of its fields for passing it
+// back to JavaScript. SharedFunctionInfo object itself is additionally
+// wrapped into BlindReference for sanitizing reasons.
+class SharedInfoWrapper : public JSArrayBasedStruct<SharedInfoWrapper> {
+ public:
+ explicit SharedInfoWrapper(Handle<JSArray> array)
+ : JSArrayBasedStruct<SharedInfoWrapper>(array) {
+ }
+
+ void SetProperties(Handle<String> name, int start_position, int end_position,
+ Handle<SharedFunctionInfo> info) {
+ HandleScope scope;
+ this->SetField(kFunctionNameOffset_, name);
+ Handle<JSValue> info_holder = WrapInJSValue(*info);
+ this->SetField(kSharedInfoOffset_, info_holder);
+ this->SetSmiValueField(kStartPositionOffset_, start_position);
+ this->SetSmiValueField(kEndPositionOffset_, end_position);
+ }
+ Handle<SharedFunctionInfo> GetInfo() {
+ Object* element = this->GetField(kSharedInfoOffset_);
+ Handle<JSValue> value_wrapper(JSValue::cast(element));
+ Handle<Object> raw_result = UnwrapJSValue(value_wrapper);
+ return Handle<SharedFunctionInfo>::cast(raw_result);
+ }
+
+ private:
+ static const int kFunctionNameOffset_ = 0;
+ static const int kStartPositionOffset_ = 1;
+ static const int kEndPositionOffset_ = 2;
+ static const int kSharedInfoOffset_ = 3;
+ static const int kSize_ = 4;
+
+ friend class JSArrayBasedStruct<SharedInfoWrapper>;
+};
+
class FunctionInfoListener {
public:
+ FunctionInfoListener() {
+ current_parent_index_ = -1;
+ len_ = 0;
+ result_ = Factory::NewJSArray(10);
+ }
+
void FunctionStarted(FunctionLiteral* fun) {
- // Implementation follows.
+ HandleScope scope;
+ FunctionInfoWrapper info = FunctionInfoWrapper::Create();
+ info.SetInitialProperties(fun->name(), fun->start_position(),
+ fun->end_position(), fun->num_parameters(),
+ current_parent_index_);
+ current_parent_index_ = len_;
+ SetElement(result_, len_, info.GetJSArray());
+ len_++;
}
void FunctionDone() {
- // Implementation follows.
+ HandleScope scope;
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
+ current_parent_index_ = info.GetParentIndex();
}
void FunctionScope(Scope* scope) {
- // Implementation follows.
+ HandleScope handle_scope;
+
+ Handle<JSArray> scope_info_list = Factory::NewJSArray(10);
+ int scope_info_length = 0;
+
+ // Saves some description of scope. It stores name and indexes of
+ // variables in the whole scope chain. Null-named slots delimit
+ // scopes of this chain.
+ Scope* outer_scope = scope->outer_scope();
+ if (outer_scope == NULL) {
+ return;
+ }
+ do {
+ ZoneList<Variable*> list(10);
+ outer_scope->CollectUsedVariables(&list);
+ int j = 0;
+ for (int i = 0; i < list.length(); i++) {
+ Variable* var1 = list[i];
+ Slot* slot = var1->slot();
+ if (slot != NULL && slot->type() == Slot::CONTEXT) {
+ if (j != i) {
+ list[j] = var1;
+ }
+ j++;
+ }
+ }
+
+ // Sort it.
+ for (int k = 1; k < j; k++) {
+ int l = k;
+ for (int m = k + 1; m < j; m++) {
+ if (list[l]->slot()->index() > list[m]->slot()->index()) {
+ l = m;
+ }
+ }
+ list[k] = list[l];
+ }
+ for (int i = 0; i < j; i++) {
+ SetElement(scope_info_list, scope_info_length, list[i]->name());
+ scope_info_length++;
+ SetElement(scope_info_list, scope_info_length,
+ Handle<Smi>(Smi::FromInt(list[i]->slot()->index())));
+ scope_info_length++;
+ }
+ SetElement(scope_info_list, scope_info_length,
+ Handle<Object>(Heap::null_value()));
+ scope_info_length++;
+
+ outer_scope = outer_scope->outer_scope();
+ } while (outer_scope != NULL);
+
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
+ info.SetScopeInfo(scope_info_list);
}
void FunctionCode(Handle<Code> function_code) {
- // Implementation follows.
+ FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
+ info.SetFunctionCode(function_code);
}
+
+ Handle<JSArray> GetResult() {
+ return result_;
+ }
+
+ private:
+ Handle<JSArray> result_;
+ int len_;
+ int current_parent_index_;
};
static FunctionInfoListener* active_function_info_listener = NULL;
+JSArray* LiveEdit::GatherCompileInfo(Handle<Script> script,
+ Handle<String> source) {
+ CompilationZoneScope zone_scope(DELETE_ON_EXIT);
+
+ FunctionInfoListener listener;
+ Handle<Object> original_source = Handle<Object>(script->source());
+ script->set_source(*source);
+ active_function_info_listener = &listener;
+ CompileScriptForTracker(script);
+ active_function_info_listener = NULL;
+ script->set_source(*original_source);
+
+ return *(listener.GetResult());
+}
+
+
+void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
+ HandleScope scope;
+ int len = Smi::cast(array->length())->value();
+ for (int i = 0; i < len; i++) {
+ Handle<SharedFunctionInfo> info(
+ SharedFunctionInfo::cast(array->GetElement(i)));
+ SharedInfoWrapper info_wrapper = SharedInfoWrapper::Create();
+ Handle<String> name_handle(String::cast(info->name()));
+ info_wrapper.SetProperties(name_handle, info->start_position(),
+ info->end_position(), info);
+ array->SetElement(i, *(info_wrapper.GetJSArray()));
+ }
+}
+
+
+void LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
+ Handle<JSArray> shared_info_array) {
+ HandleScope scope;
+
+ FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+
+ Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
+
+ shared_info->set_code(*(compile_info_wrapper.GetFunctionCode()),
+ UPDATE_WRITE_BARRIER);
+ shared_info->set_start_position(compile_info_wrapper.GetStartPosition());
+ shared_info->set_end_position(compile_info_wrapper.GetEndPosition());
+ // update breakpoints, original code, constructor stub
+}
+
+
+void LiveEdit::RelinkFunctionToScript(Handle<JSArray> shared_info_array,
+ Handle<Script> script_handle) {
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+ Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
+
+ shared_info->set_script(*script_handle);
+}
+
+
+// For a script text change (defined as position_change_array), translates
+// position in unchanged text to position in changed text.
+// Text change is a set of non-overlapping regions in text, that have changed
+// their contents and length. It is specified as array of groups of 3 numbers:
+// (change_begin, change_end, change_end_new_position).
+// Each group describes a change in text; groups are sorted by change_begin.
+// Only position in text beyond any changes may be successfully translated.
+// If a positions is inside some region that changed, result is currently
+// undefined.
+static int TranslatePosition(int original_position,
+ Handle<JSArray> position_change_array) {
+ int position_diff = 0;
+ int array_len = Smi::cast(position_change_array->length())->value();
+ // TODO(635): binary search may be used here
+ for (int i = 0; i < array_len; i += 3) {
+ int chunk_start =
+ Smi::cast(position_change_array->GetElement(i))->value();
+ int chunk_end =
+ Smi::cast(position_change_array->GetElement(i + 1))->value();
+ int chunk_changed_end =
+ Smi::cast(position_change_array->GetElement(i + 2))->value();
+ position_diff = chunk_changed_end - chunk_end;
+ if (original_position < chunk_start) {
+ break;
+ }
+ // Position mustn't be inside a chunk.
+ ASSERT(original_position >= chunk_end);
+ }
+
+ return original_position + position_diff;
+}
+
+
+void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
+ Handle<JSArray> position_change_array) {
+ SharedInfoWrapper shared_info_wrapper(shared_info_array);
+ Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
+
+ info->set_start_position(TranslatePosition(info->start_position(),
+ position_change_array));
+ info->set_end_position(TranslatePosition(info->end_position(),
+ position_change_array));
+
+ // Also patch rinfos (both in working code and original code), breakpoints.
+}
+
+
LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
if (active_function_info_listener != NULL) {
active_function_info_listener->FunctionStarted(fun);
}
}
+
+
LiveEditFunctionTracker::~LiveEditFunctionTracker() {
if (active_function_info_listener != NULL) {
active_function_info_listener->FunctionDone();
}
}
+
+
void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
if (active_function_info_listener != NULL) {
active_function_info_listener->FunctionCode(code);
}
}
+
+
void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
if (active_function_info_listener != NULL) {
active_function_info_listener->FunctionScope(scope);
}
}
+
+
bool LiveEditFunctionTracker::IsActive() {
return active_function_info_listener != NULL;
}
+
+#else // ENABLE_DEBUGGER_SUPPORT
+
+// This ifdef-else-endif section provides working or stub implementation of
+// LiveEditFunctionTracker.
+LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
+}
+
+
+LiveEditFunctionTracker::~LiveEditFunctionTracker() {
+}
+
+
+void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
+}
+
+
+void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
+}
+
+
+bool LiveEditFunctionTracker::IsActive() {
+ return false;
+}
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
+
} } // namespace v8::internal
diff --git a/src/liveedit.h b/src/liveedit.h
index 73aa7d3..efbcd74 100644
--- a/src/liveedit.h
+++ b/src/liveedit.h
@@ -73,6 +73,34 @@
static bool IsActive();
};
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+class LiveEdit : AllStatic {
+ public:
+ static JSArray* GatherCompileInfo(Handle<Script> script,
+ Handle<String> source);
+
+ static void WrapSharedFunctionInfos(Handle<JSArray> array);
+
+ static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
+ Handle<JSArray> shared_info_array);
+
+ static void RelinkFunctionToScript(Handle<JSArray> shared_info_array,
+ Handle<Script> script_handle);
+
+ static void PatchFunctionPositions(Handle<JSArray> shared_info_array,
+ Handle<JSArray> position_change_array);
+
+ // A copy of this is in liveedit-delay.js.
+ enum FunctionPatchabilityStatus {
+ FUNCTION_AVAILABLE_FOR_PATCH = 0,
+ FUNCTION_BLOCKED_ON_STACK = 1
+ };
+};
+
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
} } // namespace v8::internal
#endif /* V*_LIVEEDIT_H_ */
diff --git a/src/log.cc b/src/log.cc
index a3fef73..588d345 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -329,7 +329,7 @@
SlidingStateWindow* Logger::sliding_state_window_ = NULL;
const char** Logger::log_events_ = NULL;
CompressionHelper* Logger::compression_helper_ = NULL;
-bool Logger::is_logging_ = false;
+int Logger::logging_nesting_ = 0;
int Logger::cpu_profiler_nesting_ = 0;
int Logger::heap_profiler_nesting_ = 0;
@@ -389,12 +389,19 @@
void Logger::IntEvent(const char* name, int value) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!Log::IsEnabled() || !FLAG_log) return;
+ if (FLAG_log) UncheckedIntEvent(name, value);
+#endif
+}
+
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+void Logger::UncheckedIntEvent(const char* name, int value) {
+ if (!Log::IsEnabled()) return;
LogMessageBuilder msg;
msg.Append("%s,%d\n", name, value);
msg.WriteToLogFile();
-#endif
}
+#endif
void Logger::HandleEvent(const char* name, Object** location) {
@@ -1169,19 +1176,18 @@
// Must be the same message as Log::kDynamicBufferSeal.
LOG(UncheckedStringEvent("profiler", "pause"));
}
+ --logging_nesting_;
}
}
if (flags &
(PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
if (--heap_profiler_nesting_ == 0) {
FLAG_log_gc = false;
+ --logging_nesting_;
}
}
if (tag != 0) {
- IntEvent("close-tag", tag);
- }
- if (GetActiveProfilerModules() == PROFILER_MODULE_NONE) {
- is_logging_ = false;
+ UncheckedIntEvent("close-tag", tag);
}
}
@@ -1189,11 +1195,11 @@
void Logger::ResumeProfiler(int flags, int tag) {
if (!Log::IsEnabled()) return;
if (tag != 0) {
- IntEvent("open-tag", tag);
+ UncheckedIntEvent("open-tag", tag);
}
if (flags & PROFILER_MODULE_CPU) {
if (cpu_profiler_nesting_++ == 0) {
- is_logging_ = true;
+ ++logging_nesting_;
if (FLAG_prof_lazy) {
profiler_->Engage();
LOG(UncheckedStringEvent("profiler", "resume"));
@@ -1209,7 +1215,7 @@
if (flags &
(PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
if (heap_profiler_nesting_++ == 0) {
- is_logging_ = true;
+ ++logging_nesting_;
FLAG_log_gc = true;
}
}
@@ -1261,6 +1267,8 @@
switch (code_object->kind()) {
case Code::FUNCTION:
return; // We log this later using LogCompiledFunctions.
+ case Code::BINARY_OP_IC:
+ // fall through
case Code::STUB:
description = CodeStub::MajorName(code_object->major_key(), true);
if (description == NULL)
@@ -1482,14 +1490,16 @@
compression_helper_ = new CompressionHelper(kCompressionWindowSize);
}
- is_logging_ = start_logging;
+ if (start_logging) {
+ logging_nesting_ = 1;
+ }
if (FLAG_prof) {
profiler_ = new Profiler();
if (!FLAG_prof_auto) {
profiler_->pause();
} else {
- is_logging_ = true;
+ logging_nesting_ = 1;
}
if (!FLAG_prof_lazy) {
profiler_->Engage();
diff --git a/src/log.h b/src/log.h
index eb8369c..613a1e2 100644
--- a/src/log.h
+++ b/src/log.h
@@ -265,7 +265,7 @@
}
static bool is_logging() {
- return is_logging_;
+ return logging_nesting_ > 0;
}
// Pause/Resume collection of profiling data.
@@ -330,6 +330,9 @@
// Logs a StringEvent regardless of whether FLAG_log is true.
static void UncheckedStringEvent(const char* name, const char* value);
+ // Logs an IntEvent regardless of whether FLAG_log is true.
+ static void UncheckedIntEvent(const char* name, int value);
+
// Stops logging and profiling in case of insufficient resources.
static void StopLoggingAndProfiling();
@@ -372,7 +375,7 @@
friend class LoggerTestHelper;
- static bool is_logging_;
+ static int logging_nesting_;
static int cpu_profiler_nesting_;
static int heap_profiler_nesting_;
#else
diff --git a/src/macros.py b/src/macros.py
index ccc2037..9da2552 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -73,6 +73,16 @@
const kYearShift = 9;
const kMonthShift = 5;
+# Limits for parts of the date, so that we support all the dates that
+# ECMA 262 - 15.9.1.1 requires us to, but at the same time be sure that
+# the date (days since 1970) is in SMI range.
+const kMinYear = -1000000;
+const kMaxYear = 1000000;
+const kMinMonth = -10000000;
+const kMaxMonth = 10000000;
+const kMinDate = -100000000;
+const kMaxDate = 100000000;
+
# Type query macros.
#
# Note: We have special support for typeof(foo) === 'bar' in the compiler.
diff --git a/src/math.js b/src/math.js
index 5745e61..034f32d 100644
--- a/src/math.js
+++ b/src/math.js
@@ -84,7 +84,7 @@
// ECMA 262 - 15.8.2.7
function MathCos(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
- return %Math_cos(x);
+ return %_Math_cos(x);
}
// ECMA 262 - 15.8.2.8
@@ -159,7 +159,7 @@
function MathPow(x, y) {
if (!IS_NUMBER(x)) x = ToNumber(x);
if (!IS_NUMBER(y)) y = ToNumber(y);
- return %Math_pow(x, y);
+ return %_Math_pow(x, y);
}
// ECMA 262 - 15.8.2.14
@@ -176,13 +176,13 @@
// ECMA 262 - 15.8.2.16
function MathSin(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
- return %Math_sin(x);
+ return %_Math_sin(x);
}
// ECMA 262 - 15.8.2.17
function MathSqrt(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
- return %Math_sqrt(x);
+ return %_Math_sqrt(x);
}
// ECMA 262 - 15.8.2.18
diff --git a/src/messages.cc b/src/messages.cc
index e16b1b2..7cb1d20 100644
--- a/src/messages.cc
+++ b/src/messages.cc
@@ -30,6 +30,7 @@
#include "api.h"
#include "execution.h"
+#include "messages.h"
#include "spaces-inl.h"
#include "top.h"
diff --git a/src/messages.js b/src/messages.js
index ca82afe..5848115 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -127,6 +127,7 @@
malformed_regexp: "Invalid regular expression: /%0/: %1",
unterminated_regexp: "Invalid regular expression: missing /",
regexp_flags: "Cannot supply flags when constructing one RegExp from another",
+ incompatible_method_receiver: "Method %0 called on incompatible receiver %1",
invalid_lhs_in_assignment: "Invalid left-hand side in assignment",
invalid_lhs_in_for_in: "Invalid left-hand side in for-in",
invalid_lhs_in_postfix_op: "Invalid left-hand side expression in postfix operation",
diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc
index 2de45f6..facccc2 100644
--- a/src/mips/codegen-mips.cc
+++ b/src/mips/codegen-mips.cc
@@ -81,7 +81,7 @@
// a1: called JS function
// cp: callee's context
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* infomode) {
UNIMPLEMENTED_MIPS();
}
@@ -292,6 +292,16 @@
}
+void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
+void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
// This should generate code that performs a charCodeAt() call or returns
// undefined in order to trigger the slow case, Runtime_StringCharCodeAt.
// It is not yet implemented on ARM, so it always goes to the slow case.
@@ -300,6 +310,11 @@
}
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
UNIMPLEMENTED_MIPS();
}
@@ -457,6 +472,34 @@
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
UNIMPLEMENTED_MIPS();
+
+ // Save callee saved registers on the stack.
+ __ MultiPush(kCalleeSaved | ra.bit());
+
+ // ********** State **********
+ //
+ // * Registers:
+ // a0: entry_address
+ // a1: function
+ // a2: reveiver_pointer
+ // a3: argc
+ //
+ // * Stack:
+ // ---------------------------
+ // args
+ // ---------------------------
+ // 4 args slots
+ // ---------------------------
+ // callee saved registers + ra
+ // ---------------------------
+ //
+ // ***************************
+
+ __ break_(0x1234);
+
+ // Restore callee saved registers from the stack.
+ __ MultiPop(kCalleeSaved | ra.bit());
+
// Load a result.
__ li(v0, Operand(0x1234));
__ jr(ra);
diff --git a/src/mips/codegen-mips.h b/src/mips/codegen-mips.h
index 147b872..987dcca 100644
--- a/src/mips/codegen-mips.h
+++ b/src/mips/codegen-mips.h
@@ -157,11 +157,10 @@
private:
// Construction/Destruction.
explicit CodeGenerator(MacroAssembler* masm);
- virtual ~CodeGenerator() { delete masm_; }
// Accessors.
inline bool is_eval();
- Scope* scope() const { return scope_; }
+ inline Scope* scope();
// Generating deferred code.
void ProcessDeferred();
@@ -184,7 +183,7 @@
#undef DEF_VISIT
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
struct InlineRuntimeLUT {
void (CodeGenerator::*method)(ZoneList<Expression*>*);
@@ -227,6 +226,9 @@
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
+ // Fast support for string.charAt(n) and string[n].
+ void GenerateCharFromCode(ZoneList<Expression*>* args);
+
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
@@ -244,6 +246,11 @@
void GenerateRegExpExec(ZoneList<Expression*>* args);
void GenerateNumberToString(ZoneList<Expression*>* args);
+ // Fast support for Math.pow().
+ void GenerateMathPow(ZoneList<Expression*>* args);
+ // Fast support for Math.sqrt().
+ void GenerateMathPow(ZoneList<Expression*>* args);
+
// Fast support for Math.sin and Math.cos.
inline void GenerateMathSin(ZoneList<Expression*>* args);
@@ -302,6 +309,7 @@
friend class JumpTarget;
friend class Reference;
friend class FastCodeGenerator;
+ friend class FullCodeGenerator;
friend class FullCodeGenSyntaxChecker;
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
diff --git a/src/mips/fast-codegen-mips.cc b/src/mips/fast-codegen-mips.cc
index c47f632..48a0ce6 100644
--- a/src/mips/fast-codegen-mips.cc
+++ b/src/mips/fast-codegen-mips.cc
@@ -35,6 +35,14 @@
#define __ ACCESS_MASM(masm_)
+Register FastCodeGenerator::accumulator0() { return no_reg; }
+Register FastCodeGenerator::accumulator1() { return no_reg; }
+Register FastCodeGenerator::scratch0() { return no_reg; }
+Register FastCodeGenerator::scratch1() { return no_reg; }
+Register FastCodeGenerator::receiver_reg() { return no_reg; }
+Register FastCodeGenerator::context_reg() { return no_reg; }
+
+
void FastCodeGenerator::Generate(CompilationInfo* info) {
UNIMPLEMENTED_MIPS();
}
@@ -45,7 +53,17 @@
}
-void FastCodeGenerator::EmitGlobalVariableLoad(Handle<String> name) {
+void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> name) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
+void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
+void FastCodeGenerator::EmitBitOr() {
UNIMPLEMENTED_MIPS();
}
diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc
index 920329e..3c29e99 100644
--- a/src/mips/full-codegen-mips.cc
+++ b/src/mips/full-codegen-mips.cc
@@ -146,6 +146,11 @@
}
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
UNIMPLEMENTED_MIPS();
}
diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc
index 5598cdf..6056166 100644
--- a/src/mips/ic-mips.cc
+++ b/src/mips/ic-mips.cc
@@ -90,11 +90,6 @@
void LoadIC::GenerateMiss(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
-}
-
-
-void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
UNIMPLEMENTED_MIPS();
}
@@ -120,11 +115,6 @@
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
-}
-
-
-void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
UNIMPLEMENTED_MIPS();
}
@@ -145,12 +135,6 @@
}
-void KeyedStoreIC::Generate(MacroAssembler* masm,
- const ExternalReference& f) {
- UNIMPLEMENTED_MIPS();
-}
-
-
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
UNIMPLEMENTED_MIPS();
}
@@ -162,7 +146,12 @@
}
-void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) {
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
UNIMPLEMENTED_MIPS();
}
@@ -172,12 +161,12 @@
}
-void StoreIC::GenerateExtendStorage(MacroAssembler* masm) {
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
UNIMPLEMENTED_MIPS();
}
-void StoreIC::GenerateMiss(MacroAssembler* masm) {
+void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
UNIMPLEMENTED_MIPS();
}
diff --git a/src/mips/jump-target-mips.cc b/src/mips/jump-target-mips.cc
index 3301d19..e8398a8 100644
--- a/src/mips/jump-target-mips.cc
+++ b/src/mips/jump-target-mips.cc
@@ -31,6 +31,7 @@
#include "codegen-inl.h"
#include "jump-target-inl.h"
#include "register-allocator-inl.h"
+#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc
index b733bdd..e49858b 100644
--- a/src/mips/macro-assembler-mips.cc
+++ b/src/mips/macro-assembler-mips.cc
@@ -422,7 +422,7 @@
// Trashes the at register if no scratch register is provided.
void MacroAssembler::Branch(Condition cond, int16_t offset, Register rs,
const Operand& rt, Register scratch) {
- Register r2;
+ Register r2 = no_reg;
if (rt.is_reg()) {
// We don't want any other register but scratch clobbered.
ASSERT(!scratch.is(rs) && !scratch.is(rt.rm_));
@@ -489,7 +489,7 @@
void MacroAssembler::Branch(Condition cond, Label* L, Register rs,
const Operand& rt, Register scratch) {
- Register r2;
+ Register r2 = no_reg;
if (rt.is_reg()) {
r2 = rt.rm_;
} else if (cond != cc_always) {
@@ -559,7 +559,7 @@
// cases, so we keep slt and add an intermediate third instruction.
void MacroAssembler::BranchAndLink(Condition cond, int16_t offset, Register rs,
const Operand& rt, Register scratch) {
- Register r2;
+ Register r2 = no_reg;
if (rt.is_reg()) {
r2 = rt.rm_;
} else if (cond != cc_always) {
@@ -634,7 +634,7 @@
void MacroAssembler::BranchAndLink(Condition cond, Label* L, Register rs,
const Operand& rt, Register scratch) {
- Register r2;
+ Register r2 = no_reg;
if (rt.is_reg()) {
r2 = rt.rm_;
} else if (cond != cc_always) {
@@ -787,6 +787,16 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // ---------------------------------------------------------------------------
+ // Debugger Support
+
+ void MacroAssembler::DebugBreak() {
+ UNIMPLEMENTED_MIPS();
+ }
+#endif
+
+
// ---------------------------------------------------------------------------
// Exception handling
@@ -826,14 +836,21 @@
}
-void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
- int num_arguments,
- int result_size) {
+void MacroAssembler::TailCallExternalReference(const ExternalReference& ext,
+ int num_arguments,
+ int result_size) {
UNIMPLEMENTED_MIPS();
}
-void MacroAssembler::JumpToRuntime(const ExternalReference& builtin) {
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid), num_arguments, result_size);
+}
+
+
+void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) {
UNIMPLEMENTED_MIPS();
}
diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h
index aea9836..b34488c 100644
--- a/src/mips/macro-assembler-mips.h
+++ b/src/mips/macro-assembler-mips.h
@@ -115,11 +115,7 @@
Heap::RootListIndex index,
Condition cond, Register src1, const Operand& src2);
- // Sets the remembered set bit for [address+offset], where address is the
- // address of the heap object 'object'. The address must be in the first 8K
- // of an allocated page. The 'scratch' register is used in the
- // implementation and all 3 registers are clobbered by the operation, as
- // well as the ip register.
+ // Sets the remembered set bit for [address+offset].
void RecordWrite(Register object, Register offset, Register scratch);
@@ -182,19 +178,8 @@
// Push multiple registers on the stack.
- // With MultiPush, lower registers are pushed first on the stack.
- // For example if you push t0, t1, s0, and ra you get:
- // | |
- // |-----------------------|
- // | t0 | +
- // |-----------------------| |
- // | t1 | |
- // |-----------------------| |
- // | s0 | v
- // |-----------------------| -
- // | ra |
- // |-----------------------|
- // | |
+ // Registers are saved in numerical order, with higher numbered registers
+ // saved in higher memory addresses
void MultiPush(RegList regs);
void MultiPushReversed(RegList regs);
void Push(Register src) {
@@ -224,6 +209,20 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // ---------------------------------------------------------------------------
+ // Debugger Support
+
+ void SaveRegistersToMemory(RegList regs);
+ void RestoreRegistersFromMemory(RegList regs);
+ void CopyRegistersFromMemoryToStack(Register base, RegList regs);
+ void CopyRegistersFromStackToMemory(Register base,
+ Register scratch,
+ RegList regs);
+ void DebugBreak();
+#endif
+
+
// ---------------------------------------------------------------------------
// Exception handling
@@ -268,21 +267,25 @@
void StubReturn(int argc);
// Call a runtime routine.
- // Eventually this should be used for all C calls.
void CallRuntime(Runtime::Function* f, int num_arguments);
// Convenience function: Same as above, but takes the fid instead.
void CallRuntime(Runtime::FunctionId fid, int num_arguments);
// Tail call of a runtime routine (jump).
- // Like JumpToRuntime, but also takes care of passing the number
+ // Like JumpToExternalReference, but also takes care of passing the number
// of parameters.
- void TailCallRuntime(const ExternalReference& ext,
+ void TailCallExternalReference(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,
int result_size);
// Jump to the builtin routine.
- void JumpToRuntime(const ExternalReference& builtin);
+ void JumpToExternalReference(const ExternalReference& builtin);
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc
index a87a49b..669fdaa 100644
--- a/src/mips/stub-cache-mips.cc
+++ b/src/mips/stub-cache-mips.cc
@@ -72,20 +72,6 @@
}
-// Generate code to load the length from a string object and return the length.
-// If the receiver object is not a string or a wrapped string object the
-// execution continues at the miss label. The register containing the
-// receiver is potentially clobbered.
-void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Label* miss) {
- UNIMPLEMENTED_MIPS();
- __ break_(0x249);
-}
-
-
void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
Register receiver,
Register scratch1,
@@ -99,7 +85,6 @@
// After executing generated code, the receiver_reg and name_reg
// may be clobbered.
void StubCompiler::GenerateStoreField(MacroAssembler* masm,
- Builtins::Name storage_extend,
JSObject* object,
int index,
Map* transition,
@@ -120,18 +105,6 @@
#define __ ACCESS_MASM(masm())
-Register StubCompiler::CheckPrototypes(JSObject* object,
- Register object_reg,
- JSObject* holder,
- Register holder_reg,
- Register scratch,
- String* name,
- Label* miss) {
- UNIMPLEMENTED_MIPS();
- return at; // UNIMPLEMENTED RETURN
-}
-
-
void StubCompiler::GenerateLoadField(JSObject* object,
JSObject* holder,
Register receiver,
@@ -192,7 +165,7 @@
}
-Object* CallStubCompiler::CompileCallField(Object* object,
+Object* CallStubCompiler::CompileCallField(JSObject* object,
JSObject* holder,
int index,
String* name) {
@@ -211,7 +184,7 @@
}
-Object* CallStubCompiler::CompileCallInterceptor(Object* object,
+Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
UNIMPLEMENTED_MIPS();
diff --git a/src/mips/virtual-frame-mips.cc b/src/mips/virtual-frame-mips.cc
index fad7ec4..e89882f 100644
--- a/src/mips/virtual-frame-mips.cc
+++ b/src/mips/virtual-frame-mips.cc
@@ -32,6 +32,7 @@
#include "codegen-inl.h"
#include "register-allocator-inl.h"
#include "scopes.h"
+#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
@@ -41,17 +42,6 @@
#define __ ACCESS_MASM(masm())
-
-// On entry to a function, the virtual frame already contains the
-// receiver and the parameters. All initial frame elements are in
-// memory.
-VirtualFrame::VirtualFrame()
- : elements_(parameter_count() + local_count() + kPreallocatedElements),
- stack_pointer_(parameter_count()) { // 0-based index of TOS.
- UNIMPLEMENTED_MIPS();
-}
-
-
void VirtualFrame::SyncElementBelowStackPointer(int index) {
UNREACHABLE();
}
diff --git a/src/mips/virtual-frame-mips.h b/src/mips/virtual-frame-mips.h
index 79f973f..e5bc93f 100644
--- a/src/mips/virtual-frame-mips.h
+++ b/src/mips/virtual-frame-mips.h
@@ -61,16 +61,17 @@
static const int kIllegalIndex = -1;
// Construct an initial virtual frame on entry to a JS function.
- VirtualFrame();
+ inline VirtualFrame();
// Construct a virtual frame as a clone of an existing one.
- explicit VirtualFrame(VirtualFrame* original);
+ explicit inline VirtualFrame(VirtualFrame* original);
CodeGenerator* cgen() { return CodeGeneratorScope::Current(); }
MacroAssembler* masm() { return cgen()->masm(); }
// Create a duplicate of an existing valid frame element.
- FrameElement CopyElementAt(int index);
+ FrameElement CopyElementAt(int index,
+ NumberInfo::Type info = NumberInfo::kUnknown);
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -366,9 +367,9 @@
void EmitMultiPushReversed(RegList regs); // higher first
// Push an element on the virtual frame.
- void Push(Register reg);
- void Push(Handle<Object> value);
- void Push(Smi* value) { Push(Handle<Object>(value)); }
+ inline void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
+ inline void Push(Handle<Object> value);
+ inline void Push(Smi* value);
// Pushing a result invalidates it (its contents become owned by the frame).
void Push(Result* result) {
@@ -384,7 +385,7 @@
// Nip removes zero or more elements from immediately below the top
// of the frame, leaving the previous top-of-frame value on top of
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
- void Nip(int num_dropped);
+ inline void Nip(int num_dropped);
// This pushes 4 arguments slots on the stack and saves asked 'a' registers
// 'a' registers are arguments register a0 to a3.
@@ -483,7 +484,7 @@
// Push a copy of a frame slot (typically a local or parameter) on top of
// the frame.
- void PushFrameSlotAt(int index);
+ inline void PushFrameSlotAt(int index);
// Push a the value of a frame slot (typically a local or parameter) on
// top of the frame and invalidate the slot.
@@ -534,7 +535,7 @@
// (via PrepareForCall).
void RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode);
- bool Equals(VirtualFrame* other);
+ inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
friend class DeferredCode;
diff --git a/src/number-info.h b/src/number-info.h
index c6f32e4..bfc0d9f 100644
--- a/src/number-info.h
+++ b/src/number-info.h
@@ -31,42 +31,160 @@
namespace v8 {
namespace internal {
-class NumberInfo : public AllStatic {
+// Unknown
+// |
+// Number
+// / |
+// HeapNumber Integer32
+// | |
+// | Smi
+// | /
+// Uninitialized.
+
+class NumberInfo {
public:
- enum Type {
- kUnknown = 0,
- kNumber = 1,
- kSmi = 3,
- kHeapNumber = 5,
- kUninitialized = 7
- };
+ NumberInfo() { }
+
+ static inline NumberInfo Unknown();
+ // We know it's a number of some sort.
+ static inline NumberInfo Number();
+ // We know it's signed or unsigned 32 bit integer.
+ static inline NumberInfo Integer32();
+ // We know it's a Smi.
+ static inline NumberInfo Smi();
+ // We know it's a heap number.
+ static inline NumberInfo HeapNumber();
+ // We haven't started collecting info yet.
+ static inline NumberInfo Uninitialized();
+
+ // Return compact representation. Very sensitive to enum values below!
+ int ThreeBitRepresentation() {
+ ASSERT(type_ != kUninitializedType);
+ int answer = type_ > 6 ? type_ -2 : type_;
+ ASSERT(answer >= 0);
+ ASSERT(answer <= 7);
+ return answer;
+ }
+
+ // Decode compact representation. Very sensitive to enum values below!
+ static NumberInfo ExpandedRepresentation(int three_bit_representation) {
+ Type t = static_cast<Type>(three_bit_representation >= 6 ?
+ three_bit_representation + 2 :
+ three_bit_representation);
+ ASSERT(t == kUnknownType ||
+ t == kNumberType ||
+ t == kInteger32Type ||
+ t == kSmiType ||
+ t == kHeapNumberType);
+ return NumberInfo(t);
+ }
+
+ int ToInt() {
+ return type_;
+ }
+
+ static NumberInfo FromInt(int bit_representation) {
+ Type t = static_cast<Type>(bit_representation);
+ ASSERT(t == kUnknownType ||
+ t == kNumberType ||
+ t == kInteger32Type ||
+ t == kSmiType ||
+ t == kHeapNumberType);
+ return NumberInfo(t);
+ }
// Return the weakest (least precise) common type.
- static Type Combine(Type a, Type b) {
- // Make use of the order of enum values.
- return static_cast<Type>(a & b);
+ static NumberInfo Combine(NumberInfo a, NumberInfo b) {
+ return NumberInfo(static_cast<Type>(a.type_ & b.type_));
}
- static bool IsNumber(Type a) {
- ASSERT(a != kUninitialized);
- return ((a & kNumber) != 0);
+ inline bool IsUnknown() {
+ return type_ == kUnknownType;
}
- static const char* ToString(Type a) {
- switch (a) {
- case kUnknown: return "UnknownType";
- case kNumber: return "NumberType";
- case kSmi: return "SmiType";
- case kHeapNumber: return "HeapNumberType";
- case kUninitialized:
+ inline bool IsNumber() {
+ ASSERT(type_ != kUninitializedType);
+ return ((type_ & kNumberType) == kNumberType);
+ }
+
+ inline bool IsSmi() {
+ ASSERT(type_ != kUninitializedType);
+ return ((type_ & kSmiType) == kSmiType);
+ }
+
+ inline bool IsInteger32() {
+ ASSERT(type_ != kUninitializedType);
+ return ((type_ & kInteger32Type) == kInteger32Type);
+ }
+
+ inline bool IsHeapNumber() {
+ ASSERT(type_ != kUninitializedType);
+ return ((type_ & kHeapNumberType) == kHeapNumberType);
+ }
+
+ inline bool IsUninitialized() {
+ return type_ == kUninitializedType;
+ }
+
+ const char* ToString() {
+ switch (type_) {
+ case kUnknownType: return "UnknownType";
+ case kNumberType: return "NumberType";
+ case kSmiType: return "SmiType";
+ case kHeapNumberType: return "HeapNumberType";
+ case kInteger32Type: return "Integer32Type";
+ case kUninitializedType:
UNREACHABLE();
return "UninitializedType";
}
UNREACHABLE();
return "Unreachable code";
}
+
+ private:
+ enum Type {
+ kUnknownType = 0,
+ kNumberType = 1,
+ kInteger32Type = 3,
+ kSmiType = 7,
+ kHeapNumberType = 9,
+ kUninitializedType = 15
+ };
+ explicit inline NumberInfo(Type t) : type_(t) { }
+
+ Type type_;
};
+
+NumberInfo NumberInfo::Unknown() {
+ return NumberInfo(kUnknownType);
+}
+
+
+NumberInfo NumberInfo::Number() {
+ return NumberInfo(kNumberType);
+}
+
+
+NumberInfo NumberInfo::Integer32() {
+ return NumberInfo(kInteger32Type);
+}
+
+
+NumberInfo NumberInfo::Smi() {
+ return NumberInfo(kSmiType);
+}
+
+
+NumberInfo NumberInfo::HeapNumber() {
+ return NumberInfo(kHeapNumberType);
+}
+
+
+NumberInfo NumberInfo::Uninitialized() {
+ return NumberInfo(kUninitializedType);
+}
+
} } // namespace v8::internal
#endif // V8_NUMBER_INFO_H_
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 9415bc1..492b492 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -644,6 +644,24 @@
}
+void CodeCache::CodeCachePrint() {
+ HeapObject::PrintHeader("CodeCache");
+ PrintF("\n - default_cache: ");
+ default_cache()->ShortPrint();
+ PrintF("\n - normal_type_cache: ");
+ normal_type_cache()->ShortPrint();
+}
+
+
+void CodeCache::CodeCacheVerify() {
+ VerifyHeapPointer(default_cache());
+ VerifyHeapPointer(normal_type_cache());
+ ASSERT(default_cache()->IsFixedArray());
+ ASSERT(normal_type_cache()->IsUndefined()
+ || normal_type_cache()->IsCodeCacheHashTable());
+}
+
+
void FixedArray::FixedArrayPrint() {
HeapObject::PrintHeader("FixedArray");
PrintF(" - length: %d", length());
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 455a84c..18f45f3 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -564,6 +564,11 @@
}
+bool Object::IsCodeCacheHashTable() {
+ return IsHashTable();
+}
+
+
bool Object::IsMapCache() {
return IsHashTable();
}
@@ -840,15 +845,17 @@
intptr_t Failure::value() const {
- return reinterpret_cast<intptr_t>(this) >> kFailureTagSize;
+ return static_cast<intptr_t>(
+ reinterpret_cast<uintptr_t>(this) >> kFailureTagSize);
}
Failure* Failure::RetryAfterGC(int requested_bytes) {
// Assert that the space encoding fits in the three bytes allotted for it.
ASSERT((LAST_SPACE & ~kSpaceTagMask) == 0);
- intptr_t requested = requested_bytes >> kObjectAlignmentBits;
- int tag_bits = kSpaceTagSize + kFailureTypeTagSize;
+ uintptr_t requested =
+ static_cast<uintptr_t>(requested_bytes >> kObjectAlignmentBits);
+ int tag_bits = kSpaceTagSize + kFailureTypeTagSize + kFailureTagSize;
if (((requested << tag_bits) >> tag_bits) != requested) {
// No room for entire requested size in the bits. Round down to
// maximally representable size.
@@ -861,7 +868,8 @@
Failure* Failure::Construct(Type type, intptr_t value) {
- intptr_t info = (static_cast<intptr_t>(value) << kFailureTypeTagSize) | type;
+ uintptr_t info =
+ (static_cast<uintptr_t>(value) << kFailureTypeTagSize) | type;
ASSERT(((info << kFailureTagSize) >> kFailureTagSize) == info);
return reinterpret_cast<Failure*>((info << kFailureTagSize) | kFailureTag);
}
@@ -1394,6 +1402,11 @@
}
+Object** FixedArray::data_start() {
+ return HeapObject::RawField(this, kHeaderSize);
+}
+
+
bool DescriptorArray::IsEmpty() {
ASSERT(this == Heap::empty_descriptor_array() ||
this->length() > 2);
@@ -1560,6 +1573,7 @@
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(CompilationCacheTable)
+CAST_ACCESSOR(CodeCacheHashTable)
CAST_ACCESSOR(MapCache)
CAST_ACCESSOR(String)
CAST_ACCESSOR(SeqString)
@@ -1637,13 +1651,11 @@
}
-Object* String::TryFlattenIfNotFlat() {
+Object* String::TryFlatten(PretenureFlag pretenure) {
// We don't need to flatten strings that are already flat. Since this code
// is inlined, it can be helpful in the flat case to not call out to Flatten.
- if (!IsFlat()) {
- return TryFlatten();
- }
- return this;
+ if (IsFlat()) return this;
+ return SlowTryFlatten(pretenure);
}
@@ -2143,14 +2155,14 @@
CodeStub::Major Code::major_key() {
- ASSERT(kind() == STUB);
+ ASSERT(kind() == STUB || kind() == BINARY_OP_IC);
return static_cast<CodeStub::Major>(READ_BYTE_FIELD(this,
kStubMajorKeyOffset));
}
void Code::set_major_key(CodeStub::Major major) {
- ASSERT(kind() == STUB);
+ ASSERT(kind() == STUB || kind() == BINARY_OP_IC);
ASSERT(0 <= major && major < 256);
WRITE_BYTE_FIELD(this, kStubMajorKeyOffset, major);
}
@@ -2252,7 +2264,7 @@
ACCESSORS(Map, instance_descriptors, DescriptorArray,
kInstanceDescriptorsOffset)
-ACCESSORS(Map, code_cache, FixedArray, kCodeCacheOffset)
+ACCESSORS(Map, code_cache, Object, kCodeCacheOffset)
ACCESSORS(Map, constructor, Object, kConstructorOffset)
ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset)
@@ -2390,6 +2402,9 @@
kThisPropertyAssignmentsCountOffset)
+ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset)
+ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset)
+
bool Script::HasValidSource() {
Object* src = this->source();
if (!src->IsString()) return true;
diff --git a/src/objects.cc b/src/objects.cc
index 53423af..7f4ab0b 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -673,7 +673,7 @@
}
-Object* String::TryFlatten() {
+Object* String::SlowTryFlatten(PretenureFlag pretenure) {
#ifdef DEBUG
// Do not attempt to flatten in debug mode when allocation is not
// allowed. This is to avoid an assertion failure when allocating.
@@ -691,7 +691,7 @@
// There's little point in putting the flat string in new space if the
// cons string is in old space. It can never get GCed until there is
// an old space GC.
- PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED : TENURED;
+ PretenureFlag tenure = Heap::InNewSpace(this) ? pretenure : TENURED;
int len = length();
Object* object;
String* result;
@@ -2180,7 +2180,7 @@
property_count += 2; // Make space for two more properties.
}
Object* obj =
- StringDictionary::Allocate(property_count * 2);
+ StringDictionary::Allocate(property_count);
if (obj->IsFailure()) return obj;
StringDictionary* dictionary = StringDictionary::cast(obj);
@@ -2768,7 +2768,7 @@
}
// Try to flatten before operating on the string.
- name->TryFlattenIfNotFlat();
+ name->TryFlatten();
// Check if there is an API defined callback object which prohibits
// callback overwriting in this object or it's prototype chain.
@@ -3033,19 +3033,79 @@
Object* Map::UpdateCodeCache(String* name, Code* code) {
- ASSERT(code->ic_state() == MONOMORPHIC);
- FixedArray* cache = code_cache();
+ // Allocate the code cache if not present.
+ if (code_cache()->IsFixedArray()) {
+ Object* result = Heap::AllocateCodeCache();
+ if (result->IsFailure()) return result;
+ set_code_cache(result);
+ }
- // When updating the code cache we disregard the type encoded in the
+ // Update the code cache.
+ return CodeCache::cast(code_cache())->Update(name, code);
+}
+
+
+Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
+ // Do a lookup if a code cache exists.
+ if (!code_cache()->IsFixedArray()) {
+ return CodeCache::cast(code_cache())->Lookup(name, flags);
+ } else {
+ return Heap::undefined_value();
+ }
+}
+
+
+int Map::IndexInCodeCache(String* name, Code* code) {
+ // Get the internal index if a code cache exists.
+ if (!code_cache()->IsFixedArray()) {
+ return CodeCache::cast(code_cache())->GetIndex(name, code);
+ }
+ return -1;
+}
+
+
+void Map::RemoveFromCodeCache(String* name, Code* code, int index) {
+ // No GC is supposed to happen between a call to IndexInCodeCache and
+ // RemoveFromCodeCache so the code cache must be there.
+ ASSERT(!code_cache()->IsFixedArray());
+ CodeCache::cast(code_cache())->RemoveByIndex(name, code, index);
+}
+
+
+Object* 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.
+ if (code->type() == NORMAL) {
+ // Make sure that a hash table is allocated for the normal load code cache.
+ if (normal_type_cache()->IsUndefined()) {
+ Object* result =
+ CodeCacheHashTable::Allocate(CodeCacheHashTable::kInitialSize);
+ if (result->IsFailure()) return result;
+ set_normal_type_cache(result);
+ }
+ return UpdateNormalTypeCache(name, code);
+ } else {
+ ASSERT(default_cache()->IsFixedArray());
+ return UpdateDefaultCache(name, code);
+ }
+}
+
+
+Object* CodeCache::UpdateDefaultCache(String* name, Code* code) {
+ // When updating the default code cache we disregard the type encoded in the
// flags. This allows call constant stubs to overwrite call field
// stubs, etc.
Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
// First check whether we can update existing code cache without
// extending it.
+ FixedArray* cache = default_cache();
int length = cache->length();
int deleted_index = -1;
- for (int i = 0; i < length; i += 2) {
+ for (int i = 0; i < length; i += kCodeCacheEntrySize) {
Object* key = cache->get(i);
if (key->IsNull()) {
if (deleted_index < 0) deleted_index = i;
@@ -3053,14 +3113,15 @@
}
if (key->IsUndefined()) {
if (deleted_index >= 0) i = deleted_index;
- cache->set(i + 0, name);
- cache->set(i + 1, code);
+ cache->set(i + kCodeCacheEntryNameOffset, name);
+ cache->set(i + kCodeCacheEntryCodeOffset, code);
return this;
}
if (name->Equals(String::cast(key))) {
- Code::Flags found = Code::cast(cache->get(i + 1))->flags();
+ Code::Flags found =
+ Code::cast(cache->get(i + kCodeCacheEntryCodeOffset))->flags();
if (Code::RemoveTypeFromFlags(found) == flags) {
- cache->set(i + 1, code);
+ cache->set(i + kCodeCacheEntryCodeOffset, code);
return this;
}
}
@@ -3069,61 +3130,207 @@
// Reached the end of the code cache. If there were deleted
// elements, reuse the space for the first of them.
if (deleted_index >= 0) {
- cache->set(deleted_index + 0, name);
- cache->set(deleted_index + 1, code);
+ cache->set(deleted_index + kCodeCacheEntryNameOffset, name);
+ cache->set(deleted_index + kCodeCacheEntryCodeOffset, code);
return this;
}
- // Extend the code cache with some new entries (at least one).
- int new_length = length + ((length >> 1) & ~1) + 2;
- ASSERT((new_length & 1) == 0); // must be a multiple of two
+ // Extend the code cache with some new entries (at least one). Must be a
+ // multiple of the entry size.
+ int new_length = length + ((length >> 1)) + kCodeCacheEntrySize;
+ new_length = new_length - new_length % kCodeCacheEntrySize;
+ ASSERT((new_length % kCodeCacheEntrySize) == 0);
Object* result = cache->CopySize(new_length);
if (result->IsFailure()) return result;
// Add the (name, code) pair to the new cache.
cache = FixedArray::cast(result);
- cache->set(length + 0, name);
- cache->set(length + 1, code);
- set_code_cache(cache);
+ cache->set(length + kCodeCacheEntryNameOffset, name);
+ cache->set(length + kCodeCacheEntryCodeOffset, code);
+ set_default_cache(cache);
return this;
}
-Object* Map::FindInCodeCache(String* name, Code::Flags flags) {
- FixedArray* cache = code_cache();
+Object* CodeCache::UpdateNormalTypeCache(String* name, Code* code) {
+ // Adding a new entry can cause a new cache to be allocated.
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ Object* new_cache = cache->Put(name, code);
+ if (new_cache->IsFailure()) return new_cache;
+ set_normal_type_cache(new_cache);
+ return this;
+}
+
+
+Object* CodeCache::Lookup(String* name, Code::Flags flags) {
+ if (Code::ExtractTypeFromFlags(flags) == NORMAL) {
+ return LookupNormalTypeCache(name, flags);
+ } else {
+ return LookupDefaultCache(name, flags);
+ }
+}
+
+
+Object* CodeCache::LookupDefaultCache(String* name, Code::Flags flags) {
+ FixedArray* cache = default_cache();
int length = cache->length();
- for (int i = 0; i < length; i += 2) {
- Object* key = cache->get(i);
+ for (int i = 0; i < length; i += kCodeCacheEntrySize) {
+ Object* key = cache->get(i + kCodeCacheEntryNameOffset);
// Skip deleted elements.
if (key->IsNull()) continue;
if (key->IsUndefined()) return key;
if (name->Equals(String::cast(key))) {
- Code* code = Code::cast(cache->get(i + 1));
- if (code->flags() == flags) return code;
+ Code* code = Code::cast(cache->get(i + kCodeCacheEntryCodeOffset));
+ if (code->flags() == flags) {
+ return code;
+ }
}
}
return Heap::undefined_value();
}
-int Map::IndexInCodeCache(Code* code) {
- FixedArray* array = code_cache();
+Object* CodeCache::LookupNormalTypeCache(String* name, Code::Flags flags) {
+ if (!normal_type_cache()->IsUndefined()) {
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ return cache->Lookup(name, flags);
+ } else {
+ return Heap::undefined_value();
+ }
+}
+
+
+int CodeCache::GetIndex(String* name, Code* code) {
+ // This is not used for normal load/store/call IC's.
+ if (code->type() == NORMAL) {
+ if (normal_type_cache()->IsUndefined()) return -1;
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ return cache->GetIndex(name, code->flags());
+ }
+
+ FixedArray* array = default_cache();
int len = array->length();
- for (int i = 0; i < len; i += 2) {
- if (array->get(i + 1) == code) return i + 1;
+ for (int i = 0; i < len; i += kCodeCacheEntrySize) {
+ if (array->get(i + kCodeCacheEntryCodeOffset) == code) return i + 1;
}
return -1;
}
-void Map::RemoveFromCodeCache(int index) {
- FixedArray* array = code_cache();
- ASSERT(array->length() >= index && array->get(index)->IsCode());
- // Use null instead of undefined for deleted elements to distinguish
- // deleted elements from unused elements. This distinction is used
- // when looking up in the cache and when updating the cache.
- array->set_null(index - 1); // key
- array->set_null(index); // code
+void CodeCache::RemoveByIndex(String* name, Code* code, int index) {
+ if (code->type() == NORMAL) {
+ ASSERT(!normal_type_cache()->IsUndefined());
+ CodeCacheHashTable* cache = CodeCacheHashTable::cast(normal_type_cache());
+ ASSERT(cache->GetIndex(name, code->flags()) == index);
+ cache->RemoveByIndex(index);
+ } else {
+ FixedArray* array = default_cache();
+ ASSERT(array->length() >= index && array->get(index)->IsCode());
+ // Use null instead of undefined for deleted elements to distinguish
+ // deleted elements from unused elements. This distinction is used
+ // when looking up in the cache and when updating the cache.
+ ASSERT_EQ(1, kCodeCacheEntryCodeOffset - kCodeCacheEntryNameOffset);
+ array->set_null(index - 1); // Name.
+ array->set_null(index); // Code.
+ }
+}
+
+
+// The key in the code cache hash table consists of the property name and the
+// code object. The actual match is on the name and the code flags. If a key
+// is created using the flags and not a code object it can only be used for
+// lookup not to create a new entry.
+class CodeCacheHashTableKey : public HashTableKey {
+ public:
+ CodeCacheHashTableKey(String* name, Code::Flags flags)
+ : name_(name), flags_(flags), code_(NULL) { }
+
+ CodeCacheHashTableKey(String* name, Code* code)
+ : name_(name),
+ flags_(code->flags()),
+ code_(code) { }
+
+
+ bool IsMatch(Object* other) {
+ if (!other->IsFixedArray()) return false;
+ FixedArray* pair = FixedArray::cast(other);
+ String* name = String::cast(pair->get(0));
+ Code::Flags flags = Code::cast(pair->get(1))->flags();
+ if (flags != flags_) {
+ return false;
+ }
+ return name_->Equals(name);
+ }
+
+ static uint32_t NameFlagsHashHelper(String* name, Code::Flags flags) {
+ return name->Hash() ^ flags;
+ }
+
+ uint32_t Hash() { return NameFlagsHashHelper(name_, flags_); }
+
+ uint32_t HashForObject(Object* obj) {
+ FixedArray* pair = FixedArray::cast(obj);
+ String* name = String::cast(pair->get(0));
+ Code* code = Code::cast(pair->get(1));
+ return NameFlagsHashHelper(name, code->flags());
+ }
+
+ Object* AsObject() {
+ ASSERT(code_ != NULL);
+ Object* obj = Heap::AllocateFixedArray(2);
+ if (obj->IsFailure()) return obj;
+ FixedArray* pair = FixedArray::cast(obj);
+ pair->set(0, name_);
+ pair->set(1, code_);
+ return pair;
+ }
+
+ private:
+ String* name_;
+ Code::Flags flags_;
+ Code* code_;
+};
+
+
+Object* CodeCacheHashTable::Lookup(String* name, Code::Flags flags) {
+ CodeCacheHashTableKey key(name, flags);
+ int entry = FindEntry(&key);
+ if (entry == kNotFound) return Heap::undefined_value();
+ return get(EntryToIndex(entry) + 1);
+}
+
+
+Object* CodeCacheHashTable::Put(String* name, Code* code) {
+ CodeCacheHashTableKey key(name, code);
+ Object* obj = EnsureCapacity(1, &key);
+ if (obj->IsFailure()) return obj;
+
+ // Don't use this, as the table might have grown.
+ CodeCacheHashTable* cache = reinterpret_cast<CodeCacheHashTable*>(obj);
+
+ int entry = cache->FindInsertionEntry(key.Hash());
+ Object* k = key.AsObject();
+ if (k->IsFailure()) return k;
+
+ cache->set(EntryToIndex(entry), k);
+ cache->set(EntryToIndex(entry) + 1, code);
+ cache->ElementAdded();
+ return cache;
+}
+
+
+int CodeCacheHashTable::GetIndex(String* name, Code::Flags flags) {
+ CodeCacheHashTableKey key(name, flags);
+ int entry = FindEntry(&key);
+ return (entry == kNotFound) ? -1 : entry;
+}
+
+
+void CodeCacheHashTable::RemoveByIndex(int index) {
+ ASSERT(index >= 0);
+ set(EntryToIndex(index), Heap::null_value());
+ set(EntryToIndex(index) + 1, Heap::null_value());
+ ElementRemoved();
}
@@ -3430,18 +3637,25 @@
int len = number_of_descriptors();
// Bottom-up max-heap construction.
- for (int i = 1; i < len; ++i) {
- int child_index = i;
- while (child_index > 0) {
- int parent_index = ((child_index + 1) >> 1) - 1;
- uint32_t parent_hash = GetKey(parent_index)->Hash();
+ // Index of the last node with children
+ const int max_parent_index = (len / 2) - 1;
+ for (int i = max_parent_index; i >= 0; --i) {
+ int parent_index = i;
+ const uint32_t parent_hash = GetKey(i)->Hash();
+ while (parent_index <= max_parent_index) {
+ int child_index = 2 * parent_index + 1;
uint32_t child_hash = GetKey(child_index)->Hash();
- if (parent_hash < child_hash) {
- Swap(parent_index, child_index);
- } else {
- break;
+ if (child_index + 1 < len) {
+ uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
+ if (right_child_hash > child_hash) {
+ child_index++;
+ child_hash = right_child_hash;
+ }
}
- child_index = parent_index;
+ if (child_hash <= parent_hash) break;
+ Swap(parent_index, child_index);
+ // Now element at child_index could be < its children.
+ parent_index = child_index; // parent_hash remains correct.
}
}
@@ -3451,21 +3665,21 @@
Swap(0, i);
// Sift down the new top element.
int parent_index = 0;
- while (true) {
- int child_index = ((parent_index + 1) << 1) - 1;
- if (child_index >= i) break;
- uint32_t child1_hash = GetKey(child_index)->Hash();
- uint32_t child2_hash = GetKey(child_index + 1)->Hash();
- uint32_t parent_hash = GetKey(parent_index)->Hash();
- if (child_index + 1 >= i || child1_hash > child2_hash) {
- if (parent_hash > child1_hash) break;
- Swap(parent_index, child_index);
- parent_index = child_index;
- } else {
- if (parent_hash > child2_hash) break;
- Swap(parent_index, child_index + 1);
- parent_index = child_index + 1;
+ const uint32_t parent_hash = GetKey(parent_index)->Hash();
+ const int max_parent_index = (i / 2) - 1;
+ while (parent_index <= max_parent_index) {
+ int child_index = parent_index * 2 + 1;
+ uint32_t child_hash = GetKey(child_index)->Hash();
+ if (child_index + 1 < i) {
+ uint32_t right_child_hash = GetKey(child_index + 1)->Hash();
+ if (right_child_hash > child_hash) {
+ child_index++;
+ child_hash = right_child_hash;
+ }
}
+ if (child_hash <= parent_hash) break;
+ Swap(parent_index, child_index);
+ parent_index = child_index;
}
}
@@ -3546,7 +3760,7 @@
// doesn't make Utf8Length faster, but it is very likely that
// the string will be accessed later (for example by WriteUtf8)
// so it's still a good idea.
- TryFlattenIfNotFlat();
+ TryFlatten();
Access<StringInputBuffer> buffer(&string_input_buffer);
buffer->Reset(0, this);
int result = 0;
@@ -4637,9 +4851,9 @@
}
-Object* String::SubString(int start, int end) {
+Object* String::SubString(int start, int end, PretenureFlag pretenure) {
if (start == 0 && end == length()) return this;
- Object* result = Heap::AllocateSubString(this, start, end);
+ Object* result = Heap::AllocateSubString(this, start, end, pretenure);
return result;
}
@@ -4951,11 +5165,9 @@
void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) {
- IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize);
- IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize);
- IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
- IteratePointers(v, kThisPropertyAssignmentsOffset,
- kThisPropertyAssignmentsOffset + kPointerSize);
+ IteratePointers(v,
+ kNameOffset,
+ kThisPropertyAssignmentsOffset + kPointerSize);
}
@@ -5126,6 +5338,7 @@
case STORE_IC: return "STORE_IC";
case KEYED_STORE_IC: return "KEYED_STORE_IC";
case CALL_IC: return "CALL_IC";
+ case BINARY_OP_IC: return "BINARY_OP_IC";
}
UNREACHABLE();
return NULL;
@@ -5247,7 +5460,7 @@
case DICTIONARY_ELEMENTS: {
if (IsJSArray()) {
uint32_t old_length =
- static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
+ static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
element_dictionary()->RemoveNumberEntries(new_length, old_length),
JSArray::cast(this)->set_length(len);
}
@@ -6903,15 +7116,17 @@
template<typename Shape, typename Key>
-Object* HashTable<Shape, Key>::Allocate(int at_least_space_for) {
- int capacity = RoundUpToPowerOf2(at_least_space_for);
- if (capacity < 4) {
- capacity = 4; // Guarantee min capacity.
+Object* HashTable<Shape, Key>::Allocate(int at_least_space_for,
+ PretenureFlag pretenure) {
+ const int kMinCapacity = 32;
+ int capacity = RoundUpToPowerOf2(at_least_space_for * 2);
+ if (capacity < kMinCapacity) {
+ capacity = kMinCapacity; // Guarantee min capacity.
} else if (capacity > HashTable::kMaxCapacity) {
return Failure::OutOfMemoryException();
}
- Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity));
+ Object* obj = Heap::AllocateHashTable(EntryToIndex(capacity), pretenure);
if (!obj->IsFailure()) {
HashTable::cast(obj)->SetNumberOfElements(0);
HashTable::cast(obj)->SetNumberOfDeletedElements(0);
@@ -6946,10 +7161,15 @@
// Return if:
// 50% is still free after adding n elements and
// at most 50% of the free elements are deleted elements.
- if ((nof + (nof >> 1) <= capacity) &&
- (nod <= (capacity - nof) >> 1)) return this;
+ if (nod <= (capacity - nof) >> 1) {
+ int needed_free = nof >> 1;
+ if (nof + needed_free <= capacity) return this;
+ }
- Object* obj = Allocate(nof * 2);
+ const int kMinCapacityForPretenure = 256;
+ bool pretenure =
+ (capacity > kMinCapacityForPretenure) && !Heap::InNewSpace(this);
+ Object* obj = Allocate(nof * 2, pretenure ? TENURED : NOT_TENURED);
if (obj->IsFailure()) return obj;
AssertNoAllocation no_gc;
@@ -6981,7 +7201,6 @@
}
-
template<typename Shape, typename Key>
uint32_t HashTable<Shape, Key>::FindInsertionEntry(uint32_t hash) {
uint32_t capacity = Capacity();
@@ -7091,8 +7310,7 @@
result_double = HeapNumber::cast(new_double);
}
- int capacity = dict->Capacity();
- Object* obj = NumberDictionary::Allocate(dict->Capacity());
+ Object* obj = NumberDictionary::Allocate(dict->NumberOfElements());
if (obj->IsFailure()) return obj;
NumberDictionary* new_dict = NumberDictionary::cast(obj);
@@ -7100,6 +7318,7 @@
uint32_t pos = 0;
uint32_t undefs = 0;
+ int capacity = dict->Capacity();
for (int i = 0; i < capacity; i++) {
Object* k = dict->KeyAt(i);
if (dict->IsKey(k)) {
diff --git a/src/objects.h b/src/objects.h
index 7533892..826fcae 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -72,6 +72,7 @@
// - Dictionary
// - SymbolTable
// - CompilationCacheTable
+// - CodeCacheHashTable
// - MapCache
// - Context
// - GlobalContext
@@ -102,6 +103,7 @@
// - TypeSwitchInfo
// - DebugInfo
// - BreakPointInfo
+// - CodeCache
//
// Formats of Object*:
// Smi: [31 bit signed int] 0
@@ -269,6 +271,7 @@
V(SIGNATURE_INFO_TYPE) \
V(TYPE_SWITCH_INFO_TYPE) \
V(SCRIPT_TYPE) \
+ V(CODE_CACHE_TYPE) \
\
V(JS_VALUE_TYPE) \
V(JS_OBJECT_TYPE) \
@@ -364,7 +367,8 @@
V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
V(SIGNATURE_INFO, SignatureInfo, signature_info) \
V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
- V(SCRIPT, Script, script)
+ V(SCRIPT, Script, script) \
+ V(CODE_CACHE, CodeCache, code_cache)
#ifdef ENABLE_DEBUGGER_SUPPORT
#define STRUCT_LIST_DEBUGGER(V) \
@@ -468,6 +472,7 @@
SIGNATURE_INFO_TYPE,
TYPE_SWITCH_INFO_TYPE,
SCRIPT_TYPE,
+ CODE_CACHE_TYPE,
#ifdef ENABLE_DEBUGGER_SUPPORT
DEBUG_INFO_TYPE,
BREAK_POINT_INFO_TYPE,
@@ -601,6 +606,7 @@
inline bool IsDictionary();
inline bool IsSymbolTable();
inline bool IsCompilationCacheTable();
+ inline bool IsCodeCacheHashTable();
inline bool IsMapCache();
inline bool IsPrimitive();
inline bool IsGlobalObject();
@@ -1626,6 +1632,9 @@
inline void set_null(int index);
inline void set_the_hole(int index);
+ // Gives access to raw memory which stores the array's data.
+ inline Object** data_start();
+
// Copy operations.
inline Object* Copy();
Object* CopySize(int new_length);
@@ -1933,7 +1942,8 @@
}
// Returns a new HashTable object. Might return Failure.
- static Object* Allocate(int at_least_space_for);
+ static Object* Allocate(int at_least_space_for,
+ PretenureFlag pretenure = NOT_TENURED);
// Returns the key at entry.
Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }
@@ -1965,6 +1975,8 @@
static const int kEntrySize = Shape::kEntrySize;
static const int kElementsStartOffset =
kHeaderSize + kElementsStartIndex * kPointerSize;
+ static const int kCapacityOffset =
+ kHeaderSize + kCapacityIndex * kPointerSize;
// Constant used for denoting a absent entry.
static const int kNotFound = -1;
@@ -2615,13 +2627,14 @@
CALL_IC,
STORE_IC,
KEYED_STORE_IC,
- // No more than eight kinds. The value currently encoded in three bits in
+ BINARY_OP_IC,
+ // No more than 16 kinds. The value currently encoded in four bits in
// Flags.
// Pseudo-kinds.
REGEXP = BUILTIN,
FIRST_IC_KIND = LOAD_IC,
- LAST_IC_KIND = KEYED_STORE_IC
+ LAST_IC_KIND = BINARY_OP_IC
};
enum {
@@ -2667,7 +2680,7 @@
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
inline bool is_call_stub() { return kind() == CALL_IC; }
- // [major_key]: For kind STUB, the major key.
+ // [major_key]: For kind STUB or BINARY_OP_IC, the major key.
inline CodeStub::Major major_key();
inline void set_major_key(CodeStub::Major major);
@@ -2774,14 +2787,14 @@
static const int kFlagsICStateShift = 0;
static const int kFlagsICInLoopShift = 3;
static const int kFlagsKindShift = 4;
- static const int kFlagsTypeShift = 7;
- static const int kFlagsArgumentsCountShift = 10;
+ static const int kFlagsTypeShift = 8;
+ static const int kFlagsArgumentsCountShift = 11;
- static const int kFlagsICStateMask = 0x00000007; // 0000000111
- static const int kFlagsICInLoopMask = 0x00000008; // 0000001000
- static const int kFlagsKindMask = 0x00000070; // 0001110000
- static const int kFlagsTypeMask = 0x00000380; // 1110000000
- static const int kFlagsArgumentsCountMask = 0xFFFFFC00;
+ static const int kFlagsICStateMask = 0x00000007; // 00000000111
+ static const int kFlagsICInLoopMask = 0x00000008; // 00000001000
+ static const int kFlagsKindMask = 0x000000F0; // 00011110000
+ static const int kFlagsTypeMask = 0x00000700; // 11100000000
+ static const int kFlagsArgumentsCountMask = 0xFFFFF800;
static const int kFlagsNotUsedInLookup =
(kFlagsICInLoopMask | kFlagsTypeMask);
@@ -2922,7 +2935,7 @@
DECL_ACCESSORS(instance_descriptors, DescriptorArray)
// [stub cache]: contains stubs compiled for this map.
- DECL_ACCESSORS(code_cache, FixedArray)
+ DECL_ACCESSORS(code_cache, Object)
Object* CopyDropDescriptors();
@@ -2958,10 +2971,10 @@
// Returns the non-negative index of the code object if it is in the
// cache and -1 otherwise.
- int IndexInCodeCache(Code* code);
+ int IndexInCodeCache(String* name, Code* code);
// Removes a code object from the code cache at the given index.
- void RemoveFromCodeCache(int index);
+ void RemoveFromCodeCache(String* name, Code* code, int index);
// For every transition in this map, makes the transition's
// target's prototype pointer point back to this map.
@@ -3026,6 +3039,11 @@
static const int kNeedsLoading = 0;
static const int kIsExtensible = 1;
+ // Layout of the default cache. It holds alternating name and code objects.
+ static const int kCodeCacheEntrySize = 2;
+ static const int kCodeCacheEntryNameOffset = 0;
+ static const int kCodeCacheEntryCodeOffset = 1;
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
};
@@ -3275,11 +3293,22 @@
static const int kDontAdaptArgumentsSentinel = -1;
// Layout description.
- // (An even number of integers has a size that is a multiple of a pointer.)
+ // Pointer fields.
static const int kNameOffset = HeapObject::kHeaderSize;
static const int kCodeOffset = kNameOffset + kPointerSize;
static const int kConstructStubOffset = kCodeOffset + kPointerSize;
- static const int kLengthOffset = kConstructStubOffset + kPointerSize;
+ static const int kInstanceClassNameOffset =
+ kConstructStubOffset + kPointerSize;
+ static const int kExternalReferenceDataOffset =
+ kInstanceClassNameOffset + kPointerSize;
+ static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize;
+ static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
+ static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
+ static const int kThisPropertyAssignmentsOffset =
+ kInferredNameOffset + kPointerSize;
+ // Integer fields.
+ static const int kLengthOffset =
+ kThisPropertyAssignmentsOffset + kPointerSize;
static const int kFormalParameterCountOffset = kLengthOffset + kIntSize;
static const int kExpectedNofPropertiesOffset =
kFormalParameterCountOffset + kIntSize;
@@ -3287,27 +3316,14 @@
kExpectedNofPropertiesOffset + kIntSize;
static const int kEndPositionOffset = kStartPositionAndTypeOffset + kIntSize;
static const int kFunctionTokenPositionOffset = kEndPositionOffset + kIntSize;
- static const int kInstanceClassNameOffset =
+ static const int kCompilerHintsOffset =
kFunctionTokenPositionOffset + kIntSize;
- static const int kExternalReferenceDataOffset =
- kInstanceClassNameOffset + kPointerSize;
- static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize;
- static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
- static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
- static const int kCompilerHintsOffset = kInferredNameOffset + kPointerSize;
- static const int kThisPropertyAssignmentsOffset =
- kCompilerHintsOffset + kPointerSize;
static const int kThisPropertyAssignmentsCountOffset =
- kThisPropertyAssignmentsOffset + kPointerSize;
- static const int kSize = kThisPropertyAssignmentsCountOffset + kPointerSize;
+ kCompilerHintsOffset + kIntSize;
+ // Total size.
+ static const int kSize = kThisPropertyAssignmentsCountOffset + kIntSize;
private:
- // Bit positions in length_and_flg.
- // The least significant bit is used as the flag.
- static const int kFlagBit = 0;
- static const int kLengthShift = 1;
- static const int kLengthMask = ~((1 << kLengthShift) - 1);
-
// Bit positions in start_position_and_type.
// The source code start position is in the 30 most significant bits of
// the start_position_and_type field.
@@ -3711,6 +3727,97 @@
};
+class CodeCache: public Struct {
+ public:
+ DECL_ACCESSORS(default_cache, FixedArray)
+ DECL_ACCESSORS(normal_type_cache, Object)
+
+ // Add the code object to the cache.
+ Object* Update(String* name, Code* code);
+
+ // Lookup code object in the cache. Returns code object if found and undefined
+ // if not.
+ Object* Lookup(String* name, Code::Flags flags);
+
+ // Get the internal index of a code object in the cache. Returns -1 if the
+ // code object is not in that cache. This index can be used to later call
+ // RemoveByIndex. The cache cannot be modified between a call to GetIndex and
+ // RemoveByIndex.
+ int GetIndex(String* name, Code* code);
+
+ // Remove an object from the cache with the provided internal index.
+ void RemoveByIndex(String* name, Code* code, int index);
+
+ static inline CodeCache* cast(Object* obj);
+
+#ifdef DEBUG
+ void CodeCachePrint();
+ void CodeCacheVerify();
+#endif
+
+ static const int kDefaultCacheOffset = HeapObject::kHeaderSize;
+ static const int kNormalTypeCacheOffset =
+ kDefaultCacheOffset + kPointerSize;
+ static const int kSize = kNormalTypeCacheOffset + kPointerSize;
+
+ private:
+ Object* UpdateDefaultCache(String* name, Code* code);
+ Object* UpdateNormalTypeCache(String* name, Code* code);
+ Object* LookupDefaultCache(String* name, Code::Flags flags);
+ Object* LookupNormalTypeCache(String* name, Code::Flags flags);
+
+ // Code cache layout of the default cache. Elements are alternating name and
+ // code objects for non normal load/store/call IC's.
+ static const int kCodeCacheEntrySize = 2;
+ static const int kCodeCacheEntryNameOffset = 0;
+ static const int kCodeCacheEntryCodeOffset = 1;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CodeCache);
+};
+
+
+class CodeCacheHashTableShape {
+ public:
+ static inline bool IsMatch(HashTableKey* key, Object* value) {
+ return key->IsMatch(value);
+ }
+
+ static inline uint32_t Hash(HashTableKey* key) {
+ return key->Hash();
+ }
+
+ static inline uint32_t HashForObject(HashTableKey* key, Object* object) {
+ return key->HashForObject(object);
+ }
+
+ static Object* AsObject(HashTableKey* key) {
+ return key->AsObject();
+ }
+
+ static const int kPrefixSize = 0;
+ static const int kEntrySize = 2;
+};
+
+
+class CodeCacheHashTable: public HashTable<CodeCacheHashTableShape,
+ HashTableKey*> {
+ public:
+ Object* Lookup(String* name, Code::Flags flags);
+ Object* Put(String* name, Code* code);
+
+ int GetIndex(String* name, Code::Flags flags);
+ void RemoveByIndex(int index);
+
+ static inline CodeCacheHashTable* cast(Object* obj);
+
+ // Initial size of the fixed array backing the hash table.
+ static const int kInitialSize = 64;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CodeCacheHashTable);
+};
+
+
enum AllowNullsFlag {ALLOW_NULLS, DISALLOW_NULLS};
enum RobustnessFlag {ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL};
@@ -3837,13 +3944,13 @@
// Try to flatten the top level ConsString that is hiding behind this
// string. This is a no-op unless the string is a ConsString. Flatten
// mutates the ConsString and might return a failure.
- Object* TryFlatten();
+ Object* SlowTryFlatten(PretenureFlag pretenure);
// Try to flatten the string. Checks first inline to see if it is necessary.
- // Do not handle allocation failures. After calling TryFlattenIfNotFlat, the
+ // Do not handle allocation failures. After calling TryFlatten, the
// string could still be a ConsString, in which case a failure is returned.
// Use FlattenString from Handles.cc to be sure to flatten.
- inline Object* TryFlattenIfNotFlat();
+ inline Object* TryFlatten(PretenureFlag pretenure = NOT_TENURED);
Vector<const char> ToAsciiVector();
Vector<const uc16> ToUC16Vector();
@@ -3853,7 +3960,7 @@
bool MarkAsUndetectable();
// Return a substring.
- Object* SubString(int from, int to);
+ Object* SubString(int from, int to, PretenureFlag pretenure = NOT_TENURED);
// String equality operations.
inline bool Equals(String* other);
@@ -4051,10 +4158,6 @@
// Casting.
static inline SeqString* cast(Object* obj);
- // Dispatched behaviour.
- // For regexp code.
- uint16_t* SeqStringGetTwoByteAddress();
-
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(SeqString);
};
diff --git a/src/parser.cc b/src/parser.cc
index 5058296..dd72669 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -31,6 +31,7 @@
#include "ast.h"
#include "bootstrapper.h"
#include "compiler.h"
+#include "messages.h"
#include "platform.h"
#include "runtime.h"
#include "parser.h"
@@ -107,13 +108,13 @@
// Returns NULL if parsing failed.
FunctionLiteral* ParseProgram(Handle<String> source,
- unibrow::CharacterStream* stream,
bool in_global_context);
FunctionLiteral* ParseLazy(Handle<String> source,
Handle<String> name,
- int start_position, bool is_expression);
- FunctionLiteral* ParseJson(Handle<String> source,
- unibrow::CharacterStream* stream);
+ int start_position,
+ int end_position,
+ bool is_expression);
+ FunctionLiteral* ParseJson(Handle<String> source);
// The minimum number of contiguous assignment that will
// be treated as an initialization block. Benchmarks show that
@@ -1212,7 +1213,7 @@
AssertNoZoneAllocation assert_no_zone_allocation;
AssertNoAllocation assert_no_allocation;
NoHandleAllocation no_handle_allocation;
- scanner_.Init(source, stream, 0, JAVASCRIPT);
+ scanner_.Initialize(source, stream, JAVASCRIPT);
ASSERT(target_stack_ == NULL);
mode_ = PARSE_EAGERLY;
DummyScope top_scope;
@@ -1226,7 +1227,6 @@
FunctionLiteral* Parser::ParseProgram(Handle<String> source,
- unibrow::CharacterStream* stream,
bool in_global_context) {
CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
@@ -1234,8 +1234,8 @@
Counters::total_parse_size.Increment(source->length());
// Initialize parser state.
- source->TryFlattenIfNotFlat();
- scanner_.Init(source, stream, 0, JAVASCRIPT);
+ source->TryFlatten();
+ scanner_.Initialize(source, JAVASCRIPT);
ASSERT(target_stack_ == NULL);
// Compute the parsing mode.
@@ -1286,15 +1286,15 @@
FunctionLiteral* Parser::ParseLazy(Handle<String> source,
Handle<String> name,
int start_position,
+ int end_position,
bool is_expression) {
CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
HistogramTimerScope timer(&Counters::parse_lazy);
- source->TryFlattenIfNotFlat();
Counters::total_parse_size.Increment(source->length());
- SafeStringInputBuffer buffer(source.location());
// Initialize parser state.
- scanner_.Init(source, &buffer, start_position, JAVASCRIPT);
+ source->TryFlatten();
+ scanner_.Initialize(source, start_position, end_position, JAVASCRIPT);
ASSERT(target_stack_ == NULL);
mode_ = PARSE_EAGERLY;
@@ -1330,16 +1330,15 @@
return result;
}
-FunctionLiteral* Parser::ParseJson(Handle<String> source,
- unibrow::CharacterStream* stream) {
+FunctionLiteral* Parser::ParseJson(Handle<String> source) {
CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
HistogramTimerScope timer(&Counters::parse);
Counters::total_parse_size.Increment(source->length());
// Initialize parser state.
- source->TryFlattenIfNotFlat();
- scanner_.Init(source, stream, 0, JSON);
+ source->TryFlatten(TENURED);
+ scanner_.Initialize(source, JSON);
ASSERT(target_stack_ == NULL);
FunctionLiteral* result = NULL;
@@ -3258,7 +3257,6 @@
result = VariableProxySentinel::this_proxy();
} else {
VariableProxy* recv = top_scope_->receiver();
- recv->var_uses()->RecordRead(1);
result = recv;
}
break;
@@ -5065,13 +5063,12 @@
return NULL;
}
Handle<String> source = Handle<String>(String::cast(script->source()));
- SafeStringInputBuffer input(source.location());
FunctionLiteral* result;
if (is_json) {
ASSERT(compile_in_global_context);
- result = parser.ParseJson(source, &input);
+ result = parser.ParseJson(source);
} else {
- result = parser.ParseProgram(source, &input, compile_in_global_context);
+ result = parser.ParseProgram(source, compile_in_global_context);
}
return result;
}
@@ -5086,13 +5083,11 @@
always_allow_natives_syntax = true;
AstBuildingParser parser(script, true, NULL, NULL); // always allow
always_allow_natives_syntax = allow_natives_syntax_before;
- // Parse the function by pulling the function source from the script source.
+ // Parse the function by pointing to the function source in the script source.
Handle<String> script_source(String::cast(script->source()));
FunctionLiteral* result =
- parser.ParseLazy(SubString(script_source, start_position, end_position),
- name,
- start_position,
- is_expression);
+ parser.ParseLazy(script_source, name,
+ start_position, end_position, is_expression);
return result;
}
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index ff75776..3617e8a 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -567,6 +567,9 @@
TickSample sample;
+ // We always sample the VM state.
+ sample.state = Logger::state();
+
// If profiling, we extract the current pc and sp.
if (active_sampler_->IsProfiling()) {
// Extracting the sample from the context is extremely machine dependent.
@@ -588,9 +591,6 @@
active_sampler_->SampleStack(&sample);
}
- // We always sample the VM state.
- sample.state = Logger::state();
-
active_sampler_->Tick(&sample);
}
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index e890f94..f1812ff 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -331,8 +331,8 @@
if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
int c;
- if (attr_r == 'r' && attr_x == 'x') {
- // Found a readable and executable entry. Skip characters until we reach
+ if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
+ // Found a read-only executable entry. Skip characters until we reach
// the beginning of the filename or the end of the line.
do {
c = getc(fp);
@@ -728,6 +728,9 @@
TickSample sample;
+ // We always sample the VM state.
+ sample.state = Logger::state();
+
// If profiling, we extract the current pc and sp.
if (active_sampler_->IsProfiling()) {
// Extracting the sample from the context is extremely machine dependent.
@@ -760,9 +763,6 @@
active_sampler_->SampleStack(&sample);
}
- // We always sample the VM state.
- sample.state = Logger::state();
-
active_sampler_->Tick(&sample);
#endif
}
diff --git a/src/platform-macos.cc b/src/platform-macos.cc
index e379ae2..4502987 100644
--- a/src/platform-macos.cc
+++ b/src/platform-macos.cc
@@ -548,6 +548,9 @@
while (sampler_->IsActive()) {
TickSample sample;
+ // We always sample the VM state.
+ sample.state = Logger::state();
+
// If profiling, we record the pc and sp of the profiled thread.
if (sampler_->IsProfiling()
&& KERN_SUCCESS == thread_suspend(profiled_thread_)) {
@@ -585,8 +588,6 @@
thread_resume(profiled_thread_);
}
- // We always sample the VM state.
- sample.state = Logger::state();
// Invoke tick handler with program counter and stack pointer.
sampler_->Tick(&sample);
diff --git a/src/platform-win32.cc b/src/platform-win32.cc
index 81b0d4c..04ffea9 100644
--- a/src/platform-win32.cc
+++ b/src/platform-win32.cc
@@ -1807,6 +1807,9 @@
while (sampler_->IsActive()) {
TickSample sample;
+ // We always sample the VM state.
+ sample.state = Logger::state();
+
// If profiling, we record the pc and sp of the profiled thread.
if (sampler_->IsProfiling()
&& SuspendThread(profiled_thread_) != (DWORD)-1) {
@@ -1826,8 +1829,6 @@
ResumeThread(profiled_thread_);
}
- // We always sample the VM state.
- sample.state = Logger::state();
// Invoke tick handler with program counter and stack pointer.
sampler_->Tick(&sample);
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index ca570a6..6e2a60e 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -604,7 +604,7 @@
ast_printer_->Print(StaticType::Type2String(expr->type()));
printed_first = true;
}
- if (expr->num() != Expression::kNoLabel) {
+ if (expr->num() != AstNode::kNoNumber) {
ast_printer_->Print(printed_first ? ", num = " : " (num = ");
ast_printer_->Print("%d", expr->num());
printed_first = true;
@@ -679,7 +679,7 @@
pos += OS::SNPrintF(buf + pos, ", type = %s",
StaticType::Type2String(type));
}
- if (num != Expression::kNoLabel) {
+ if (num != AstNode::kNoNumber) {
pos += OS::SNPrintF(buf + pos, ", num = %d", num);
}
OS::SNPrintF(buf + pos, ")");
@@ -740,7 +740,7 @@
PrintLiteralWithModeIndented("VAR", scope->parameter(i),
scope->parameter(i)->name(),
scope->parameter(i)->type(),
- Expression::kNoLabel);
+ AstNode::kNoNumber);
}
}
}
@@ -786,7 +786,7 @@
node->proxy()->AsVariable(),
node->proxy()->name(),
node->proxy()->AsVariable()->type(),
- Expression::kNoLabel);
+ AstNode::kNoNumber);
} else {
// function declarations
PrintIndented("FUNCTION ");
diff --git a/src/regexp-delay.js b/src/regexp-delay.js
index 7bec455..843d0aa 100644
--- a/src/regexp-delay.js
+++ b/src/regexp-delay.js
@@ -142,7 +142,7 @@
function RegExpExec(string) {
if (!IS_REGEXP(this)) {
- throw MakeTypeError('method_called_on_incompatible',
+ throw MakeTypeError('incompatible_method_receiver',
['RegExp.prototype.exec', this]);
}
if (%_ArgumentsLength() == 0) {
@@ -152,8 +152,12 @@
}
string = regExpInput;
}
- var s = ToString(string);
- var length = s.length;
+ var s;
+ if (IS_STRING(string)) {
+ s = string;
+ } else {
+ s = ToString(string);
+ }
var lastIndex = this.lastIndex;
var i = this.global ? TO_INTEGER(lastIndex) : 0;
@@ -172,16 +176,23 @@
}
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
- var result = new $Array(numResults);
- for (var i = 0; i < numResults; i++) {
- var matchStart = lastMatchInfo[CAPTURE(i << 1)];
- var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
- if (matchStart != -1 && matchEnd != -1) {
- result[i] = SubString(s, matchStart, matchEnd);
- } else {
- // Make sure the element is present. Avoid reading the undefined
- // property from the global object since this may change.
- result[i] = void 0;
+ var result;
+ if (numResults === 1) {
+ var matchStart = lastMatchInfo[CAPTURE(0)];
+ var matchEnd = lastMatchInfo[CAPTURE(1)];
+ result = [SubString(s, matchStart, matchEnd)];
+ } else {
+ result = new $Array(numResults);
+ for (var i = 0; i < numResults; i++) {
+ var matchStart = lastMatchInfo[CAPTURE(i << 1)];
+ var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
+ if (matchStart != -1 && matchEnd != -1) {
+ result[i] = SubString(s, matchStart, matchEnd);
+ } else {
+ // Make sure the element is present. Avoid reading the undefined
+ // property from the global object since this may change.
+ result[i] = void 0;
+ }
}
}
@@ -199,7 +210,7 @@
// else implements.
function RegExpTest(string) {
if (!IS_REGEXP(this)) {
- throw MakeTypeError('method_called_on_incompatible',
+ throw MakeTypeError('incompatible_method_receiver',
['RegExp.prototype.test', this]);
}
if (%_ArgumentsLength() == 0) {
diff --git a/src/register-allocator-inl.h b/src/register-allocator-inl.h
index 718da36..8453104 100644
--- a/src/register-allocator-inl.h
+++ b/src/register-allocator-inl.h
@@ -103,6 +103,45 @@
registers_.Unuse(ToNumber(reg));
}
+
+NumberInfo Result::number_info() const {
+ ASSERT(is_valid());
+ if (!is_constant()) {
+ return NumberInfo::FromInt(NumberInfoField::decode(value_));
+ }
+ Handle<Object> value = handle();
+ if (value->IsSmi()) return NumberInfo::Smi();
+ if (value->IsHeapNumber()) return NumberInfo::HeapNumber();
+ return NumberInfo::Unknown();
+}
+
+
+void Result::set_number_info(NumberInfo info) {
+ ASSERT(is_valid());
+ value_ &= ~NumberInfoField::mask();
+ value_ |= NumberInfoField::encode(info.ToInt());
+}
+
+
+bool Result::is_number() const {
+ return number_info().IsNumber();
+}
+
+
+bool Result::is_smi() const {
+ return number_info().IsSmi();
+}
+
+
+bool Result::is_integer32() const {
+ return number_info().IsInteger32();
+}
+
+
+bool Result::is_heap_number() const {
+ return number_info().IsHeapNumber();
+}
+
} } // namespace v8::internal
#endif // V8_REGISTER_ALLOCATOR_INL_H_
diff --git a/src/register-allocator.cc b/src/register-allocator.cc
index 1655b4a..64e4428 100644
--- a/src/register-allocator.cc
+++ b/src/register-allocator.cc
@@ -38,11 +38,11 @@
// Result implementation.
-Result::Result(Register reg, NumberInfo::Type info) {
+Result::Result(Register reg, NumberInfo info) {
ASSERT(reg.is_valid() && !RegisterAllocator::IsReserved(reg));
CodeGeneratorScope::Current()->allocator()->Use(reg);
value_ = TypeField::encode(REGISTER)
- | NumberInfoField::encode(info)
+ | NumberInfoField::encode(info.ToInt())
| DataField::encode(reg.code_);
}
@@ -53,23 +53,6 @@
}
-NumberInfo::Type Result::number_info() {
- ASSERT(is_valid());
- if (!is_constant()) return NumberInfoField::decode(value_);
- Handle<Object> value = handle();
- if (value->IsSmi()) return NumberInfo::kSmi;
- if (value->IsHeapNumber()) return NumberInfo::kHeapNumber;
- return NumberInfo::kUnknown;
-}
-
-
-void Result::set_number_info(NumberInfo::Type info) {
- ASSERT(is_valid());
- value_ = value_ & ~NumberInfoField::mask();
- value_ = value_ | NumberInfoField::encode(info);
-}
-
-
// -------------------------------------------------------------------------
// RegisterAllocator implementation.
diff --git a/src/register-allocator.h b/src/register-allocator.h
index 747200a..2a7b820 100644
--- a/src/register-allocator.h
+++ b/src/register-allocator.h
@@ -65,12 +65,12 @@
Result() { invalidate(); }
// Construct a register Result.
- explicit Result(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
+ explicit Result(Register reg, NumberInfo info = NumberInfo::Unknown());
// Construct a Result whose value is a compile-time constant.
explicit Result(Handle<Object> value) {
value_ = TypeField::encode(CONSTANT)
- | NumberInfoField::encode(NumberInfo::kUninitialized)
+ | NumberInfoField::encode(NumberInfo::Uninitialized().ToInt())
| DataField::encode(ConstantList()->length());
ConstantList()->Add(value);
}
@@ -101,13 +101,12 @@
void invalidate() { value_ = TypeField::encode(INVALID); }
- NumberInfo::Type number_info();
- void set_number_info(NumberInfo::Type info);
- bool is_number() {
- return (number_info() & NumberInfo::kNumber) != 0;
- }
- bool is_smi() { return number_info() == NumberInfo::kSmi; }
- bool is_heap_number() { return number_info() == NumberInfo::kHeapNumber; }
+ inline NumberInfo number_info() const;
+ inline void set_number_info(NumberInfo info);
+ inline bool is_number() const;
+ inline bool is_smi() const;
+ inline bool is_integer32() const;
+ inline bool is_heap_number() const;
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
@@ -140,8 +139,8 @@
uint32_t value_;
class TypeField: public BitField<Type, 0, 2> {};
- class NumberInfoField : public BitField<NumberInfo::Type, 2, 3> {};
- class DataField: public BitField<uint32_t, 5, 32 - 5> {};
+ class NumberInfoField : public BitField<int, 2, 4> {};
+ class DataField: public BitField<uint32_t, 6, 32 - 6> {};
inline void CopyTo(Result* destination) const;
diff --git a/src/rewriter.cc b/src/rewriter.cc
index b05cfae..8a32212 100644
--- a/src/rewriter.cc
+++ b/src/rewriter.cc
@@ -244,6 +244,12 @@
!Heap::result_symbol()->Equals(*var->name())) {
func_name_inferrer_.PushName(var->name());
}
+
+ if (FLAG_safe_int32_compiler) {
+ if (var->IsStackAllocated() && !var->is_arguments()) {
+ node->set_side_effect_free(true);
+ }
+ }
}
}
@@ -252,11 +258,14 @@
Handle<Object> literal = node->handle();
if (literal->IsSmi()) {
node->type()->SetAsLikelySmi();
+ node->set_side_effect_free(true);
} else if (literal->IsString()) {
Handle<String> lit_str(Handle<String>::cast(literal));
if (!Heap::prototype_symbol()->Equals(*lit_str)) {
func_name_inferrer_.PushName(lit_str);
}
+ } else if (literal->IsHeapNumber()) {
+ node->set_side_effect_free(true);
}
}
@@ -414,6 +423,27 @@
void AstOptimizer::VisitUnaryOperation(UnaryOperation* node) {
Visit(node->expression());
+ if (FLAG_safe_int32_compiler) {
+ switch (node->op()) {
+ case Token::BIT_NOT:
+ node->expression()->set_to_int32(true);
+ // Fall through.
+ case Token::ADD:
+ case Token::SUB:
+ case Token::NOT:
+ node->set_side_effect_free(node->expression()->side_effect_free());
+ break;
+ case Token::DELETE:
+ case Token::TYPEOF:
+ case Token::VOID:
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ } else if (node->op() == Token::BIT_NOT) {
+ node->expression()->set_to_int32(true);
+ }
}
@@ -442,6 +472,8 @@
node->type()->SetAsLikelySmiIfUnknown();
node->left()->type()->SetAsLikelySmiIfUnknown();
node->right()->type()->SetAsLikelySmiIfUnknown();
+ node->left()->set_to_int32(true);
+ node->right()->set_to_int32(true);
break;
case Token::ADD:
case Token::SUB:
@@ -483,6 +515,32 @@
}
}
}
+
+ if (FLAG_safe_int32_compiler) {
+ switch (node->op()) {
+ case Token::COMMA:
+ case Token::OR:
+ case Token::AND:
+ break;
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ case Token::SHL:
+ case Token::SAR:
+ case Token::SHR:
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ node->set_side_effect_free(node->left()->side_effect_free() &&
+ node->right()->side_effect_free());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
}
diff --git a/src/runtime.cc b/src/runtime.cc
index 6459aa7..6d3a158 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -38,6 +38,7 @@
#include "debug.h"
#include "execution.h"
#include "jsregexp.h"
+#include "liveedit.h"
#include "parser.h"
#include "platform.h"
#include "runtime.h"
@@ -1230,6 +1231,17 @@
}
+static Object* Runtime_FinishArrayPrototypeSetup(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(JSArray, prototype, 0);
+ // This is necessary to enable fast checks for absence of elements
+ // on Array.prototype and below.
+ prototype->set_elements(Heap::empty_fixed_array());
+ return Smi::FromInt(0);
+}
+
+
static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 4);
@@ -2608,8 +2620,8 @@
int d = str1->Get(0) - str2->Get(0);
if (d != 0) return Smi::FromInt(d);
- str1->TryFlattenIfNotFlat();
- str2->TryFlattenIfNotFlat();
+ str1->TryFlatten();
+ str2->TryFlatten();
static StringInputBuffer buf1;
static StringInputBuffer buf2;
@@ -2818,7 +2830,7 @@
// string->Get(index).
static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
if (index < static_cast<uint32_t>(string->length())) {
- string->TryFlattenIfNotFlat();
+ string->TryFlatten();
return LookupSingleCharacterStringFromCode(
string->Get(index));
}
@@ -2846,6 +2858,11 @@
return prototype->GetElement(index);
}
+ return GetElement(object, index);
+}
+
+
+Object* Runtime::GetElement(Handle<Object> object, uint32_t index) {
return object->GetElement(index);
}
@@ -3072,7 +3089,7 @@
result = SetElement(js_object, index, value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
- key_string->TryFlattenIfNotFlat();
+ key_string->TryFlatten();
result = SetProperty(js_object, key_string, value, attr);
}
if (result.is_null()) return Failure::Exception();
@@ -3121,7 +3138,7 @@
return js_object->SetElement(index, *value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
- key_string->TryFlattenIfNotFlat();
+ key_string->TryFlatten();
return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
*value,
attr);
@@ -3173,7 +3190,7 @@
key_string = Handle<String>::cast(converted);
}
- key_string->TryFlattenIfNotFlat();
+ key_string->TryFlatten();
return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
}
@@ -3669,7 +3686,7 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, subject, args[0]);
- subject->TryFlattenIfNotFlat();
+ subject->TryFlatten();
return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
}
@@ -3751,7 +3768,7 @@
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlattenIfNotFlat();
+ source->TryFlatten();
int escaped_length = 0;
int length = source->length();
@@ -3864,7 +3881,7 @@
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlattenIfNotFlat();
+ source->TryFlatten();
bool ascii = true;
int length = source->length();
@@ -3904,7 +3921,7 @@
CONVERT_CHECKED(String, s, args[0]);
CONVERT_SMI_CHECKED(radix, args[1]);
- s->TryFlattenIfNotFlat();
+ s->TryFlatten();
int len = s->length();
int i;
@@ -4068,18 +4085,83 @@
}
-template <class Converter>
-static Object* ConvertCase(Arguments args,
- unibrow::Mapping<Converter, 128>* mapping) {
+static inline SeqAsciiString* TryGetSeqAsciiString(String* s) {
+ if (!s->IsFlat() || !s->IsAsciiRepresentation()) return NULL;
+ if (s->IsConsString()) {
+ ASSERT(ConsString::cast(s)->second()->length() == 0);
+ return SeqAsciiString::cast(ConsString::cast(s)->first());
+ }
+ return SeqAsciiString::cast(s);
+}
+
+
+namespace {
+
+struct ToLowerTraits {
+ typedef unibrow::ToLowercase UnibrowConverter;
+
+ static bool ConvertAscii(char* dst, char* src, int length) {
+ bool changed = false;
+ for (int i = 0; i < length; ++i) {
+ char c = src[i];
+ if ('A' <= c && c <= 'Z') {
+ c += ('a' - 'A');
+ changed = true;
+ }
+ dst[i] = c;
+ }
+ return changed;
+ }
+};
+
+
+struct ToUpperTraits {
+ typedef unibrow::ToUppercase UnibrowConverter;
+
+ static bool ConvertAscii(char* dst, char* src, int length) {
+ bool changed = false;
+ for (int i = 0; i < length; ++i) {
+ char c = src[i];
+ if ('a' <= c && c <= 'z') {
+ c -= ('a' - 'A');
+ changed = true;
+ }
+ dst[i] = c;
+ }
+ return changed;
+ }
+};
+
+} // namespace
+
+
+template <typename ConvertTraits>
+static Object* ConvertCase(
+ Arguments args,
+ unibrow::Mapping<typename ConvertTraits::UnibrowConverter, 128>* mapping) {
NoHandleAllocation ha;
-
CONVERT_CHECKED(String, s, args[0]);
- s->TryFlattenIfNotFlat();
+ s->TryFlatten();
- int input_string_length = s->length();
+ const int length = s->length();
// Assume that the string is not empty; we need this assumption later
- if (input_string_length == 0) return s;
- int length = input_string_length;
+ if (length == 0) return s;
+
+ // Simpler handling of ascii strings.
+ //
+ // NOTE: This assumes that the upper/lower case of an ascii
+ // character is also ascii. This is currently the case, but it
+ // might break in the future if we implement more context and locale
+ // dependent upper/lower conversions.
+ SeqAsciiString* seq_ascii = TryGetSeqAsciiString(s);
+ if (seq_ascii != NULL) {
+ Object* o = Heap::AllocateRawAsciiString(length);
+ if (o->IsFailure()) return o;
+ SeqAsciiString* result = SeqAsciiString::cast(o);
+ bool has_changed_character = ConvertTraits::ConvertAscii(
+ result->GetChars(), seq_ascii->GetChars(), length);
+ return has_changed_character ? result : s;
+ }
Object* answer = ConvertCaseHelper(s, length, length, mapping);
if (answer->IsSmi()) {
@@ -4091,18 +4173,20 @@
static Object* Runtime_StringToLowerCase(Arguments args) {
- return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
+ return ConvertCase<ToLowerTraits>(args, &to_lower_mapping);
}
static Object* Runtime_StringToUpperCase(Arguments args) {
- return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
+ return ConvertCase<ToUpperTraits>(args, &to_upper_mapping);
}
+
static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
return unibrow::WhiteSpace::Is(c) || c == 0x200b;
}
+
static Object* Runtime_StringTrim(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -4111,7 +4195,7 @@
CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
- s->TryFlattenIfNotFlat();
+ s->TryFlatten();
int length = s->length();
int left = 0;
@@ -4130,6 +4214,82 @@
return s->SubString(left, right);
}
+
+// Copies ascii characters to the given fixed array looking up
+// one-char strings in the cache. Gives up on the first char that is
+// not in the cache and fills the remainder with smi zeros. Returns
+// the length of the successfully copied prefix.
+static int CopyCachedAsciiCharsToArray(const char* chars,
+ FixedArray* elements,
+ int length) {
+ AssertNoAllocation nogc;
+ FixedArray* ascii_cache = Heap::single_character_string_cache();
+ Object* undefined = Heap::undefined_value();
+ int i;
+ for (i = 0; i < length; ++i) {
+ Object* value = ascii_cache->get(chars[i]);
+ if (value == undefined) break;
+ ASSERT(!Heap::InNewSpace(value));
+ elements->set(i, value, SKIP_WRITE_BARRIER);
+ }
+ if (i < length) {
+ ASSERT(Smi::FromInt(0) == 0);
+ memset(elements->data_start() + i, 0, kPointerSize * (length - i));
+ }
+#ifdef DEBUG
+ for (int j = 0; j < length; ++j) {
+ Object* element = elements->get(j);
+ ASSERT(element == Smi::FromInt(0) ||
+ (element->IsString() && String::cast(element)->LooksValid()));
+ }
+#endif
+ return i;
+}
+
+
+// Converts a String to JSArray.
+// For example, "foo" => ["f", "o", "o"].
+static Object* Runtime_StringToArray(Arguments args) {
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_CHECKED(String, s, 0);
+
+ s->TryFlatten();
+ const int length = s->length();
+
+ Handle<FixedArray> elements;
+ if (s->IsFlat() && s->IsAsciiRepresentation()) {
+ Object* obj = Heap::AllocateUninitializedFixedArray(length);
+ if (obj->IsFailure()) return obj;
+ elements = Handle<FixedArray>(FixedArray::cast(obj));
+
+ Vector<const char> chars = s->ToAsciiVector();
+ // Note, this will initialize all elements (not only the prefix)
+ // to prevent GC from seeing partially initialized array.
+ int num_copied_from_cache = CopyCachedAsciiCharsToArray(chars.start(),
+ *elements,
+ length);
+
+ for (int i = num_copied_from_cache; i < length; ++i) {
+ elements->set(i, *LookupSingleCharacterStringFromCode(chars[i]));
+ }
+ } else {
+ elements = Factory::NewFixedArray(length);
+ for (int i = 0; i < length; ++i) {
+ elements->set(i, *LookupSingleCharacterStringFromCode(s->Get(i)));
+ }
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < length; ++i) {
+ ASSERT(String::cast(elements->get(i))->length() == 1);
+ }
+#endif
+
+ return *Factory::NewJSArrayWithElements(elements);
+}
+
+
bool Runtime::IsUpperCaseChar(uint16_t ch) {
unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
int char_length = to_upper_mapping.get(ch, 0, chars);
@@ -4152,9 +4312,12 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsSmi()) return obj;
- CONVERT_DOUBLE_CHECKED(number, obj);
+ CONVERT_DOUBLE_CHECKED(number, args[0]);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
return Heap::NumberFromDouble(DoubleToInteger(number));
}
@@ -4163,9 +4326,7 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
- CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
+ CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, args[0]);
return Heap::NumberFromUint32(number);
}
@@ -4174,9 +4335,12 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
- Object* obj = args[0];
- if (obj->IsSmi()) return obj;
- CONVERT_DOUBLE_CHECKED(number, obj);
+ CONVERT_DOUBLE_CHECKED(number, args[0]);
+
+ // We do not include 0 so that we don't have to treat +0 / -0 cases.
+ if (number > 0 && number <= Smi::kMaxValue) {
+ return Smi::FromInt(static_cast<int>(number));
+ }
return Heap::NumberFromInt32(DoubleToInt32(number));
}
@@ -4593,6 +4757,66 @@
}
+static Object* StringInputBufferCompare(String* x, String* y) {
+ static StringInputBuffer bufx;
+ static StringInputBuffer bufy;
+ bufx.Reset(x);
+ bufy.Reset(y);
+ while (bufx.has_more() && bufy.has_more()) {
+ int d = bufx.GetNext() - bufy.GetNext();
+ if (d < 0) return Smi::FromInt(LESS);
+ else if (d > 0) return Smi::FromInt(GREATER);
+ }
+
+ // x is (non-trivial) prefix of y:
+ if (bufy.has_more()) return Smi::FromInt(LESS);
+ // y is prefix of x:
+ return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
+}
+
+
+static Object* FlatStringCompare(String* x, String* y) {
+ ASSERT(x->IsFlat());
+ ASSERT(y->IsFlat());
+ Object* equal_prefix_result = Smi::FromInt(EQUAL);
+ int prefix_length = x->length();
+ if (y->length() < prefix_length) {
+ prefix_length = y->length();
+ equal_prefix_result = Smi::FromInt(GREATER);
+ } else if (y->length() > prefix_length) {
+ equal_prefix_result = Smi::FromInt(LESS);
+ }
+ int r;
+ if (x->IsAsciiRepresentation()) {
+ Vector<const char> x_chars = x->ToAsciiVector();
+ if (y->IsAsciiRepresentation()) {
+ Vector<const char> y_chars = y->ToAsciiVector();
+ r = memcmp(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y->ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ } else {
+ Vector<const uc16> x_chars = x->ToUC16Vector();
+ if (y->IsAsciiRepresentation()) {
+ Vector<const char> y_chars = y->ToAsciiVector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ } else {
+ Vector<const uc16> y_chars = y->ToUC16Vector();
+ r = CompareChars(x_chars.start(), y_chars.start(), prefix_length);
+ }
+ }
+ Object* result;
+ if (r == 0) {
+ result = equal_prefix_result;
+ } else {
+ result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER);
+ }
+ ASSERT(result == StringInputBufferCompare(x, y));
+ return result;
+}
+
+
static Object* Runtime_StringCompare(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -4615,29 +4839,18 @@
if (d < 0) return Smi::FromInt(LESS);
else if (d > 0) return Smi::FromInt(GREATER);
- x->TryFlattenIfNotFlat();
- y->TryFlattenIfNotFlat();
+ x->TryFlatten();
+ y->TryFlatten();
- static StringInputBuffer bufx;
- static StringInputBuffer bufy;
- bufx.Reset(x);
- bufy.Reset(y);
- while (bufx.has_more() && bufy.has_more()) {
- int d = bufx.GetNext() - bufy.GetNext();
- if (d < 0) return Smi::FromInt(LESS);
- else if (d > 0) return Smi::FromInt(GREATER);
- }
-
- // x is (non-trivial) prefix of y:
- if (bufy.has_more()) return Smi::FromInt(LESS);
- // y is prefix of x:
- return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
+ return (x->IsFlat() && y->IsFlat()) ? FlatStringCompare(x, y)
+ : StringInputBufferCompare(x, y);
}
static Object* Runtime_Math_abs(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_abs.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::AllocateHeapNumber(fabs(x));
@@ -4647,6 +4860,7 @@
static Object* Runtime_Math_acos(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_acos.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
@@ -4656,6 +4870,7 @@
static Object* Runtime_Math_asin(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_asin.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
@@ -4665,6 +4880,7 @@
static Object* Runtime_Math_atan(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_atan.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
@@ -4674,6 +4890,7 @@
static Object* Runtime_Math_atan2(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
+ Counters::math_atan2.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
CONVERT_DOUBLE_CHECKED(y, args[1]);
@@ -4697,6 +4914,7 @@
static Object* Runtime_Math_ceil(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_ceil.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::NumberFromDouble(ceiling(x));
@@ -4706,6 +4924,7 @@
static Object* Runtime_Math_cos(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_cos.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::COS, x);
@@ -4715,6 +4934,7 @@
static Object* Runtime_Math_exp(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_exp.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::EXP, x);
@@ -4724,6 +4944,7 @@
static Object* Runtime_Math_floor(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_floor.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::NumberFromDouble(floor(x));
@@ -4733,6 +4954,7 @@
static Object* Runtime_Math_log(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_log.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::LOG, x);
@@ -4773,6 +4995,7 @@
static Object* Runtime_Math_pow(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
+ Counters::math_pow.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
@@ -4807,10 +5030,27 @@
}
}
+// Fast version of Math.pow if we know that y is not an integer and
+// y is not -0.5 or 0.5. Used as slowcase from codegen.
+static Object* Runtime_Math_pow_cfunction(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 2);
+ CONVERT_DOUBLE_CHECKED(x, args[0]);
+ CONVERT_DOUBLE_CHECKED(y, args[1]);
+ if (y == 0) {
+ return Smi::FromInt(1);
+ } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
+ return Heap::nan_value();
+ } else {
+ return Heap::AllocateHeapNumber(pow(x, y));
+ }
+}
+
static Object* Runtime_Math_round(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_round.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
@@ -4823,6 +5063,7 @@
static Object* Runtime_Math_sin(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_sin.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::SIN, x);
@@ -4832,6 +5073,7 @@
static Object* Runtime_Math_sqrt(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_sqrt.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::AllocateHeapNumber(sqrt(x));
@@ -4841,12 +5083,65 @@
static Object* Runtime_Math_tan(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_tan.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::TAN, x);
}
+static Object* Runtime_DateMakeDay(Arguments args) {
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 3);
+
+ CONVERT_SMI_CHECKED(year, args[0]);
+ CONVERT_SMI_CHECKED(month, args[1]);
+ CONVERT_SMI_CHECKED(date, args[2]);
+
+ static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
+ 181, 212, 243, 273, 304, 334};
+ static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
+ 182, 213, 244, 274, 305, 335};
+
+ year += month / 12;
+ month %= 12;
+ if (month < 0) {
+ year--;
+ month += 12;
+ }
+
+ ASSERT(month >= 0);
+ ASSERT(month < 12);
+
+ // year_delta is an arbitrary number such that:
+ // a) year_delta = -1 (mod 400)
+ // b) year + year_delta > 0 for years in the range defined by
+ // ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
+ // Jan 1 1970. This is required so that we don't run into integer
+ // division of negative numbers.
+ // c) there shouldn't be overflow for 32-bit integers in the following
+ // operations.
+ static const int year_delta = 399999;
+ static const int base_day = 365 * (1970 + year_delta) +
+ (1970 + year_delta) / 4 -
+ (1970 + year_delta) / 100 +
+ (1970 + year_delta) / 400;
+
+ int year1 = year + year_delta;
+ int day_from_year = 365 * year1 +
+ year1 / 4 -
+ year1 / 100 +
+ year1 / 400 -
+ base_day;
+
+ if (year % 4 || (year % 100 == 0 && year % 400 != 0)) {
+ return Smi::FromInt(day_from_year + day_from_month[month] + date - 1);
+ }
+
+ return Smi::FromInt(day_from_year + day_from_month_leap[month] + date - 1);
+}
+
+
static Object* Runtime_NewArgumentsFast(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -5448,6 +5743,7 @@
}
args[0]->Print();
if (args[0]->IsHeapObject()) {
+ PrintF("\n");
HeapObject::cast(args[0])->map()->Print();
}
#else
@@ -7996,6 +8292,195 @@
return f->shared()->inferred_name();
}
+
+static int FindSharedFunctionInfosForScript(Script* script,
+ FixedArray* buffer) {
+ AssertNoAllocation no_allocations;
+
+ int counter = 0;
+ int buffer_size = buffer->length();
+ HeapIterator iterator;
+ for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
+ ASSERT(obj != NULL);
+ if (!obj->IsSharedFunctionInfo()) {
+ continue;
+ }
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(obj);
+ if (shared->script() != script) {
+ continue;
+ }
+ if (counter < buffer_size) {
+ buffer->set(counter, shared);
+ }
+ counter++;
+ }
+ return counter;
+}
+
+// For a script finds all SharedFunctionInfo's in the heap that points
+// to this script. Returns JSArray of SharedFunctionInfo wrapped
+// in OpaqueReferences.
+static Object* Runtime_LiveEditFindSharedFunctionInfosForScript(
+ Arguments args) {
+ ASSERT(args.length() == 1);
+ HandleScope scope;
+ CONVERT_CHECKED(JSValue, script_value, args[0]);
+
+ Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
+
+ const int kBufferSize = 32;
+
+ Handle<FixedArray> array;
+ array = Factory::NewFixedArray(kBufferSize);
+ int number = FindSharedFunctionInfosForScript(*script, *array);
+ if (number > kBufferSize) {
+ array = Factory::NewFixedArray(number);
+ FindSharedFunctionInfosForScript(*script, *array);
+ }
+
+ Handle<JSArray> result = Factory::NewJSArrayWithElements(array);
+ result->set_length(Smi::FromInt(number));
+
+ LiveEdit::WrapSharedFunctionInfos(result);
+
+ return *result;
+}
+
+// For a script calculates compilation information about all its functions.
+// The script source is explicitly specified by the second argument.
+// The source of the actual script is not used, however it is important that
+// all generated code keeps references to this particular instance of script.
+// Returns a JSArray of compilation infos. The array is ordered so that
+// each function with all its descendant is always stored in a continues range
+// with the function itself going first. The root function is a script function.
+static Object* Runtime_LiveEditGatherCompileInfo(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_CHECKED(JSValue, script, args[0]);
+ CONVERT_ARG_CHECKED(String, source, 1);
+ Handle<Script> script_handle = Handle<Script>(Script::cast(script->value()));
+
+ JSArray* result = LiveEdit::GatherCompileInfo(script_handle, source);
+
+ if (Top::has_pending_exception()) {
+ return Failure::Exception();
+ }
+
+ return result;
+}
+
+// Changes the source of the script to a new_source and creates a new
+// script representing the old version of the script source.
+static Object* Runtime_LiveEditReplaceScript(Arguments args) {
+ ASSERT(args.length() == 3);
+ HandleScope scope;
+ CONVERT_CHECKED(JSValue, original_script_value, args[0]);
+ CONVERT_ARG_CHECKED(String, new_source, 1);
+ CONVERT_ARG_CHECKED(String, old_script_name, 2);
+ Handle<Script> original_script =
+ Handle<Script>(Script::cast(original_script_value->value()));
+
+ Handle<String> original_source(String::cast(original_script->source()));
+
+ original_script->set_source(*new_source);
+ Handle<Script> old_script = Factory::NewScript(original_source);
+ old_script->set_name(*old_script_name);
+ old_script->set_line_offset(original_script->line_offset());
+ old_script->set_column_offset(original_script->column_offset());
+ old_script->set_data(original_script->data());
+ old_script->set_type(original_script->type());
+ old_script->set_context_data(original_script->context_data());
+ old_script->set_compilation_type(original_script->compilation_type());
+ old_script->set_eval_from_shared(original_script->eval_from_shared());
+ old_script->set_eval_from_instructions_offset(
+ original_script->eval_from_instructions_offset());
+
+
+ Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
+
+ return *(GetScriptWrapper(old_script));
+}
+
+// Replaces code of SharedFunctionInfo with a new one.
+static Object* Runtime_LiveEditReplaceFunctionCode(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, new_compile_info, 0);
+ CONVERT_ARG_CHECKED(JSArray, shared_info, 1);
+
+ LiveEdit::ReplaceFunctionCode(new_compile_info, shared_info);
+
+ return Heap::undefined_value();
+}
+
+// Connects SharedFunctionInfo to another script.
+static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
+ CONVERT_ARG_CHECKED(JSValue, script_value, 1);
+ Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
+
+ LiveEdit::RelinkFunctionToScript(shared_info_array, script);
+
+ return Heap::undefined_value();
+}
+
+// Updates positions of a shared function info (first parameter) according
+// to script source change. Text change is described in second parameter as
+// array of groups of 3 numbers:
+// (change_begin, change_end, change_end_new_position).
+// Each group describes a change in text; groups are sorted by change_begin.
+static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
+ ASSERT(args.length() == 2);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
+ CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);
+
+ LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
+
+ return Heap::undefined_value();
+}
+
+
+static LiveEdit::FunctionPatchabilityStatus FindFunctionCodeOnStacks(
+ Handle<SharedFunctionInfo> shared) {
+ // TODO(635): check all threads, not only the current one.
+ for (StackFrameIterator it; !it.done(); it.Advance()) {
+ StackFrame* frame = it.frame();
+ if (frame->code() == shared->code()) {
+ return LiveEdit::FUNCTION_BLOCKED_ON_STACK;
+ }
+ }
+ return LiveEdit::FUNCTION_AVAILABLE_FOR_PATCH;
+}
+
+// For array of SharedFunctionInfo's (each wrapped in JSValue)
+// checks that none of them have activations on stacks (of any thread).
+// Returns array of the same length with corresponding results of
+// LiveEdit::FunctionPatchabilityStatus type.
+static Object* Runtime_LiveEditCheckStackActivations(Arguments args) {
+ ASSERT(args.length() == 1);
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
+
+
+ int len = Smi::cast(shared_array->length())->value();
+ Handle<JSArray> result = Factory::NewJSArray(len);
+
+ for (int i = 0; i < len; i++) {
+ JSValue* wrapper = JSValue::cast(shared_array->GetElement(i));
+ Handle<SharedFunctionInfo> shared(
+ SharedFunctionInfo::cast(wrapper->value()));
+ LiveEdit::FunctionPatchabilityStatus check_res =
+ FindFunctionCodeOnStacks(shared);
+ SetElement(result, i, Handle<Smi>(Smi::FromInt(check_res)));
+ }
+
+ return *result;
+}
+
+
#endif // ENABLE_DEBUGGER_SUPPORT
#ifdef ENABLE_LOGGING_AND_PROFILING
diff --git a/src/runtime.h b/src/runtime.h
index e2e5c22..8c2f86d 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -60,6 +60,7 @@
F(GetArgumentsProperty, 1, 1) \
F(ToFastProperties, 1, 1) \
F(ToSlowProperties, 1, 1) \
+ F(FinishArrayPrototypeSetup, 1, 1) \
\
F(IsInPrototypeChain, 2, 1) \
F(SetHiddenPrototype, 2, 1) \
@@ -142,6 +143,7 @@
F(Math_floor, 1, 1) \
F(Math_log, 1, 1) \
F(Math_pow, 2, 1) \
+ F(Math_pow_cfunction, 2, 1) \
F(Math_round, 1, 1) \
F(Math_sin, 1, 1) \
F(Math_sqrt, 1, 1) \
@@ -161,6 +163,7 @@
F(StringReplaceRegExpWithString, 4, 1) \
F(StringMatch, 3, 1) \
F(StringTrim, 3, 1) \
+ F(StringToArray, 1, 1) \
\
/* Numbers */ \
F(NumberToRadixString, 2, 1) \
@@ -200,6 +203,7 @@
F(DateLocalTimezone, 1, 1) \
F(DateLocalTimeOffset, 0, 1) \
F(DateDaylightSavingsOffset, 1, 1) \
+ F(DateMakeDay, 3, 1) \
\
/* Numbers */ \
F(NumberIsFinite, 1, 1) \
@@ -321,7 +325,14 @@
F(SystemBreak, 0, 1) \
F(DebugDisassembleFunction, 1, 1) \
F(DebugDisassembleConstructor, 1, 1) \
- F(FunctionGetInferredName, 1, 1)
+ F(FunctionGetInferredName, 1, 1) \
+ F(LiveEditFindSharedFunctionInfosForScript, 1, 1) \
+ F(LiveEditGatherCompileInfo, 2, 1) \
+ F(LiveEditReplaceScript, 3, 1) \
+ F(LiveEditReplaceFunctionCode, 2, 1) \
+ F(LiveEditRelinkFunctionToScript, 2, 1) \
+ F(LiveEditPatchFunctionPositions, 2, 1) \
+ F(LiveEditCheckStackActivations, 1, 1)
#else
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
#endif
@@ -400,6 +411,7 @@
// Support getting the characters in a string using [] notation as
// in Firefox/SpiderMonkey, Safari and Opera.
static Object* GetElementOrCharAt(Handle<Object> object, uint32_t index);
+ static Object* GetElement(Handle<Object> object, uint32_t index);
static Object* SetObjectProperty(Handle<Object> object,
Handle<Object> key,
diff --git a/src/runtime.js b/src/runtime.js
index e9d9848..d777041 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -478,7 +478,7 @@
if (!%_IsSmi(char_code)) {
return %StringCharAt(this, pos);
}
- return %CharFromCode(char_code);
+ return %_CharFromCode(char_code);
}
diff --git a/src/scanner.cc b/src/scanner.cc
index cf7e49f..8943119 100755
--- a/src/scanner.cc
+++ b/src/scanner.cc
@@ -28,6 +28,7 @@
#include "v8.h"
#include "ast.h"
+#include "handles.h"
#include "scanner.h"
namespace v8 {
@@ -86,12 +87,7 @@
UTF16Buffer::UTF16Buffer()
- : pos_(0), size_(0) { }
-
-
-Handle<String> UTF16Buffer::SubString(int start, int end) {
- return internal::SubString(data_, start, end);
-}
+ : pos_(0), end_(Scanner::kNoEndPosition) { }
// CharacterStreamUTF16Buffer
@@ -100,10 +96,14 @@
void CharacterStreamUTF16Buffer::Initialize(Handle<String> data,
- unibrow::CharacterStream* input) {
- data_ = data;
- pos_ = 0;
+ unibrow::CharacterStream* input,
+ int start_position,
+ int end_position) {
stream_ = input;
+ if (start_position > 0) {
+ SeekForward(start_position);
+ }
+ end_ = end_position != Scanner::kNoEndPosition ? end_position : kMaxInt;
}
@@ -115,6 +115,8 @@
uc32 CharacterStreamUTF16Buffer::Advance() {
+ ASSERT(end_ != Scanner::kNoEndPosition);
+ ASSERT(end_ >= 0);
// NOTE: It is of importance to Persian / Farsi resources that we do
// *not* strip format control characters in the scanner; see
//
@@ -126,7 +128,7 @@
if (!pushback_buffer()->is_empty()) {
pos_++;
return last_ = pushback_buffer()->RemoveLast();
- } else if (stream_->has_more()) {
+ } else if (stream_->has_more() && pos_ < end_) {
pos_++;
uc32 next = stream_->GetNext();
return last_ = next;
@@ -146,25 +148,32 @@
}
-// TwoByteStringUTF16Buffer
-TwoByteStringUTF16Buffer::TwoByteStringUTF16Buffer()
+// ExternalStringUTF16Buffer
+template <typename StringType, typename CharType>
+ExternalStringUTF16Buffer<StringType, CharType>::ExternalStringUTF16Buffer()
: raw_data_(NULL) { }
-void TwoByteStringUTF16Buffer::Initialize(
- Handle<ExternalTwoByteString> data) {
+template <typename StringType, typename CharType>
+void ExternalStringUTF16Buffer<StringType, CharType>::Initialize(
+ Handle<StringType> data,
+ int start_position,
+ int end_position) {
ASSERT(!data.is_null());
-
- data_ = data;
- pos_ = 0;
-
raw_data_ = data->resource()->data();
- size_ = data->length();
+
+ ASSERT(end_position <= data->length());
+ if (start_position > 0) {
+ SeekForward(start_position);
+ }
+ end_ =
+ end_position != Scanner::kNoEndPosition ? end_position : data->length();
}
-uc32 TwoByteStringUTF16Buffer::Advance() {
- if (pos_ < size_) {
+template <typename StringType, typename CharType>
+uc32 ExternalStringUTF16Buffer<StringType, CharType>::Advance() {
+ if (pos_ < end_) {
return raw_data_[pos_++];
} else {
// note: currently the following increment is necessary to avoid a
@@ -175,14 +184,16 @@
}
-void TwoByteStringUTF16Buffer::PushBack(uc32 ch) {
+template <typename StringType, typename CharType>
+void ExternalStringUTF16Buffer<StringType, CharType>::PushBack(uc32 ch) {
pos_--;
ASSERT(pos_ >= Scanner::kCharacterLookaheadBufferSize);
ASSERT(raw_data_[pos_ - Scanner::kCharacterLookaheadBufferSize] == ch);
}
-void TwoByteStringUTF16Buffer::SeekForward(int pos) {
+template <typename StringType, typename CharType>
+void ExternalStringUTF16Buffer<StringType, CharType>::SeekForward(int pos) {
pos_ = pos;
}
@@ -327,21 +338,56 @@
: stack_overflow_(false), is_pre_parsing_(pre == PREPARSE) { }
+void Scanner::Initialize(Handle<String> source,
+ ParserLanguage language) {
+ safe_string_input_buffer_.Reset(source.location());
+ Init(source, &safe_string_input_buffer_, 0, source->length(), language);
+}
+
+
+void Scanner::Initialize(Handle<String> source,
+ unibrow::CharacterStream* stream,
+ ParserLanguage language) {
+ Init(source, stream, 0, kNoEndPosition, language);
+}
+
+
+void Scanner::Initialize(Handle<String> source,
+ int start_position,
+ int end_position,
+ ParserLanguage language) {
+ safe_string_input_buffer_.Reset(source.location());
+ Init(source, &safe_string_input_buffer_,
+ start_position, end_position, language);
+}
+
+
void Scanner::Init(Handle<String> source,
unibrow::CharacterStream* stream,
- int position,
+ int start_position,
+ int end_position,
ParserLanguage language) {
// Initialize the source buffer.
if (!source.is_null() && StringShape(*source).IsExternalTwoByte()) {
two_byte_string_buffer_.Initialize(
- Handle<ExternalTwoByteString>::cast(source));
+ Handle<ExternalTwoByteString>::cast(source),
+ start_position,
+ end_position);
source_ = &two_byte_string_buffer_;
+ } else if (!source.is_null() && StringShape(*source).IsExternalAscii()) {
+ ascii_string_buffer_.Initialize(
+ Handle<ExternalAsciiString>::cast(source),
+ start_position,
+ end_position);
+ source_ = &ascii_string_buffer_;
} else {
- char_stream_buffer_.Initialize(source, stream);
+ char_stream_buffer_.Initialize(source,
+ stream,
+ start_position,
+ end_position);
source_ = &char_stream_buffer_;
}
- position_ = position;
is_parsing_json_ = (language == JSON);
// Set c0_ (one character ahead)
@@ -358,11 +404,6 @@
}
-Handle<String> Scanner::SubString(int start, int end) {
- return source_->SubString(start - position_, end - position_);
-}
-
-
Token::Value Scanner::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
diff --git a/src/scanner.h b/src/scanner.h
index f0035c0..d5efdff 100644
--- a/src/scanner.h
+++ b/src/scanner.h
@@ -84,32 +84,34 @@
};
+// Interface through which the scanner reads characters from the input source.
class UTF16Buffer {
public:
UTF16Buffer();
virtual ~UTF16Buffer() {}
virtual void PushBack(uc32 ch) = 0;
- // returns a value < 0 when the buffer end is reached
+ // Returns a value < 0 when the buffer end is reached.
virtual uc32 Advance() = 0;
virtual void SeekForward(int pos) = 0;
int pos() const { return pos_; }
- int size() const { return size_; }
- Handle<String> SubString(int start, int end);
protected:
- Handle<String> data_;
- int pos_;
- int size_;
+ int pos_; // Current position in the buffer.
+ int end_; // Position where scanning should stop (EOF).
};
+// UTF16 buffer to read characters from a character stream.
class CharacterStreamUTF16Buffer: public UTF16Buffer {
public:
CharacterStreamUTF16Buffer();
virtual ~CharacterStreamUTF16Buffer() {}
- void Initialize(Handle<String> data, unibrow::CharacterStream* stream);
+ void Initialize(Handle<String> data,
+ unibrow::CharacterStream* stream,
+ int start_position,
+ int end_position);
virtual void PushBack(uc32 ch);
virtual uc32 Advance();
virtual void SeekForward(int pos);
@@ -123,17 +125,21 @@
};
-class TwoByteStringUTF16Buffer: public UTF16Buffer {
+// UTF16 buffer to read characters from an external string.
+template <typename StringType, typename CharType>
+class ExternalStringUTF16Buffer: public UTF16Buffer {
public:
- TwoByteStringUTF16Buffer();
- virtual ~TwoByteStringUTF16Buffer() {}
- void Initialize(Handle<ExternalTwoByteString> data);
+ ExternalStringUTF16Buffer();
+ virtual ~ExternalStringUTF16Buffer() {}
+ void Initialize(Handle<StringType> data,
+ int start_position,
+ int end_position);
virtual void PushBack(uc32 ch);
virtual uc32 Advance();
virtual void SeekForward(int pos);
private:
- const uint16_t* raw_data_;
+ const CharType* raw_data_; // Pointer to the actual array of characters.
};
@@ -263,11 +269,15 @@
// Construction
explicit Scanner(ParserMode parse_mode);
- // Initialize the Scanner to scan source:
- void Init(Handle<String> source,
- unibrow::CharacterStream* stream,
- int position,
- ParserLanguage language);
+ // Initialize the Scanner to scan source.
+ void Initialize(Handle<String> source,
+ ParserLanguage language);
+ void Initialize(Handle<String> source,
+ unibrow::CharacterStream* stream,
+ ParserLanguage language);
+ void Initialize(Handle<String> source,
+ int start_position, int end_position,
+ ParserLanguage language);
// Returns the next token.
Token::Value Next();
@@ -335,7 +345,6 @@
// tokens, which is what it is used for.
void SeekForward(int pos);
- Handle<String> SubString(int start_pos, int end_pos);
bool stack_overflow() { return stack_overflow_; }
static StaticResource<Utf8Decoder>* utf8_decoder() { return &utf8_decoder_; }
@@ -350,14 +359,28 @@
static unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace;
static const int kCharacterLookaheadBufferSize = 1;
+ static const int kNoEndPosition = 1;
private:
- CharacterStreamUTF16Buffer char_stream_buffer_;
- TwoByteStringUTF16Buffer two_byte_string_buffer_;
+ void Init(Handle<String> source,
+ unibrow::CharacterStream* stream,
+ int start_position, int end_position,
+ ParserLanguage language);
- // Source.
+
+ // Different UTF16 buffers used to pull characters from. Based on input one of
+ // these will be initialized as the actual data source.
+ CharacterStreamUTF16Buffer char_stream_buffer_;
+ ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t>
+ two_byte_string_buffer_;
+ ExternalStringUTF16Buffer<ExternalAsciiString, char> ascii_string_buffer_;
+
+ // Source. Will point to one of the buffers declared above.
UTF16Buffer* source_;
- int position_;
+
+ // Used to convert the source string into a character stream when a stream
+ // is not passed to the scanner.
+ SafeStringInputBuffer safe_string_input_buffer_;
// Buffer to hold literal values (identifiers, strings, numbers)
// using 0-terminated UTF-8 encoding.
@@ -460,7 +483,7 @@
// Return the current source position.
int source_pos() {
- return source_->pos() - kCharacterLookaheadBufferSize + position_;
+ return source_->pos() - kCharacterLookaheadBufferSize;
}
// Decodes a unicode escape-sequence which is part of an identifier.
diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc
index de1841b..8b7e2ad 100644
--- a/src/scopeinfo.cc
+++ b/src/scopeinfo.cc
@@ -82,7 +82,7 @@
List<Variable*, Allocator> heap_locals(locals.length());
for (int i = 0; i < locals.length(); i++) {
Variable* var = locals[i];
- if (var->var_uses()->is_used()) {
+ if (var->is_used()) {
Slot* slot = var->slot();
if (slot != NULL) {
switch (slot->type()) {
@@ -130,7 +130,7 @@
if (scope->is_function_scope()) {
Variable* var = scope->function();
if (var != NULL &&
- var->var_uses()->is_used() &&
+ var->is_used() &&
var->slot()->type() == Slot::CONTEXT) {
function_name_ = var->name();
// Note that we must not find the function name in the context slot
diff --git a/src/scopeinfo.h b/src/scopeinfo.h
index 28d169a..927ac66 100644
--- a/src/scopeinfo.h
+++ b/src/scopeinfo.h
@@ -29,6 +29,7 @@
#define V8_SCOPEINFO_H_
#include "variables.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/scopes.cc b/src/scopes.cc
index 701e5e3..b55e5d5 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -309,7 +309,7 @@
// which is the current user of this function).
for (int i = 0; i < temps_.length(); i++) {
Variable* var = temps_[i];
- if (var->var_uses()->is_used()) {
+ if (var->is_used()) {
locals->Add(var);
}
}
@@ -317,7 +317,7 @@
p != NULL;
p = variables_.Next(p)) {
Variable* var = reinterpret_cast<Variable*>(p->value);
- if (var->var_uses()->is_used()) {
+ if (var->is_used()) {
locals->Add(var);
}
}
@@ -418,17 +418,16 @@
static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) {
- if (var->var_uses()->is_used() || var->rewrite() != NULL) {
+ if (var->is_used() || var->rewrite() != NULL) {
Indent(indent, Variable::Mode2String(var->mode()));
PrintF(" ");
PrintName(var->name());
PrintF("; // ");
- if (var->rewrite() != NULL) PrintF("%s, ", printer->Print(var->rewrite()));
- if (var->is_accessed_from_inner_scope()) PrintF("inner scope access, ");
- PrintF("var ");
- var->var_uses()->Print();
- PrintF(", obj ");
- var->obj_uses()->Print();
+ if (var->rewrite() != NULL) {
+ PrintF("%s, ", printer->Print(var->rewrite()));
+ if (var->is_accessed_from_inner_scope()) PrintF(", ");
+ }
+ if (var->is_accessed_from_inner_scope()) PrintF("inner scope access");
PrintF("\n");
}
}
@@ -738,10 +737,10 @@
(var->is_accessed_from_inner_scope_ ||
scope_calls_eval_ || inner_scope_calls_eval_ ||
scope_contains_with_)) {
- var->var_uses()->RecordAccess(1);
+ var->set_is_used(true);
}
// Global variables do not need to be allocated.
- return !var->is_global() && var->var_uses()->is_used();
+ return !var->is_global() && var->is_used();
}
@@ -847,7 +846,7 @@
new Literal(Handle<Object>(Smi::FromInt(i))),
RelocInfo::kNoPosition,
Property::SYNTHETIC);
- arguments_shadow->var_uses()->RecordUses(var->var_uses());
+ if (var->is_used()) arguments_shadow->set_is_used(true);
}
}
diff --git a/src/serialize.cc b/src/serialize.cc
index 0819ec2..a95a7d6 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -359,79 +359,87 @@
UNCLASSIFIED,
7,
"Heap::NewSpaceStart()");
- Add(ExternalReference::heap_always_allocate_scope_depth().address(),
+ Add(ExternalReference::new_space_mask().address(),
UNCLASSIFIED,
8,
+ "Heap::NewSpaceMask()");
+ Add(ExternalReference::heap_always_allocate_scope_depth().address(),
+ UNCLASSIFIED,
+ 9,
"Heap::always_allocate_scope_depth()");
Add(ExternalReference::new_space_allocation_limit_address().address(),
UNCLASSIFIED,
- 9,
+ 10,
"Heap::NewSpaceAllocationLimitAddress()");
Add(ExternalReference::new_space_allocation_top_address().address(),
UNCLASSIFIED,
- 10,
+ 11,
"Heap::NewSpaceAllocationTopAddress()");
#ifdef ENABLE_DEBUGGER_SUPPORT
Add(ExternalReference::debug_break().address(),
UNCLASSIFIED,
- 11,
+ 12,
"Debug::Break()");
Add(ExternalReference::debug_step_in_fp_address().address(),
UNCLASSIFIED,
- 12,
+ 13,
"Debug::step_in_fp_addr()");
#endif
Add(ExternalReference::double_fp_operation(Token::ADD).address(),
UNCLASSIFIED,
- 13,
+ 14,
"add_two_doubles");
Add(ExternalReference::double_fp_operation(Token::SUB).address(),
UNCLASSIFIED,
- 14,
+ 15,
"sub_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MUL).address(),
UNCLASSIFIED,
- 15,
+ 16,
"mul_two_doubles");
Add(ExternalReference::double_fp_operation(Token::DIV).address(),
UNCLASSIFIED,
- 16,
+ 17,
"div_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MOD).address(),
UNCLASSIFIED,
- 17,
+ 18,
"mod_two_doubles");
Add(ExternalReference::compare_doubles().address(),
UNCLASSIFIED,
- 18,
+ 19,
"compare_doubles");
#ifdef V8_NATIVE_REGEXP
Add(ExternalReference::re_case_insensitive_compare_uc16().address(),
UNCLASSIFIED,
- 19,
+ 20,
"NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()");
Add(ExternalReference::re_check_stack_guard_state().address(),
UNCLASSIFIED,
- 20,
+ 21,
"RegExpMacroAssembler*::CheckStackGuardState()");
Add(ExternalReference::re_grow_stack().address(),
UNCLASSIFIED,
- 21,
+ 22,
"NativeRegExpMacroAssembler::GrowStack()");
Add(ExternalReference::re_word_character_map().address(),
UNCLASSIFIED,
- 22,
+ 23,
"NativeRegExpMacroAssembler::word_character_map");
#endif
// Keyed lookup cache.
Add(ExternalReference::keyed_lookup_cache_keys().address(),
UNCLASSIFIED,
- 23,
+ 24,
"KeyedLookupCache::keys()");
Add(ExternalReference::keyed_lookup_cache_field_offsets().address(),
UNCLASSIFIED,
- 24,
+ 25,
"KeyedLookupCache::field_offsets()");
+ Add(ExternalReference::transcendental_cache_array_address().address(),
+ UNCLASSIFIED,
+ 26,
+ "TranscendentalCache::caches()");
}
diff --git a/src/spaces.cc b/src/spaces.cc
index 2c495d8..08399ee 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -1379,6 +1379,7 @@
CASE(STORE_IC);
CASE(KEYED_STORE_IC);
CASE(CALL_IC);
+ CASE(BINARY_OP_IC);
}
}
@@ -1413,7 +1414,7 @@
PrintF("\n Object Histogram:\n");
for (int i = 0; i <= LAST_TYPE; i++) {
if (heap_histograms[i].number() > 0) {
- PrintF(" %-33s%10d (%10d bytes)\n",
+ PrintF(" %-34s%10d (%10d bytes)\n",
heap_histograms[i].name(),
heap_histograms[i].number(),
heap_histograms[i].bytes());
@@ -1430,7 +1431,7 @@
STRING_TYPE_LIST(INCREMENT)
#undef INCREMENT
if (string_number > 0) {
- PrintF(" %-33s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number,
+ PrintF(" %-34s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number,
string_bytes);
}
@@ -1499,7 +1500,7 @@
PrintF("\n Object Histogram:\n");
for (int i = 0; i <= LAST_TYPE; i++) {
if (allocated_histogram_[i].number() > 0) {
- PrintF(" %-33s%10d (%10d bytes)\n",
+ PrintF(" %-34s%10d (%10d bytes)\n",
allocated_histogram_[i].name(),
allocated_histogram_[i].number(),
allocated_histogram_[i].bytes());
diff --git a/src/splay-tree-inl.h b/src/splay-tree-inl.h
new file mode 100644
index 0000000..16fe25e
--- /dev/null
+++ b/src/splay-tree-inl.h
@@ -0,0 +1,276 @@
+// 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_SPLAY_TREE_INL_H_
+#define V8_SPLAY_TREE_INL_H_
+
+#include "splay-tree.h"
+
+namespace v8 {
+namespace internal {
+
+
+template<typename Config, class Allocator>
+SplayTree<Config, Allocator>::~SplayTree() {
+ NodeDeleter deleter;
+ ForEachNode(&deleter);
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Insert(const Key& key, Locator* locator) {
+ if (is_empty()) {
+ // If the tree is empty, insert the new node.
+ root_ = new Node(key, Config::kNoValue);
+ } else {
+ // Splay on the key to move the last node on the search path
+ // for the key to the root of the tree.
+ Splay(key);
+ // Ignore repeated insertions with the same key.
+ int cmp = Config::Compare(key, root_->key_);
+ if (cmp == 0) {
+ locator->bind(root_);
+ return false;
+ }
+ // Insert the new node.
+ Node* node = new Node(key, Config::kNoValue);
+ if (cmp > 0) {
+ node->left_ = root_;
+ node->right_ = root_->right_;
+ root_->right_ = NULL;
+ } else {
+ node->right_ = root_;
+ node->left_ = root_->left_;
+ root_->left_ = NULL;
+ }
+ root_ = node;
+ }
+ locator->bind(root_);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Find(const Key& key, Locator* locator) {
+ if (is_empty())
+ return false;
+ Splay(key);
+ if (Config::Compare(key, root_->key_) == 0) {
+ locator->bind(root_);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindGreatestLessThan(const Key& key,
+ Locator* locator) {
+ if (is_empty())
+ return false;
+ // Splay on the key to move the node with the given key or the last
+ // node on the search path to the top of the tree.
+ Splay(key);
+ // Now the result is either the root node or the greatest node in
+ // the left subtree.
+ int cmp = Config::Compare(root_->key_, key);
+ if (cmp <= 0) {
+ locator->bind(root_);
+ return true;
+ } else {
+ Node* temp = root_;
+ root_ = root_->left_;
+ bool result = FindGreatest(locator);
+ root_ = temp;
+ return result;
+ }
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindLeastGreaterThan(const Key& key,
+ Locator* locator) {
+ if (is_empty())
+ return false;
+ // Splay on the key to move the node with the given key or the last
+ // node on the search path to the top of the tree.
+ Splay(key);
+ // Now the result is either the root node or the least node in
+ // the right subtree.
+ int cmp = Config::Compare(root_->key_, key);
+ if (cmp >= 0) {
+ locator->bind(root_);
+ return true;
+ } else {
+ Node* temp = root_;
+ root_ = root_->right_;
+ bool result = FindLeast(locator);
+ root_ = temp;
+ return result;
+ }
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindGreatest(Locator* locator) {
+ if (is_empty())
+ return false;
+ Node* current = root_;
+ while (current->right_ != NULL)
+ current = current->right_;
+ locator->bind(current);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::FindLeast(Locator* locator) {
+ if (is_empty())
+ return false;
+ Node* current = root_;
+ while (current->left_ != NULL)
+ current = current->left_;
+ locator->bind(current);
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+bool SplayTree<Config, Allocator>::Remove(const Key& key) {
+ // Bail if the tree is empty
+ if (is_empty())
+ return false;
+ // Splay on the key to move the node with the given key to the top.
+ Splay(key);
+ // Bail if the key is not in the tree
+ if (Config::Compare(key, root_->key_) != 0)
+ return false;
+ if (root_->left_ == NULL) {
+ // No left child, so the new tree is just the right child.
+ root_ = root_->right_;
+ } else {
+ // Left child exists.
+ Node* right = root_->right_;
+ // Make the original left child the new root.
+ root_ = root_->left_;
+ // Splay to make sure that the new root has an empty right child.
+ Splay(key);
+ // Insert the original right child as the right child of the new
+ // root.
+ root_->right_ = right;
+ }
+ return true;
+}
+
+
+template<typename Config, class Allocator>
+void SplayTree<Config, Allocator>::Splay(const Key& key) {
+ if (is_empty())
+ return;
+ Node dummy_node(Config::kNoKey, Config::kNoValue);
+ // Create a dummy node. The use of the dummy node is a bit
+ // counter-intuitive: The right child of the dummy node will hold
+ // the L tree of the algorithm. The left child of the dummy node
+ // will hold the R tree of the algorithm. Using a dummy node, left
+ // and right will always be nodes and we avoid special cases.
+ Node* dummy = &dummy_node;
+ Node* left = dummy;
+ Node* right = dummy;
+ Node* current = root_;
+ while (true) {
+ int cmp = Config::Compare(key, current->key_);
+ if (cmp < 0) {
+ if (current->left_ == NULL)
+ break;
+ if (Config::Compare(key, current->left_->key_) < 0) {
+ // Rotate right.
+ Node* temp = current->left_;
+ current->left_ = temp->right_;
+ temp->right_ = current;
+ current = temp;
+ if (current->left_ == NULL)
+ break;
+ }
+ // Link right.
+ right->left_ = current;
+ right = current;
+ current = current->left_;
+ } else if (cmp > 0) {
+ if (current->right_ == NULL)
+ break;
+ if (Config::Compare(key, current->right_->key_) > 0) {
+ // Rotate left.
+ Node* temp = current->right_;
+ current->right_ = temp->left_;
+ temp->left_ = current;
+ current = temp;
+ if (current->right_ == NULL)
+ break;
+ }
+ // Link left.
+ left->right_ = current;
+ left = current;
+ current = current->right_;
+ } else {
+ break;
+ }
+ }
+ // Assemble.
+ left->right_ = current->left_;
+ right->left_ = current->right_;
+ current->left_ = dummy->right_;
+ current->right_ = dummy->left_;
+ root_ = current;
+}
+
+
+template <typename Config, class Allocator> template <class Callback>
+void SplayTree<Config, Allocator>::ForEach(Callback* callback) {
+ NodeToPairAdaptor<Callback> callback_adaptor(callback);
+ ForEachNode(&callback_adaptor);
+}
+
+
+template <typename Config, class Allocator> template <class Callback>
+void SplayTree<Config, Allocator>::ForEachNode(Callback* callback) {
+ // Pre-allocate some space for tiny trees.
+ List<Node*, Allocator> nodes_to_visit(10);
+ if (root_ != NULL) nodes_to_visit.Add(root_);
+ int pos = 0;
+ while (pos < nodes_to_visit.length()) {
+ Node* node = nodes_to_visit[pos++];
+ if (node->left() != NULL) nodes_to_visit.Add(node->left());
+ if (node->right() != NULL) nodes_to_visit.Add(node->right());
+ callback->Call(node);
+ }
+}
+
+
+} } // namespace v8::internal
+
+#endif // V8_SPLAY_TREE_INL_H_
diff --git a/src/splay-tree.h b/src/splay-tree.h
new file mode 100644
index 0000000..b0f415d
--- /dev/null
+++ b/src/splay-tree.h
@@ -0,0 +1,191 @@
+// 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_SPLAY_TREE_H_
+#define V8_SPLAY_TREE_H_
+
+namespace v8 {
+namespace internal {
+
+
+// A splay tree. The config type parameter encapsulates the different
+// configurations of a concrete splay tree:
+//
+// typedef Key: the key type
+// typedef Value: the value type
+// static const kNoKey: the dummy key used when no key is set
+// static const kNoValue: the dummy value used to initialize nodes
+// int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function
+//
+// The tree is also parameterized by an allocation policy
+// (Allocator). The policy is used for allocating lists in the C free
+// store or the zone; see zone.h.
+
+// Forward defined as
+// template <typename Config, class Allocator = FreeStoreAllocationPolicy>
+// class SplayTree;
+template <typename Config, class Allocator>
+class SplayTree {
+ public:
+ typedef typename Config::Key Key;
+ typedef typename Config::Value Value;
+
+ class Locator;
+
+ SplayTree() : root_(NULL) { }
+ ~SplayTree();
+
+ INLINE(void* operator new(size_t size)) {
+ return Allocator::New(static_cast<int>(size));
+ }
+ INLINE(void operator delete(void* p, size_t)) { return Allocator::Delete(p); }
+
+ // Inserts the given key in this tree with the given value. Returns
+ // true if a node was inserted, otherwise false. If found the locator
+ // is enabled and provides access to the mapping for the key.
+ bool Insert(const Key& key, Locator* locator);
+
+ // Looks up the key in this tree and returns true if it was found,
+ // otherwise false. If the node is found the locator is enabled and
+ // provides access to the mapping for the key.
+ bool Find(const Key& key, Locator* locator);
+
+ // Finds the mapping with the greatest key less than or equal to the
+ // given key.
+ bool FindGreatestLessThan(const Key& key, Locator* locator);
+
+ // Find the mapping with the greatest key in this tree.
+ bool FindGreatest(Locator* locator);
+
+ // Finds the mapping with the least key greater than or equal to the
+ // given key.
+ bool FindLeastGreaterThan(const Key& key, Locator* locator);
+
+ // Find the mapping with the least key in this tree.
+ bool FindLeast(Locator* locator);
+
+ // Remove the node with the given key from the tree.
+ bool Remove(const Key& key);
+
+ bool is_empty() { return root_ == NULL; }
+
+ // Perform the splay operation for the given key. Moves the node with
+ // the given key to the top of the tree. If no node has the given
+ // key, the last node on the search path is moved to the top of the
+ // tree.
+ void Splay(const Key& key);
+
+ class Node {
+ public:
+ Node(const Key& key, const Value& value)
+ : key_(key),
+ value_(value),
+ left_(NULL),
+ right_(NULL) { }
+
+ INLINE(void* operator new(size_t size)) {
+ return Allocator::New(static_cast<int>(size));
+ }
+ INLINE(void operator delete(void* p, size_t)) {
+ return Allocator::Delete(p);
+ }
+
+ Key key() { return key_; }
+ Value value() { return value_; }
+ Node* left() { return left_; }
+ Node* right() { return right_; }
+ private:
+
+ friend class SplayTree;
+ friend class Locator;
+ Key key_;
+ Value value_;
+ Node* left_;
+ Node* right_;
+ };
+
+ // A locator provides access to a node in the tree without actually
+ // exposing the node.
+ class Locator BASE_EMBEDDED {
+ public:
+ explicit Locator(Node* node) : node_(node) { }
+ Locator() : node_(NULL) { }
+ const Key& key() { return node_->key_; }
+ Value& value() { return node_->value_; }
+ void set_value(const Value& value) { node_->value_ = value; }
+ inline void bind(Node* node) { node_ = node; }
+ private:
+ Node* node_;
+ };
+
+ template <class Callback>
+ void ForEach(Callback* callback);
+
+ protected:
+
+ // Resets tree root. Existing nodes become unreachable.
+ void ResetRoot() { root_ = NULL; }
+
+ private:
+
+ template<class Callback>
+ class NodeToPairAdaptor BASE_EMBEDDED {
+ public:
+ explicit NodeToPairAdaptor(Callback* callback)
+ : callback_(callback) { }
+ void Call(Node* node) {
+ callback_->Call(node->key(), node->value());
+ }
+
+ private:
+ Callback* callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(NodeToPairAdaptor);
+ };
+
+ class NodeDeleter BASE_EMBEDDED {
+ public:
+ NodeDeleter() { }
+ void Call(Node* node) { delete node; }
+
+ private:
+
+ DISALLOW_COPY_AND_ASSIGN(NodeDeleter);
+ };
+
+ template <class Callback>
+ void ForEachNode(Callback* callback);
+
+ Node* root_;
+
+ DISALLOW_COPY_AND_ASSIGN(SplayTree);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_SPLAY_TREE_H_
diff --git a/src/string.js b/src/string.js
index 49f403d..067578c 100644
--- a/src/string.js
+++ b/src/string.js
@@ -69,7 +69,7 @@
if (index >= subject.length || index < 0) return "";
char_code = %StringCharCodeAt(subject, index);
}
- return %CharFromCode(char_code);
+ return %_CharFromCode(char_code);
}
@@ -184,7 +184,7 @@
if (!%_IsSmi(char_code)) {
char_code = %StringCharCodeAt(string, start);
}
- return %CharFromCode(char_code);
+ return %_CharFromCode(char_code);
}
return %_SubString(string, start, end);
}
@@ -530,11 +530,7 @@
var separator_length = separator.length;
// If the separator string is empty then return the elements in the subject.
- if (separator_length === 0) {
- var result = $Array(length);
- for (var i = 0; i < length; i++) result[i] = subject[i];
- return result;
- }
+ if (separator_length === 0) return %StringToArray(subject);
var result = [];
var start_index = 0;
@@ -726,7 +722,7 @@
// ECMA-262, section 15.5.3.2
function StringFromCharCode(code) {
var n = %_ArgumentsLength();
- if (n == 1) return %CharFromCode(ToNumber(code) & 0xffff)
+ if (n == 1) return %_CharFromCode(ToNumber(code) & 0xffff)
// NOTE: This is not super-efficient, but it is necessary because we
// want to avoid converting to numbers from within the virtual
diff --git a/src/top.cc b/src/top.cc
index b9db4be..7c8a1c9 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -31,6 +31,7 @@
#include "bootstrapper.h"
#include "debug.h"
#include "execution.h"
+#include "messages.h"
#include "platform.h"
#include "simulator.h"
#include "string-stream.h"
diff --git a/src/usage-analyzer.cc b/src/usage-analyzer.cc
deleted file mode 100644
index 74cf982..0000000
--- a/src/usage-analyzer.cc
+++ /dev/null
@@ -1,426 +0,0 @@
-// Copyright 2006-2008 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 "ast.h"
-#include "scopes.h"
-#include "usage-analyzer.h"
-
-namespace v8 {
-namespace internal {
-
-// Weight boundaries
-static const int MinWeight = 1;
-static const int MaxWeight = 1000000;
-static const int InitialWeight = 100;
-
-
-class UsageComputer: public AstVisitor {
- public:
- static bool Traverse(AstNode* node);
-
- // AST node visit functions.
-#define DECLARE_VISIT(type) void Visit##type(type* node);
- AST_NODE_LIST(DECLARE_VISIT)
-#undef DECLARE_VISIT
-
- void VisitVariable(Variable* var);
-
- private:
- int weight_;
- bool is_write_;
-
- UsageComputer(int weight, bool is_write);
- virtual ~UsageComputer();
-
- // Helper functions
- void RecordUses(UseCount* uses);
- void Read(Expression* x);
- void Write(Expression* x);
- void ReadList(ZoneList<Expression*>* list);
- void ReadList(ZoneList<ObjectLiteral::Property*>* list);
-
- friend class WeightScaler;
-};
-
-
-class WeightScaler BASE_EMBEDDED {
- public:
- WeightScaler(UsageComputer* uc, float scale);
- ~WeightScaler();
-
- private:
- UsageComputer* uc_;
- int old_weight_;
-};
-
-
-// ----------------------------------------------------------------------------
-// Implementation of UsageComputer
-
-bool UsageComputer::Traverse(AstNode* node) {
- UsageComputer uc(InitialWeight, false);
- uc.Visit(node);
- return !uc.HasStackOverflow();
-}
-
-
-void UsageComputer::VisitBlock(Block* node) {
- VisitStatements(node->statements());
-}
-
-
-void UsageComputer::VisitDeclaration(Declaration* node) {
- Write(node->proxy());
- if (node->fun() != NULL)
- VisitFunctionLiteral(node->fun());
-}
-
-
-void UsageComputer::VisitExpressionStatement(ExpressionStatement* node) {
- Visit(node->expression());
-}
-
-
-void UsageComputer::VisitEmptyStatement(EmptyStatement* node) {
- // nothing to do
-}
-
-
-void UsageComputer::VisitIfStatement(IfStatement* node) {
- Read(node->condition());
- { WeightScaler ws(this, 0.5); // executed 50% of the time
- Visit(node->then_statement());
- Visit(node->else_statement());
- }
-}
-
-
-void UsageComputer::VisitContinueStatement(ContinueStatement* node) {
- // nothing to do
-}
-
-
-void UsageComputer::VisitBreakStatement(BreakStatement* node) {
- // nothing to do
-}
-
-
-void UsageComputer::VisitReturnStatement(ReturnStatement* node) {
- Read(node->expression());
-}
-
-
-void UsageComputer::VisitWithEnterStatement(WithEnterStatement* node) {
- Read(node->expression());
-}
-
-
-void UsageComputer::VisitWithExitStatement(WithExitStatement* node) {
- // nothing to do
-}
-
-
-void UsageComputer::VisitSwitchStatement(SwitchStatement* node) {
- Read(node->tag());
- ZoneList<CaseClause*>* cases = node->cases();
- for (int i = cases->length(); i-- > 0;) {
- WeightScaler ws(this, static_cast<float>(1.0 / cases->length()));
- CaseClause* clause = cases->at(i);
- if (!clause->is_default())
- Read(clause->label());
- VisitStatements(clause->statements());
- }
-}
-
-
-void UsageComputer::VisitDoWhileStatement(DoWhileStatement* node) {
- WeightScaler ws(this, 10.0);
- Read(node->cond());
- Visit(node->body());
-}
-
-
-void UsageComputer::VisitWhileStatement(WhileStatement* node) {
- WeightScaler ws(this, 10.0);
- Read(node->cond());
- Visit(node->body());
-}
-
-
-void UsageComputer::VisitForStatement(ForStatement* node) {
- if (node->init() != NULL) Visit(node->init());
- { WeightScaler ws(this, 10.0); // executed in each iteration
- if (node->cond() != NULL) Read(node->cond());
- if (node->next() != NULL) Visit(node->next());
- Visit(node->body());
- }
-}
-
-
-void UsageComputer::VisitForInStatement(ForInStatement* node) {
- WeightScaler ws(this, 10.0);
- Write(node->each());
- Read(node->enumerable());
- Visit(node->body());
-}
-
-
-void UsageComputer::VisitTryCatchStatement(TryCatchStatement* node) {
- Visit(node->try_block());
- { WeightScaler ws(this, 0.25);
- Write(node->catch_var());
- Visit(node->catch_block());
- }
-}
-
-
-void UsageComputer::VisitTryFinallyStatement(TryFinallyStatement* node) {
- Visit(node->try_block());
- Visit(node->finally_block());
-}
-
-
-void UsageComputer::VisitDebuggerStatement(DebuggerStatement* node) {
-}
-
-
-void UsageComputer::VisitFunctionLiteral(FunctionLiteral* node) {
- ZoneList<Declaration*>* decls = node->scope()->declarations();
- for (int i = 0; i < decls->length(); i++) VisitDeclaration(decls->at(i));
- VisitStatements(node->body());
-}
-
-
-void UsageComputer::VisitFunctionBoilerplateLiteral(
- FunctionBoilerplateLiteral* node) {
- // Do nothing.
-}
-
-
-void UsageComputer::VisitConditional(Conditional* node) {
- Read(node->condition());
- { WeightScaler ws(this, 0.5);
- Read(node->then_expression());
- Read(node->else_expression());
- }
-}
-
-
-void UsageComputer::VisitSlot(Slot* node) {
- UNREACHABLE();
-}
-
-
-void UsageComputer::VisitVariable(Variable* node) {
- RecordUses(node->var_uses());
-}
-
-
-void UsageComputer::VisitVariableProxy(VariableProxy* node) {
- // The proxy may refer to a variable in which case it was bound via
- // VariableProxy::BindTo.
- RecordUses(node->var_uses());
-}
-
-
-void UsageComputer::VisitLiteral(Literal* node) {
- // nothing to do
-}
-
-void UsageComputer::VisitRegExpLiteral(RegExpLiteral* node) {
- // nothing to do
-}
-
-
-void UsageComputer::VisitObjectLiteral(ObjectLiteral* node) {
- ReadList(node->properties());
-}
-
-
-void UsageComputer::VisitArrayLiteral(ArrayLiteral* node) {
- ReadList(node->values());
-}
-
-
-void UsageComputer::VisitCatchExtensionObject(CatchExtensionObject* node) {
- Read(node->value());
-}
-
-
-void UsageComputer::VisitAssignment(Assignment* node) {
- if (node->op() != Token::ASSIGN)
- Read(node->target());
- Write(node->target());
- Read(node->value());
-}
-
-
-void UsageComputer::VisitThrow(Throw* node) {
- Read(node->exception());
-}
-
-
-void UsageComputer::VisitProperty(Property* node) {
- // In any case (read or write) we read both the
- // node's object and the key.
- Read(node->obj());
- Read(node->key());
- // If the node's object is a variable proxy,
- // we have a 'simple' object property access. We count
- // the access via the variable or proxy's object uses.
- VariableProxy* proxy = node->obj()->AsVariableProxy();
- if (proxy != NULL) {
- RecordUses(proxy->obj_uses());
- }
-}
-
-
-void UsageComputer::VisitCall(Call* node) {
- Read(node->expression());
- ReadList(node->arguments());
-}
-
-
-void UsageComputer::VisitCallNew(CallNew* node) {
- Read(node->expression());
- ReadList(node->arguments());
-}
-
-
-void UsageComputer::VisitCallRuntime(CallRuntime* node) {
- ReadList(node->arguments());
-}
-
-
-void UsageComputer::VisitUnaryOperation(UnaryOperation* node) {
- Read(node->expression());
-}
-
-
-void UsageComputer::VisitCountOperation(CountOperation* node) {
- Read(node->expression());
- Write(node->expression());
-}
-
-
-void UsageComputer::VisitBinaryOperation(BinaryOperation* node) {
- Read(node->left());
- Read(node->right());
-}
-
-
-void UsageComputer::VisitCompareOperation(CompareOperation* node) {
- Read(node->left());
- Read(node->right());
-}
-
-
-void UsageComputer::VisitThisFunction(ThisFunction* node) {
-}
-
-
-UsageComputer::UsageComputer(int weight, bool is_write) {
- weight_ = weight;
- is_write_ = is_write;
-}
-
-
-UsageComputer::~UsageComputer() {
- // nothing to do
-}
-
-
-void UsageComputer::RecordUses(UseCount* uses) {
- if (is_write_)
- uses->RecordWrite(weight_);
- else
- uses->RecordRead(weight_);
-}
-
-
-void UsageComputer::Read(Expression* x) {
- if (is_write_) {
- UsageComputer uc(weight_, false);
- uc.Visit(x);
- } else {
- Visit(x);
- }
-}
-
-
-void UsageComputer::Write(Expression* x) {
- if (!is_write_) {
- UsageComputer uc(weight_, true);
- uc.Visit(x);
- } else {
- Visit(x);
- }
-}
-
-
-void UsageComputer::ReadList(ZoneList<Expression*>* list) {
- for (int i = list->length(); i-- > 0; )
- Read(list->at(i));
-}
-
-
-void UsageComputer::ReadList(ZoneList<ObjectLiteral::Property*>* list) {
- for (int i = list->length(); i-- > 0; )
- Read(list->at(i)->value());
-}
-
-
-// ----------------------------------------------------------------------------
-// Implementation of WeightScaler
-
-WeightScaler::WeightScaler(UsageComputer* uc, float scale) {
- uc_ = uc;
- old_weight_ = uc->weight_;
- int new_weight = static_cast<int>(uc->weight_ * scale);
- if (new_weight <= 0) new_weight = MinWeight;
- else if (new_weight > MaxWeight) new_weight = MaxWeight;
- uc->weight_ = new_weight;
-}
-
-
-WeightScaler::~WeightScaler() {
- uc_->weight_ = old_weight_;
-}
-
-
-// ----------------------------------------------------------------------------
-// Interface to variable usage analysis
-
-bool AnalyzeVariableUsage(FunctionLiteral* lit) {
- if (!FLAG_usage_computation) return true;
- HistogramTimerScope timer(&Counters::usage_analysis);
- return UsageComputer::Traverse(lit);
-}
-
-} } // namespace v8::internal
diff --git a/src/usage-analyzer.h b/src/usage-analyzer.h
deleted file mode 100644
index 1b0ea4a..0000000
--- a/src/usage-analyzer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2006-2008 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_USAGE_ANALYZER_H_
-#define V8_USAGE_ANALYZER_H_
-
-namespace v8 {
-namespace internal {
-
-// Compute usage counts for all variables.
-// Used for variable allocation.
-bool AnalyzeVariableUsage(FunctionLiteral* lit);
-
-} } // namespace v8::internal
-
-#endif // V8_USAGE_ANALYZER_H_
diff --git a/src/utils.h b/src/utils.h
index 2fcd241..deab09f 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -528,11 +528,11 @@
sinkchar* limit = dest + chars;
#ifdef V8_HOST_CAN_READ_UNALIGNED
if (sizeof(*dest) == sizeof(*src)) {
- // Number of characters in a uint32_t.
- static const int kStepSize = sizeof(uint32_t) / sizeof(*dest); // NOLINT
+ // Number of characters in a uintptr_t.
+ static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT
while (dest <= limit - kStepSize) {
- *reinterpret_cast<uint32_t*>(dest) =
- *reinterpret_cast<const uint32_t*>(src);
+ *reinterpret_cast<uintptr_t*>(dest) =
+ *reinterpret_cast<const uintptr_t*>(src);
dest += kStepSize;
src += kStepSize;
}
@@ -544,6 +544,58 @@
}
+// Compare ASCII/16bit chars to ASCII/16bit chars.
+template <typename lchar, typename rchar>
+static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) {
+ const lchar* limit = lhs + chars;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ if (sizeof(*lhs) == sizeof(*rhs)) {
+ // Number of characters in a uintptr_t.
+ static const int kStepSize = sizeof(uintptr_t) / sizeof(*lhs); // NOLINT
+ while (lhs <= limit - kStepSize) {
+ if (*reinterpret_cast<const uintptr_t*>(lhs) !=
+ *reinterpret_cast<const uintptr_t*>(rhs)) {
+ break;
+ }
+ lhs += kStepSize;
+ rhs += kStepSize;
+ }
+ }
+#endif
+ while (lhs < limit) {
+ int r = static_cast<int>(*lhs) - static_cast<int>(*rhs);
+ if (r != 0) return r;
+ ++lhs;
+ ++rhs;
+ }
+ return 0;
+}
+
+
+template <typename T>
+static inline void MemsetPointer(T** dest, T* value, int counter) {
+#if defined(V8_HOST_ARCH_IA32)
+#define STOS "stosl"
+#elif defined(V8_HOST_ARCH_X64)
+#define STOS "stosq"
+#endif
+
+#if defined(__GNUC__) && defined(STOS)
+ asm("cld;"
+ "rep ; " STOS
+ : /* no output */
+ : "c" (counter), "a" (value), "D" (dest)
+ : /* no clobbered list as all inputs are considered clobbered */);
+#else
+ for (int i = 0; i < counter; i++) {
+ dest[i] = value;
+ }
+#endif
+
+#undef STOS
+}
+
+
// Calculate 10^exponent.
int TenToThe(int exponent);
diff --git a/src/v8-counters.h b/src/v8-counters.h
index eaac2db..2b493fb 100644
--- a/src/v8-counters.h
+++ b/src/v8-counters.h
@@ -169,7 +169,24 @@
SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
SC(regexp_entry_native, V8.RegExpEntryNative) \
SC(number_to_string_native, V8.NumberToStringNative) \
- SC(number_to_string_runtime, V8.NumberToStringRuntime)
+ SC(number_to_string_runtime, V8.NumberToStringRuntime) \
+ SC(math_abs, V8.MathAbs) \
+ SC(math_acos, V8.MathAcos) \
+ SC(math_asin, V8.MathAsin) \
+ SC(math_atan, V8.MathAtan) \
+ SC(math_atan2, V8.MathAtan2) \
+ SC(math_ceil, V8.MathCeil) \
+ SC(math_cos, V8.MathCos) \
+ SC(math_exp, V8.MathExp) \
+ SC(math_floor, V8.MathFloor) \
+ SC(math_log, V8.MathLog) \
+ SC(math_pow, V8.MathPow) \
+ SC(math_round, V8.MathRound) \
+ SC(math_sin, V8.MathSin) \
+ SC(math_sqrt, V8.MathSqrt) \
+ SC(math_tan, V8.MathTan) \
+ SC(transcendental_cache_hit, V8.TranscendentalCacheHit) \
+ SC(transcendental_cache_miss, V8.TranscendentalCacheMiss)
// This file contains all the v8 counters that are in use.
class Counters : AllStatic {
diff --git a/src/v8.h b/src/v8.h
index b3624c5..d58f30f 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -67,7 +67,7 @@
#include "spaces-inl.h"
#include "heap-inl.h"
#include "log-inl.h"
-#include "messages.h"
+#include "handles-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/variables.cc b/src/variables.cc
index 3bcd48a..f46a54d 100644
--- a/src/variables.cc
+++ b/src/variables.cc
@@ -35,57 +35,6 @@
namespace internal {
// ----------------------------------------------------------------------------
-// Implementation UseCount.
-
-UseCount::UseCount()
- : nreads_(0),
- nwrites_(0) {
-}
-
-
-void UseCount::RecordRead(int weight) {
- ASSERT(weight > 0);
- nreads_ += weight;
- // We must have a positive nreads_ here. Handle
- // any kind of overflow by setting nreads_ to
- // some large-ish value.
- if (nreads_ <= 0) nreads_ = 1000000;
- ASSERT(is_read() & is_used());
-}
-
-
-void UseCount::RecordWrite(int weight) {
- ASSERT(weight > 0);
- nwrites_ += weight;
- // We must have a positive nwrites_ here. Handle
- // any kind of overflow by setting nwrites_ to
- // some large-ish value.
- if (nwrites_ <= 0) nwrites_ = 1000000;
- ASSERT(is_written() && is_used());
-}
-
-
-void UseCount::RecordAccess(int weight) {
- RecordRead(weight);
- RecordWrite(weight);
-}
-
-
-void UseCount::RecordUses(UseCount* uses) {
- if (uses->nreads() > 0) RecordRead(uses->nreads());
- if (uses->nwrites() > 0) RecordWrite(uses->nwrites());
-}
-
-
-#ifdef DEBUG
-void UseCount::Print() {
- // PrintF("r = %d, w = %d", nreads_, nwrites_);
- PrintF("%du = %dr + %dw", nuses(), nreads(), nwrites());
-}
-#endif
-
-
-// ----------------------------------------------------------------------------
// Implementation StaticType.
@@ -136,6 +85,12 @@
}
+bool Variable::IsStackAllocated() const {
+ Slot* s = slot();
+ return s != NULL && s->IsStackAllocated();
+}
+
+
Variable::Variable(Scope* scope,
Handle<String> name,
Mode mode,
@@ -148,6 +103,7 @@
kind_(kind),
local_if_not_shadowed_(NULL),
is_accessed_from_inner_scope_(false),
+ is_used_(false),
rewrite_(NULL) {
// names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol());
diff --git a/src/variables.h b/src/variables.h
index ac7f294..a68aa33 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -33,35 +33,6 @@
namespace v8 {
namespace internal {
-class UseCount BASE_EMBEDDED {
- public:
- UseCount();
-
- // Inform the node of a "use". The weight can be used to indicate
- // heavier use, for instance if the variable is accessed inside a loop.
- void RecordRead(int weight);
- void RecordWrite(int weight);
- void RecordAccess(int weight); // records a read & write
- void RecordUses(UseCount* uses);
-
- int nreads() const { return nreads_; }
- int nwrites() const { return nwrites_; }
- int nuses() const { return nreads_ + nwrites_; }
-
- bool is_read() const { return nreads() > 0; }
- bool is_written() const { return nwrites() > 0; }
- bool is_used() const { return nuses() > 0; }
-
-#ifdef DEBUG
- void Print();
-#endif
-
- private:
- int nreads_;
- int nwrites_;
-};
-
-
// Variables and AST expression nodes can track their "type" to enable
// optimizations and removal of redundant checks when generating code.
@@ -168,13 +139,15 @@
bool is_accessed_from_inner_scope() const {
return is_accessed_from_inner_scope_;
}
- UseCount* var_uses() { return &var_uses_; }
- UseCount* obj_uses() { return &obj_uses_; }
+ bool is_used() { return is_used_; }
+ void set_is_used(bool flag) { is_used_ = flag; }
bool IsVariable(Handle<String> n) const {
return !is_this() && name().is_identical_to(n);
}
+ bool IsStackAllocated() const;
+
bool is_dynamic() const {
return (mode_ == DYNAMIC ||
mode_ == DYNAMIC_GLOBAL ||
@@ -216,8 +189,7 @@
// Usage info.
bool is_accessed_from_inner_scope_; // set by variable resolver
- UseCount var_uses_; // uses of the variable value
- UseCount obj_uses_; // uses of the object the variable points to
+ bool is_used_;
// Static type information
StaticType type_;
diff --git a/src/version.cc b/src/version.cc
index b69a4ee..79832ea 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,8 +34,8 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 1
-#define BUILD_NUMBER 2
-#define PATCH_LEVEL 8
+#define BUILD_NUMBER 3
+#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
// Define SONAME to have the SCons build the put a specific SONAME into the
diff --git a/src/virtual-frame-inl.h b/src/virtual-frame-inl.h
index 3476e41..9aa88fe 100644
--- a/src/virtual-frame-inl.h
+++ b/src/virtual-frame-inl.h
@@ -33,6 +33,21 @@
namespace v8 {
namespace internal {
+
+// On entry to a function, the virtual frame already contains the receiver,
+// the parameters, and a return address. All frame elements are in memory.
+VirtualFrame::VirtualFrame()
+ : elements_(parameter_count() + local_count() + kPreallocatedElements),
+ stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
+ for (int i = 0; i <= stack_pointer_; i++) {
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::Unknown()));
+ }
+ for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
+ register_locations_[i] = kIllegalIndex;
+ }
+}
+
+
// When cloned, a frame is a deep copy of the original.
VirtualFrame::VirtualFrame(VirtualFrame* original)
: elements_(original->element_count()),
@@ -50,7 +65,7 @@
}
-void VirtualFrame::Push(Register reg, NumberInfo::Type info) {
+void VirtualFrame::Push(Register reg, NumberInfo info) {
if (is_used(reg)) {
int index = register_location(reg);
FrameElement element = CopyElementAt(index, info);
diff --git a/src/virtual-frame.cc b/src/virtual-frame.cc
index d5b5f42..8c13a6a 100644
--- a/src/virtual-frame.cc
+++ b/src/virtual-frame.cc
@@ -43,7 +43,7 @@
// not conflict with the existing type information and must be equally or
// more precise. The default parameter value kUninitialized means that there
// is no additional information.
-FrameElement VirtualFrame::CopyElementAt(int index, NumberInfo::Type info) {
+FrameElement VirtualFrame::CopyElementAt(int index, NumberInfo info) {
ASSERT(index >= 0);
ASSERT(index < element_count());
@@ -74,14 +74,14 @@
result.set_index(index);
elements_[index].set_copied();
// Update backing element's number information.
- NumberInfo::Type existing = elements_[index].number_info();
- ASSERT(existing != NumberInfo::kUninitialized);
+ NumberInfo existing = elements_[index].number_info();
+ ASSERT(!existing.IsUninitialized());
// Assert that the new type information (a) does not conflict with the
// existing one and (b) is equally or more precise.
- ASSERT((info == NumberInfo::kUninitialized) ||
- (existing | info) != NumberInfo::kUninitialized);
- ASSERT(existing <= info);
- elements_[index].set_number_info(info != NumberInfo::kUninitialized
+ ASSERT((info.ToInt() & existing.ToInt()) == existing.ToInt());
+ ASSERT((info.ToInt() | existing.ToInt()) == info.ToInt());
+
+ elements_[index].set_number_info(!info.IsUninitialized()
? info
: existing);
break;
@@ -104,7 +104,7 @@
ASSERT(stack_pointer_ == element_count() - 1);
for (int i = 0; i < count; i++) {
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::Unknown()));
}
stack_pointer_ += count;
}
@@ -152,7 +152,7 @@
SyncElementAt(index);
// Number type information is preserved.
// Copies get their number information from their backing element.
- NumberInfo::Type info;
+ NumberInfo info;
if (!elements_[index].is_copy()) {
info = elements_[index].number_info();
} else {
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index b3c5e33..0c5d481 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -61,10 +61,10 @@
ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
}
- // JumpToRuntime expects rax to contain the number of arguments
+ // JumpToExternalReference expects rax to contain the number of arguments
// including the receiver and the extra arguments.
__ addq(rax, Immediate(num_extra_args + 1));
- __ JumpToRuntime(ExternalReference(id), 1);
+ __ JumpToExternalReference(ExternalReference(id), 1);
}
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index 6ffb5ac..b10c3f9 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -278,6 +278,7 @@
void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
+ Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
// Initialize state.
info_ = info;
@@ -3876,6 +3877,49 @@
}
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateCharFromCode");
+ ASSERT(args->length() == 1);
+
+ Load(args->at(0));
+ Result code = frame_->Pop();
+ code.ToRegister();
+ ASSERT(code.is_valid());
+
+ Result temp = allocator()->Allocate();
+ ASSERT(temp.is_valid());
+
+ JumpTarget slow_case;
+ JumpTarget exit;
+
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ Condition is_smi = __ CheckSmi(code.reg());
+ slow_case.Branch(NegateCondition(is_smi), &code, not_taken);
+
+ __ SmiToInteger32(kScratchRegister, code.reg());
+ __ cmpl(kScratchRegister, Immediate(String::kMaxAsciiCharCode));
+ slow_case.Branch(above, &code, not_taken);
+
+ __ Move(temp.reg(), Factory::single_character_string_cache());
+ __ movq(temp.reg(), FieldOperand(temp.reg(),
+ kScratchRegister, times_pointer_size,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(temp.reg(), Heap::kUndefinedValueRootIndex);
+ slow_case.Branch(equal, &code, not_taken);
+ code.Unuse();
+
+ frame_->Push(&temp);
+ exit.Jump();
+
+ slow_case.Bind(&code);
+ frame_->Push(&code);
+ Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
+ frame_->Push(&result);
+
+ exit.Bind();
+}
+
+
void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
@@ -3888,6 +3932,25 @@
}
+// Generates the Math.pow method - currently just calls runtime.
+void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 2);
+ Load(args->at(0));
+ Load(args->at(1));
+ Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
+ frame_->Push(&res);
+}
+
+
+// Generates the Math.sqrt method - currently just calls runtime.
+void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result res = frame_->CallRuntime(Runtime::kMath_sqrt, 1);
+ frame_->Push(&res);
+}
+
+
void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
Load(args->at(0));
@@ -3955,21 +4018,12 @@
frame_->SpillAll();
__ push(rsi);
- // Make sure the frame is aligned like the OS expects.
- static const int kFrameAlignment = OS::ActivationFrameAlignment();
- if (kFrameAlignment > 0) {
- ASSERT(IsPowerOf2(kFrameAlignment));
- __ movq(rbx, rsp); // Save in AMD-64 abi callee-saved register.
- __ and_(rsp, Immediate(-kFrameAlignment));
- }
+ static const int num_arguments = 0;
+ __ PrepareCallCFunction(num_arguments);
// Call V8::RandomPositiveSmi().
- __ Call(FUNCTION_ADDR(V8::RandomPositiveSmi), RelocInfo::RUNTIME_ENTRY);
-
- // Restore stack pointer from callee-saved register.
- if (kFrameAlignment > 0) {
- __ movq(rsp, rbx);
- }
+ __ CallCFunction(ExternalReference::random_positive_smi_function(),
+ num_arguments);
__ pop(rsi);
Result result = allocator_->Allocate(rax);
@@ -4002,6 +4056,24 @@
}
+void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ Result answer = frame_->CallRuntime(Runtime::kMath_sin, 1);
+ frame_->Push(&answer);
+}
+
+
+void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ Result answer = frame_->CallRuntime(Runtime::kMath_cos, 1);
+ frame_->Push(&answer);
+}
+
+
void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
@@ -5217,7 +5289,7 @@
}
// Get number type of left and right sub-expressions.
- NumberInfo::Type operands_type =
+ NumberInfo operands_type =
NumberInfo::Combine(left.number_info(), right.number_info());
Result answer;
@@ -5253,7 +5325,7 @@
// Set NumberInfo of result according to the operation performed.
// We rely on the fact that smis have a 32 bit payload on x64.
ASSERT(kSmiValueSize == 32);
- NumberInfo::Type result_type = NumberInfo::kUnknown;
+ NumberInfo result_type = NumberInfo::Unknown();
switch (op) {
case Token::COMMA:
result_type = right.number_info();
@@ -5267,32 +5339,32 @@
case Token::BIT_XOR:
case Token::BIT_AND:
// Result is always a smi.
- result_type = NumberInfo::kSmi;
+ result_type = NumberInfo::Smi();
break;
case Token::SAR:
case Token::SHL:
// Result is always a smi.
- result_type = NumberInfo::kSmi;
+ result_type = NumberInfo::Smi();
break;
case Token::SHR:
// Result of x >>> y is always a smi if y >= 1, otherwise a number.
result_type = (right.is_constant() && right.handle()->IsSmi()
&& Smi::cast(*right.handle())->value() >= 1)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
+ ? NumberInfo::Smi()
+ : NumberInfo::Number();
break;
case Token::ADD:
// Result could be a string or a number. Check types of inputs.
- result_type = NumberInfo::IsNumber(operands_type)
- ? NumberInfo::kNumber
- : NumberInfo::kUnknown;
+ result_type = operands_type.IsNumber()
+ ? NumberInfo::Number()
+ : NumberInfo::Unknown();
break;
case Token::SUB:
case Token::MUL:
case Token::DIV:
case Token::MOD:
// Result is always a number.
- result_type = NumberInfo::kNumber;
+ result_type = NumberInfo::Number();
break;
default:
UNREACHABLE();
@@ -6324,7 +6396,7 @@
__ push(rsi);
__ push(rdx);
__ push(rcx); // Restore return address.
- __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+ __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
}
@@ -6366,7 +6438,7 @@
// Need to collect. Call into runtime system.
__ bind(&gc);
- __ TailCallRuntime(ExternalReference(Runtime::kNewContext), 1, 1);
+ __ TailCallRuntime(Runtime::kNewContext, 1, 1);
}
@@ -6422,8 +6494,7 @@
__ ret(3 * kPointerSize);
__ bind(&slow_case);
- ExternalReference runtime(Runtime::kCreateArrayLiteralShallow);
- __ TailCallRuntime(runtime, 3, 1);
+ __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
}
@@ -6782,10 +6853,10 @@
// time or if regexp entry in generated code is turned off runtime switch or
// at compilation.
#ifndef V8_NATIVE_REGEXP
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
#else // V8_NATIVE_REGEXP
if (!FLAG_regexp_entry_native) {
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
return;
}
@@ -7129,7 +7200,7 @@
// Do the runtime call to execute the regexp.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
#endif // V8_NATIVE_REGEXP
}
@@ -7540,7 +7611,7 @@
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
}
@@ -7597,9 +7668,7 @@
__ pop(rbx); // Return address.
__ push(rdx);
__ push(rbx);
- Runtime::Function* f =
- Runtime::FunctionForId(Runtime::kGetArgumentsProperty);
- __ TailCallRuntime(ExternalReference(f), 1, f->result_size);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
}
@@ -8088,8 +8157,7 @@
__ push(rax);
// Do tail-call to runtime routine.
- Runtime::Function* f = Runtime::FunctionForId(Runtime::kStackGuard);
- __ TailCallRuntime(ExternalReference(f), 1, f->result_size);
+ __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
}
@@ -8273,7 +8341,7 @@
args_in_registers_ ? "RegArgs" : "StackArgs",
args_reversed_ ? "_R" : "",
use_sse3_ ? "SSE3" : "SSE2",
- NumberInfo::ToString(operands_type_));
+ operands_type_.ToString());
return name_;
}
@@ -8597,7 +8665,7 @@
case Token::DIV: {
// rax: y
// rdx: x
- if (NumberInfo::IsNumber(operands_type_)) {
+ if (operands_type_.IsNumber()) {
if (FLAG_debug_code) {
// Assert at runtime that inputs are only numbers.
__ AbortIfNotNumber(rdx, "GenericBinaryOpStub operand not a number.");
@@ -8839,6 +8907,11 @@
}
+Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
+ return Handle<Code>::null();
+}
+
+
int CompareStub::MinorKey() {
// Encode the three parameters in a unique 16 bit value.
ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
@@ -8936,16 +9009,11 @@
// rbx: length of first string
// rcx: length of second string
// rdx: second string
- // r8: instance type of first string if string check was performed above
- // r9: instance type of first string if string check was performed above
- Label string_add_flat_result;
+ // r8: map of first string if string check was performed above
+ // r9: map of second string if string check was performed above
+ Label string_add_flat_result, longer_than_two;
__ bind(&both_not_zero_length);
- // Look at the length of the result of adding the two strings.
- __ addl(rbx, rcx);
- // Use the runtime system when adding two one character strings, as it
- // contains optimizations for this specific case using the symbol table.
- __ cmpl(rbx, Immediate(2));
- __ j(equal, &string_add_runtime);
+
// If arguments where known to be strings, maps are not loaded to r8 and r9
// by the code above.
if (!string_check_) {
@@ -8955,6 +9023,35 @@
// Get the instance types of the two strings as they will be needed soon.
__ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset));
__ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset));
+
+ // Look at the length of the result of adding the two strings.
+ __ addl(rbx, rcx);
+ // Use the runtime system when adding two one character strings, as it
+ // contains optimizations for this specific case using the symbol table.
+ __ cmpl(rbx, Immediate(2));
+ __ j(not_equal, &longer_than_two);
+
+ // Check that both strings are non-external ascii strings.
+ __ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
+ &string_add_runtime);
+
+ // Get the two characters forming the sub string.
+ __ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
+ __ movzxbq(rcx, FieldOperand(rdx, 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, make_flat_ascii_string;
+ GenerateTwoCharacterSymbolTableProbe(masm, rbx, rcx, r14, r12, rdi, r15,
+ &make_two_character_string);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&make_two_character_string);
+ __ Set(rbx, 2);
+ __ jmp(&make_flat_ascii_string);
+
+ __ bind(&longer_than_two);
// Check if resulting string will be flat.
__ cmpl(rbx, Immediate(String::kMinNonFlatLength));
__ j(below, &string_add_flat_result);
@@ -9021,6 +9118,8 @@
__ j(zero, &non_ascii_string_add_flat_result);
__ testl(r9, Immediate(kAsciiStringTag));
__ j(zero, &string_add_runtime);
+
+ __ bind(&make_flat_ascii_string);
// Both strings are ascii strings. As they are short they are both flat.
__ AllocateAsciiString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
// rcx: result string
@@ -9087,7 +9186,7 @@
// Just jump to runtime to add the two strings.
__ bind(&string_add_runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
}
@@ -9171,6 +9270,179 @@
__ bind(&done);
}
+void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* not_found) {
+ // 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;
+ __ movq(scratch, c1);
+ __ subq(scratch, Immediate(static_cast<int>('0')));
+ __ cmpq(scratch, Immediate(static_cast<int>('9' - '0')));
+ __ j(above, ¬_array_index);
+ __ movq(scratch, c2);
+ __ subq(scratch, Immediate(static_cast<int>('0')));
+ __ cmpq(scratch, Immediate(static_cast<int>('9' - '0')));
+ __ j(below_equal, not_found);
+
+ __ bind(¬_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ GenerateHashInit(masm, hash, c1, scratch);
+ GenerateHashAddCharacter(masm, hash, c2, scratch);
+ GenerateHashGetHash(masm, hash, scratch);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ shl(c2, Immediate(kBitsPerByte));
+ __ orl(chars, c2);
+
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string.
+
+ // Load the symbol table.
+ Register symbol_table = c2;
+ __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
+
+ // Calculate capacity mask from the symbol table capacity.
+ Register mask = scratch2;
+ __ movq(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
+ __ SmiToInteger32(mask, mask);
+ __ decl(mask);
+
+ Register undefined = scratch4;
+ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
+
+ // Registers
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string (32-bit int)
+ // symbol_table: symbol table
+ // mask: capacity mask (32-bit int)
+ // undefined: undefined value
+ // scratch: -
+
+ // Perform a number of probes in the symbol table.
+ static const int kProbes = 4;
+ Label found_in_symbol_table;
+ Label next_probe[kProbes];
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in symbol table.
+ __ movl(scratch, hash);
+ if (i > 0) {
+ __ addl(scratch, Immediate(SymbolTable::GetProbeOffset(i)));
+ }
+ __ andl(scratch, mask);
+
+ // Load the entry from the symble table.
+ Register candidate = scratch; // Scratch register contains candidate.
+ ASSERT_EQ(1, SymbolTable::kEntrySize);
+ __ movq(candidate,
+ FieldOperand(symbol_table,
+ scratch,
+ times_pointer_size,
+ SymbolTable::kElementsStartOffset));
+
+ // If entry is undefined no string with this hash can be found.
+ __ cmpq(candidate, undefined);
+ __ j(equal, not_found);
+
+ // If length is not 2 the string is not a candidate.
+ __ cmpl(FieldOperand(candidate, String::kLengthOffset), Immediate(2));
+ __ j(not_equal, &next_probe[i]);
+
+ // We use kScratchRegister as a temporary register in assumption that
+ // JumpIfInstanceTypeIsNotSequentialAscii does not use it implicitly
+ Register temp = kScratchRegister;
+
+ // Check that the candidate is a non-external ascii string.
+ __ movq(temp, FieldOperand(candidate, HeapObject::kMapOffset));
+ __ movzxbl(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(
+ temp, temp, &next_probe[i]);
+
+ // Check if the two characters match.
+ __ movl(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize));
+ __ andl(temp, Immediate(0x0000ffff));
+ __ cmpl(chars, temp);
+ __ j(equal, &found_in_symbol_table);
+ __ 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 = scratch;
+ __ bind(&found_in_symbol_table);
+ if (!result.is(rax)) {
+ __ movq(rax, result);
+ }
+}
+
+
+void StringStubBase::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash = character + (character << 10);
+ __ movl(hash, character);
+ __ shll(hash, Immediate(10));
+ __ addl(hash, character);
+ // hash ^= hash >> 6;
+ __ movl(scratch, hash);
+ __ sarl(scratch, Immediate(6));
+ __ xorl(hash, scratch);
+}
+
+
+void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash += character;
+ __ addl(hash, character);
+ // hash += hash << 10;
+ __ movl(scratch, hash);
+ __ shll(scratch, Immediate(10));
+ __ addl(hash, scratch);
+ // hash ^= hash >> 6;
+ __ movl(scratch, hash);
+ __ sarl(scratch, Immediate(6));
+ __ xorl(hash, scratch);
+}
+
+
+void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch) {
+ // hash += hash << 3;
+ __ movl(scratch, hash);
+ __ shll(scratch, Immediate(3));
+ __ addl(hash, scratch);
+ // hash ^= hash >> 11;
+ __ movl(scratch, hash);
+ __ sarl(scratch, Immediate(11));
+ __ xorl(hash, scratch);
+ // hash += hash << 15;
+ __ movl(scratch, hash);
+ __ shll(scratch, Immediate(15));
+ __ addl(hash, scratch);
+
+ // if (hash == 0) hash = 27;
+ Label hash_not_zero;
+ __ testl(hash, hash);
+ __ j(not_zero, &hash_not_zero);
+ __ movl(hash, Immediate(27));
+ __ bind(&hash_not_zero);
+}
void SubStringStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -9197,25 +9469,55 @@
// rax: string
// rbx: instance type
// Calculate length of sub string using the smi values.
+ Label result_longer_than_two;
__ movq(rcx, Operand(rsp, kToOffset));
__ movq(rdx, Operand(rsp, kFromOffset));
__ JumpIfNotBothPositiveSmi(rcx, rdx, &runtime);
__ SmiSub(rcx, rcx, rdx, NULL); // Overflow doesn't happen.
__ j(negative, &runtime);
- // Handle sub-strings of length 2 and less in the runtime system.
+ // 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.
__ SmiToInteger32(rcx, rcx);
__ cmpl(rcx, Immediate(2));
- __ j(below_equal, &runtime);
+ __ j(greater, &result_longer_than_two);
+ __ j(less, &runtime);
+
+ // Sub string of length 2 requested.
+ // rax: string
+ // rbx: instance type
+ // rcx: sub string length (value is 2)
+ // rdx: from index (smi)
+ __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &runtime);
+
+ // Get the two characters forming the sub string.
+ __ SmiToInteger32(rdx, rdx); // From index is no longer smi.
+ __ movzxbq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize));
+ __ movzxbq(rcx,
+ FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize + 1));
+
+ // Try to lookup two character string in symbol table.
+ Label make_two_character_string;
+ GenerateTwoCharacterSymbolTableProbe(masm, rbx, rcx, rax, rdx, rdi, r14,
+ &make_two_character_string);
+ __ ret(3 * kPointerSize);
+
+ __ bind(&make_two_character_string);
+ // Setup registers for allocating the two character string.
+ __ movq(rax, Operand(rsp, kStringOffset));
+ __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
+ __ Set(rcx, 2);
+
+ __ bind(&result_longer_than_two);
// rax: string
// rbx: instance type
// rcx: result string length
// Check for flat ascii string
Label non_ascii_flat;
- __ and_(rbx, Immediate(kStringRepresentationMask | kStringEncodingMask));
- __ cmpb(rbx, Immediate(kSeqStringTag | kAsciiStringTag));
- __ j(not_equal, &non_ascii_flat);
+ __ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &non_ascii_flat);
// Allocate the result.
__ AllocateAsciiString(rax, rcx, rbx, rdx, rdi, &runtime);
@@ -9281,7 +9583,7 @@
// Just jump to runtime to create the sub string.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
}
@@ -9401,7 +9703,7 @@
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
// tagged as a small integer.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
}
#undef __
diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h
index 309e294..8f5fdc1 100644
--- a/src/x64/codegen-x64.h
+++ b/src/x64/codegen-x64.h
@@ -547,6 +547,9 @@
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
+ // Fast support for string.charAt(n) and string[n].
+ void GenerateCharFromCode(ZoneList<Expression*>* args);
+
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
@@ -572,7 +575,17 @@
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
- // Simple condition analysis.
+ // Fast support for Math.pow().
+ void GenerateMathPow(ZoneList<Expression*>* args);
+
+ // Fast call to math functions.
+ void GenerateMathSin(ZoneList<Expression*>* args);
+ void GenerateMathCos(ZoneList<Expression*>* args);
+
+ // Fast case for sqrt
+ void GenerateMathSqrt(ZoneList<Expression*>* args);
+
+// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
ALWAYS_FALSE,
@@ -651,7 +664,7 @@
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags,
- NumberInfo::Type operands_type = NumberInfo::kUnknown)
+ NumberInfo operands_type = NumberInfo::Unknown())
: op_(op),
mode_(mode),
flags_(flags),
@@ -683,7 +696,7 @@
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
char* name_;
- NumberInfo::Type operands_type_;
+ NumberInfo operands_type_;
const char* GetName();
@@ -697,7 +710,7 @@
static_cast<int>(flags_),
static_cast<int>(args_in_registers_),
static_cast<int>(args_reversed_),
- NumberInfo::ToString(operands_type_));
+ operands_type_.ToString());
}
#endif
@@ -708,7 +721,7 @@
class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
class ArgsReversedBits: public BitField<bool, 11, 1> {};
class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
- class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
+ class NumberInfoBits: public BitField<int, 13, 3> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
@@ -719,7 +732,7 @@
| SSE3Bits::encode(use_sse3_)
| ArgsInRegistersBits::encode(args_in_registers_)
| ArgsReversedBits::encode(args_reversed_)
- | NumberInfoBits::encode(operands_type_);
+ | NumberInfoBits::encode(operands_type_.ThreeBitRepresentation());
}
void Generate(MacroAssembler* masm);
@@ -763,6 +776,33 @@
Register src, // Must be rsi.
Register count, // Must be rcx.
bool ascii);
+
+
+ // Probe the symbol table for a two character string. If the string is
+ // not found by probing a jump to the label not_found is performed. This jump
+ // does not guarantee that the string is not in the symbol table. If the
+ // string is found the code falls through with the string in register rax.
+ void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4,
+ Label* not_found);
+
+ // Generate string hash.
+ void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch);
};
diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc
index af2a1ff..5e76901 100644
--- a/src/x64/fast-codegen-x64.cc
+++ b/src/x64/fast-codegen-x64.cc
@@ -189,6 +189,7 @@
void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
ASSERT(info_ == NULL);
info_ = compilation_info;
+ Comment cmnt(masm_, "[ function compiled by fast code generator");
// Save the caller's frame pointer and set up our own.
Comment prologue_cmnt(masm(), ";; Prologue");
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 669e175..bfa1a44 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -56,6 +56,7 @@
ASSERT(info_ == NULL);
info_ = info;
SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
if (mode == PRIMARY) {
__ push(rbp); // Caller's frame pointer.
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index 0e93637..e05f68d 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -241,7 +241,8 @@
__ push(rbx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -258,7 +259,7 @@
__ push(rbx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1);
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
}
@@ -608,7 +609,7 @@
__ push(rdx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(
+ __ TailCallExternalReference(ExternalReference(
IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
__ bind(&slow);
@@ -631,7 +632,8 @@
__ push(rcx); // return address
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
@@ -650,7 +652,7 @@
__ push(rcx); // return address
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
+ __ TailCallRuntime(Runtime::kSetProperty, 3, 1);
}
@@ -1223,7 +1225,8 @@
__ push(rbx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -1385,7 +1388,8 @@
__ push(rbx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
@@ -1449,7 +1453,8 @@
__ push(value);
__ push(scratch); // return address
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_ArrayLength)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength));
+ __ TailCallExternalReference(ref, 2, 1);
__ bind(&miss);
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 90a9c75..dca2780 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -396,9 +396,9 @@
}
-void MacroAssembler::TailCallRuntime(ExternalReference const& ext,
- int num_arguments,
- int result_size) {
+void MacroAssembler::TailCallExternalReference(const ExternalReference& ext,
+ int num_arguments,
+ int result_size) {
// ----------- S t a t e -------------
// -- rsp[0] : return address
// -- rsp[8] : argument num_arguments - 1
@@ -411,12 +411,19 @@
// should remove this need and make the runtime routine entry code
// smarter.
movq(rax, Immediate(num_arguments));
- JumpToRuntime(ext, result_size);
+ JumpToExternalReference(ext, result_size);
}
-void MacroAssembler::JumpToRuntime(const ExternalReference& ext,
- int result_size) {
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid), num_arguments, result_size);
+}
+
+
+void MacroAssembler::JumpToExternalReference(const ExternalReference& ext,
+ int result_size) {
// Set the entry point and jump to the C entry runtime stub.
movq(rbx, ext);
CEntryStub ces(result_size);
@@ -1393,6 +1400,50 @@
}
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
+ Register instance_type,
+ Register scratch,
+ Label *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);
+}
+
+
+void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* 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);
+}
+
+
void MacroAssembler::Move(Register dst, Handle<Object> source) {
ASSERT(!source->IsFailure());
if (source->IsSmi()) {
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 2673086..bbb6e21 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -426,6 +426,20 @@
Register scratch2,
Label* on_not_both_flat_ascii);
+ // 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.
+ void JumpIfInstanceTypeIsNotSequentialAscii(Register instance_type,
+ Register scratch,
+ Label *on_not_flat_ascii_string);
+
+ void JumpIfBothInstanceTypesAreNotSequentialAscii(
+ Register first_object_instance_type,
+ Register second_object_instance_type,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail);
+
// ---------------------------------------------------------------------------
// Macro instructions.
@@ -645,7 +659,6 @@
void StubReturn(int argc);
// Call a runtime routine.
- // Eventually this should be used for all C calls.
void CallRuntime(Runtime::Function* f, int num_arguments);
// Convenience function: Same as above, but takes the fid instead.
@@ -656,14 +669,19 @@
int num_arguments);
// Tail call of a runtime routine (jump).
- // Like JumpToRuntime, but also takes care of passing the number
- // of arguments.
- void TailCallRuntime(const ExternalReference& ext,
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(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,
int result_size);
// Jump to a runtime routine.
- void JumpToRuntime(const ExternalReference& ext, int result_size);
+ void JumpToExternalReference(const ExternalReference& ext, int result_size);
// Before calling a C-function from generated code, align arguments on stack.
// After aligning the frame, arguments must be stored in esp[0], esp[4],
diff --git a/src/x64/regexp-macro-assembler-x64.cc b/src/x64/regexp-macro-assembler-x64.cc
index 026301b..6e7d9c9 100644
--- a/src/x64/regexp-macro-assembler-x64.cc
+++ b/src/x64/regexp-macro-assembler-x64.cc
@@ -335,7 +335,7 @@
#endif
__ push(backtrack_stackpointer());
- int num_arguments = 3;
+ static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments);
// Put arguments into parameter registers. Parameters are
@@ -849,7 +849,7 @@
#endif
// Call GrowStack(backtrack_stackpointer())
- int num_arguments = 2;
+ static const int num_arguments = 2;
__ PrepareCallCFunction(num_arguments);
#ifdef _WIN64
// Microsoft passes parameters in rcx, rdx.
@@ -1029,7 +1029,7 @@
void RegExpMacroAssemblerX64::CallCheckStackGuardState() {
// This function call preserves no register values. Caller should
// store anything volatile in a C call or overwritten by this function.
- int num_arguments = 3;
+ static const int num_arguments = 3;
__ PrepareCallCFunction(num_arguments);
#ifdef _WIN64
// Second argument: Code* of self. (Do this before overwriting r8).
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 9c8b4f7..5d73b5f 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -236,7 +236,7 @@
__ Push(Handle<Map>(transition));
__ push(rax);
__ push(scratch);
- __ TailCallRuntime(
+ __ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
return;
}
@@ -526,7 +526,7 @@
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
__ bind(&cleanup);
__ pop(scratch1);
@@ -548,7 +548,7 @@
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
}
private:
@@ -1360,7 +1360,7 @@
// Do tail-call to the runtime system.
ExternalReference store_callback_property =
ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
- __ TailCallRuntime(store_callback_property, 4, 1);
+ __ TailCallExternalReference(store_callback_property, 4, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1438,7 +1438,7 @@
// Do tail-call to the runtime system.
ExternalReference store_ic_property =
ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
- __ TailCallRuntime(store_ic_property, 3, 1);
+ __ TailCallExternalReference(store_ic_property, 3, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1637,7 +1637,7 @@
// Do tail-call to the runtime system.
ExternalReference load_callback_property =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(load_callback_property, 5, 1);
+ __ TailCallExternalReference(load_callback_property, 5, 1);
return true;
}
diff --git a/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc
index e322c61..79be20b 100644
--- a/src/x64/virtual-frame-x64.cc
+++ b/src/x64/virtual-frame-x64.cc
@@ -37,23 +37,6 @@
#define __ ACCESS_MASM(masm())
-// -------------------------------------------------------------------------
-// VirtualFrame implementation.
-
-// On entry to a function, the virtual frame already contains the receiver,
-// the parameters, and a return address. All frame elements are in memory.
-VirtualFrame::VirtualFrame()
- : elements_(parameter_count() + local_count() + kPreallocatedElements),
- stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
- for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
- }
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- register_locations_[i] = kIllegalIndex;
- }
-}
-
-
void VirtualFrame::Enter() {
// Registers live on entry to a JS frame:
// rsp: stack pointer, points to return address from this function.
@@ -194,7 +177,7 @@
}
-void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Register reg, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -202,7 +185,7 @@
}
-void VirtualFrame::EmitPush(const Operand& operand, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(const Operand& operand, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -210,7 +193,7 @@
}
-void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Immediate immediate, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -220,7 +203,7 @@
void VirtualFrame::EmitPush(Smi* smi_value) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kSmi));
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::Smi()));
stack_pointer_++;
__ Push(smi_value);
}
@@ -228,11 +211,11 @@
void VirtualFrame::EmitPush(Handle<Object> value) {
ASSERT(stack_pointer_ == element_count() - 1);
- NumberInfo::Type info = NumberInfo::kUnknown;
+ NumberInfo info = NumberInfo::Unknown();
if (value->IsSmi()) {
- info = NumberInfo::kSmi;
+ info = NumberInfo::Smi();
} else if (value->IsHeapNumber()) {
- info = NumberInfo::kHeapNumber;
+ info = NumberInfo::HeapNumber();
}
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -240,7 +223,7 @@
}
-void VirtualFrame::EmitPush(Heap::RootListIndex index, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Heap::RootListIndex index, NumberInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -497,7 +480,7 @@
if (element.is_constant() || element.is_copy()) {
if (element.is_synced()) {
// Just spill.
- elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown);
+ elements_[i] = FrameElement::MemoryElement(NumberInfo::Unknown());
} else {
// Allocate to a register.
FrameElement backing_element; // Invalid if not a copy.
@@ -509,7 +492,7 @@
elements_[i] =
FrameElement::RegisterElement(fresh.reg(),
FrameElement::NOT_SYNCED,
- NumberInfo::kUnknown);
+ NumberInfo::Unknown());
Use(fresh.reg(), i);
// Emit a move.
@@ -538,7 +521,7 @@
// The copy flag is not relied on before the end of this loop,
// including when registers are spilled.
elements_[i].clear_copied();
- elements_[i].set_number_info(NumberInfo::kUnknown);
+ elements_[i].set_number_info(NumberInfo::Unknown());
}
}
}
@@ -745,7 +728,7 @@
ASSERT(element.is_valid());
// Get number type information of the result.
- NumberInfo::Type info;
+ NumberInfo info;
if (!element.is_copy()) {
info = element.number_info();
} else {
diff --git a/src/x64/virtual-frame-x64.h b/src/x64/virtual-frame-x64.h
index d4df484..ddd606b 100644
--- a/src/x64/virtual-frame-x64.h
+++ b/src/x64/virtual-frame-x64.h
@@ -73,7 +73,7 @@
static const int kIllegalIndex = -1;
// Construct an initial virtual frame on entry to a JS function.
- VirtualFrame();
+ inline VirtualFrame();
// Construct a virtual frame as a clone of an existing one.
explicit inline VirtualFrame(VirtualFrame* original);
@@ -83,7 +83,7 @@
// Create a duplicate of an existing valid frame element.
FrameElement CopyElementAt(int index,
- NumberInfo::Type info = NumberInfo::kUninitialized);
+ NumberInfo info = NumberInfo::Uninitialized());
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -383,19 +383,19 @@
// Push an element on top of the expression stack and emit a
// corresponding push instruction.
void EmitPush(Register reg,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
void EmitPush(const Operand& operand,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
void EmitPush(Heap::RootListIndex index,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
void EmitPush(Immediate immediate,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ NumberInfo info = NumberInfo::Unknown());
void EmitPush(Smi* value);
// Uses kScratchRegister, emits appropriate relocation info.
void EmitPush(Handle<Object> value);
// Push an element on the virtual frame.
- inline void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
+ inline void Push(Register reg, NumberInfo info = NumberInfo::Unknown());
inline void Push(Handle<Object> value);
inline void Push(Smi* value);
diff --git a/src/zone-inl.h b/src/zone-inl.h
index 121ba19..5893a2f 100644
--- a/src/zone-inl.h
+++ b/src/zone-inl.h
@@ -68,227 +68,12 @@
}
-template <typename C>
-bool ZoneSplayTree<C>::Insert(const Key& key, Locator* locator) {
- if (is_empty()) {
- // If the tree is empty, insert the new node.
- root_ = new Node(key, C::kNoValue);
- } else {
- // Splay on the key to move the last node on the search path
- // for the key to the root of the tree.
- Splay(key);
- // Ignore repeated insertions with the same key.
- int cmp = C::Compare(key, root_->key_);
- if (cmp == 0) {
- locator->bind(root_);
- return false;
- }
- // Insert the new node.
- Node* node = new Node(key, C::kNoValue);
- if (cmp > 0) {
- node->left_ = root_;
- node->right_ = root_->right_;
- root_->right_ = NULL;
- } else {
- node->right_ = root_;
- node->left_ = root_->left_;
- root_->left_ = NULL;
- }
- root_ = node;
- }
- locator->bind(root_);
- return true;
-}
-
-
-template <typename C>
-bool ZoneSplayTree<C>::Find(const Key& key, Locator* locator) {
- if (is_empty())
- return false;
- Splay(key);
- if (C::Compare(key, root_->key_) == 0) {
- locator->bind(root_);
- return true;
- } else {
- return false;
- }
-}
-
-
-template <typename C>
-bool ZoneSplayTree<C>::FindGreatestLessThan(const Key& key,
- Locator* locator) {
- if (is_empty())
- return false;
- // Splay on the key to move the node with the given key or the last
- // node on the search path to the top of the tree.
- Splay(key);
- // Now the result is either the root node or the greatest node in
- // the left subtree.
- int cmp = C::Compare(root_->key_, key);
- if (cmp <= 0) {
- locator->bind(root_);
- return true;
- } else {
- Node* temp = root_;
- root_ = root_->left_;
- bool result = FindGreatest(locator);
- root_ = temp;
- return result;
- }
-}
-
-
-template <typename C>
-bool ZoneSplayTree<C>::FindLeastGreaterThan(const Key& key,
- Locator* locator) {
- if (is_empty())
- return false;
- // Splay on the key to move the node with the given key or the last
- // node on the search path to the top of the tree.
- Splay(key);
- // Now the result is either the root node or the least node in
- // the right subtree.
- int cmp = C::Compare(root_->key_, key);
- if (cmp >= 0) {
- locator->bind(root_);
- return true;
- } else {
- Node* temp = root_;
- root_ = root_->right_;
- bool result = FindLeast(locator);
- root_ = temp;
- return result;
- }
-}
-
-
-template <typename C>
-bool ZoneSplayTree<C>::FindGreatest(Locator* locator) {
- if (is_empty())
- return false;
- Node* current = root_;
- while (current->right_ != NULL)
- current = current->right_;
- locator->bind(current);
- return true;
-}
-
-
-template <typename C>
-bool ZoneSplayTree<C>::FindLeast(Locator* locator) {
- if (is_empty())
- return false;
- Node* current = root_;
- while (current->left_ != NULL)
- current = current->left_;
- locator->bind(current);
- return true;
-}
-
-
-template <typename C>
-bool ZoneSplayTree<C>::Remove(const Key& key) {
- // Bail if the tree is empty
- if (is_empty())
- return false;
- // Splay on the key to move the node with the given key to the top.
- Splay(key);
- // Bail if the key is not in the tree
- if (C::Compare(key, root_->key_) != 0)
- return false;
- if (root_->left_ == NULL) {
- // No left child, so the new tree is just the right child.
- root_ = root_->right_;
- } else {
- // Left child exists.
- Node* right = root_->right_;
- // Make the original left child the new root.
- root_ = root_->left_;
- // Splay to make sure that the new root has an empty right child.
- Splay(key);
- // Insert the original right child as the right child of the new
- // root.
- root_->right_ = right;
- }
- return true;
-}
-
-
-template <typename C>
-void ZoneSplayTree<C>::Splay(const Key& key) {
- if (is_empty())
- return;
- Node dummy_node(C::kNoKey, C::kNoValue);
- // Create a dummy node. The use of the dummy node is a bit
- // counter-intuitive: The right child of the dummy node will hold
- // the L tree of the algorithm. The left child of the dummy node
- // will hold the R tree of the algorithm. Using a dummy node, left
- // and right will always be nodes and we avoid special cases.
- Node* dummy = &dummy_node;
- Node* left = dummy;
- Node* right = dummy;
- Node* current = root_;
- while (true) {
- int cmp = C::Compare(key, current->key_);
- if (cmp < 0) {
- if (current->left_ == NULL)
- break;
- if (C::Compare(key, current->left_->key_) < 0) {
- // Rotate right.
- Node* temp = current->left_;
- current->left_ = temp->right_;
- temp->right_ = current;
- current = temp;
- if (current->left_ == NULL)
- break;
- }
- // Link right.
- right->left_ = current;
- right = current;
- current = current->left_;
- } else if (cmp > 0) {
- if (current->right_ == NULL)
- break;
- if (C::Compare(key, current->right_->key_) > 0) {
- // Rotate left.
- Node* temp = current->right_;
- current->right_ = temp->left_;
- temp->left_ = current;
- current = temp;
- if (current->right_ == NULL)
- break;
- }
- // Link left.
- left->right_ = current;
- left = current;
- current = current->right_;
- } else {
- break;
- }
- }
- // Assemble.
- left->right_ = current->left_;
- right->left_ = current->right_;
- current->left_ = dummy->right_;
- current->right_ = dummy->left_;
- root_ = current;
-}
-
-
-template <typename Config> template <class Callback>
-void ZoneSplayTree<Config>::ForEach(Callback* callback) {
- // Pre-allocate some space for tiny trees.
- ZoneList<Node*> nodes_to_visit(10);
- nodes_to_visit.Add(root_);
- int pos = 0;
- while (pos < nodes_to_visit.length()) {
- Node* node = nodes_to_visit[pos++];
- if (node == NULL) continue;
- callback->Call(node->key(), node->value());
- nodes_to_visit.Add(node->left());
- nodes_to_visit.Add(node->right());
- }
+template <typename Config>
+ZoneSplayTree<Config>::~ZoneSplayTree() {
+ // Reset the root to avoid unneeded iteration over all tree nodes
+ // in the destructor. For a zone-allocated tree, nodes will be
+ // freed by the Zone.
+ SplayTree<Config, ZoneListAllocationPolicy>::ResetRoot();
}
diff --git a/src/zone.cc b/src/zone.cc
index 33fe557..01df450 100644
--- a/src/zone.cc
+++ b/src/zone.cc
@@ -28,6 +28,7 @@
#include "v8.h"
#include "zone-inl.h"
+#include "splay-tree-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/zone.h b/src/zone.h
index 0d006dd..3397356 100644
--- a/src/zone.h
+++ b/src/zone.h
@@ -205,98 +205,14 @@
// A zone splay tree. The config type parameter encapsulates the
-// different configurations of a concrete splay tree:
-//
-// typedef Key: the key type
-// typedef Value: the value type
-// static const kNoKey: the dummy key used when no key is set
-// static const kNoValue: the dummy value used to initialize nodes
-// int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function
-//
+// different configurations of a concrete splay tree (see splay-tree.h).
+// The tree itself and all its elements are allocated in the Zone.
template <typename Config>
-class ZoneSplayTree : public ZoneObject {
+class ZoneSplayTree: public SplayTree<Config, ZoneListAllocationPolicy> {
public:
- typedef typename Config::Key Key;
- typedef typename Config::Value Value;
-
- class Locator;
-
- ZoneSplayTree() : root_(NULL) { }
-
- // Inserts the given key in this tree with the given value. Returns
- // true if a node was inserted, otherwise false. If found the locator
- // is enabled and provides access to the mapping for the key.
- bool Insert(const Key& key, Locator* locator);
-
- // Looks up the key in this tree and returns true if it was found,
- // otherwise false. If the node is found the locator is enabled and
- // provides access to the mapping for the key.
- bool Find(const Key& key, Locator* locator);
-
- // Finds the mapping with the greatest key less than or equal to the
- // given key.
- bool FindGreatestLessThan(const Key& key, Locator* locator);
-
- // Find the mapping with the greatest key in this tree.
- bool FindGreatest(Locator* locator);
-
- // Finds the mapping with the least key greater than or equal to the
- // given key.
- bool FindLeastGreaterThan(const Key& key, Locator* locator);
-
- // Find the mapping with the least key in this tree.
- bool FindLeast(Locator* locator);
-
- // Remove the node with the given key from the tree.
- bool Remove(const Key& key);
-
- bool is_empty() { return root_ == NULL; }
-
- // Perform the splay operation for the given key. Moves the node with
- // the given key to the top of the tree. If no node has the given
- // key, the last node on the search path is moved to the top of the
- // tree.
- void Splay(const Key& key);
-
- class Node : public ZoneObject {
- public:
- Node(const Key& key, const Value& value)
- : key_(key),
- value_(value),
- left_(NULL),
- right_(NULL) { }
- Key key() { return key_; }
- Value value() { return value_; }
- Node* left() { return left_; }
- Node* right() { return right_; }
- private:
- friend class ZoneSplayTree;
- friend class Locator;
- Key key_;
- Value value_;
- Node* left_;
- Node* right_;
- };
-
- // A locator provides access to a node in the tree without actually
- // exposing the node.
- class Locator {
- public:
- explicit Locator(Node* node) : node_(node) { }
- Locator() : node_(NULL) { }
- const Key& key() { return node_->key_; }
- Value& value() { return node_->value_; }
- void set_value(const Value& value) { node_->value_ = value; }
- inline void bind(Node* node) { node_ = node; }
- private:
- Node* node_;
- };
-
- template <class Callback>
- void ForEach(Callback* callback);
-
- private:
- Node* root_;
+ ZoneSplayTree()
+ : SplayTree<Config, ZoneListAllocationPolicy>() {}
+ ~ZoneSplayTree();
};