Merge branch 'v0.4'

This commit is contained in:
Ryan Dahl 2011-02-25 16:48:48 -08:00
commit 8838e14ac0
155 changed files with 7288 additions and 4021 deletions

2
deps/libeio/wscript vendored
View File

@ -125,5 +125,7 @@ def build(bld):
libeio.install_path = None libeio.install_path = None
if bld.env["USE_DEBUG"]: if bld.env["USE_DEBUG"]:
libeio.clone("debug"); libeio.clone("debug");
if Options.options.product_type != 'program':
libeio.ccflags = "-fPIC"
bld.install_files('${PREFIX}/include/node/', 'eio.h'); bld.install_files('${PREFIX}/include/node/', 'eio.h');

13
deps/v8/ChangeLog vendored
View File

@ -1,3 +1,16 @@
2011-02-24: Version 3.1.6
Fixed a number of crash bugs.
Added support for Cygwin (issue 64).
Improved Crankshaft for x64 and ARM.
Added Crankshaft support for stores to pixel arrays.
Fixed issue in CPU profiler with Crankshaft.
2011-02-16: Version 3.1.5 2011-02-16: Version 3.1.5
Change RegExp parsing to disallow /(*)/. Change RegExp parsing to disallow /(*)/.

1
deps/v8/SConstruct vendored
View File

@ -306,6 +306,7 @@ V8_EXTRA_FLAGS = {
'gcc': { 'gcc': {
'all': { 'all': {
'WARNINGFLAGS': ['-Wall', 'WARNINGFLAGS': ['-Wall',
'-Werror',
'-W', '-W',
'-Wno-unused-parameter', '-Wno-unused-parameter',
'-Wnon-virtual-dtor'] '-Wnon-virtual-dtor']

View File

@ -154,6 +154,7 @@ SOURCES = {
arm/jump-target-arm.cc arm/jump-target-arm.cc
arm/lithium-arm.cc arm/lithium-arm.cc
arm/lithium-codegen-arm.cc arm/lithium-codegen-arm.cc
arm/lithium-gap-resolver-arm.cc
arm/macro-assembler-arm.cc arm/macro-assembler-arm.cc
arm/regexp-macro-assembler-arm.cc arm/regexp-macro-assembler-arm.cc
arm/register-allocator-arm.cc arm/register-allocator-arm.cc

View File

@ -446,8 +446,15 @@ MaybeObject* Accessors::FunctionGetPrototype(Object* object, void*) {
bool found_it = false; bool found_it = false;
JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it); JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value(); if (!found_it) return Heap::undefined_value();
while (!function->should_have_prototype()) {
found_it = false;
function = FindInPrototypeChain<JSFunction>(object->GetPrototype(),
&found_it);
// There has to be one because we hit the getter.
ASSERT(found_it);
}
if (!function->has_prototype()) { if (!function->has_prototype()) {
if (!function->should_have_prototype()) return Heap::undefined_value();
Object* prototype; Object* prototype;
{ MaybeObject* maybe_prototype = Heap::AllocateFunctionPrototype(function); { MaybeObject* maybe_prototype = Heap::AllocateFunctionPrototype(function);
if (!maybe_prototype->ToObject(&prototype)) return maybe_prototype; if (!maybe_prototype->ToObject(&prototype)) return maybe_prototype;
@ -467,6 +474,13 @@ MaybeObject* Accessors::FunctionSetPrototype(JSObject* object,
bool found_it = false; bool found_it = false;
JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it); JSFunction* function = FindInPrototypeChain<JSFunction>(object, &found_it);
if (!found_it) return Heap::undefined_value(); if (!found_it) return Heap::undefined_value();
if (!function->should_have_prototype()) {
// Since we hit this accessor, object will have no prototype property.
return object->SetLocalPropertyIgnoreAttributes(Heap::prototype_symbol(),
value,
NONE);
}
if (function->has_initial_map()) { if (function->has_initial_map()) {
// If the function has allocated the initial map // If the function has allocated the initial map
// replace it with a copy containing the new prototype. // replace it with a copy containing the new prototype.

View File

@ -1848,11 +1848,31 @@ void Assembler::vldr(const DwVfpRegister dst,
offset = -offset; offset = -offset;
u = 0; u = 0;
} }
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
ASSERT(offset >= 0); ASSERT(offset >= 0);
emit(cond | u*B23 | 0xD1*B20 | base.code()*B16 | dst.code()*B12 | if ((offset % 4) == 0 && (offset / 4) < 256) {
0xB*B8 | ((offset / 4) & 255)); emit(cond | u*B23 | 0xD1*B20 | base.code()*B16 | dst.code()*B12 |
0xB*B8 | ((offset / 4) & 255));
} else {
// Larger offsets must be handled by computing the correct address
// in the ip register.
ASSERT(!base.is(ip));
if (u == 1) {
add(ip, base, Operand(offset));
} else {
sub(ip, base, Operand(offset));
}
emit(cond | 0xD1*B20 | ip.code()*B16 | dst.code()*B12 | 0xB*B8);
}
}
void Assembler::vldr(const DwVfpRegister dst,
const MemOperand& operand,
const Condition cond) {
ASSERT(!operand.rm().is_valid());
ASSERT(operand.am_ == Offset);
vldr(dst, operand.rn(), operand.offset(), cond);
} }
@ -1870,13 +1890,33 @@ void Assembler::vldr(const SwVfpRegister dst,
offset = -offset; offset = -offset;
u = 0; u = 0;
} }
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
ASSERT(offset >= 0);
int sd, d; int sd, d;
dst.split_code(&sd, &d); dst.split_code(&sd, &d);
ASSERT(offset >= 0);
if ((offset % 4) == 0 && (offset / 4) < 256) {
emit(cond | u*B23 | d*B22 | 0xD1*B20 | base.code()*B16 | sd*B12 | emit(cond | u*B23 | d*B22 | 0xD1*B20 | base.code()*B16 | sd*B12 |
0xA*B8 | ((offset / 4) & 255)); 0xA*B8 | ((offset / 4) & 255));
} else {
// Larger offsets must be handled by computing the correct address
// in the ip register.
ASSERT(!base.is(ip));
if (u == 1) {
add(ip, base, Operand(offset));
} else {
sub(ip, base, Operand(offset));
}
emit(cond | d*B22 | 0xD1*B20 | ip.code()*B16 | sd*B12 | 0xA*B8);
}
}
void Assembler::vldr(const SwVfpRegister dst,
const MemOperand& operand,
const Condition cond) {
ASSERT(!operand.rm().is_valid());
ASSERT(operand.am_ == Offset);
vldr(dst, operand.rn(), operand.offset(), cond);
} }
@ -1894,11 +1934,30 @@ void Assembler::vstr(const DwVfpRegister src,
offset = -offset; offset = -offset;
u = 0; u = 0;
} }
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
ASSERT(offset >= 0); ASSERT(offset >= 0);
emit(cond | u*B23 | 0xD0*B20 | base.code()*B16 | src.code()*B12 | if ((offset % 4) == 0 && (offset / 4) < 256) {
0xB*B8 | ((offset / 4) & 255)); emit(cond | u*B23 | 0xD0*B20 | base.code()*B16 | src.code()*B12 |
0xB*B8 | ((offset / 4) & 255));
} else {
// Larger offsets must be handled by computing the correct address
// in the ip register.
ASSERT(!base.is(ip));
if (u == 1) {
add(ip, base, Operand(offset));
} else {
sub(ip, base, Operand(offset));
}
emit(cond | 0xD0*B20 | ip.code()*B16 | src.code()*B12 | 0xB*B8);
}
}
void Assembler::vstr(const DwVfpRegister src,
const MemOperand& operand,
const Condition cond) {
ASSERT(!operand.rm().is_valid());
ASSERT(operand.am_ == Offset);
vstr(src, operand.rn(), operand.offset(), cond);
} }
@ -1916,13 +1975,32 @@ void Assembler::vstr(const SwVfpRegister src,
offset = -offset; offset = -offset;
u = 0; u = 0;
} }
ASSERT(offset % 4 == 0);
ASSERT((offset / 4) < 256);
ASSERT(offset >= 0);
int sd, d; int sd, d;
src.split_code(&sd, &d); src.split_code(&sd, &d);
emit(cond | u*B23 | d*B22 | 0xD0*B20 | base.code()*B16 | sd*B12 | ASSERT(offset >= 0);
0xA*B8 | ((offset / 4) & 255)); if ((offset % 4) == 0 && (offset / 4) < 256) {
emit(cond | u*B23 | d*B22 | 0xD0*B20 | base.code()*B16 | sd*B12 |
0xA*B8 | ((offset / 4) & 255));
} else {
// Larger offsets must be handled by computing the correct address
// in the ip register.
ASSERT(!base.is(ip));
if (u == 1) {
add(ip, base, Operand(offset));
} else {
sub(ip, base, Operand(offset));
}
emit(cond | d*B22 | 0xD0*B20 | ip.code()*B16 | sd*B12 | 0xA*B8);
}
}
void Assembler::vstr(const SwVfpRegister src,
const MemOperand& operand,
const Condition cond) {
ASSERT(!operand.rm().is_valid());
ASSERT(operand.am_ == Offset);
vldr(src, operand.rn(), operand.offset(), cond);
} }

View File

@ -387,7 +387,7 @@ class Operand BASE_EMBEDDED {
// Return true if this is a register operand. // Return true if this is a register operand.
INLINE(bool is_reg() const); INLINE(bool is_reg() const);
// Return true of this operand fits in one instruction so that no // Return true if this operand fits in one instruction so that no
// 2-instruction solution with a load into the ip register is necessary. // 2-instruction solution with a load into the ip register is necessary.
bool is_single_instruction() const; bool is_single_instruction() const;
bool must_use_constant_pool() const; bool must_use_constant_pool() const;
@ -439,7 +439,7 @@ class MemOperand BASE_EMBEDDED {
offset_ = offset; offset_ = offset;
} }
uint32_t offset() { uint32_t offset() const {
ASSERT(rm_.is(no_reg)); ASSERT(rm_.is(no_reg));
return offset_; return offset_;
} }
@ -447,6 +447,10 @@ class MemOperand BASE_EMBEDDED {
Register rn() const { return rn_; } Register rn() const { return rn_; }
Register rm() const { return rm_; } Register rm() const { return rm_; }
bool OffsetIsUint12Encodable() const {
return offset_ >= 0 ? is_uint12(offset_) : is_uint12(-offset_);
}
private: private:
Register rn_; // base Register rn_; // base
Register rm_; // register offset Register rm_; // register offset
@ -902,22 +906,34 @@ class Assembler : public Malloced {
void vldr(const DwVfpRegister dst, void vldr(const DwVfpRegister dst,
const Register base, const Register base,
int offset, // Offset must be a multiple of 4. int offset,
const Condition cond = al);
void vldr(const DwVfpRegister dst,
const MemOperand& src,
const Condition cond = al); const Condition cond = al);
void vldr(const SwVfpRegister dst, void vldr(const SwVfpRegister dst,
const Register base, const Register base,
int offset, // Offset must be a multiple of 4. int offset,
const Condition cond = al);
void vldr(const SwVfpRegister dst,
const MemOperand& src,
const Condition cond = al); const Condition cond = al);
void vstr(const DwVfpRegister src, void vstr(const DwVfpRegister src,
const Register base, const Register base,
int offset, // Offset must be a multiple of 4. int offset,
const Condition cond = al);
void vstr(const DwVfpRegister src,
const MemOperand& dst,
const Condition cond = al); const Condition cond = al);
void vstr(const SwVfpRegister src, void vstr(const SwVfpRegister src,
const Register base, const Register base,
int offset, // Offset must be a multiple of 4. int offset,
const Condition cond = al);
void vstr(const SwVfpRegister src,
const MemOperand& dst,
const Condition cond = al); const Condition cond = al);
void vmov(const DwVfpRegister dst, void vmov(const DwVfpRegister dst,

View File

@ -2661,8 +2661,8 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
// Allocate new heap number for result. // Allocate new heap number for result.
Register result = r5; Register result = r5;
__ AllocateHeapNumber( GenerateHeapResultAllocation(
result, scratch1, scratch2, heap_number_map, gc_required); masm, result, heap_number_map, scratch1, scratch2, gc_required);
// Load the operands. // Load the operands.
if (smi_operands) { if (smi_operands) {
@ -2811,8 +2811,14 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
// Allocate new heap number for result. // Allocate new heap number for result.
__ bind(&result_not_a_smi); __ bind(&result_not_a_smi);
__ AllocateHeapNumber( Register result = r5;
r5, scratch1, scratch2, heap_number_map, gc_required); if (smi_operands) {
__ AllocateHeapNumber(
result, scratch1, scratch2, heap_number_map, gc_required);
} else {
GenerateHeapResultAllocation(
masm, result, heap_number_map, scratch1, scratch2, gc_required);
}
// r2: Answer as signed int32. // r2: Answer as signed int32.
// r5: Heap number to write answer into. // r5: Heap number to write answer into.
@ -2934,45 +2940,47 @@ void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) { void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
Label call_runtime; Label call_runtime, call_string_add_or_runtime;
GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS); GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
// If all else fails, use the runtime system to get the correct GenerateFPOperation(masm, false, &call_string_add_or_runtime, &call_runtime);
// result.
__ bind(&call_runtime);
// Try to add strings before calling runtime. __ bind(&call_string_add_or_runtime);
if (op_ == Token::ADD) { if (op_ == Token::ADD) {
GenerateAddStrings(masm); GenerateAddStrings(masm);
} }
GenericBinaryOpStub stub(op_, mode_, r1, r0); __ bind(&call_runtime);
__ TailCallStub(&stub); GenerateCallRuntime(masm);
} }
void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) { void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
ASSERT(op_ == Token::ADD); ASSERT(op_ == Token::ADD);
Label left_not_string, call_runtime;
Register left = r1; Register left = r1;
Register right = r0; Register right = r0;
Label call_runtime;
// Check if first argument is a string. // Check if left argument is a string.
__ JumpIfSmi(left, &call_runtime); __ JumpIfSmi(left, &left_not_string);
__ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE); __ CompareObjectType(left, r2, r2, FIRST_NONSTRING_TYPE);
__ b(ge, &call_runtime); __ b(ge, &left_not_string);
// First argument is a a string, test second. StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_left_stub);
// Left operand is not a string, test right.
__ bind(&left_not_string);
__ JumpIfSmi(right, &call_runtime); __ JumpIfSmi(right, &call_runtime);
__ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE); __ CompareObjectType(right, r2, r2, FIRST_NONSTRING_TYPE);
__ b(ge, &call_runtime); __ b(ge, &call_runtime);
// First and second argument are strings. StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
GenerateRegisterArgsPush(masm); GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_stub); __ TailCallStub(&string_add_right_stub);
// At least one argument is not a string. // At least one argument is not a string.
__ bind(&call_runtime); __ bind(&call_runtime);
@ -3706,7 +3714,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// The offset was stored in r4 safepoint slot. // The offset was stored in r4 safepoint slot.
// (See LCodeGen::DoDeferredLInstanceOfKnownGlobal) // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal)
__ ldr(scratch, MacroAssembler::SafepointRegisterSlot(r4)); __ LoadFromSafepointRegisterSlot(scratch, r4);
__ sub(inline_site, lr, scratch); __ sub(inline_site, lr, scratch);
// Get the map location in scratch and patch it. // Get the map location in scratch and patch it.
__ GetRelocatedValueLocation(inline_site, scratch); __ GetRelocatedValueLocation(inline_site, scratch);
@ -5438,18 +5446,19 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
void StringAddStub::Generate(MacroAssembler* masm) { void StringAddStub::Generate(MacroAssembler* masm) {
Label string_add_runtime; Label string_add_runtime, call_builtin;
Builtins::JavaScript builtin_id = Builtins::ADD;
// Stack on entry: // Stack on entry:
// sp[0]: second argument. // sp[0]: second argument (right).
// sp[4]: first argument. // sp[4]: first argument (left).
// Load the two arguments. // Load the two arguments.
__ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument. __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
__ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument. __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
// Make sure that both arguments are strings if not known in advance. // Make sure that both arguments are strings if not known in advance.
if (string_check_) { if (flags_ == NO_STRING_ADD_FLAGS) {
STATIC_ASSERT(kSmiTag == 0);
__ JumpIfEitherSmi(r0, r1, &string_add_runtime); __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
// Load instance types. // Load instance types.
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
@ -5461,13 +5470,27 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ tst(r4, Operand(kIsNotStringMask)); __ tst(r4, Operand(kIsNotStringMask));
__ tst(r5, Operand(kIsNotStringMask), eq); __ tst(r5, Operand(kIsNotStringMask), eq);
__ b(ne, &string_add_runtime); __ b(ne, &string_add_runtime);
} else {
// Here at least one of the arguments is definitely a string.
// We convert the one that is not known to be a string.
if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
GenerateConvertArgument(
masm, 1 * kPointerSize, r0, r2, r3, r4, r5, &call_builtin);
builtin_id = Builtins::STRING_ADD_RIGHT;
} else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
GenerateConvertArgument(
masm, 0 * kPointerSize, r1, r2, r3, r4, r5, &call_builtin);
builtin_id = Builtins::STRING_ADD_LEFT;
}
} }
// Both arguments are strings. // Both arguments are strings.
// r0: first string // r0: first string
// r1: second string // r1: second string
// r4: first string instance type (if string_check_) // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// r5: second string instance type (if string_check_) // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
{ {
Label strings_not_empty; Label strings_not_empty;
// Check if either of the strings are empty. In that case return the other. // Check if either of the strings are empty. In that case return the other.
@ -5495,8 +5518,8 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// r1: second string // r1: second string
// r2: length of first string // r2: length of first string
// r3: length of second string // r3: length of second string
// r4: first string instance type (if string_check_) // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// r5: second string instance type (if string_check_) // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// Look at the length of the result of adding the two strings. // Look at the length of the result of adding the two strings.
Label string_add_flat_result, longer_than_two; Label string_add_flat_result, longer_than_two;
// Adding two lengths can't overflow. // Adding two lengths can't overflow.
@ -5508,7 +5531,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ b(ne, &longer_than_two); __ b(ne, &longer_than_two);
// Check that both strings are non-external ascii strings. // Check that both strings are non-external ascii strings.
if (!string_check_) { if (flags_ != NO_STRING_ADD_FLAGS) {
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
@ -5556,7 +5579,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// If result is not supposed to be flat, allocate a cons string object. // If result is not supposed to be flat, allocate a cons string object.
// If both strings are ascii the result is an ascii cons string. // If both strings are ascii the result is an ascii cons string.
if (!string_check_) { if (flags_ != NO_STRING_ADD_FLAGS) {
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
@ -5604,11 +5627,11 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// r1: second string // r1: second string
// r2: length of first string // r2: length of first string
// r3: length of second string // r3: length of second string
// r4: first string instance type (if string_check_) // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// r5: second string instance type (if string_check_) // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
// r6: sum of lengths. // r6: sum of lengths.
__ bind(&string_add_flat_result); __ bind(&string_add_flat_result);
if (!string_check_) { if (flags_ != NO_STRING_ADD_FLAGS) {
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset)); __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset)); __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
@ -5706,6 +5729,60 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// Just jump to runtime to add the two strings. // Just jump to runtime to add the two strings.
__ bind(&string_add_runtime); __ bind(&string_add_runtime);
__ TailCallRuntime(Runtime::kStringAdd, 2, 1); __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
if (call_builtin.is_linked()) {
__ bind(&call_builtin);
__ InvokeBuiltin(builtin_id, JUMP_JS);
}
}
void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
int stack_offset,
Register arg,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4,
Label* slow) {
// First check if the argument is already a string.
Label not_string, done;
__ JumpIfSmi(arg, &not_string);
__ CompareObjectType(arg, scratch1, scratch1, FIRST_NONSTRING_TYPE);
__ b(lt, &done);
// Check the number to string cache.
Label not_cached;
__ bind(&not_string);
// Puts the cached result into scratch1.
NumberToStringStub::GenerateLookupNumberStringCache(masm,
arg,
scratch1,
scratch2,
scratch3,
scratch4,
false,
&not_cached);
__ mov(arg, scratch1);
__ str(arg, MemOperand(sp, stack_offset));
__ jmp(&done);
// Check if the argument is a safe string wrapper.
__ bind(&not_cached);
__ JumpIfSmi(arg, slow);
__ CompareObjectType(
arg, scratch1, scratch2, JS_VALUE_TYPE); // map -> scratch1.
__ b(ne, slow);
__ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
__ and_(scratch2,
scratch2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
__ cmp(scratch2,
Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
__ b(ne, slow);
__ ldr(arg, FieldMemOperand(arg, JSValue::kValueOffset));
__ str(arg, MemOperand(sp, stack_offset));
__ bind(&done);
} }

View File

@ -335,24 +335,36 @@ class TypeRecordingBinaryOpStub: public CodeStub {
// Flag that indicates how to generate code for the stub StringAddStub. // Flag that indicates how to generate code for the stub StringAddStub.
enum StringAddFlags { enum StringAddFlags {
NO_STRING_ADD_FLAGS = 0, NO_STRING_ADD_FLAGS = 0,
NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. // Omit left string check in stub (left is definitely a string).
NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0,
// Omit right string check in stub (right is definitely a string).
NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1,
// Omit both string checks in stub.
NO_STRING_CHECK_IN_STUB =
NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB
}; };
class StringAddStub: public CodeStub { class StringAddStub: public CodeStub {
public: public:
explicit StringAddStub(StringAddFlags flags) { explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
}
private: private:
Major MajorKey() { return StringAdd; } Major MajorKey() { return StringAdd; }
int MinorKey() { return string_check_ ? 0 : 1; } int MinorKey() { return flags_; }
void Generate(MacroAssembler* masm); void Generate(MacroAssembler* masm);
// Should the stub check whether arguments are strings? void GenerateConvertArgument(MacroAssembler* masm,
bool string_check_; int stack_offset,
Register arg,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4,
Label* slow);
const StringAddFlags flags_;
}; };

View File

@ -5850,8 +5850,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
} else if (variable != NULL) { } else if (variable != NULL) {
// Delete of an unqualified identifier is disallowed in strict mode // Delete of an unqualified identifier is disallowed in strict mode
// so this code can only be reached in non-strict mode. // but "delete this" is.
ASSERT(strict_mode_flag() == kNonStrictMode); ASSERT(strict_mode_flag() == kNonStrictMode || variable->is_this());
Slot* slot = variable->AsSlot(); Slot* slot = variable->AsSlot();
if (variable->is_global()) { if (variable->is_global()) {
LoadGlobal(); LoadGlobal();

View File

@ -50,6 +50,11 @@ void CPU::Setup() {
void CPU::FlushICache(void* start, size_t size) { void CPU::FlushICache(void* start, size_t size) {
// Nothing to do flushing no instructions.
if (size == 0) {
return;
}
#if defined (USE_SIMULATOR) #if defined (USE_SIMULATOR)
// Not generating ARM instructions for C-code. This means that we are // Not generating ARM instructions for C-code. This means that we are
// building an ARM emulator based target. We should notify the simulator // building an ARM emulator based target. We should notify the simulator

View File

@ -429,14 +429,16 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
fp_value, output_offset, value); fp_value, output_offset, value);
} }
// The context can be gotten from the function so long as we don't // For the bottommost output frame the context can be gotten from the input
// optimize functions that need local contexts. // frame. For all subsequent output frames it can be gotten from the function
// so long as we don't inline functions that need local contexts.
output_offset -= kPointerSize; output_offset -= kPointerSize;
input_offset -= kPointerSize; input_offset -= kPointerSize;
value = reinterpret_cast<intptr_t>(function->context()); if (is_bottommost) {
// The context for the bottommost output frame should also agree with the value = input_->GetFrameSlot(input_offset);
// input frame. } else {
ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value); value = reinterpret_cast<intptr_t>(function->context());
}
output_frame->SetFrameSlot(output_offset, value); output_frame->SetFrameSlot(output_offset, value);
if (is_topmost) { if (is_topmost) {
output_frame->SetRegister(cp.code(), value); output_frame->SetRegister(cp.code(), value);

View File

@ -219,46 +219,47 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
Move(dot_arguments_slot, r3, r1, r2); Move(dot_arguments_slot, r3, r1, r2);
} }
{ Comment cmnt(masm_, "[ Declarations");
// For named function expressions, declare the function name as a
// constant.
if (scope()->is_function_scope() && scope()->function() != NULL) {
EmitDeclaration(scope()->function(), Variable::CONST, NULL);
}
// Visit all the explicit declarations unless there is an illegal
// redeclaration.
if (scope()->HasIllegalRedeclaration()) {
scope()->VisitIllegalRedeclaration(this);
} else {
VisitDeclarations(scope()->declarations());
}
}
if (FLAG_trace) { if (FLAG_trace) {
__ CallRuntime(Runtime::kTraceEnter, 0); __ CallRuntime(Runtime::kTraceEnter, 0);
} }
// Check the stack for overflow or break request. // Visit the declarations and body unless there is an illegal
{ Comment cmnt(masm_, "[ Stack check"); // redeclaration.
PrepareForBailout(info->function(), NO_REGISTERS); if (scope()->HasIllegalRedeclaration()) {
Label ok; Comment cmnt(masm_, "[ Declarations");
__ LoadRoot(ip, Heap::kStackLimitRootIndex); scope()->VisitIllegalRedeclaration(this);
__ cmp(sp, Operand(ip));
__ b(hs, &ok); } else {
StackCheckStub stub; { Comment cmnt(masm_, "[ Declarations");
__ CallStub(&stub); // For named function expressions, declare the function name as a
__ bind(&ok); // constant.
} if (scope()->is_function_scope() && scope()->function() != NULL) {
EmitDeclaration(scope()->function(), Variable::CONST, NULL);
{ Comment cmnt(masm_, "[ Body"); }
ASSERT(loop_depth() == 0); VisitDeclarations(scope()->declarations());
VisitStatements(function()->body()); }
ASSERT(loop_depth() == 0);
{ Comment cmnt(masm_, "[ Stack check");
PrepareForBailout(info->function(), NO_REGISTERS);
Label ok;
__ LoadRoot(ip, Heap::kStackLimitRootIndex);
__ cmp(sp, Operand(ip));
__ b(hs, &ok);
StackCheckStub stub;
__ CallStub(&stub);
__ bind(&ok);
}
{ Comment cmnt(masm_, "[ Body");
ASSERT(loop_depth() == 0);
VisitStatements(function()->body());
ASSERT(loop_depth() == 0);
}
} }
// Always emit a 'return undefined' in case control fell off the end of
// the body.
{ Comment cmnt(masm_, "[ return <undefined>;"); { Comment cmnt(masm_, "[ return <undefined>;");
// Emit a 'return undefined' in case control fell off the end of the
// body.
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex); __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
} }
EmitReturnSequence(); EmitReturnSequence();
@ -694,10 +695,11 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
// We bypass the general EmitSlotSearch because we know more about // We bypass the general EmitSlotSearch because we know more about
// this specific context. // this specific context.
// The variable in the decl always resides in the current context. // The variable in the decl always resides in the current function
// context.
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
if (FLAG_debug_code) { if (FLAG_debug_code) {
// Check if we have the correct context pointer. // Check that we're not inside a 'with'.
__ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX));
__ cmp(r1, cp); __ cmp(r1, cp);
__ Check(eq, "Unexpected declaration in current context."); __ Check(eq, "Unexpected declaration in current context.");
@ -1037,7 +1039,7 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
Slot* slot, Slot* slot,
Label* slow) { Label* slow) {
ASSERT(slot->type() == Slot::CONTEXT); ASSERT(slot->type() == Slot::CONTEXT);
Register current = cp; Register context = cp;
Register next = r3; Register next = r3;
Register temp = r4; Register temp = r4;
@ -1045,22 +1047,25 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
if (s->num_heap_slots() > 0) { if (s->num_heap_slots() > 0) {
if (s->calls_eval()) { if (s->calls_eval()) {
// Check that extension is NULL. // Check that extension is NULL.
__ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX));
__ tst(temp, temp); __ tst(temp, temp);
__ b(ne, slow); __ b(ne, slow);
} }
__ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX)); __ ldr(next, ContextOperand(context, Context::CLOSURE_INDEX));
__ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
// Walk the rest of the chain without clobbering cp. // Walk the rest of the chain without clobbering cp.
current = next; context = next;
} }
} }
// Check that last extension is NULL. // Check that last extension is NULL.
__ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX));
__ tst(temp, temp); __ tst(temp, temp);
__ b(ne, slow); __ b(ne, slow);
__ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX));
return ContextOperand(temp, slot->index()); // This function is used only for loads, not stores, so it's safe to
// return an cp-based operand (the write barrier cannot be allowed to
// destroy the cp register).
return ContextOperand(context, slot->index());
} }
@ -2004,34 +2009,60 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
: Builtins::StoreIC_Initialize)); : Builtins::StoreIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
} else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { } else if (op == Token::INIT_CONST) {
// Perform the assignment for non-const variables and for initialization // Like var declarations, const declarations are hoisted to function
// of const variables. Const assignments are simply skipped. // scope. However, unlike var initializers, const initializers are able
Label done; // to drill a hole to that function context, even from inside a 'with'
// context. We thus bypass the normal static scope lookup.
Slot* slot = var->AsSlot();
Label skip;
switch (slot->type()) {
case Slot::PARAMETER:
// No const parameters.
UNREACHABLE();
break;
case Slot::LOCAL:
// Detect const reinitialization by checking for the hole value.
__ ldr(r1, MemOperand(fp, SlotOffset(slot)));
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(r1, ip);
__ b(ne, &skip);
__ str(result_register(), MemOperand(fp, SlotOffset(slot)));
break;
case Slot::CONTEXT: {
__ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX));
__ ldr(r2, ContextOperand(r1, slot->index()));
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(r2, ip);
__ b(ne, &skip);
__ str(r0, ContextOperand(r1, slot->index()));
int offset = Context::SlotOffset(slot->index());
__ mov(r3, r0); // Preserve the stored value in r0.
__ RecordWrite(r1, Operand(offset), r3, r2);
break;
}
case Slot::LOOKUP:
__ push(r0);
__ mov(r0, Operand(slot->var()->name()));
__ Push(cp, r0); // Context and name.
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
break;
}
__ bind(&skip);
} else if (var->mode() != Variable::CONST) {
// Perform the assignment for non-const variables. Const assignments
// are simply skipped.
Slot* slot = var->AsSlot(); Slot* slot = var->AsSlot();
switch (slot->type()) { switch (slot->type()) {
case Slot::PARAMETER: case Slot::PARAMETER:
case Slot::LOCAL: case Slot::LOCAL:
if (op == Token::INIT_CONST) {
// Detect const reinitialization by checking for the hole value.
__ ldr(r1, MemOperand(fp, SlotOffset(slot)));
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(r1, ip);
__ b(ne, &done);
}
// Perform the assignment. // Perform the assignment.
__ str(result_register(), MemOperand(fp, SlotOffset(slot))); __ str(result_register(), MemOperand(fp, SlotOffset(slot)));
break; break;
case Slot::CONTEXT: { case Slot::CONTEXT: {
MemOperand target = EmitSlotSearch(slot, r1); MemOperand target = EmitSlotSearch(slot, r1);
if (op == Token::INIT_CONST) {
// Detect const reinitialization by checking for the hole value.
__ ldr(r2, target);
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(r2, ip);
__ b(ne, &done);
}
// Perform the assignment and issue the write barrier. // Perform the assignment and issue the write barrier.
__ str(result_register(), target); __ str(result_register(), target);
// RecordWrite may destroy all its register arguments. // RecordWrite may destroy all its register arguments.
@ -2042,20 +2073,13 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
} }
case Slot::LOOKUP: case Slot::LOOKUP:
// Call the runtime for the assignment. The runtime will ignore // Call the runtime for the assignment.
// const reinitialization.
__ push(r0); // Value. __ push(r0); // Value.
__ mov(r0, Operand(slot->var()->name())); __ mov(r0, Operand(slot->var()->name()));
__ Push(cp, r0); // Context and name. __ Push(cp, r0); // Context and name.
if (op == Token::INIT_CONST) { __ CallRuntime(Runtime::kStoreContextSlot, 3);
// The runtime will ignore const redeclaration.
__ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
} else {
__ CallRuntime(Runtime::kStoreContextSlot, 3);
}
break; break;
} }
__ bind(&done);
} }
} }
@ -3373,8 +3397,8 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} }
} else if (var != NULL) { } else if (var != NULL) {
// Delete of an unqualified identifier is disallowed in strict mode // Delete of an unqualified identifier is disallowed in strict mode
// so this code can only be reached in non-strict mode. // but "delete this" is.
ASSERT(strict_mode_flag() == kNonStrictMode); ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this());
if (var->is_global()) { if (var->is_global()) {
__ ldr(r2, GlobalObjectOperand()); __ ldr(r2, GlobalObjectOperand());
__ mov(r1, Operand(var->name())); __ mov(r1, Operand(var->name()));
@ -3414,17 +3438,23 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::NOT: { case Token::NOT: {
Comment cmnt(masm_, "[ UnaryOperation (NOT)"); Comment cmnt(masm_, "[ UnaryOperation (NOT)");
Label materialize_true, materialize_false; if (context()->IsEffect()) {
Label* if_true = NULL; // Unary NOT has no side effects so it's only necessary to visit the
Label* if_false = NULL; // subexpression. Match the optimizing compiler by not branching.
Label* fall_through = NULL; VisitForEffect(expr->expression());
} else {
Label materialize_true, materialize_false;
Label* if_true = NULL;
Label* if_false = NULL;
Label* fall_through = NULL;
// Notice that the labels are swapped. // Notice that the labels are swapped.
context()->PrepareTest(&materialize_true, &materialize_false, context()->PrepareTest(&materialize_true, &materialize_false,
&if_false, &if_true, &fall_through); &if_false, &if_true, &fall_through);
if (context()->IsTest()) ForwardBailoutToChild(expr); if (context()->IsTest()) ForwardBailoutToChild(expr);
VisitForControl(expr->expression(), if_true, if_false, fall_through); VisitForControl(expr->expression(), if_true, if_false, fall_through);
context()->Plug(if_false, if_true); // Labels swapped. context()->Plug(if_false, if_true); // Labels swapped.
}
break; break;
} }

View File

@ -346,7 +346,7 @@ void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
} }
void LStoreNamed::PrintDataTo(StringStream* stream) { void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream); object()->PrintTo(stream);
stream->Add("."); stream->Add(".");
stream->Add(*String::cast(*name())->ToCString()); stream->Add(*String::cast(*name())->ToCString());
@ -355,7 +355,25 @@ void LStoreNamed::PrintDataTo(StringStream* stream) {
} }
void LStoreKeyed::PrintDataTo(StringStream* stream) { void LStoreNamedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
stream->Add(" <- ");
value()->PrintTo(stream);
}
void LStoreKeyedFastElement::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add("[");
key()->PrintTo(stream);
stream->Add("] <- ");
value()->PrintTo(stream);
}
void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream); object()->PrintTo(stream);
stream->Add("["); stream->Add("[");
key()->PrintTo(stream); key()->PrintTo(stream);
@ -1204,8 +1222,7 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
case kMathSqrt: case kMathSqrt:
return DefineSameAsFirst(result); return DefineSameAsFirst(result);
case kMathRound: case kMathRound:
Abort("MathRound LUnaryMathOperation not implemented"); return AssignEnvironment(DefineAsRegister(result));
return NULL;
case kMathPowHalf: case kMathPowHalf:
Abort("MathPowHalf LUnaryMathOperation not implemented"); Abort("MathPowHalf LUnaryMathOperation not implemented");
return NULL; return NULL;
@ -1418,8 +1435,19 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
LInstruction* LChunkBuilder::DoPower(HPower* instr) { LInstruction* LChunkBuilder::DoPower(HPower* instr) {
Abort("LPower instruction not implemented on ARM"); ASSERT(instr->representation().IsDouble());
return NULL; // We call a C function for double power. It can't trigger a GC.
// We need to use fixed result register for the call.
Representation exponent_type = instr->right()->representation();
ASSERT(instr->left()->representation().IsDouble());
LOperand* left = UseFixedDouble(instr->left(), d1);
LOperand* right = exponent_type.IsDouble() ?
UseFixedDouble(instr->right(), d2) :
UseFixed(instr->right(), r0);
LPower* result = new LPower(left, right);
return MarkAsCall(DefineFixedDouble(result, d3),
instr,
CAN_DEOPTIMIZE_EAGERLY);
} }
@ -1709,11 +1737,13 @@ LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
LOperand* context = UseTempRegister(instr->context()); LOperand* context;
LOperand* value; LOperand* value;
if (instr->NeedsWriteBarrier()) { if (instr->NeedsWriteBarrier()) {
context = UseTempRegister(instr->context());
value = UseTempRegister(instr->value()); value = UseTempRegister(instr->value());
} else { } else {
context = UseRegister(instr->context());
value = UseRegister(instr->value()); value = UseRegister(instr->value());
} }
return new LStoreContextSlot(context, value); return new LStoreContextSlot(context, value);
@ -1806,6 +1836,13 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement(
} }
LInstruction* LChunkBuilder::DoStorePixelArrayElement(
HStorePixelArrayElement* instr) {
Abort("DoStorePixelArrayElement not implemented");
return NULL;
}
LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
LOperand* obj = UseFixed(instr->object(), r2); LOperand* obj = UseFixed(instr->object(), r2);
LOperand* key = UseFixed(instr->key(), r1); LOperand* key = UseFixed(instr->key(), r1);
@ -1911,8 +1948,10 @@ LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) {
// There are no real uses of the arguments object (we bail out in all other // There are no real uses of the arguments object.
// cases). // arguments.length and element access are supported directly on
// stack arguments, and any real arguments object use causes a bailout.
// So this value is never used.
return NULL; return NULL;
} }

View File

@ -42,8 +42,6 @@ class LCodeGen;
#define LITHIUM_ALL_INSTRUCTION_LIST(V) \ #define LITHIUM_ALL_INSTRUCTION_LIST(V) \
V(ControlInstruction) \ V(ControlInstruction) \
V(Call) \ V(Call) \
V(StoreKeyed) \
V(StoreNamed) \
LITHIUM_CONCRETE_INSTRUCTION_LIST(V) LITHIUM_CONCRETE_INSTRUCTION_LIST(V)
@ -135,6 +133,7 @@ class LCodeGen;
V(OuterContext) \ V(OuterContext) \
V(Parameter) \ V(Parameter) \
V(PixelArrayLength) \ V(PixelArrayLength) \
V(Power) \
V(PushArgument) \ V(PushArgument) \
V(RegExpLiteral) \ V(RegExpLiteral) \
V(Return) \ V(Return) \
@ -1058,6 +1057,18 @@ class LAddI: public LTemplateInstruction<1, 2, 0> {
}; };
class LPower: public LTemplateInstruction<1, 2, 0> {
public:
LPower(LOperand* left, LOperand* right) {
inputs_[0] = left;
inputs_[1] = right;
}
DECLARE_CONCRETE_INSTRUCTION(Power, "power")
DECLARE_HYDROGEN_ACCESSOR(Power)
};
class LArithmeticD: public LTemplateInstruction<1, 2, 0> { class LArithmeticD: public LTemplateInstruction<1, 2, 0> {
public: public:
LArithmeticD(Token::Value op, LOperand* left, LOperand* right) LArithmeticD(Token::Value op, LOperand* left, LOperand* right)
@ -1510,15 +1521,38 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
}; };
class LStoreNamed: public LTemplateInstruction<0, 2, 0> { class LStoreNamedField: public LTemplateInstruction<0, 2, 0> {
public: public:
LStoreNamed(LOperand* obj, LOperand* val) { LStoreNamedField(LOperand* obj, LOperand* val) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = val; inputs_[1] = val;
} }
DECLARE_INSTRUCTION(StoreNamed) DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
DECLARE_HYDROGEN_ACCESSOR(StoreNamed) DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
bool is_in_object() { return hydrogen()->is_in_object(); }
int offset() { return hydrogen()->offset(); }
bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
Handle<Map> transition() const { return hydrogen()->transition(); }
};
class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
public:
LStoreNamedGeneric(LOperand* obj, LOperand* val) {
inputs_[0] = obj;
inputs_[1] = val;
}
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
virtual void PrintDataTo(StringStream* stream); virtual void PrintDataTo(StringStream* stream);
@ -1528,40 +1562,17 @@ class LStoreNamed: public LTemplateInstruction<0, 2, 0> {
}; };
class LStoreNamedField: public LStoreNamed { class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> {
public: public:
LStoreNamedField(LOperand* obj, LOperand* val) LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) {
: LStoreNamed(obj, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
bool is_in_object() { return hydrogen()->is_in_object(); }
int offset() { return hydrogen()->offset(); }
bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
Handle<Map> transition() const { return hydrogen()->transition(); }
};
class LStoreNamedGeneric: public LStoreNamed {
public:
LStoreNamedGeneric(LOperand* obj, LOperand* val)
: LStoreNamed(obj, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
};
class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
public:
LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = key; inputs_[1] = key;
inputs_[2] = val; inputs_[2] = val;
} }
DECLARE_INSTRUCTION(StoreKeyed) DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
"store-keyed-fast-element")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement)
virtual void PrintDataTo(StringStream* stream); virtual void PrintDataTo(StringStream* stream);
@ -1571,23 +1582,21 @@ class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
}; };
class LStoreKeyedFastElement: public LStoreKeyed { class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
public: public:
LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* val) {
: LStoreKeyed(obj, key, val) {} inputs_[0] = obj;
inputs_[1] = key;
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement, inputs_[2] = val;
"store-keyed-fast-element") }
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement)
};
class LStoreKeyedGeneric: public LStoreKeyed {
public:
LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* val)
: LStoreKeyed(obj, key, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic") DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
}; };

View File

@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "arm/lithium-codegen-arm.h" #include "arm/lithium-codegen-arm.h"
#include "arm/lithium-gap-resolver-arm.h"
#include "code-stubs.h" #include "code-stubs.h"
#include "stub-cache.h" #include "stub-cache.h"
@ -54,157 +55,6 @@ class SafepointGenerator : public PostCallGenerator {
}; };
class LGapNode: public ZoneObject {
public:
explicit LGapNode(LOperand* operand)
: operand_(operand), resolved_(false), visited_id_(-1) { }
LOperand* operand() const { return operand_; }
bool IsResolved() const { return !IsAssigned() || resolved_; }
void MarkResolved() {
ASSERT(!IsResolved());
resolved_ = true;
}
int visited_id() const { return visited_id_; }
void set_visited_id(int id) {
ASSERT(id > visited_id_);
visited_id_ = id;
}
bool IsAssigned() const { return assigned_from_.is_set(); }
LGapNode* assigned_from() const { return assigned_from_.get(); }
void set_assigned_from(LGapNode* n) { assigned_from_.set(n); }
private:
LOperand* operand_;
SetOncePointer<LGapNode> assigned_from_;
bool resolved_;
int visited_id_;
};
LGapResolver::LGapResolver()
: nodes_(32),
identified_cycles_(4),
result_(16),
next_visited_id_(0) {
}
const ZoneList<LMoveOperands>* LGapResolver::Resolve(
const ZoneList<LMoveOperands>* moves,
LOperand* marker_operand) {
nodes_.Rewind(0);
identified_cycles_.Rewind(0);
result_.Rewind(0);
next_visited_id_ = 0;
for (int i = 0; i < moves->length(); ++i) {
LMoveOperands move = moves->at(i);
if (!move.IsRedundant()) RegisterMove(move);
}
for (int i = 0; i < identified_cycles_.length(); ++i) {
ResolveCycle(identified_cycles_[i], marker_operand);
}
int unresolved_nodes;
do {
unresolved_nodes = 0;
for (int j = 0; j < nodes_.length(); j++) {
LGapNode* node = nodes_[j];
if (!node->IsResolved() && node->assigned_from()->IsResolved()) {
AddResultMove(node->assigned_from(), node);
node->MarkResolved();
}
if (!node->IsResolved()) ++unresolved_nodes;
}
} while (unresolved_nodes > 0);
return &result_;
}
void LGapResolver::AddResultMove(LGapNode* from, LGapNode* to) {
AddResultMove(from->operand(), to->operand());
}
void LGapResolver::AddResultMove(LOperand* from, LOperand* to) {
result_.Add(LMoveOperands(from, to));
}
void LGapResolver::ResolveCycle(LGapNode* start, LOperand* marker_operand) {
ZoneList<LOperand*> cycle_operands(8);
cycle_operands.Add(marker_operand);
LGapNode* cur = start;
do {
cur->MarkResolved();
cycle_operands.Add(cur->operand());
cur = cur->assigned_from();
} while (cur != start);
cycle_operands.Add(marker_operand);
for (int i = cycle_operands.length() - 1; i > 0; --i) {
LOperand* from = cycle_operands[i];
LOperand* to = cycle_operands[i - 1];
AddResultMove(from, to);
}
}
bool LGapResolver::CanReach(LGapNode* a, LGapNode* b, int visited_id) {
ASSERT(a != b);
LGapNode* cur = a;
while (cur != b && cur->visited_id() != visited_id && cur->IsAssigned()) {
cur->set_visited_id(visited_id);
cur = cur->assigned_from();
}
return cur == b;
}
bool LGapResolver::CanReach(LGapNode* a, LGapNode* b) {
ASSERT(a != b);
return CanReach(a, b, next_visited_id_++);
}
void LGapResolver::RegisterMove(LMoveOperands move) {
if (move.source()->IsConstantOperand()) {
// Constant moves should be last in the machine code. Therefore add them
// first to the result set.
AddResultMove(move.source(), move.destination());
} else {
LGapNode* from = LookupNode(move.source());
LGapNode* to = LookupNode(move.destination());
if (to->IsAssigned() && to->assigned_from() == from) {
move.Eliminate();
return;
}
ASSERT(!to->IsAssigned());
if (CanReach(from, to)) {
// This introduces a cycle. Save.
identified_cycles_.Add(from);
}
to->set_assigned_from(from);
}
}
LGapNode* LGapResolver::LookupNode(LOperand* operand) {
for (int i = 0; i < nodes_.length(); ++i) {
if (nodes_[i]->operand()->Equals(operand)) return nodes_[i];
}
// No node found => create a new one.
LGapNode* result = new LGapNode(operand);
nodes_.Add(result);
return result;
}
#define __ masm()-> #define __ masm()->
bool LCodeGen::GenerateCode() { bool LCodeGen::GenerateCode() {
@ -294,6 +144,44 @@ bool LCodeGen::GeneratePrologue() {
} }
} }
// Possibly allocate a local context.
int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
if (heap_slots > 0) {
Comment(";;; Allocate local context");
// Argument to NewContext is the function, which is in r1.
__ push(r1);
if (heap_slots <= FastNewContextStub::kMaximumSlots) {
FastNewContextStub stub(heap_slots);
__ CallStub(&stub);
} else {
__ CallRuntime(Runtime::kNewContext, 1);
}
RecordSafepoint(Safepoint::kNoDeoptimizationIndex);
// Context is returned in both r0 and cp. It replaces the context
// passed to us. It's saved in the stack and kept live in cp.
__ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
// Copy any necessary parameters into the context.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Slot* slot = scope()->parameter(i)->AsSlot();
if (slot != NULL && slot->type() == Slot::CONTEXT) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
// Load parameter from stack.
__ ldr(r0, MemOperand(fp, parameter_offset));
// Store it in the context.
__ mov(r1, Operand(Context::SlotOffset(slot->index())));
__ str(r0, MemOperand(cp, r1));
// Update the write barrier. This clobbers all involved
// registers, so we have to use two more registers to avoid
// clobbering cp.
__ mov(r2, Operand(cp));
__ RecordWrite(r2, Operand(r1), r3, r0);
}
}
Comment(";;; End allocate local context");
}
// Trace the call. // Trace the call.
if (FLAG_trace) { if (FLAG_trace) {
__ CallRuntime(Runtime::kTraceEnter, 0); __ CallRuntime(Runtime::kTraceEnter, 0);
@ -464,7 +352,6 @@ Operand LCodeGen::ToOperand(LOperand* op) {
MemOperand LCodeGen::ToMemOperand(LOperand* op) const { MemOperand LCodeGen::ToMemOperand(LOperand* op) const {
// TODO(regis): Revisit.
ASSERT(!op->IsRegister()); ASSERT(!op->IsRegister());
ASSERT(!op->IsDoubleRegister()); ASSERT(!op->IsDoubleRegister());
ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot()); ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot());
@ -480,6 +367,21 @@ MemOperand LCodeGen::ToMemOperand(LOperand* op) const {
} }
MemOperand LCodeGen::ToHighMemOperand(LOperand* op) const {
ASSERT(op->IsDoubleStackSlot());
int index = op->index();
if (index >= 0) {
// Local or spill slot. Skip the frame pointer, function, context,
// and the first word of the double in the fixed part of the frame.
return MemOperand(fp, -(index + 3) * kPointerSize + kPointerSize);
} else {
// Incoming parameter. Skip the return address and the first word of
// the double.
return MemOperand(fp, -(index - 1) * kPointerSize + kPointerSize);
}
}
void LCodeGen::WriteTranslation(LEnvironment* environment, void LCodeGen::WriteTranslation(LEnvironment* environment,
Translation* translation) { Translation* translation) {
if (environment == NULL) return; if (environment == NULL) return;
@ -751,6 +653,12 @@ void LCodeGen::RecordSafepoint(LPointerMap* pointers,
} }
void LCodeGen::RecordSafepoint(int deoptimization_index) {
LPointerMap empty_pointers(RelocInfo::kNoPosition);
RecordSafepoint(&empty_pointers, deoptimization_index);
}
void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
int arguments, int arguments,
int deoptimization_index) { int deoptimization_index) {
@ -787,116 +695,7 @@ void LCodeGen::DoLabel(LLabel* label) {
void LCodeGen::DoParallelMove(LParallelMove* move) { void LCodeGen::DoParallelMove(LParallelMove* move) {
// d0 must always be a scratch register. resolver_.Resolve(move);
DoubleRegister dbl_scratch = d0;
LUnallocated marker_operand(LUnallocated::NONE);
Register core_scratch = scratch0();
bool destroys_core_scratch = false;
const ZoneList<LMoveOperands>* moves =
resolver_.Resolve(move->move_operands(), &marker_operand);
for (int i = moves->length() - 1; i >= 0; --i) {
LMoveOperands move = moves->at(i);
LOperand* from = move.source();
LOperand* to = move.destination();
ASSERT(!from->IsDoubleRegister() ||
!ToDoubleRegister(from).is(dbl_scratch));
ASSERT(!to->IsDoubleRegister() || !ToDoubleRegister(to).is(dbl_scratch));
ASSERT(!from->IsRegister() || !ToRegister(from).is(core_scratch));
ASSERT(!to->IsRegister() || !ToRegister(to).is(core_scratch));
if (from == &marker_operand) {
if (to->IsRegister()) {
__ mov(ToRegister(to), core_scratch);
ASSERT(destroys_core_scratch);
} else if (to->IsStackSlot()) {
__ str(core_scratch, ToMemOperand(to));
ASSERT(destroys_core_scratch);
} else if (to->IsDoubleRegister()) {
__ vmov(ToDoubleRegister(to), dbl_scratch);
} else {
ASSERT(to->IsDoubleStackSlot());
// TODO(regis): Why is vstr not taking a MemOperand?
// __ vstr(dbl_scratch, ToMemOperand(to));
MemOperand to_operand = ToMemOperand(to);
__ vstr(dbl_scratch, to_operand.rn(), to_operand.offset());
}
} else if (to == &marker_operand) {
if (from->IsRegister() || from->IsConstantOperand()) {
__ mov(core_scratch, ToOperand(from));
destroys_core_scratch = true;
} else if (from->IsStackSlot()) {
__ ldr(core_scratch, ToMemOperand(from));
destroys_core_scratch = true;
} else if (from->IsDoubleRegister()) {
__ vmov(dbl_scratch, ToDoubleRegister(from));
} else {
ASSERT(from->IsDoubleStackSlot());
// TODO(regis): Why is vldr not taking a MemOperand?
// __ vldr(dbl_scratch, ToMemOperand(from));
MemOperand from_operand = ToMemOperand(from);
__ vldr(dbl_scratch, from_operand.rn(), from_operand.offset());
}
} else if (from->IsConstantOperand()) {
if (to->IsRegister()) {
__ mov(ToRegister(to), ToOperand(from));
} else {
ASSERT(to->IsStackSlot());
__ mov(ip, ToOperand(from));
__ str(ip, ToMemOperand(to));
}
} else if (from->IsRegister()) {
if (to->IsRegister()) {
__ mov(ToRegister(to), ToOperand(from));
} else {
ASSERT(to->IsStackSlot());
__ str(ToRegister(from), ToMemOperand(to));
}
} else if (to->IsRegister()) {
ASSERT(from->IsStackSlot());
__ ldr(ToRegister(to), ToMemOperand(from));
} else if (from->IsStackSlot()) {
ASSERT(to->IsStackSlot());
__ ldr(ip, ToMemOperand(from));
__ str(ip, ToMemOperand(to));
} else if (from->IsDoubleRegister()) {
if (to->IsDoubleRegister()) {
__ vmov(ToDoubleRegister(to), ToDoubleRegister(from));
} else {
ASSERT(to->IsDoubleStackSlot());
// TODO(regis): Why is vstr not taking a MemOperand?
// __ vstr(dbl_scratch, ToMemOperand(to));
MemOperand to_operand = ToMemOperand(to);
__ vstr(ToDoubleRegister(from), to_operand.rn(), to_operand.offset());
}
} else if (to->IsDoubleRegister()) {
ASSERT(from->IsDoubleStackSlot());
// TODO(regis): Why is vldr not taking a MemOperand?
// __ vldr(ToDoubleRegister(to), ToMemOperand(from));
MemOperand from_operand = ToMemOperand(from);
__ vldr(ToDoubleRegister(to), from_operand.rn(), from_operand.offset());
} else {
ASSERT(to->IsDoubleStackSlot() && from->IsDoubleStackSlot());
// TODO(regis): Why is vldr not taking a MemOperand?
// __ vldr(dbl_scratch, ToMemOperand(from));
MemOperand from_operand = ToMemOperand(from);
__ vldr(dbl_scratch, from_operand.rn(), from_operand.offset());
// TODO(regis): Why is vstr not taking a MemOperand?
// __ vstr(dbl_scratch, ToMemOperand(to));
MemOperand to_operand = ToMemOperand(to);
__ vstr(dbl_scratch, to_operand.rn(), to_operand.offset());
}
}
if (destroys_core_scratch) {
__ ldr(core_scratch, MemOperand(fp, -kPointerSize));
}
LInstruction* next = GetNextInstruction();
if (next != NULL && next->IsLazyBailout()) {
int pc = masm()->pc_offset();
safepoints_.SetPcAfterGap(pc);
}
} }
@ -987,7 +786,7 @@ void LCodeGen::DoModI(LModI* instr) {
DeferredModI(LCodeGen* codegen, LModI* instr) DeferredModI(LCodeGen* codegen, LModI* instr)
: LDeferredCode(codegen), instr_(instr) { } : LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() { virtual void Generate() {
codegen()->DoDeferredGenericBinaryStub(instr_, Token::MOD); codegen()->DoDeferredBinaryOpStub(instr_, Token::MOD);
} }
private: private:
LModI* instr_; LModI* instr_;
@ -1016,7 +815,7 @@ void LCodeGen::DoModI(LModI* instr) {
__ bind(&ok); __ bind(&ok);
} }
// Try a few common cases before using the generic stub. // Try a few common cases before using the stub.
Label call_stub; Label call_stub;
const int kUnfolds = 3; const int kUnfolds = 3;
// Skip if either side is negative. // Skip if either side is negative.
@ -1044,7 +843,7 @@ void LCodeGen::DoModI(LModI* instr) {
__ and_(result, scratch, Operand(left)); __ and_(result, scratch, Operand(left));
__ bind(&call_stub); __ bind(&call_stub);
// Call the generic stub. The numbers in r0 and r1 have // Call the stub. The numbers in r0 and r1 have
// to be tagged to Smis. If that is not possible, deoptimize. // to be tagged to Smis. If that is not possible, deoptimize.
DeferredModI* deferred = new DeferredModI(this, instr); DeferredModI* deferred = new DeferredModI(this, instr);
__ TrySmiTag(left, &deoptimize, scratch); __ TrySmiTag(left, &deoptimize, scratch);
@ -1070,7 +869,7 @@ void LCodeGen::DoDivI(LDivI* instr) {
DeferredDivI(LCodeGen* codegen, LDivI* instr) DeferredDivI(LCodeGen* codegen, LDivI* instr)
: LDeferredCode(codegen), instr_(instr) { } : LDeferredCode(codegen), instr_(instr) { }
virtual void Generate() { virtual void Generate() {
codegen()->DoDeferredGenericBinaryStub(instr_, Token::DIV); codegen()->DoDeferredBinaryOpStub(instr_, Token::DIV);
} }
private: private:
LDivI* instr_; LDivI* instr_;
@ -1123,7 +922,7 @@ void LCodeGen::DoDivI(LDivI* instr) {
__ mov(result, Operand(left, ASR, 2), LeaveCC, eq); __ mov(result, Operand(left, ASR, 2), LeaveCC, eq);
__ b(eq, &done); __ b(eq, &done);
// Call the generic stub. The numbers in r0 and r1 have // Call the stub. The numbers in r0 and r1 have
// to be tagged to Smis. If that is not possible, deoptimize. // to be tagged to Smis. If that is not possible, deoptimize.
DeferredDivI* deferred = new DeferredDivI(this, instr); DeferredDivI* deferred = new DeferredDivI(this, instr);
@ -1145,19 +944,33 @@ void LCodeGen::DoDivI(LDivI* instr) {
template<int T> template<int T>
void LCodeGen::DoDeferredGenericBinaryStub(LTemplateInstruction<1, 2, T>* instr, void LCodeGen::DoDeferredBinaryOpStub(LTemplateInstruction<1, 2, T>* instr,
Token::Value op) { Token::Value op) {
Register left = ToRegister(instr->InputAt(0)); Register left = ToRegister(instr->InputAt(0));
Register right = ToRegister(instr->InputAt(1)); Register right = ToRegister(instr->InputAt(1));
__ PushSafepointRegistersAndDoubles(); __ PushSafepointRegistersAndDoubles();
GenericBinaryOpStub stub(op, OVERWRITE_LEFT, left, right); // Move left to r1 and right to r0 for the stub call.
if (left.is(r1)) {
__ Move(r0, right);
} else if (left.is(r0) && right.is(r1)) {
__ Swap(r0, r1, r2);
} else if (left.is(r0)) {
ASSERT(!right.is(r1));
__ mov(r1, r0);
__ mov(r0, right);
} else {
ASSERT(!left.is(r0) && !right.is(r0));
__ mov(r0, right);
__ mov(r1, left);
}
TypeRecordingBinaryOpStub stub(op, OVERWRITE_LEFT);
__ CallStub(&stub); __ CallStub(&stub);
RecordSafepointWithRegistersAndDoubles(instr->pointer_map(), RecordSafepointWithRegistersAndDoubles(instr->pointer_map(),
0, 0,
Safepoint::kNoDeoptimizationIndex); Safepoint::kNoDeoptimizationIndex);
// Overwrite the stored value of r0 with the result of the stub. // Overwrite the stored value of r0 with the result of the stub.
__ StoreToSafepointRegistersAndDoublesSlot(r0); __ StoreToSafepointRegistersAndDoublesSlot(r0, r0);
__ PopSafepointRegistersAndDoubles(); __ PopSafepointRegistersAndDoubles();
} }
@ -1413,7 +1226,7 @@ void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
__ vmov(r2, r3, right); __ vmov(r2, r3, right);
__ CallCFunction(ExternalReference::double_fp_operation(Token::MOD), 4); __ CallCFunction(ExternalReference::double_fp_operation(Token::MOD), 4);
// Move the result in the double result register. // Move the result in the double result register.
__ vmov(ToDoubleRegister(instr->result()), r0, r1); __ GetCFunctionDoubleResult(ToDoubleRegister(instr->result()));
// Restore r0-r3. // Restore r0-r3.
__ ldm(ia_w, sp, r0.bit() | r1.bit() | r2.bit() | r3.bit()); __ ldm(ia_w, sp, r0.bit() | r1.bit() | r2.bit() | r3.bit());
@ -1431,10 +1244,7 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
ASSERT(ToRegister(instr->InputAt(1)).is(r0)); ASSERT(ToRegister(instr->InputAt(1)).is(r0));
ASSERT(ToRegister(instr->result()).is(r0)); ASSERT(ToRegister(instr->result()).is(r0));
// TODO(regis): Implement TypeRecordingBinaryOpStub and replace current TypeRecordingBinaryOpStub stub(instr->op(), NO_OVERWRITE);
// GenericBinaryOpStub:
// TypeRecordingBinaryOpStub stub(instr->op(), NO_OVERWRITE);
GenericBinaryOpStub stub(instr->op(), NO_OVERWRITE, r1, r0);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
} }
@ -2174,7 +1984,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
__ bind(&before_push_delta); __ bind(&before_push_delta);
__ BlockConstPoolFor(kAdditionalDelta); __ BlockConstPoolFor(kAdditionalDelta);
__ mov(temp, Operand(delta * kPointerSize)); __ mov(temp, Operand(delta * kPointerSize));
__ StoreToSafepointRegisterSlot(temp); __ StoreToSafepointRegisterSlot(temp, temp);
__ Call(stub.GetCode(), RelocInfo::CODE_TARGET); __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
ASSERT_EQ(kAdditionalDelta, ASSERT_EQ(kAdditionalDelta,
masm_->InstructionsGeneratedSince(&before_push_delta)); masm_->InstructionsGeneratedSince(&before_push_delta));
@ -2182,7 +1992,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
// Put the result value into the result register slot and // Put the result value into the result register slot and
// restore all registers. // restore all registers.
__ StoreToSafepointRegisterSlot(result); __ StoreToSafepointRegisterSlot(result, result);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }
@ -2302,17 +2112,13 @@ void LCodeGen::DoStoreGlobal(LStoreGlobal* instr) {
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) { void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
Register context = ToRegister(instr->context()); Register context = ToRegister(instr->context());
Register result = ToRegister(instr->result()); Register result = ToRegister(instr->result());
__ ldr(result, __ ldr(result, ContextOperand(context, instr->slot_index()));
MemOperand(context, Context::SlotOffset(Context::FCONTEXT_INDEX)));
__ ldr(result, ContextOperand(result, instr->slot_index()));
} }
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
Register context = ToRegister(instr->context()); Register context = ToRegister(instr->context());
Register value = ToRegister(instr->value()); Register value = ToRegister(instr->value());
__ ldr(context,
MemOperand(context, Context::SlotOffset(Context::FCONTEXT_INDEX)));
__ str(value, ContextOperand(context, instr->slot_index())); __ str(value, ContextOperand(context, instr->slot_index()));
if (instr->needs_write_barrier()) { if (instr->needs_write_barrier()) {
int offset = Context::SlotOffset(instr->slot_index()); int offset = Context::SlotOffset(instr->slot_index());
@ -2715,7 +2521,7 @@ void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
// Set the pointer to the new heap number in tmp. // Set the pointer to the new heap number in tmp.
if (!tmp1.is(r0)) __ mov(tmp1, Operand(r0)); if (!tmp1.is(r0)) __ mov(tmp1, Operand(r0));
// Restore input_reg after call to runtime. // Restore input_reg after call to runtime.
__ LoadFromSafepointRegisterSlot(input); __ LoadFromSafepointRegisterSlot(input, input);
__ ldr(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset)); __ ldr(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
__ bind(&allocated); __ bind(&allocated);
@ -2726,7 +2532,7 @@ void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
__ ldr(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset)); __ ldr(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset));
__ str(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset)); __ str(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset));
__ str(tmp1, masm()->SafepointRegisterSlot(input)); __ StoreToSafepointRegisterSlot(tmp1, input);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
__ bind(&done); __ bind(&done);
@ -2843,6 +2649,30 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
} }
void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
Register result = ToRegister(instr->result());
Register scratch1 = scratch0();
Register scratch2 = result;
EmitVFPTruncate(kRoundToNearest,
double_scratch0().low(),
input,
scratch1,
scratch2);
DeoptimizeIf(ne, instr->environment());
__ vmov(result, double_scratch0().low());
// Test for -0.
Label done;
__ cmp(result, Operand(0));
__ b(ne, &done);
__ vmov(scratch1, input.high());
__ tst(scratch1, Operand(HeapNumber::kSignMask));
DeoptimizeIf(ne, instr->environment());
__ bind(&done);
}
void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) { void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); DoubleRegister input = ToDoubleRegister(instr->InputAt(0));
ASSERT(ToDoubleRegister(instr->result()).is(input)); ASSERT(ToDoubleRegister(instr->result()).is(input));
@ -2850,6 +2680,64 @@ void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
} }
void LCodeGen::DoPower(LPower* instr) {
LOperand* left = instr->InputAt(0);
LOperand* right = instr->InputAt(1);
Register scratch = scratch0();
DoubleRegister result_reg = ToDoubleRegister(instr->result());
Representation exponent_type = instr->hydrogen()->right()->representation();
if (exponent_type.IsDouble()) {
// Prepare arguments and call C function.
__ PrepareCallCFunction(4, scratch);
__ vmov(r0, r1, ToDoubleRegister(left));
__ vmov(r2, r3, ToDoubleRegister(right));
__ CallCFunction(ExternalReference::power_double_double_function(), 4);
} else if (exponent_type.IsInteger32()) {
ASSERT(ToRegister(right).is(r0));
// Prepare arguments and call C function.
__ PrepareCallCFunction(4, scratch);
__ mov(r2, ToRegister(right));
__ vmov(r0, r1, ToDoubleRegister(left));
__ CallCFunction(ExternalReference::power_double_int_function(), 4);
} else {
ASSERT(exponent_type.IsTagged());
ASSERT(instr->hydrogen()->left()->representation().IsDouble());
Register right_reg = ToRegister(right);
// Check for smi on the right hand side.
Label non_smi, call;
__ JumpIfNotSmi(right_reg, &non_smi);
// Untag smi and convert it to a double.
__ SmiUntag(right_reg);
SwVfpRegister single_scratch = double_scratch0().low();
__ vmov(single_scratch, right_reg);
__ vcvt_f64_s32(result_reg, single_scratch);
__ jmp(&call);
// Heap number map check.
__ bind(&non_smi);
__ ldr(scratch, FieldMemOperand(right_reg, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
__ cmp(scratch, Operand(ip));
DeoptimizeIf(ne, instr->environment());
int32_t value_offset = HeapNumber::kValueOffset - kHeapObjectTag;
__ add(scratch, right_reg, Operand(value_offset));
__ vldr(result_reg, scratch, 0);
// Prepare arguments and call C function.
__ bind(&call);
__ PrepareCallCFunction(4, scratch);
__ vmov(r0, r1, ToDoubleRegister(left));
__ vmov(r2, r3, result_reg);
__ CallCFunction(ExternalReference::power_double_double_function(), 4);
}
// Store the result in the result register.
__ GetCFunctionDoubleResult(result_reg);
}
void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) { void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
switch (instr->op()) { switch (instr->op()) {
case kMathAbs: case kMathAbs:
@ -2858,6 +2746,9 @@ void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
case kMathFloor: case kMathFloor:
DoMathFloor(instr); DoMathFloor(instr);
break; break;
case kMathRound:
DoMathRound(instr);
break;
case kMathSqrt: case kMathSqrt:
DoMathSqrt(instr); DoMathSqrt(instr);
break; break;
@ -3157,8 +3048,7 @@ void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
__ AbortIfNotSmi(r0); __ AbortIfNotSmi(r0);
} }
__ SmiUntag(r0); __ SmiUntag(r0);
MemOperand result_stack_slot = masm()->SafepointRegisterSlot(result); __ StoreToSafepointRegisterSlot(r0, result);
__ str(r0, result_stack_slot);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }
@ -3239,9 +3129,7 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
// register is stored, as this register is in the pointer map, but contains an // register is stored, as this register is in the pointer map, but contains an
// integer value. // integer value.
__ mov(ip, Operand(0)); __ mov(ip, Operand(0));
int reg_stack_index = __ SafepointRegisterStackIndex(reg.code()); __ StoreToSafepointRegisterSlot(ip, reg);
__ str(ip, MemOperand(sp, reg_stack_index * kPointerSize));
__ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
RecordSafepointWithRegisters( RecordSafepointWithRegisters(
instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
@ -3252,7 +3140,7 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
__ bind(&done); __ bind(&done);
__ sub(ip, reg, Operand(kHeapObjectTag)); __ sub(ip, reg, Operand(kHeapObjectTag));
__ vstr(dbl_scratch, ip, HeapNumber::kValueOffset); __ vstr(dbl_scratch, ip, HeapNumber::kValueOffset);
__ str(reg, MemOperand(sp, reg_stack_index * kPointerSize)); __ StoreToSafepointRegisterSlot(reg, reg);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }
@ -3297,8 +3185,7 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) {
__ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
RecordSafepointWithRegisters( RecordSafepointWithRegisters(
instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
int reg_stack_index = __ SafepointRegisterStackIndex(reg.code()); __ StoreToSafepointRegisterSlot(r0, reg);
__ str(r0, MemOperand(sp, reg_stack_index * kPointerSize));
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }

View File

@ -29,7 +29,7 @@
#define V8_ARM_LITHIUM_CODEGEN_ARM_H_ #define V8_ARM_LITHIUM_CODEGEN_ARM_H_
#include "arm/lithium-arm.h" #include "arm/lithium-arm.h"
#include "arm/lithium-gap-resolver-arm.h"
#include "deoptimizer.h" #include "deoptimizer.h"
#include "safepoint-table.h" #include "safepoint-table.h"
#include "scopes.h" #include "scopes.h"
@ -39,31 +39,8 @@ namespace internal {
// Forward declarations. // Forward declarations.
class LDeferredCode; class LDeferredCode;
class LGapNode;
class SafepointGenerator; class SafepointGenerator;
class LGapResolver BASE_EMBEDDED {
public:
LGapResolver();
const ZoneList<LMoveOperands>* Resolve(const ZoneList<LMoveOperands>* moves,
LOperand* marker_operand);
private:
LGapNode* LookupNode(LOperand* operand);
bool CanReach(LGapNode* a, LGapNode* b, int visited_id);
bool CanReach(LGapNode* a, LGapNode* b);
void RegisterMove(LMoveOperands move);
void AddResultMove(LOperand* from, LOperand* to);
void AddResultMove(LGapNode* from, LGapNode* to);
void ResolveCycle(LGapNode* start, LOperand* marker_operand);
ZoneList<LGapNode*> nodes_;
ZoneList<LGapNode*> identified_cycles_;
ZoneList<LMoveOperands> result_;
int next_visited_id_;
};
class LCodeGen BASE_EMBEDDED { class LCodeGen BASE_EMBEDDED {
public: public:
LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info) LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
@ -79,10 +56,35 @@ class LCodeGen BASE_EMBEDDED {
scope_(chunk->graph()->info()->scope()), scope_(chunk->graph()->info()->scope()),
status_(UNUSED), status_(UNUSED),
deferred_(8), deferred_(8),
osr_pc_offset_(-1) { osr_pc_offset_(-1),
resolver_(this) {
PopulateDeoptimizationLiteralsWithInlinedFunctions(); PopulateDeoptimizationLiteralsWithInlinedFunctions();
} }
// Simple accessors.
MacroAssembler* masm() const { return masm_; }
// Support for converting LOperands to assembler types.
// LOperand must be a register.
Register ToRegister(LOperand* op) const;
// LOperand is loaded into scratch, unless already a register.
Register EmitLoadRegister(LOperand* op, Register scratch);
// LOperand must be a double register.
DoubleRegister ToDoubleRegister(LOperand* op) const;
// LOperand is loaded into dbl_scratch, unless already a double register.
DoubleRegister EmitLoadDoubleRegister(LOperand* op,
SwVfpRegister flt_scratch,
DoubleRegister dbl_scratch);
int ToInteger32(LConstantOperand* op) const;
Operand ToOperand(LOperand* op);
MemOperand ToMemOperand(LOperand* op) const;
// Returns a MemOperand pointing to the high word of a DoubleStackSlot.
MemOperand ToHighMemOperand(LOperand* op) const;
// Try to generate code for the entire chunk, but it may fail if the // Try to generate code for the entire chunk, but it may fail if the
// chunk contains constructs we cannot handle. Returns true if the // chunk contains constructs we cannot handle. Returns true if the
// code generation attempt succeeded. // code generation attempt succeeded.
@ -94,8 +96,8 @@ class LCodeGen BASE_EMBEDDED {
// Deferred code support. // Deferred code support.
template<int T> template<int T>
void DoDeferredGenericBinaryStub(LTemplateInstruction<1, 2, T>* instr, void DoDeferredBinaryOpStub(LTemplateInstruction<1, 2, T>* instr,
Token::Value op); Token::Value op);
void DoDeferredNumberTagD(LNumberTagD* instr); void DoDeferredNumberTagD(LNumberTagD* instr);
void DoDeferredNumberTagI(LNumberTagI* instr); void DoDeferredNumberTagI(LNumberTagI* instr);
void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredTaggedToI(LTaggedToI* instr);
@ -136,7 +138,6 @@ class LCodeGen BASE_EMBEDDED {
LChunk* chunk() const { return chunk_; } LChunk* chunk() const { return chunk_; }
Scope* scope() const { return scope_; } Scope* scope() const { return scope_; }
HGraph* graph() const { return chunk_->graph(); } HGraph* graph() const { return chunk_->graph(); }
MacroAssembler* masm() const { return masm_; }
Register scratch0() { return r9; } Register scratch0() { return r9; }
DwVfpRegister double_scratch0() { return d0; } DwVfpRegister double_scratch0() { return d0; }
@ -202,24 +203,6 @@ class LCodeGen BASE_EMBEDDED {
Register ToRegister(int index) const; Register ToRegister(int index) const;
DoubleRegister ToDoubleRegister(int index) const; DoubleRegister ToDoubleRegister(int index) const;
// LOperand must be a register.
Register ToRegister(LOperand* op) const;
// LOperand is loaded into scratch, unless already a register.
Register EmitLoadRegister(LOperand* op, Register scratch);
// LOperand must be a double register.
DoubleRegister ToDoubleRegister(LOperand* op) const;
// LOperand is loaded into dbl_scratch, unless already a double register.
DoubleRegister EmitLoadDoubleRegister(LOperand* op,
SwVfpRegister flt_scratch,
DoubleRegister dbl_scratch);
int ToInteger32(LConstantOperand* op) const;
Operand ToOperand(LOperand* op);
MemOperand ToMemOperand(LOperand* op) const;
// Specific math operations - used from DoUnaryMathOperation. // Specific math operations - used from DoUnaryMathOperation.
void EmitIntegerMathAbs(LUnaryMathOperation* instr); void EmitIntegerMathAbs(LUnaryMathOperation* instr);
void DoMathAbs(LUnaryMathOperation* instr); void DoMathAbs(LUnaryMathOperation* instr);
@ -229,6 +212,7 @@ class LCodeGen BASE_EMBEDDED {
Register scratch1, Register scratch1,
Register scratch2); Register scratch2);
void DoMathFloor(LUnaryMathOperation* instr); void DoMathFloor(LUnaryMathOperation* instr);
void DoMathRound(LUnaryMathOperation* instr);
void DoMathSqrt(LUnaryMathOperation* instr); void DoMathSqrt(LUnaryMathOperation* instr);
// Support for recording safepoint and position information. // Support for recording safepoint and position information.
@ -237,6 +221,7 @@ class LCodeGen BASE_EMBEDDED {
int arguments, int arguments,
int deoptimization_index); int deoptimization_index);
void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); void RecordSafepoint(LPointerMap* pointers, int deoptimization_index);
void RecordSafepoint(int deoptimization_index);
void RecordSafepointWithRegisters(LPointerMap* pointers, void RecordSafepointWithRegisters(LPointerMap* pointers,
int arguments, int arguments,
int deoptimization_index); int deoptimization_index);

View File

@ -0,0 +1,303 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "arm/lithium-gap-resolver-arm.h"
#include "arm/lithium-codegen-arm.h"
namespace v8 {
namespace internal {
static const Register kSavedValueRegister = { 9 };
static const DoubleRegister kSavedDoubleValueRegister = { 0 };
LGapResolver::LGapResolver(LCodeGen* owner)
: cgen_(owner), moves_(32), root_index_(0), in_cycle_(false),
saved_destination_(NULL) { }
void LGapResolver::Resolve(LParallelMove* parallel_move) {
ASSERT(moves_.is_empty());
// Build up a worklist of moves.
BuildInitialMoveList(parallel_move);
for (int i = 0; i < moves_.length(); ++i) {
LMoveOperands move = moves_[i];
// Skip constants to perform them last. They don't block other moves
// and skipping such moves with register destinations keeps those
// registers free for the whole algorithm.
if (!move.IsEliminated() && !move.source()->IsConstantOperand()) {
root_index_ = i; // Any cycle is found when by reaching this move again.
PerformMove(i);
if (in_cycle_) {
RestoreValue();
}
}
}
// Perform the moves with constant sources.
for (int i = 0; i < moves_.length(); ++i) {
if (!moves_[i].IsEliminated()) {
ASSERT(moves_[i].source()->IsConstantOperand());
EmitMove(i);
}
}
moves_.Rewind(0);
}
void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) {
// Perform a linear sweep of the moves to add them to the initial list of
// moves to perform, ignoring any move that is redundant (the source is
// the same as the destination, the destination is ignored and
// unallocated, or the move was already eliminated).
const ZoneList<LMoveOperands>* moves = parallel_move->move_operands();
for (int i = 0; i < moves->length(); ++i) {
LMoveOperands move = moves->at(i);
if (!move.IsRedundant()) moves_.Add(move);
}
Verify();
}
void LGapResolver::PerformMove(int index) {
// Each call to this function performs a move and deletes it from the move
// graph. We first recursively perform any move blocking this one. We
// mark a move as "pending" on entry to PerformMove in order to detect
// cycles in the move graph.
// We can only find a cycle, when doing a depth-first traversal of moves,
// be encountering the starting move again. So by spilling the source of
// the starting move, we break the cycle. All moves are then unblocked,
// and the starting move is completed by writing the spilled value to
// its destination. All other moves from the spilled source have been
// completed prior to breaking the cycle.
// An additional complication is that moves to MemOperands with large
// offsets (more than 1K or 4K) require us to spill this spilled value to
// the stack, to free up the register.
ASSERT(!moves_[index].IsPending());
ASSERT(!moves_[index].IsRedundant());
// Clear this move's destination to indicate a pending move. The actual
// destination is saved in a stack allocated local. Multiple moves can
// be pending because this function is recursive.
ASSERT(moves_[index].source() != NULL); // Or else it will look eliminated.
LOperand* destination = moves_[index].destination();
moves_[index].set_destination(NULL);
// Perform a depth-first traversal of the move graph to resolve
// dependencies. Any unperformed, unpending move with a source the same
// as this one's destination blocks this one so recursively perform all
// such moves.
for (int i = 0; i < moves_.length(); ++i) {
LMoveOperands other_move = moves_[i];
if (other_move.Blocks(destination) && !other_move.IsPending()) {
PerformMove(i);
// If there is a blocking, pending move it must be moves_[root_index_]
// and all other moves with the same source as moves_[root_index_] are
// sucessfully executed (because they are cycle-free) by this loop.
}
}
// We are about to resolve this move and don't need it marked as
// pending, so restore its destination.
moves_[index].set_destination(destination);
// The move may be blocked on a pending move, which must be the starting move.
// In this case, we have a cycle, and we save the source of this move to
// a scratch register to break it.
LMoveOperands other_move = moves_[root_index_];
if (other_move.Blocks(destination)) {
ASSERT(other_move.IsPending());
BreakCycle(index);
return;
}
// This move is no longer blocked.
EmitMove(index);
}
void LGapResolver::Verify() {
#ifdef ENABLE_SLOW_ASSERTS
// No operand should be the destination for more than one move.
for (int i = 0; i < moves_.length(); ++i) {
LOperand* destination = moves_[i].destination();
for (int j = i + 1; j < moves_.length(); ++j) {
SLOW_ASSERT(!destination->Equals(moves_[j].destination()));
}
}
#endif
}
#define __ ACCESS_MASM(cgen_->masm())
void LGapResolver::BreakCycle(int index) {
// We save in a register the value that should end up in the source of
// moves_[root_index]. After performing all moves in the tree rooted
// in that move, we save the value to that source.
ASSERT(moves_[index].destination()->Equals(moves_[root_index_].source()));
ASSERT(!in_cycle_);
in_cycle_ = true;
LOperand* source = moves_[index].source();
saved_destination_ = moves_[index].destination();
if (source->IsRegister()) {
__ mov(kSavedValueRegister, cgen_->ToRegister(source));
} else if (source->IsStackSlot()) {
__ ldr(kSavedValueRegister, cgen_->ToMemOperand(source));
} else if (source->IsDoubleRegister()) {
__ vmov(kSavedDoubleValueRegister, cgen_->ToDoubleRegister(source));
} else if (source->IsDoubleStackSlot()) {
__ vldr(kSavedDoubleValueRegister, cgen_->ToMemOperand(source));
} else {
UNREACHABLE();
}
// This move will be done by restoring the saved value to the destination.
moves_[index].Eliminate();
}
void LGapResolver::RestoreValue() {
ASSERT(in_cycle_);
ASSERT(saved_destination_ != NULL);
// Spilled value is in kSavedValueRegister or kSavedDoubleValueRegister.
if (saved_destination_->IsRegister()) {
__ mov(cgen_->ToRegister(saved_destination_), kSavedValueRegister);
} else if (saved_destination_->IsStackSlot()) {
__ str(kSavedValueRegister, cgen_->ToMemOperand(saved_destination_));
} else if (saved_destination_->IsDoubleRegister()) {
__ vmov(cgen_->ToDoubleRegister(saved_destination_),
kSavedDoubleValueRegister);
} else if (saved_destination_->IsDoubleStackSlot()) {
__ vstr(kSavedDoubleValueRegister,
cgen_->ToMemOperand(saved_destination_));
} else {
UNREACHABLE();
}
in_cycle_ = false;
saved_destination_ = NULL;
}
void LGapResolver::EmitMove(int index) {
LOperand* source = moves_[index].source();
LOperand* destination = moves_[index].destination();
// Dispatch on the source and destination operand kinds. Not all
// combinations are possible.
if (source->IsRegister()) {
Register source_register = cgen_->ToRegister(source);
if (destination->IsRegister()) {
__ mov(cgen_->ToRegister(destination), source_register);
} else {
ASSERT(destination->IsStackSlot());
__ str(source_register, cgen_->ToMemOperand(destination));
}
} else if (source->IsStackSlot()) {
MemOperand source_operand = cgen_->ToMemOperand(source);
if (destination->IsRegister()) {
__ ldr(cgen_->ToRegister(destination), source_operand);
} else {
ASSERT(destination->IsStackSlot());
MemOperand destination_operand = cgen_->ToMemOperand(destination);
if (in_cycle_) {
if (!destination_operand.OffsetIsUint12Encodable()) {
// ip is overwritten while saving the value to the destination.
// Therefore we can't use ip. It is OK if the read from the source
// destroys ip, since that happens before the value is read.
__ vldr(kSavedDoubleValueRegister.low(), source_operand);
__ vstr(kSavedDoubleValueRegister.low(), destination_operand);
} else {
__ ldr(ip, source_operand);
__ str(ip, destination_operand);
}
} else {
__ ldr(kSavedValueRegister, source_operand);
__ str(kSavedValueRegister, destination_operand);
}
}
} else if (source->IsConstantOperand()) {
Operand source_operand = cgen_->ToOperand(source);
if (destination->IsRegister()) {
__ mov(cgen_->ToRegister(destination), source_operand);
} else {
ASSERT(destination->IsStackSlot());
ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone.
MemOperand destination_operand = cgen_->ToMemOperand(destination);
__ mov(kSavedValueRegister, source_operand);
__ str(kSavedValueRegister, cgen_->ToMemOperand(destination));
}
} else if (source->IsDoubleRegister()) {
DoubleRegister source_register = cgen_->ToDoubleRegister(source);
if (destination->IsDoubleRegister()) {
__ vmov(cgen_->ToDoubleRegister(destination), source_register);
} else {
ASSERT(destination->IsDoubleStackSlot());
MemOperand destination_operand = cgen_->ToMemOperand(destination);
__ vstr(source_register, destination_operand);
}
} else if (source->IsDoubleStackSlot()) {
MemOperand source_operand = cgen_->ToMemOperand(source);
if (destination->IsDoubleRegister()) {
__ vldr(cgen_->ToDoubleRegister(destination), source_operand);
} else {
ASSERT(destination->IsDoubleStackSlot());
MemOperand destination_operand = cgen_->ToMemOperand(destination);
if (in_cycle_) {
// kSavedDoubleValueRegister was used to break the cycle,
// but kSavedValueRegister is free.
MemOperand source_high_operand =
cgen_->ToHighMemOperand(source);
MemOperand destination_high_operand =
cgen_->ToHighMemOperand(destination);
__ ldr(kSavedValueRegister, source_operand);
__ str(kSavedValueRegister, destination_operand);
__ ldr(kSavedValueRegister, source_high_operand);
__ str(kSavedValueRegister, destination_high_operand);
} else {
__ vldr(kSavedDoubleValueRegister, source_operand);
__ vstr(kSavedDoubleValueRegister, destination_operand);
}
}
} else {
UNREACHABLE();
}
moves_[index].Eliminate();
}
#undef __
} } // namespace v8::internal

View File

@ -0,0 +1,84 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_ARM_LITHIUM_GAP_RESOLVER_ARM_H_
#define V8_ARM_LITHIUM_GAP_RESOLVER_ARM_H_
#include "v8.h"
#include "lithium.h"
namespace v8 {
namespace internal {
class LCodeGen;
class LGapResolver;
class LGapResolver BASE_EMBEDDED {
public:
explicit LGapResolver(LCodeGen* owner);
// Resolve a set of parallel moves, emitting assembler instructions.
void Resolve(LParallelMove* parallel_move);
private:
// Build the initial list of moves.
void BuildInitialMoveList(LParallelMove* parallel_move);
// Perform the move at the moves_ index in question (possibly requiring
// other moves to satisfy dependencies).
void PerformMove(int index);
// If a cycle is found in the series of moves, save the blocking value to
// a scratch register. The cycle must be found by hitting the root of the
// depth-first search.
void BreakCycle(int index);
// After a cycle has been resolved, restore the value from the scratch
// register to its proper destination.
void RestoreValue();
// Emit a move and remove it from the move graph.
void EmitMove(int index);
// Verify the move list before performing moves.
void Verify();
LCodeGen* cgen_;
// List of moves not yet resolved.
ZoneList<LMoveOperands> moves_;
int root_index_;
bool in_cycle_;
LOperand* saved_destination_;
};
} } // namespace v8::internal
#endif // V8_ARM_LITHIUM_GAP_RESOLVER_ARM_H_

View File

@ -485,18 +485,19 @@ void MacroAssembler::PopSafepointRegistersAndDoubles() {
PopSafepointRegisters(); PopSafepointRegisters();
} }
void MacroAssembler::StoreToSafepointRegistersAndDoublesSlot(Register reg) { void MacroAssembler::StoreToSafepointRegistersAndDoublesSlot(Register src,
str(reg, SafepointRegistersAndDoublesSlot(reg)); Register dst) {
str(src, SafepointRegistersAndDoublesSlot(dst));
} }
void MacroAssembler::StoreToSafepointRegisterSlot(Register reg) { void MacroAssembler::StoreToSafepointRegisterSlot(Register src, Register dst) {
str(reg, SafepointRegisterSlot(reg)); str(src, SafepointRegisterSlot(dst));
} }
void MacroAssembler::LoadFromSafepointRegisterSlot(Register reg) { void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
ldr(reg, SafepointRegisterSlot(reg)); ldr(dst, SafepointRegisterSlot(src));
} }
@ -745,6 +746,14 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles,
} }
} }
void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
#if !defined(USE_ARM_EABI)
UNREACHABLE();
#else
vmov(dst, r0, r1);
#endif
}
void MacroAssembler::InvokePrologue(const ParameterCount& expected, void MacroAssembler::InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual, const ParameterCount& actual,
@ -2154,11 +2163,22 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
ldr(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX))); ldr(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset)); ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
} }
// The context may be an intermediate context, not a function context. } else {
ldr(dst, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX))); // Slot is in the current function context. Move it into the
} else { // Slot is in the current function context. // destination register in case we store into it (the write barrier
// The context may be an intermediate context, not a function context. // cannot be allowed to destroy the context in esi).
ldr(dst, MemOperand(cp, Context::SlotOffset(Context::FCONTEXT_INDEX))); mov(dst, cp);
}
// We should not have found a 'with' context by walking the context chain
// (i.e., the static scope chain and runtime context chain do not agree).
// A variable occurring in such a scope should have slot type LOOKUP and
// not CONTEXT.
if (FLAG_debug_code) {
ldr(ip, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
cmp(dst, ip);
Check(eq, "Yo dawg, I heard you liked function contexts "
"so I put function contexts in all your contexts");
} }
} }

View File

@ -240,12 +240,13 @@ class MacroAssembler: public Assembler {
void PopSafepointRegisters(); void PopSafepointRegisters();
void PushSafepointRegistersAndDoubles(); void PushSafepointRegistersAndDoubles();
void PopSafepointRegistersAndDoubles(); void PopSafepointRegistersAndDoubles();
void StoreToSafepointRegisterSlot(Register reg); // Store value in register src in the safepoint stack slot for
void StoreToSafepointRegistersAndDoublesSlot(Register reg); // register dst.
void LoadFromSafepointRegisterSlot(Register reg); void StoreToSafepointRegisterSlot(Register src, Register dst);
static int SafepointRegisterStackIndex(int reg_code); void StoreToSafepointRegistersAndDoublesSlot(Register src, Register dst);
static MemOperand SafepointRegisterSlot(Register reg); // Load the value of the src register from its safepoint stack slot
static MemOperand SafepointRegistersAndDoublesSlot(Register reg); // into register dst.
void LoadFromSafepointRegisterSlot(Register dst, Register src);
// Load two consecutive registers with two consecutive memory locations. // Load two consecutive registers with two consecutive memory locations.
void Ldrd(Register dst1, void Ldrd(Register dst1,
@ -683,6 +684,8 @@ class MacroAssembler: public Assembler {
void CallCFunction(ExternalReference function, int num_arguments); void CallCFunction(ExternalReference function, int num_arguments);
void CallCFunction(Register function, int num_arguments); void CallCFunction(Register function, int num_arguments);
void GetCFunctionDoubleResult(const DoubleRegister dst);
// Calls an API function. Allocates HandleScope, extracts returned value // Calls an API function. Allocates HandleScope, extracts returned value
// from handle and propagates exceptions. Restores context. // from handle and propagates exceptions. Restores context.
// stack_space - space to be unwound on exit (includes the call js // stack_space - space to be unwound on exit (includes the call js
@ -883,10 +886,19 @@ class MacroAssembler: public Assembler {
Register scratch1, Register scratch1,
Register scratch2); Register scratch2);
// Compute memory operands for safepoint stack slots.
static int SafepointRegisterStackIndex(int reg_code);
MemOperand SafepointRegisterSlot(Register reg);
MemOperand SafepointRegistersAndDoublesSlot(Register reg);
bool generating_stub_; bool generating_stub_;
bool allow_stub_calls_; bool allow_stub_calls_;
// This handle will be patched with the code object on installation. // This handle will be patched with the code object on installation.
Handle<Object> code_object_; Handle<Object> code_object_;
// Needs access to SafepointRegisterStackIndex for optimized frame
// traversal.
friend class OptimizedFrame;
}; };

View File

@ -2332,8 +2332,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
break; break;
case STRING_CHECK: case STRING_CHECK:
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
// Check that the object is a two-byte string or a symbol. // Check that the object is a two-byte string or a symbol.
@ -2348,8 +2349,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
break; break;
case NUMBER_CHECK: { case NUMBER_CHECK: {
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
Label fast; Label fast;
@ -2369,8 +2371,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
} }
case BOOLEAN_CHECK: { case BOOLEAN_CHECK: {
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
Label fast; Label fast;

View File

@ -228,6 +228,7 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) {
WriteTaggedPC(pc_delta, kEmbeddedObjectTag); WriteTaggedPC(pc_delta, kEmbeddedObjectTag);
} else if (rmode == RelocInfo::CODE_TARGET) { } else if (rmode == RelocInfo::CODE_TARGET) {
WriteTaggedPC(pc_delta, kCodeTargetTag); WriteTaggedPC(pc_delta, kCodeTargetTag);
ASSERT(begin_pos - pos_ <= RelocInfo::kMaxCallSize);
} else if (RelocInfo::IsPosition(rmode)) { } else if (RelocInfo::IsPosition(rmode)) {
// Use signed delta-encoding for data. // Use signed delta-encoding for data.
intptr_t data_delta = rinfo->data() - last_data_; intptr_t data_delta = rinfo->data() - last_data_;
@ -251,6 +252,7 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) {
WriteExtraTaggedPC(pc_delta, kPCJumpTag); WriteExtraTaggedPC(pc_delta, kPCJumpTag);
WriteExtraTaggedData(rinfo->data() - last_data_, kCommentTag); WriteExtraTaggedData(rinfo->data() - last_data_, kCommentTag);
last_data_ = rinfo->data(); last_data_ = rinfo->data();
ASSERT(begin_pos - pos_ == RelocInfo::kRelocCommentSize);
} else { } else {
// For all other modes we simply use the mode as the extra tag. // For all other modes we simply use the mode as the extra tag.
// None of these modes need a data component. // None of these modes need a data component.
@ -850,12 +852,14 @@ double power_double_double(double x, double y) {
ExternalReference ExternalReference::power_double_double_function() { ExternalReference ExternalReference::power_double_double_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(power_double_double))); return ExternalReference(Redirect(FUNCTION_ADDR(power_double_double),
FP_RETURN_CALL));
} }
ExternalReference ExternalReference::power_double_int_function() { ExternalReference ExternalReference::power_double_int_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(power_double_int))); return ExternalReference(Redirect(FUNCTION_ADDR(power_double_int),
FP_RETURN_CALL));
} }

View File

@ -184,6 +184,14 @@ class RelocInfo BASE_EMBEDDED {
// we do not normally record relocation info. // we do not normally record relocation info.
static const char* kFillerCommentString; static const char* kFillerCommentString;
// The size of a comment is equal to tree bytes for the extra tagged pc +
// the tag for the data, and kPointerSize for the actual pointer to the
// comment.
static const int kRelocCommentSize = 3 + kPointerSize;
// The maximum size for a call instruction including pc-jump.
static const int kMaxCallSize = 6;
enum Mode { enum Mode {
// Please note the order is important (see IsCodeTarget, IsGCRelocMode). // Please note the order is important (see IsCodeTarget, IsGCRelocMode).
CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor. CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor.

View File

@ -261,10 +261,8 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
Handle<SharedFunctionInfo> shared = info->shared_info(); Handle<SharedFunctionInfo> shared = info->shared_info();
shared->EnableDeoptimizationSupport(*unoptimized.code()); shared->EnableDeoptimizationSupport(*unoptimized.code());
// The existing unoptimized code was replaced with the new one. // The existing unoptimized code was replaced with the new one.
Compiler::RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, Compiler::RecordFunctionCompilation(
Handle<String>(shared->DebugName()), Logger::LAZY_COMPILE_TAG, &unoptimized, shared);
shared->start_position(),
&unoptimized);
} }
} }
@ -273,7 +271,7 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
// optimizable marker in the code object and optimize anyway. This // optimizable marker in the code object and optimize anyway. This
// is safe as long as the unoptimized code has deoptimization // is safe as long as the unoptimized code has deoptimization
// support. // support.
ASSERT(FLAG_always_opt || info->shared_info()->code()->optimizable()); ASSERT(FLAG_always_opt || code->optimizable());
ASSERT(info->shared_info()->has_deoptimization_support()); ASSERT(info->shared_info()->has_deoptimization_support());
if (FLAG_trace_hydrogen) { if (FLAG_trace_hydrogen) {
@ -283,8 +281,7 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
} }
TypeFeedbackOracle oracle( TypeFeedbackOracle oracle(
Handle<Code>(info->shared_info()->code()), code, Handle<Context>(info->closure()->context()->global_context()));
Handle<Context>(info->closure()->context()->global_context()));
HGraphBuilder builder(&oracle); HGraphBuilder builder(&oracle);
HPhase phase(HPhase::kTotal); HPhase phase(HPhase::kTotal);
HGraph* graph = builder.CreateGraph(info); HGraph* graph = builder.CreateGraph(info);
@ -294,9 +291,9 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
} }
if (graph != NULL && FLAG_build_lithium) { if (graph != NULL && FLAG_build_lithium) {
Handle<Code> code = graph->Compile(); Handle<Code> optimized_code = graph->Compile();
if (!code.is_null()) { if (!optimized_code.is_null()) {
info->SetCode(code); info->SetCode(optimized_code);
FinishOptimization(info->closure(), start); FinishOptimization(info->closure(), start);
return true; return true;
} }
@ -415,13 +412,25 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
return Handle<SharedFunctionInfo>::null(); return Handle<SharedFunctionInfo>::null();
} }
// Allocate function.
ASSERT(!info->code().is_null()); ASSERT(!info->code().is_null());
Handle<SharedFunctionInfo> result =
Factory::NewSharedFunctionInfo(
lit->name(),
lit->materialized_literal_count(),
info->code(),
SerializedScopeInfo::Create(info->scope()));
ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
Compiler::SetFunctionInfo(result, lit, true, script);
if (script->name()->IsString()) { if (script->name()->IsString()) {
PROFILE(CodeCreateEvent( PROFILE(CodeCreateEvent(
info->is_eval() info->is_eval()
? Logger::EVAL_TAG ? Logger::EVAL_TAG
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script), : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*info->code(), *info->code(),
*result,
String::cast(script->name()))); String::cast(script->name())));
GDBJIT(AddCode(Handle<String>(String::cast(script->name())), GDBJIT(AddCode(Handle<String>(String::cast(script->name())),
script, script,
@ -432,21 +441,11 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
? Logger::EVAL_TAG ? Logger::EVAL_TAG
: Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script), : Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*info->code(), *info->code(),
"")); *result,
Heap::empty_string()));
GDBJIT(AddCode(Handle<String>(), script, info->code())); GDBJIT(AddCode(Handle<String>(), script, info->code()));
} }
// Allocate function.
Handle<SharedFunctionInfo> result =
Factory::NewSharedFunctionInfo(
lit->name(),
lit->materialized_literal_count(),
info->code(),
SerializedScopeInfo::Create(info->scope()));
ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
Compiler::SetFunctionInfo(result, lit, true, script);
// Hint to the runtime system used when allocating space for initial // Hint to the runtime system used when allocating space for initial
// property space by setting the expected number of properties for // property space by setting the expected number of properties for
// the instances of the function. // the instances of the function.
@ -613,10 +612,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
ASSERT(!info->code().is_null()); ASSERT(!info->code().is_null());
Handle<Code> code = info->code(); Handle<Code> code = info->code();
Handle<JSFunction> function = info->closure(); Handle<JSFunction> function = info->closure();
RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, RecordFunctionCompilation(Logger::LAZY_COMPILE_TAG, info, shared);
Handle<String>(shared->DebugName()),
shared->start_position(),
info);
if (info->IsOptimizing()) { if (info->IsOptimizing()) {
function->ReplaceCode(*code); function->ReplaceCode(*code);
@ -724,10 +720,6 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
ASSERT(!info.code().is_null()); ASSERT(!info.code().is_null());
// Function compilation complete. // Function compilation complete.
RecordFunctionCompilation(Logger::FUNCTION_TAG,
literal->debug_name(),
literal->start_position(),
&info);
scope_info = SerializedScopeInfo::Create(info.scope()); scope_info = SerializedScopeInfo::Create(info.scope());
} }
@ -738,6 +730,7 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal,
info.code(), info.code(),
scope_info); scope_info);
SetFunctionInfo(result, literal, false, script); SetFunctionInfo(result, literal, false, script);
RecordFunctionCompilation(Logger::FUNCTION_TAG, &info, result);
result->set_allows_lazy_compilation(allow_lazy); result->set_allows_lazy_compilation(allow_lazy);
// Set the expected number of properties for instances and return // Set the expected number of properties for instances and return
@ -776,28 +769,31 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag, void Compiler::RecordFunctionCompilation(Logger::LogEventsAndTags tag,
Handle<String> name, CompilationInfo* info,
int start_position, Handle<SharedFunctionInfo> shared) {
CompilationInfo* info) { // SharedFunctionInfo is passed separately, because if CompilationInfo
// was created using Script object, it will not have it.
// Log the code generation. If source information is available include // Log the code generation. If source information is available include
// script name and line number. Check explicitly whether logging is // script name and line number. Check explicitly whether logging is
// enabled as finding the line number is not free. // enabled as finding the line number is not free.
if (Logger::is_logging() || if (Logger::is_logging() || CpuProfiler::is_profiling()) {
CpuProfiler::is_profiling()) {
Handle<Script> script = info->script(); Handle<Script> script = info->script();
Handle<Code> code = info->code(); Handle<Code> code = info->code();
if (*code == Builtins::builtin(Builtins::LazyCompile)) return;
if (script->name()->IsString()) { if (script->name()->IsString()) {
int line_num = GetScriptLineNumber(script, start_position) + 1; int line_num = GetScriptLineNumber(script, shared->start_position()) + 1;
USE(line_num); USE(line_num);
PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script), PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
*code, *code,
*name, *shared,
String::cast(script->name()), String::cast(script->name()),
line_num)); line_num));
} else { } else {
PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script), PROFILE(CodeCreateEvent(Logger::ToNativeByScript(tag, *script),
*code, *code,
*name)); *shared,
shared->DebugName()));
} }
} }

View File

@ -265,9 +265,8 @@ class Compiler : public AllStatic {
#endif #endif
static void RecordFunctionCompilation(Logger::LogEventsAndTags tag, static void RecordFunctionCompilation(Logger::LogEventsAndTags tag,
Handle<String> name, CompilationInfo* info,
int start_position, Handle<SharedFunctionInfo> shared);
CompilationInfo* info);
}; };

View File

@ -41,6 +41,9 @@ namespace internal {
void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) { void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->AddCode(start, entry, size); code_map->AddCode(start, entry, size);
if (sfi_address != NULL) {
entry->set_shared_id(code_map->GetSFITag(sfi_address));
}
} }
@ -54,8 +57,8 @@ void CodeDeleteEventRecord::UpdateCodeMap(CodeMap* code_map) {
} }
void CodeAliasEventRecord::UpdateCodeMap(CodeMap* code_map) { void SFIMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->AddAlias(start, entry, code_start); code_map->MoveCode(from, to);
} }

View File

@ -53,13 +53,7 @@ ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
ticks_buffer_(sizeof(TickSampleEventRecord), ticks_buffer_(sizeof(TickSampleEventRecord),
kTickSamplesBufferChunkSize, kTickSamplesBufferChunkSize,
kTickSamplesBufferChunksCount), kTickSamplesBufferChunksCount),
enqueue_order_(0), enqueue_order_(0) {
known_functions_(new HashMap(AddressesMatch)) {
}
ProfilerEventsProcessor::~ProfilerEventsProcessor() {
delete known_functions_;
} }
@ -75,6 +69,7 @@ void ProfilerEventsProcessor::CallbackCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start; rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, prefix, name); rec->entry = generator_->NewCodeEntry(tag, prefix, name);
rec->size = 1; rec->size = 1;
rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec); events_buffer_.Enqueue(evt_rec);
} }
@ -84,7 +79,8 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
String* resource_name, String* resource_name,
int line_number, int line_number,
Address start, Address start,
unsigned size) { unsigned size,
Address sfi_address) {
if (FilterOutCodeCreateEvent(tag)) return; if (FilterOutCodeCreateEvent(tag)) return;
CodeEventsContainer evt_rec; CodeEventsContainer evt_rec;
CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
@ -93,6 +89,7 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start; rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number); rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number);
rec->size = size; rec->size = size;
rec->sfi_address = sfi_address;
events_buffer_.Enqueue(evt_rec); events_buffer_.Enqueue(evt_rec);
} }
@ -109,6 +106,7 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start; rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, name); rec->entry = generator_->NewCodeEntry(tag, name);
rec->size = size; rec->size = size;
rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec); events_buffer_.Enqueue(evt_rec);
} }
@ -125,6 +123,7 @@ void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag,
rec->start = start; rec->start = start;
rec->entry = generator_->NewCodeEntry(tag, args_count); rec->entry = generator_->NewCodeEntry(tag, args_count);
rec->size = size; rec->size = size;
rec->sfi_address = NULL;
events_buffer_.Enqueue(evt_rec); events_buffer_.Enqueue(evt_rec);
} }
@ -150,57 +149,14 @@ void ProfilerEventsProcessor::CodeDeleteEvent(Address from) {
} }
void ProfilerEventsProcessor::FunctionCreateEvent(Address alias, void ProfilerEventsProcessor::SFIMoveEvent(Address from, Address to) {
Address start,
int security_token_id) {
CodeEventsContainer evt_rec; CodeEventsContainer evt_rec;
CodeAliasEventRecord* rec = &evt_rec.CodeAliasEventRecord_; SFIMoveEventRecord* rec = &evt_rec.SFIMoveEventRecord_;
rec->type = CodeEventRecord::CODE_ALIAS; rec->type = CodeEventRecord::SFI_MOVE;
rec->order = ++enqueue_order_; rec->order = ++enqueue_order_;
rec->start = alias; rec->from = from;
rec->entry = generator_->NewCodeEntry(security_token_id); rec->to = to;
rec->code_start = start;
events_buffer_.Enqueue(evt_rec); events_buffer_.Enqueue(evt_rec);
known_functions_->Lookup(alias, AddressHash(alias), true);
}
void ProfilerEventsProcessor::FunctionMoveEvent(Address from, Address to) {
CodeMoveEvent(from, to);
if (IsKnownFunction(from)) {
known_functions_->Remove(from, AddressHash(from));
known_functions_->Lookup(to, AddressHash(to), true);
}
}
void ProfilerEventsProcessor::FunctionDeleteEvent(Address from) {
CodeDeleteEvent(from);
known_functions_->Remove(from, AddressHash(from));
}
bool ProfilerEventsProcessor::IsKnownFunction(Address start) {
HashMap::Entry* entry =
known_functions_->Lookup(start, AddressHash(start), false);
return entry != NULL;
}
void ProfilerEventsProcessor::ProcessMovedFunctions() {
for (int i = 0; i < moved_functions_.length(); ++i) {
JSFunction* function = moved_functions_[i];
CpuProfiler::FunctionCreateEvent(function);
}
moved_functions_.Clear();
}
void ProfilerEventsProcessor::RememberMovedFunction(JSFunction* function) {
moved_functions_.Add(function);
} }
@ -227,13 +183,12 @@ void ProfilerEventsProcessor::AddCurrentStack() {
TickSample* sample = &record.sample; TickSample* sample = &record.sample;
sample->state = Top::current_vm_state(); sample->state = Top::current_vm_state();
sample->pc = reinterpret_cast<Address>(sample); // Not NULL. sample->pc = reinterpret_cast<Address>(sample); // Not NULL.
sample->tos = NULL;
sample->frames_count = 0; sample->frames_count = 0;
for (StackTraceFrameIterator it; for (StackTraceFrameIterator it;
!it.done() && sample->frames_count < TickSample::kMaxFramesCount; !it.done() && sample->frames_count < TickSample::kMaxFramesCount;
it.Advance()) { it.Advance()) {
JavaScriptFrame* frame = it.frame(); sample->stack[sample->frames_count++] = it.frame()->pc();
sample->stack[sample->frames_count++] =
reinterpret_cast<Address>(frame->function());
} }
record.order = enqueue_order_; record.order = enqueue_order_;
ticks_from_vm_buffer_.Enqueue(record); ticks_from_vm_buffer_.Enqueue(record);
@ -393,20 +348,38 @@ void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Heap::empty_string(), Heap::empty_string(),
v8::CpuProfileNode::kNoLineNumberInfo, v8::CpuProfileNode::kNoLineNumberInfo,
code->address(), code->address(),
code->ExecutableSize()); code->ExecutableSize(),
NULL);
} }
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, String* name, Code* code,
String* source, int line) { SharedFunctionInfo* shared,
String* name) {
singleton_->processor_->CodeCreateEvent( singleton_->processor_->CodeCreateEvent(
tag, tag,
name, name,
Heap::empty_string(),
v8::CpuProfileNode::kNoLineNumberInfo,
code->address(),
code->ExecutableSize(),
shared->address());
}
void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* source, int line) {
singleton_->processor_->CodeCreateEvent(
tag,
shared->DebugName(),
source, source,
line, line,
code->address(), code->address(),
code->ExecutableSize()); code->ExecutableSize(),
shared->address());
} }
@ -430,44 +403,8 @@ void CpuProfiler::CodeDeleteEvent(Address from) {
} }
void CpuProfiler::FunctionCreateEvent(JSFunction* function) { void CpuProfiler::SFIMoveEvent(Address from, Address to) {
int security_token_id = TokenEnumerator::kNoSecurityToken; singleton_->processor_->SFIMoveEvent(from, to);
if (function->unchecked_context()->IsContext()) {
security_token_id = singleton_->token_enumerator_->GetTokenId(
function->context()->global_context()->security_token());
}
singleton_->processor_->FunctionCreateEvent(
function->address(),
function->shared()->code()->address(),
security_token_id);
}
void CpuProfiler::ProcessMovedFunctions() {
singleton_->processor_->ProcessMovedFunctions();
}
void CpuProfiler::FunctionCreateEventFromMove(JSFunction* function) {
// This function is called from GC iterators (during Scavenge,
// MC, and MS), so marking bits can be set on objects. That's
// why unchecked accessors are used here.
// The same function can be reported several times.
if (function->unchecked_code() == Builtins::builtin(Builtins::LazyCompile)
|| singleton_->processor_->IsKnownFunction(function->address())) return;
singleton_->processor_->RememberMovedFunction(function);
}
void CpuProfiler::FunctionMoveEvent(Address from, Address to) {
singleton_->processor_->FunctionMoveEvent(from, to);
}
void CpuProfiler::FunctionDeleteEvent(Address from) {
singleton_->processor_->FunctionDeleteEvent(from);
} }
@ -539,7 +476,6 @@ void CpuProfiler::StartProcessorIfNotStarted() {
FLAG_log_code = saved_log_code_flag; FLAG_log_code = saved_log_code_flag;
} }
Logger::LogCompiledFunctions(); Logger::LogCompiledFunctions();
Logger::LogFunctionObjects();
Logger::LogAccessorCallbacks(); Logger::LogAccessorCallbacks();
} }
// Enable stack sampling. // Enable stack sampling.

View File

@ -50,7 +50,7 @@ class TokenEnumerator;
V(CODE_CREATION, CodeCreateEventRecord) \ V(CODE_CREATION, CodeCreateEventRecord) \
V(CODE_MOVE, CodeMoveEventRecord) \ V(CODE_MOVE, CodeMoveEventRecord) \
V(CODE_DELETE, CodeDeleteEventRecord) \ V(CODE_DELETE, CodeDeleteEventRecord) \
V(CODE_ALIAS, CodeAliasEventRecord) V(SFI_MOVE, SFIMoveEventRecord)
class CodeEventRecord { class CodeEventRecord {
@ -73,6 +73,7 @@ class CodeCreateEventRecord : public CodeEventRecord {
Address start; Address start;
CodeEntry* entry; CodeEntry* entry;
unsigned size; unsigned size;
Address sfi_address;
INLINE(void UpdateCodeMap(CodeMap* code_map)); INLINE(void UpdateCodeMap(CodeMap* code_map));
}; };
@ -95,11 +96,10 @@ class CodeDeleteEventRecord : public CodeEventRecord {
}; };
class CodeAliasEventRecord : public CodeEventRecord { class SFIMoveEventRecord : public CodeEventRecord {
public: public:
Address start; Address from;
CodeEntry* entry; Address to;
Address code_start;
INLINE(void UpdateCodeMap(CodeMap* code_map)); INLINE(void UpdateCodeMap(CodeMap* code_map));
}; };
@ -134,7 +134,7 @@ class TickSampleEventRecord BASE_EMBEDDED {
class ProfilerEventsProcessor : public Thread { class ProfilerEventsProcessor : public Thread {
public: public:
explicit ProfilerEventsProcessor(ProfileGenerator* generator); explicit ProfilerEventsProcessor(ProfileGenerator* generator);
virtual ~ProfilerEventsProcessor(); virtual ~ProfilerEventsProcessor() {}
// Thread control. // Thread control.
virtual void Run(); virtual void Run();
@ -148,7 +148,8 @@ class ProfilerEventsProcessor : public Thread {
void CodeCreateEvent(Logger::LogEventsAndTags tag, void CodeCreateEvent(Logger::LogEventsAndTags tag,
String* name, String* name,
String* resource_name, int line_number, String* resource_name, int line_number,
Address start, unsigned size); Address start, unsigned size,
Address sfi_address);
void CodeCreateEvent(Logger::LogEventsAndTags tag, void CodeCreateEvent(Logger::LogEventsAndTags tag,
const char* name, const char* name,
Address start, unsigned size); Address start, unsigned size);
@ -157,17 +158,12 @@ class ProfilerEventsProcessor : public Thread {
Address start, unsigned size); Address start, unsigned size);
void CodeMoveEvent(Address from, Address to); void CodeMoveEvent(Address from, Address to);
void CodeDeleteEvent(Address from); void CodeDeleteEvent(Address from);
void FunctionCreateEvent(Address alias, Address start, int security_token_id); void SFIMoveEvent(Address from, Address to);
void FunctionMoveEvent(Address from, Address to);
void FunctionDeleteEvent(Address from);
void RegExpCodeCreateEvent(Logger::LogEventsAndTags tag, void RegExpCodeCreateEvent(Logger::LogEventsAndTags tag,
const char* prefix, String* name, const char* prefix, String* name,
Address start, unsigned size); Address start, unsigned size);
// Puts current stack into tick sample events buffer. // Puts current stack into tick sample events buffer.
void AddCurrentStack(); void AddCurrentStack();
bool IsKnownFunction(Address start);
void ProcessMovedFunctions();
void RememberMovedFunction(JSFunction* function);
// Tick sample events are filled directly in the buffer of the circular // Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all // queue (because the structure is of fixed width, but usually not all
@ -188,13 +184,6 @@ class ProfilerEventsProcessor : public Thread {
bool ProcessTicks(unsigned dequeue_order); bool ProcessTicks(unsigned dequeue_order);
INLINE(static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag)); INLINE(static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag));
INLINE(static bool AddressesMatch(void* key1, void* key2)) {
return key1 == key2;
}
INLINE(static uint32_t AddressHash(Address addr)) {
return ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr)));
}
ProfileGenerator* generator_; ProfileGenerator* generator_;
bool running_; bool running_;
@ -202,10 +191,6 @@ class ProfilerEventsProcessor : public Thread {
SamplingCircularQueue ticks_buffer_; SamplingCircularQueue ticks_buffer_;
UnboundQueue<TickSampleEventRecord> ticks_from_vm_buffer_; UnboundQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
unsigned enqueue_order_; unsigned enqueue_order_;
// Used from the VM thread.
HashMap* known_functions_;
List<JSFunction*> moved_functions_;
}; };
} } // namespace v8::internal } } // namespace v8::internal
@ -251,23 +236,22 @@ class CpuProfiler {
static void CodeCreateEvent(Logger::LogEventsAndTags tag, static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, String* name); Code* code, String* name);
static void CodeCreateEvent(Logger::LogEventsAndTags tag, static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, String* name, Code* code,
SharedFunctionInfo *shared,
String* name);
static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code,
SharedFunctionInfo *shared,
String* source, int line); String* source, int line);
static void CodeCreateEvent(Logger::LogEventsAndTags tag, static void CodeCreateEvent(Logger::LogEventsAndTags tag,
Code* code, int args_count); Code* code, int args_count);
static void CodeMovingGCEvent() {} static void CodeMovingGCEvent() {}
static void CodeMoveEvent(Address from, Address to); static void CodeMoveEvent(Address from, Address to);
static void CodeDeleteEvent(Address from); static void CodeDeleteEvent(Address from);
static void FunctionCreateEvent(JSFunction* function);
// Reports function creation in case we had missed it (e.g.
// if it was created from compiled code).
static void FunctionCreateEventFromMove(JSFunction* function);
static void FunctionMoveEvent(Address from, Address to);
static void FunctionDeleteEvent(Address from);
static void GetterCallbackEvent(String* name, Address entry_point); static void GetterCallbackEvent(String* name, Address entry_point);
static void RegExpCodeCreateEvent(Code* code, String* source); static void RegExpCodeCreateEvent(Code* code, String* source);
static void ProcessMovedFunctions();
static void SetterCallbackEvent(String* name, Address entry_point); static void SetterCallbackEvent(String* name, Address entry_point);
static void SFIMoveEvent(Address from, Address to);
static INLINE(bool is_profiling()) { static INLINE(bool is_profiling()) {
return NoBarrier_Load(&is_profiling_); return NoBarrier_Load(&is_profiling_);

View File

@ -106,6 +106,11 @@ static Handle<Object> Invoke(bool construct,
ASSERT(*has_pending_exception == Top::has_pending_exception()); ASSERT(*has_pending_exception == Top::has_pending_exception());
if (*has_pending_exception) { if (*has_pending_exception) {
Top::ReportPendingMessages(); Top::ReportPendingMessages();
if (Top::pending_exception() == Failure::OutOfMemoryException()) {
if (!HandleScopeImplementer::instance()->ignore_out_of_memory()) {
V8::FatalProcessOutOfMemory("JS", true);
}
}
return Handle<Object>(); return Handle<Object>();
} else { } else {
Top::clear_pending_message(); Top::clear_pending_message();

View File

@ -120,6 +120,7 @@ DEFINE_bool(time_hydrogen, false, "timing for hydrogen")
DEFINE_bool(trace_hydrogen, false, "trace generated hydrogen to file") DEFINE_bool(trace_hydrogen, false, "trace generated hydrogen to file")
DEFINE_bool(trace_inlining, false, "trace inlining decisions") DEFINE_bool(trace_inlining, false, "trace inlining decisions")
DEFINE_bool(trace_alloc, false, "trace register allocator") DEFINE_bool(trace_alloc, false, "trace register allocator")
DEFINE_bool(trace_all_uses, false, "trace all use positions")
DEFINE_bool(trace_range, false, "trace range analysis") DEFINE_bool(trace_range, false, "trace range analysis")
DEFINE_bool(trace_gvn, false, "trace global value numbering") DEFINE_bool(trace_gvn, false, "trace global value numbering")
DEFINE_bool(trace_representation, false, "trace representation types") DEFINE_bool(trace_representation, false, "trace representation types")
@ -134,7 +135,11 @@ DEFINE_bool(deoptimize_uncommon_cases, true, "deoptimize uncommon cases")
DEFINE_bool(polymorphic_inlining, true, "polymorphic inlining") DEFINE_bool(polymorphic_inlining, true, "polymorphic inlining")
DEFINE_bool(aggressive_loop_invariant_motion, true, DEFINE_bool(aggressive_loop_invariant_motion, true,
"aggressive motion of instructions out of loops") "aggressive motion of instructions out of loops")
#ifdef V8_TARGET_ARCH_X64
DEFINE_bool(use_osr, false, "use on-stack replacement")
#else
DEFINE_bool(use_osr, true, "use on-stack replacement") DEFINE_bool(use_osr, true, "use on-stack replacement")
#endif
DEFINE_bool(trace_osr, false, "trace on-stack replacement") DEFINE_bool(trace_osr, false, "trace on-stack replacement")
DEFINE_int(stress_runs, 0, "number of stress runs") DEFINE_int(stress_runs, 0, "number of stress runs")
DEFINE_bool(optimize_closures, true, "optimize closures") DEFINE_bool(optimize_closures, true, "optimize closures")

View File

@ -1411,9 +1411,8 @@ static void AddUnwindInfo(CodeDescription *desc) {
#ifdef V8_TARGET_ARCH_X64 #ifdef V8_TARGET_ARCH_X64
if (desc->tag() == GDBJITInterface::FUNCTION) { if (desc->tag() == GDBJITInterface::FUNCTION) {
// To avoid propagating unwinding information through // To avoid propagating unwinding information through
// compilation pipeline we rely on function prologue // compilation pipeline we use an approximation.
// and epilogue being the same for all code objects generated // For most use cases this should not affect usability.
// by the full code generator.
static const int kFramePointerPushOffset = 1; static const int kFramePointerPushOffset = 1;
static const int kFramePointerSetOffset = 4; static const int kFramePointerSetOffset = 4;
static const int kFramePointerPopOffset = -3; static const int kFramePointerPopOffset = -3;
@ -1427,19 +1426,6 @@ static void AddUnwindInfo(CodeDescription *desc) {
uintptr_t frame_pointer_pop_address = uintptr_t frame_pointer_pop_address =
desc->CodeEnd() + kFramePointerPopOffset; desc->CodeEnd() + kFramePointerPopOffset;
#ifdef DEBUG
static const uint8_t kFramePointerPushInstruction = 0x48; // push ebp
static const uint16_t kFramePointerSetInstruction = 0x5756; // mov ebp, esp
static const uint8_t kFramePointerPopInstruction = 0xBE; // pop ebp
ASSERT(*reinterpret_cast<uint8_t*>(frame_pointer_push_address) ==
kFramePointerPushInstruction);
ASSERT(*reinterpret_cast<uint16_t*>(frame_pointer_set_address) ==
kFramePointerSetInstruction);
ASSERT(*reinterpret_cast<uint8_t*>(frame_pointer_pop_address) ==
kFramePointerPopInstruction);
#endif
desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH, desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH,
frame_pointer_push_address); frame_pointer_push_address);
desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET, desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET,

View File

@ -834,49 +834,39 @@ bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
} }
bool CompileLazy(Handle<JSFunction> function, static bool CompileLazyFunction(Handle<JSFunction> function,
ClearExceptionFlag flag) { ClearExceptionFlag flag,
InLoopFlag in_loop_flag) {
bool result = true; bool result = true;
if (function->shared()->is_compiled()) { if (function->shared()->is_compiled()) {
function->ReplaceCode(function->shared()->code()); function->ReplaceCode(function->shared()->code());
function->shared()->set_code_age(0); function->shared()->set_code_age(0);
} else { } else {
CompilationInfo info(function); CompilationInfo info(function);
if (in_loop_flag == IN_LOOP) info.MarkAsInLoop();
result = CompileLazyHelper(&info, flag); result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled()); ASSERT(!result || function->is_compiled());
} }
if (result && function->is_compiled()) {
PROFILE(FunctionCreateEvent(*function));
}
return result; return result;
} }
bool CompileLazy(Handle<JSFunction> function,
ClearExceptionFlag flag) {
return CompileLazyFunction(function, flag, NOT_IN_LOOP);
}
bool CompileLazyInLoop(Handle<JSFunction> function, bool CompileLazyInLoop(Handle<JSFunction> function,
ClearExceptionFlag flag) { ClearExceptionFlag flag) {
bool result = true; return CompileLazyFunction(function, flag, IN_LOOP);
if (function->shared()->is_compiled()) {
function->ReplaceCode(function->shared()->code());
function->shared()->set_code_age(0);
} else {
CompilationInfo info(function);
info.MarkAsInLoop();
result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled());
}
if (result && function->is_compiled()) {
PROFILE(FunctionCreateEvent(*function));
}
return result;
} }
bool CompileOptimized(Handle<JSFunction> function, int osr_ast_id) { bool CompileOptimized(Handle<JSFunction> function, int osr_ast_id) {
CompilationInfo info(function); CompilationInfo info(function);
info.SetOptimizing(osr_ast_id); info.SetOptimizing(osr_ast_id);
bool result = CompileLazyHelper(&info, KEEP_EXCEPTION); return CompileLazyHelper(&info, KEEP_EXCEPTION);
if (result) PROFILE(FunctionCreateEvent(*function));
return result;
} }

204
deps/v8/src/heap.cc vendored
View File

@ -134,7 +134,7 @@ Heap::HeapState Heap::gc_state_ = NOT_IN_GC;
int Heap::mc_count_ = 0; int Heap::mc_count_ = 0;
int Heap::ms_count_ = 0; int Heap::ms_count_ = 0;
int Heap::gc_count_ = 0; unsigned int Heap::gc_count_ = 0;
GCTracer* Heap::tracer_ = NULL; GCTracer* Heap::tracer_ = NULL;
@ -515,7 +515,6 @@ bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
if (FLAG_log_gc) HeapProfiler::WriteSample(); if (FLAG_log_gc) HeapProfiler::WriteSample();
if (CpuProfiler::is_profiling()) CpuProfiler::ProcessMovedFunctions();
#endif #endif
return next_gc_likely_to_collect_more; return next_gc_likely_to_collect_more;
@ -1350,9 +1349,8 @@ class ScavengingVisitor : public StaticVisitorBase {
HEAP_PROFILE(ObjectMoveEvent(source->address(), target->address())); HEAP_PROFILE(ObjectMoveEvent(source->address(), target->address()));
#if defined(ENABLE_LOGGING_AND_PROFILING) #if defined(ENABLE_LOGGING_AND_PROFILING)
if (Logger::is_logging() || CpuProfiler::is_profiling()) { if (Logger::is_logging() || CpuProfiler::is_profiling()) {
if (target->IsJSFunction()) { if (target->IsSharedFunctionInfo()) {
PROFILE(FunctionMoveEvent(source->address(), target->address())); PROFILE(SFIMoveEvent(source->address(), target->address()));
PROFILE(FunctionCreateEventFromMove(JSFunction::cast(target)));
} }
} }
#endif #endif
@ -2924,9 +2922,8 @@ MaybeObject* Heap::AllocateFunctionPrototype(JSFunction* function) {
// constructor to the function. // constructor to the function.
Object* result; Object* result;
{ MaybeObject* maybe_result = { MaybeObject* maybe_result =
JSObject::cast(prototype)->SetProperty(constructor_symbol(), JSObject::cast(prototype)->SetLocalPropertyIgnoreAttributes(
function, constructor_symbol(), function, DONT_ENUM);
DONT_ENUM);
if (!maybe_result->ToObject(&result)) return maybe_result; if (!maybe_result->ToObject(&result)) return maybe_result;
} }
return prototype; return prototype;
@ -3797,9 +3794,9 @@ bool Heap::IdleNotification() {
static const int kIdlesBeforeMarkSweep = 7; static const int kIdlesBeforeMarkSweep = 7;
static const int kIdlesBeforeMarkCompact = 8; static const int kIdlesBeforeMarkCompact = 8;
static const int kMaxIdleCount = kIdlesBeforeMarkCompact + 1; static const int kMaxIdleCount = kIdlesBeforeMarkCompact + 1;
static const int kGCsBetweenCleanup = 4; static const unsigned int kGCsBetweenCleanup = 4;
static int number_idle_notifications = 0; static int number_idle_notifications = 0;
static int last_gc_count = gc_count_; static unsigned int last_gc_count = gc_count_;
bool uncommit = true; bool uncommit = true;
bool finished = false; bool finished = false;
@ -3808,7 +3805,7 @@ bool Heap::IdleNotification() {
// GCs have taken place. This allows another round of cleanup based // GCs have taken place. This allows another round of cleanup based
// on idle notifications if enough work has been carried out to // on idle notifications if enough work has been carried out to
// provoke a number of garbage collections. // provoke a number of garbage collections.
if (gc_count_ < last_gc_count + kGCsBetweenCleanup) { if (gc_count_ - last_gc_count < kGCsBetweenCleanup) {
number_idle_notifications = number_idle_notifications =
Min(number_idle_notifications + 1, kMaxIdleCount); Min(number_idle_notifications + 1, kMaxIdleCount);
} else { } else {
@ -5182,32 +5179,77 @@ void HeapIterator::reset() {
} }
#ifdef DEBUG #if defined(DEBUG) || defined(LIVE_OBJECT_LIST)
static bool search_for_any_global; Object* const PathTracer::kAnyGlobalObject = reinterpret_cast<Object*>(NULL);
static Object* search_target;
static bool found_target;
static List<Object*> object_stack(20);
class PathTracer::MarkVisitor: public ObjectVisitor {
// Tags 0, 1, and 3 are used. Use 2 for marking visited HeapObject.
static const int kMarkTag = 2;
static void MarkObjectRecursively(Object** p);
class MarkObjectVisitor : public ObjectVisitor {
public: public:
explicit MarkVisitor(PathTracer* tracer) : tracer_(tracer) {}
void VisitPointers(Object** start, Object** end) { void VisitPointers(Object** start, Object** end) {
// Copy all HeapObject pointers in [start, end) // Scan all HeapObject pointers in [start, end)
for (Object** p = start; p < end; p++) { for (Object** p = start; !tracer_->found() && (p < end); p++) {
if ((*p)->IsHeapObject()) if ((*p)->IsHeapObject())
MarkObjectRecursively(p); tracer_->MarkRecursively(p, this);
} }
} }
private:
PathTracer* tracer_;
}; };
static MarkObjectVisitor mark_visitor;
static void MarkObjectRecursively(Object** p) { class PathTracer::UnmarkVisitor: public ObjectVisitor {
public:
explicit UnmarkVisitor(PathTracer* tracer) : tracer_(tracer) {}
void VisitPointers(Object** start, Object** end) {
// Scan all HeapObject pointers in [start, end)
for (Object** p = start; p < end; p++) {
if ((*p)->IsHeapObject())
tracer_->UnmarkRecursively(p, this);
}
}
private:
PathTracer* tracer_;
};
void PathTracer::VisitPointers(Object** start, Object** end) {
bool done = ((what_to_find_ == FIND_FIRST) && found_target_);
// Visit all HeapObject pointers in [start, end)
for (Object** p = start; !done && (p < end); p++) {
if ((*p)->IsHeapObject()) {
TracePathFrom(p);
done = ((what_to_find_ == FIND_FIRST) && found_target_);
}
}
}
void PathTracer::Reset() {
found_target_ = false;
object_stack_.Clear();
}
void PathTracer::TracePathFrom(Object** root) {
ASSERT((search_target_ == kAnyGlobalObject) ||
search_target_->IsHeapObject());
found_target_in_trace_ = false;
object_stack_.Clear();
MarkVisitor mark_visitor(this);
MarkRecursively(root, &mark_visitor);
UnmarkVisitor unmark_visitor(this);
UnmarkRecursively(root, &unmark_visitor);
ProcessResults();
}
void PathTracer::MarkRecursively(Object** p, MarkVisitor* mark_visitor) {
if (!(*p)->IsHeapObject()) return; if (!(*p)->IsHeapObject()) return;
HeapObject* obj = HeapObject::cast(*p); HeapObject* obj = HeapObject::cast(*p);
@ -5216,14 +5258,17 @@ static void MarkObjectRecursively(Object** p) {
if (!map->IsHeapObject()) return; // visited before if (!map->IsHeapObject()) return; // visited before
if (found_target) return; // stop if target found if (found_target_in_trace_) return; // stop if target found
object_stack.Add(obj); object_stack_.Add(obj);
if ((search_for_any_global && obj->IsJSGlobalObject()) || if (((search_target_ == kAnyGlobalObject) && obj->IsJSGlobalObject()) ||
(!search_for_any_global && (obj == search_target))) { (obj == search_target_)) {
found_target = true; found_target_in_trace_ = true;
found_target_ = true;
return; return;
} }
bool is_global_context = obj->IsGlobalContext();
// not visited yet // not visited yet
Map* map_p = reinterpret_cast<Map*>(HeapObject::cast(map)); Map* map_p = reinterpret_cast<Map*>(HeapObject::cast(map));
@ -5231,31 +5276,30 @@ static void MarkObjectRecursively(Object** p) {
obj->set_map(reinterpret_cast<Map*>(map_addr + kMarkTag)); obj->set_map(reinterpret_cast<Map*>(map_addr + kMarkTag));
MarkObjectRecursively(&map); // Scan the object body.
if (is_global_context && (visit_mode_ == VISIT_ONLY_STRONG)) {
// This is specialized to scan Context's properly.
Object** start = reinterpret_cast<Object**>(obj->address() +
Context::kHeaderSize);
Object** end = reinterpret_cast<Object**>(obj->address() +
Context::kHeaderSize + Context::FIRST_WEAK_SLOT * kPointerSize);
mark_visitor->VisitPointers(start, end);
} else {
obj->IterateBody(map_p->instance_type(),
obj->SizeFromMap(map_p),
mark_visitor);
}
obj->IterateBody(map_p->instance_type(), obj->SizeFromMap(map_p), // Scan the map after the body because the body is a lot more interesting
&mark_visitor); // when doing leak detection.
MarkRecursively(&map, mark_visitor);
if (!found_target) // don't pop if found the target if (!found_target_in_trace_) // don't pop if found the target
object_stack.RemoveLast(); object_stack_.RemoveLast();
} }
static void UnmarkObjectRecursively(Object** p); void PathTracer::UnmarkRecursively(Object** p, UnmarkVisitor* unmark_visitor) {
class UnmarkObjectVisitor : public ObjectVisitor {
public:
void VisitPointers(Object** start, Object** end) {
// Copy all HeapObject pointers in [start, end)
for (Object** p = start; p < end; p++) {
if ((*p)->IsHeapObject())
UnmarkObjectRecursively(p);
}
}
};
static UnmarkObjectVisitor unmark_visitor;
static void UnmarkObjectRecursively(Object** p) {
if (!(*p)->IsHeapObject()) return; if (!(*p)->IsHeapObject()) return;
HeapObject* obj = HeapObject::cast(*p); HeapObject* obj = HeapObject::cast(*p);
@ -5274,63 +5318,38 @@ static void UnmarkObjectRecursively(Object** p) {
obj->set_map(reinterpret_cast<Map*>(map_p)); obj->set_map(reinterpret_cast<Map*>(map_p));
UnmarkObjectRecursively(reinterpret_cast<Object**>(&map_p)); UnmarkRecursively(reinterpret_cast<Object**>(&map_p), unmark_visitor);
obj->IterateBody(Map::cast(map_p)->instance_type(), obj->IterateBody(Map::cast(map_p)->instance_type(),
obj->SizeFromMap(Map::cast(map_p)), obj->SizeFromMap(Map::cast(map_p)),
&unmark_visitor); unmark_visitor);
} }
static void MarkRootObjectRecursively(Object** root) { void PathTracer::ProcessResults() {
if (search_for_any_global) { if (found_target_) {
ASSERT(search_target == NULL);
} else {
ASSERT(search_target->IsHeapObject());
}
found_target = false;
object_stack.Clear();
MarkObjectRecursively(root);
UnmarkObjectRecursively(root);
if (found_target) {
PrintF("=====================================\n"); PrintF("=====================================\n");
PrintF("==== Path to object ====\n"); PrintF("==== Path to object ====\n");
PrintF("=====================================\n\n"); PrintF("=====================================\n\n");
ASSERT(!object_stack.is_empty()); ASSERT(!object_stack_.is_empty());
for (int i = 0; i < object_stack.length(); i++) { for (int i = 0; i < object_stack_.length(); i++) {
if (i > 0) PrintF("\n |\n |\n V\n\n"); if (i > 0) PrintF("\n |\n |\n V\n\n");
Object* obj = object_stack[i]; Object* obj = object_stack_[i];
obj->Print(); obj->Print();
} }
PrintF("=====================================\n"); PrintF("=====================================\n");
} }
} }
#endif // DEBUG || LIVE_OBJECT_LIST
// Helper class for visiting HeapObjects recursively. #ifdef DEBUG
class MarkRootVisitor: public ObjectVisitor {
public:
void VisitPointers(Object** start, Object** end) {
// Visit all HeapObject pointers in [start, end)
for (Object** p = start; p < end; p++) {
if ((*p)->IsHeapObject())
MarkRootObjectRecursively(p);
}
}
};
// Triggers a depth-first traversal of reachable objects from roots // Triggers a depth-first traversal of reachable objects from roots
// and finds a path to a specific heap object and prints it. // and finds a path to a specific heap object and prints it.
void Heap::TracePathToObject(Object* target) { void Heap::TracePathToObject(Object* target) {
search_target = target; PathTracer tracer(target, PathTracer::FIND_ALL, VISIT_ALL);
search_for_any_global = false; IterateRoots(&tracer, VISIT_ONLY_STRONG);
MarkRootVisitor root_visitor;
IterateRoots(&root_visitor, VISIT_ONLY_STRONG);
} }
@ -5338,11 +5357,10 @@ void Heap::TracePathToObject(Object* target) {
// and finds a path to any global object and prints it. Useful for // and finds a path to any global object and prints it. Useful for
// determining the source for leaks of global objects. // determining the source for leaks of global objects.
void Heap::TracePathToGlobal() { void Heap::TracePathToGlobal() {
search_target = NULL; PathTracer tracer(PathTracer::kAnyGlobalObject,
search_for_any_global = true; PathTracer::FIND_ALL,
VISIT_ALL);
MarkRootVisitor root_visitor; IterateRoots(&tracer, VISIT_ONLY_STRONG);
IterateRoots(&root_visitor, VISIT_ONLY_STRONG);
} }
#endif #endif

67
deps/v8/src/heap.h vendored
View File

@ -30,6 +30,8 @@
#include <math.h> #include <math.h>
#include "globals.h"
#include "list.h"
#include "spaces.h" #include "spaces.h"
#include "splay-tree-inl.h" #include "splay-tree-inl.h"
#include "v8-counters.h" #include "v8-counters.h"
@ -1180,7 +1182,7 @@ class Heap : public AllStatic {
static int mc_count_; // how many mark-compact collections happened static int mc_count_; // how many mark-compact collections happened
static int ms_count_; // how many mark-sweep collections happened static int ms_count_; // how many mark-sweep collections happened
static int gc_count_; // how many gc happened static unsigned int gc_count_; // how many gc happened
// Total length of the strings we failed to flatten since the last GC. // Total length of the strings we failed to flatten since the last GC.
static int unflattened_strings_length_; static int unflattened_strings_length_;
@ -1907,7 +1909,7 @@ class GCTracer BASE_EMBEDDED {
void set_collector(GarbageCollector collector) { collector_ = collector; } void set_collector(GarbageCollector collector) { collector_ = collector; }
// Sets the GC count. // Sets the GC count.
void set_gc_count(int count) { gc_count_ = count; } void set_gc_count(unsigned int count) { gc_count_ = count; }
// Sets the full GC count. // Sets the full GC count.
void set_full_gc_count(int count) { full_gc_count_ = count; } void set_full_gc_count(int count) { full_gc_count_ = count; }
@ -1950,7 +1952,7 @@ class GCTracer BASE_EMBEDDED {
// A count (including this one, eg, the first collection is 1) of the // A count (including this one, eg, the first collection is 1) of the
// number of garbage collections. // number of garbage collections.
int gc_count_; unsigned int gc_count_;
// A count (including this one) of the number of full garbage collections. // A count (including this one) of the number of full garbage collections.
int full_gc_count_; int full_gc_count_;
@ -2152,6 +2154,65 @@ class WeakObjectRetainer {
}; };
#if defined(DEBUG) || defined(LIVE_OBJECT_LIST)
// Helper class for tracing paths to a search target Object from all roots.
// The TracePathFrom() method can be used to trace paths from a specific
// object to the search target object.
class PathTracer : public ObjectVisitor {
public:
enum WhatToFind {
FIND_ALL, // Will find all matches.
FIND_FIRST // Will stop the search after first match.
};
// For the WhatToFind arg, if FIND_FIRST is specified, tracing will stop
// after the first match. If FIND_ALL is specified, then tracing will be
// done for all matches.
PathTracer(Object* search_target,
WhatToFind what_to_find,
VisitMode visit_mode)
: search_target_(search_target),
found_target_(false),
found_target_in_trace_(false),
what_to_find_(what_to_find),
visit_mode_(visit_mode),
object_stack_(20),
no_alloc() {}
virtual void VisitPointers(Object** start, Object** end);
void Reset();
void TracePathFrom(Object** root);
bool found() const { return found_target_; }
static Object* const kAnyGlobalObject;
protected:
class MarkVisitor;
class UnmarkVisitor;
void MarkRecursively(Object** p, MarkVisitor* mark_visitor);
void UnmarkRecursively(Object** p, UnmarkVisitor* unmark_visitor);
virtual void ProcessResults();
// Tags 0, 1, and 3 are used. Use 2 for marking visited HeapObject.
static const int kMarkTag = 2;
Object* search_target_;
bool found_target_;
bool found_target_in_trace_;
WhatToFind what_to_find_;
VisitMode visit_mode_;
List<Object*> object_stack_;
AssertNoAllocation no_alloc; // i.e. no gc allowed.
DISALLOW_IMPLICIT_CONSTRUCTORS(PathTracer);
};
#endif // DEBUG || LIVE_OBJECT_LIST
} } // namespace v8::internal } } // namespace v8::internal
#endif // V8_HEAP_H_ #endif // V8_HEAP_H_

View File

@ -57,10 +57,13 @@ const char* Representation::Mnemonic() const {
case kTagged: return "t"; case kTagged: return "t";
case kDouble: return "d"; case kDouble: return "d";
case kInteger32: return "i"; case kInteger32: return "i";
default: case kExternal: return "x";
case kNumRepresentations:
UNREACHABLE(); UNREACHABLE();
return NULL; return NULL;
} }
UNREACHABLE();
return NULL;
} }
@ -221,7 +224,7 @@ HType HType::TypeFromValue(Handle<Object> value) {
} }
int HValue::LookupOperandIndex(int occurrence_index, HValue* op) const { int HValue::LookupOperandIndex(int occurrence_index, HValue* op) {
for (int i = 0; i < OperandCount(); ++i) { for (int i = 0; i < OperandCount(); ++i) {
if (OperandAt(i) == op) { if (OperandAt(i) == op) {
if (occurrence_index == 0) return i; if (occurrence_index == 0) return i;
@ -237,7 +240,7 @@ bool HValue::IsDefinedAfter(HBasicBlock* other) const {
} }
bool HValue::UsesMultipleTimes(HValue* op) const { bool HValue::UsesMultipleTimes(HValue* op) {
bool seen = false; bool seen = false;
for (int i = 0; i < OperandCount(); ++i) { for (int i = 0; i < OperandCount(); ++i) {
if (OperandAt(i) == op) { if (OperandAt(i) == op) {
@ -249,7 +252,7 @@ bool HValue::UsesMultipleTimes(HValue* op) const {
} }
bool HValue::Equals(HValue* other) const { bool HValue::Equals(HValue* other) {
if (other->opcode() != opcode()) return false; if (other->opcode() != opcode()) return false;
if (!other->representation().Equals(representation())) return false; if (!other->representation().Equals(representation())) return false;
if (!other->type_.Equals(type_)) return false; if (!other->type_.Equals(type_)) return false;
@ -264,7 +267,7 @@ bool HValue::Equals(HValue* other) const {
} }
intptr_t HValue::Hashcode() const { intptr_t HValue::Hashcode() {
intptr_t result = opcode(); intptr_t result = opcode();
int count = OperandCount(); int count = OperandCount();
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
@ -281,33 +284,6 @@ void HValue::SetOperandAt(int index, HValue* value) {
} }
void HLoadKeyedGeneric::InternalSetOperandAt(int index, HValue* value) {
if (index < 2) {
operands_[index] = value;
} else {
context_ = value;
}
}
void HStoreKeyedGeneric::InternalSetOperandAt(int index, HValue* value) {
if (index < 3) {
operands_[index] = value;
} else {
context_ = value;
}
}
void HStoreNamedGeneric::InternalSetOperandAt(int index, HValue* value) {
if (index < 2) {
operands_[index] = value;
} else {
context_ = value;
}
}
void HValue::ReplaceAndDelete(HValue* other) { void HValue::ReplaceAndDelete(HValue* other) {
ReplaceValue(other); ReplaceValue(other);
Delete(); Delete();
@ -438,7 +414,7 @@ void HValue::ComputeInitialRange() {
} }
void HInstruction::PrintTo(StringStream* stream) const { void HInstruction::PrintTo(StringStream* stream) {
stream->Add("%s", Mnemonic()); stream->Add("%s", Mnemonic());
if (HasSideEffects()) stream->Add("*"); if (HasSideEffects()) stream->Add("*");
stream->Add(" "); stream->Add(" ");
@ -561,69 +537,64 @@ void HInstruction::Verify() {
#endif #endif
void HCall::PrintDataTo(StringStream* stream) const { void HUnaryCall::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream);
stream->Add(" ");
stream->Add("#%d", argument_count()); stream->Add("#%d", argument_count());
} }
void HUnaryCall::PrintDataTo(StringStream* stream) const { void HBinaryCall::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream);
stream->Add(" ");
HCall::PrintDataTo(stream);
}
void HBinaryCall::PrintDataTo(StringStream* stream) const {
first()->PrintNameTo(stream); first()->PrintNameTo(stream);
stream->Add(" "); stream->Add(" ");
second()->PrintNameTo(stream); second()->PrintNameTo(stream);
stream->Add(" "); stream->Add(" ");
HCall::PrintDataTo(stream); stream->Add("#%d", argument_count());
} }
void HCallConstantFunction::PrintDataTo(StringStream* stream) const { void HCallConstantFunction::PrintDataTo(StringStream* stream) {
if (IsApplyFunction()) { if (IsApplyFunction()) {
stream->Add("optimized apply "); stream->Add("optimized apply ");
} else { } else {
stream->Add("%o ", function()->shared()->DebugName()); stream->Add("%o ", function()->shared()->DebugName());
} }
HCall::PrintDataTo(stream); stream->Add("#%d", argument_count());
} }
void HCallNamed::PrintDataTo(StringStream* stream) const { void HCallNamed::PrintDataTo(StringStream* stream) {
stream->Add("%o ", *name()); stream->Add("%o ", *name());
HUnaryCall::PrintDataTo(stream); HUnaryCall::PrintDataTo(stream);
} }
void HCallGlobal::PrintDataTo(StringStream* stream) const { void HCallGlobal::PrintDataTo(StringStream* stream) {
stream->Add("%o ", *name()); stream->Add("%o ", *name());
HUnaryCall::PrintDataTo(stream); HUnaryCall::PrintDataTo(stream);
} }
void HCallKnownGlobal::PrintDataTo(StringStream* stream) const { void HCallKnownGlobal::PrintDataTo(StringStream* stream) {
stream->Add("o ", target()->shared()->DebugName()); stream->Add("o ", target()->shared()->DebugName());
HCall::PrintDataTo(stream); stream->Add("#%d", argument_count());
} }
void HCallRuntime::PrintDataTo(StringStream* stream) const { void HCallRuntime::PrintDataTo(StringStream* stream) {
stream->Add("%o ", *name()); stream->Add("%o ", *name());
HCall::PrintDataTo(stream); stream->Add("#%d", argument_count());
} }
void HClassOfTest::PrintDataTo(StringStream* stream) const { void HClassOfTest::PrintDataTo(StringStream* stream) {
stream->Add("class_of_test("); stream->Add("class_of_test(");
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
stream->Add(", \"%o\")", *class_name()); stream->Add(", \"%o\")", *class_name());
} }
void HAccessArgumentsAt::PrintDataTo(StringStream* stream) const { void HAccessArgumentsAt::PrintDataTo(StringStream* stream) {
arguments()->PrintNameTo(stream); arguments()->PrintNameTo(stream);
stream->Add("["); stream->Add("[");
index()->PrintNameTo(stream); index()->PrintNameTo(stream);
@ -632,7 +603,7 @@ void HAccessArgumentsAt::PrintDataTo(StringStream* stream) const {
} }
void HControlInstruction::PrintDataTo(StringStream* stream) const { void HControlInstruction::PrintDataTo(StringStream* stream) {
if (FirstSuccessor() != NULL) { if (FirstSuccessor() != NULL) {
int first_id = FirstSuccessor()->block_id(); int first_id = FirstSuccessor()->block_id();
if (SecondSuccessor() == NULL) { if (SecondSuccessor() == NULL) {
@ -645,13 +616,13 @@ void HControlInstruction::PrintDataTo(StringStream* stream) const {
} }
void HUnaryControlInstruction::PrintDataTo(StringStream* stream) const { void HUnaryControlInstruction::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
HControlInstruction::PrintDataTo(stream); HControlInstruction::PrintDataTo(stream);
} }
void HCompareMap::PrintDataTo(StringStream* stream) const { void HCompareMap::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
stream->Add(" (%p)", *map()); stream->Add(" (%p)", *map());
HControlInstruction::PrintDataTo(stream); HControlInstruction::PrintDataTo(stream);
@ -679,19 +650,19 @@ const char* HUnaryMathOperation::OpName() const {
} }
void HUnaryMathOperation::PrintDataTo(StringStream* stream) const { void HUnaryMathOperation::PrintDataTo(StringStream* stream) {
const char* name = OpName(); const char* name = OpName();
stream->Add("%s ", name); stream->Add("%s ", name);
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
} }
void HUnaryOperation::PrintDataTo(StringStream* stream) const { void HUnaryOperation::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
} }
void HHasInstanceType::PrintDataTo(StringStream* stream) const { void HHasInstanceType::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
switch (from_) { switch (from_) {
case FIRST_JS_OBJECT_TYPE: case FIRST_JS_OBJECT_TYPE:
@ -712,14 +683,14 @@ void HHasInstanceType::PrintDataTo(StringStream* stream) const {
} }
void HTypeofIs::PrintDataTo(StringStream* stream) const { void HTypeofIs::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
stream->Add(" == "); stream->Add(" == ");
stream->Add(type_literal_->ToAsciiVector()); stream->Add(type_literal_->ToAsciiVector());
} }
void HChange::PrintDataTo(StringStream* stream) const { void HChange::PrintDataTo(StringStream* stream) {
HUnaryOperation::PrintDataTo(stream); HUnaryOperation::PrintDataTo(stream);
stream->Add(" %s to %s", from_.Mnemonic(), to_.Mnemonic()); stream->Add(" %s to %s", from_.Mnemonic(), to_.Mnemonic());
@ -735,26 +706,26 @@ HCheckInstanceType* HCheckInstanceType::NewIsJSObjectOrJSFunction(
} }
void HCheckMap::PrintDataTo(StringStream* stream) const { void HCheckMap::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
stream->Add(" %p", *map()); stream->Add(" %p", *map());
} }
void HCheckFunction::PrintDataTo(StringStream* stream) const { void HCheckFunction::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
stream->Add(" %p", *target()); stream->Add(" %p", *target());
} }
void HCallStub::PrintDataTo(StringStream* stream) const { void HCallStub::PrintDataTo(StringStream* stream) {
stream->Add("%s ", stream->Add("%s ",
CodeStub::MajorName(major_key_, false)); CodeStub::MajorName(major_key_, false));
HUnaryCall::PrintDataTo(stream); HUnaryCall::PrintDataTo(stream);
} }
void HInstanceOf::PrintDataTo(StringStream* stream) const { void HInstanceOf::PrintDataTo(StringStream* stream) {
left()->PrintNameTo(stream); left()->PrintNameTo(stream);
stream->Add(" "); stream->Add(" ");
right()->PrintNameTo(stream); right()->PrintNameTo(stream);
@ -899,7 +870,7 @@ Range* HMod::InferRange() {
} }
void HPhi::PrintTo(StringStream* stream) const { void HPhi::PrintTo(StringStream* stream) {
stream->Add("["); stream->Add("[");
for (int i = 0; i < OperandCount(); ++i) { for (int i = 0; i < OperandCount(); ++i) {
HValue* value = OperandAt(i); HValue* value = OperandAt(i);
@ -925,7 +896,7 @@ void HPhi::AddInput(HValue* value) {
} }
HValue* HPhi::GetRedundantReplacement() const { HValue* HPhi::GetRedundantReplacement() {
HValue* candidate = NULL; HValue* candidate = NULL;
int count = OperandCount(); int count = OperandCount();
int position = 0; int position = 0;
@ -977,7 +948,7 @@ void HPhi::AddIndirectUsesTo(int* dest) {
} }
void HSimulate::PrintDataTo(StringStream* stream) const { void HSimulate::PrintDataTo(StringStream* stream) {
stream->Add("id=%d ", ast_id()); stream->Add("id=%d ", ast_id());
if (pop_count_ > 0) stream->Add("pop %d", pop_count_); if (pop_count_ > 0) stream->Add("pop %d", pop_count_);
if (values_.length() > 0) { if (values_.length() > 0) {
@ -994,7 +965,7 @@ void HSimulate::PrintDataTo(StringStream* stream) const {
} }
void HEnterInlined::PrintDataTo(StringStream* stream) const { void HEnterInlined::PrintDataTo(StringStream* stream) {
SmartPointer<char> name = function()->debug_name()->ToCString(); SmartPointer<char> name = function()->debug_name()->ToCString();
stream->Add("%s, id=%d", *name, function()->id()); stream->Add("%s, id=%d", *name, function()->id());
} }
@ -1035,7 +1006,7 @@ HConstant* HConstant::CopyToTruncatedInt32() const {
} }
void HConstant::PrintDataTo(StringStream* stream) const { void HConstant::PrintDataTo(StringStream* stream) {
handle()->ShortPrint(stream); handle()->ShortPrint(stream);
} }
@ -1045,7 +1016,7 @@ bool HArrayLiteral::IsCopyOnWrite() const {
} }
void HBinaryOperation::PrintDataTo(StringStream* stream) const { void HBinaryOperation::PrintDataTo(StringStream* stream) {
left()->PrintNameTo(stream); left()->PrintNameTo(stream);
stream->Add(" "); stream->Add(" ");
right()->PrintNameTo(stream); right()->PrintNameTo(stream);
@ -1129,7 +1100,7 @@ Range* HShl::InferRange() {
void HCompare::PrintDataTo(StringStream* stream) const { void HCompare::PrintDataTo(StringStream* stream) {
stream->Add(Token::Name(token())); stream->Add(Token::Name(token()));
stream->Add(" "); stream->Add(" ");
HBinaryOperation::PrintDataTo(stream); HBinaryOperation::PrintDataTo(stream);
@ -1148,18 +1119,18 @@ void HCompare::SetInputRepresentation(Representation r) {
} }
void HParameter::PrintDataTo(StringStream* stream) const { void HParameter::PrintDataTo(StringStream* stream) {
stream->Add("%u", index()); stream->Add("%u", index());
} }
void HLoadNamedField::PrintDataTo(StringStream* stream) const { void HLoadNamedField::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream); object()->PrintNameTo(stream);
stream->Add(" @%d%s", offset(), is_in_object() ? "[in-object]" : ""); stream->Add(" @%d%s", offset(), is_in_object() ? "[in-object]" : "");
} }
void HLoadKeyed::PrintDataTo(StringStream* stream) const { void HLoadKeyedFastElement::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream); object()->PrintNameTo(stream);
stream->Add("["); stream->Add("[");
key()->PrintNameTo(stream); key()->PrintNameTo(stream);
@ -1167,7 +1138,15 @@ void HLoadKeyed::PrintDataTo(StringStream* stream) const {
} }
void HLoadPixelArrayElement::PrintDataTo(StringStream* stream) const { void HLoadKeyedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
stream->Add("[");
key()->PrintNameTo(stream);
stream->Add("]");
}
void HLoadPixelArrayElement::PrintDataTo(StringStream* stream) {
external_pointer()->PrintNameTo(stream); external_pointer()->PrintNameTo(stream);
stream->Add("["); stream->Add("[");
key()->PrintNameTo(stream); key()->PrintNameTo(stream);
@ -1175,7 +1154,7 @@ void HLoadPixelArrayElement::PrintDataTo(StringStream* stream) const {
} }
void HStoreNamed::PrintDataTo(StringStream* stream) const { void HStoreNamedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream); object()->PrintNameTo(stream);
stream->Add("."); stream->Add(".");
ASSERT(name()->IsString()); ASSERT(name()->IsString());
@ -1185,15 +1164,20 @@ void HStoreNamed::PrintDataTo(StringStream* stream) const {
} }
void HStoreNamedField::PrintDataTo(StringStream* stream) const { void HStoreNamedField::PrintDataTo(StringStream* stream) {
HStoreNamed::PrintDataTo(stream); object()->PrintNameTo(stream);
stream->Add(".");
ASSERT(name()->IsString());
stream->Add(*String::cast(*name())->ToCString());
stream->Add(" = ");
value()->PrintNameTo(stream);
if (!transition().is_null()) { if (!transition().is_null()) {
stream->Add(" (transition map %p)", *transition()); stream->Add(" (transition map %p)", *transition());
} }
} }
void HStoreKeyed::PrintDataTo(StringStream* stream) const { void HStoreKeyedFastElement::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream); object()->PrintNameTo(stream);
stream->Add("["); stream->Add("[");
key()->PrintNameTo(stream); key()->PrintNameTo(stream);
@ -1202,25 +1186,43 @@ void HStoreKeyed::PrintDataTo(StringStream* stream) const {
} }
void HLoadGlobal::PrintDataTo(StringStream* stream) const { void HStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
stream->Add("[");
key()->PrintNameTo(stream);
stream->Add("] = ");
value()->PrintNameTo(stream);
}
void HStorePixelArrayElement::PrintDataTo(StringStream* stream) {
external_pointer()->PrintNameTo(stream);
stream->Add("[");
key()->PrintNameTo(stream);
stream->Add("] = ");
value()->PrintNameTo(stream);
}
void HLoadGlobal::PrintDataTo(StringStream* stream) {
stream->Add("[%p]", *cell()); stream->Add("[%p]", *cell());
if (check_hole_value()) stream->Add(" (deleteable/read-only)"); if (check_hole_value()) stream->Add(" (deleteable/read-only)");
} }
void HStoreGlobal::PrintDataTo(StringStream* stream) const { void HStoreGlobal::PrintDataTo(StringStream* stream) {
stream->Add("[%p] = ", *cell()); stream->Add("[%p] = ", *cell());
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
} }
void HLoadContextSlot::PrintDataTo(StringStream* stream) const { void HLoadContextSlot::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
stream->Add("[%d]", slot_index()); stream->Add("[%d]", slot_index());
} }
void HStoreContextSlot::PrintDataTo(StringStream* stream) const { void HStoreContextSlot::PrintDataTo(StringStream* stream) {
context()->PrintNameTo(stream); context()->PrintNameTo(stream);
stream->Add("[%d] = ", slot_index()); stream->Add("[%d] = ", slot_index());
value()->PrintNameTo(stream); value()->PrintNameTo(stream);
@ -1230,33 +1232,33 @@ void HStoreContextSlot::PrintDataTo(StringStream* stream) const {
// Implementation of type inference and type conversions. Calculates // Implementation of type inference and type conversions. Calculates
// the inferred type of this instruction based on the input operands. // the inferred type of this instruction based on the input operands.
HType HValue::CalculateInferredType() const { HType HValue::CalculateInferredType() {
return type_; return type_;
} }
HType HCheckMap::CalculateInferredType() const { HType HCheckMap::CalculateInferredType() {
return value()->type(); return value()->type();
} }
HType HCheckFunction::CalculateInferredType() const { HType HCheckFunction::CalculateInferredType() {
return value()->type(); return value()->type();
} }
HType HCheckNonSmi::CalculateInferredType() const { HType HCheckNonSmi::CalculateInferredType() {
// TODO(kasperl): Is there any way to signal that this isn't a smi? // TODO(kasperl): Is there any way to signal that this isn't a smi?
return HType::Tagged(); return HType::Tagged();
} }
HType HCheckSmi::CalculateInferredType() const { HType HCheckSmi::CalculateInferredType() {
return HType::Smi(); return HType::Smi();
} }
HType HPhi::CalculateInferredType() const { HType HPhi::CalculateInferredType() {
HType result = HType::Uninitialized(); HType result = HType::Uninitialized();
for (int i = 0; i < OperandCount(); ++i) { for (int i = 0; i < OperandCount(); ++i) {
HType current = OperandAt(i)->type(); HType current = OperandAt(i)->type();
@ -1266,77 +1268,77 @@ HType HPhi::CalculateInferredType() const {
} }
HType HConstant::CalculateInferredType() const { HType HConstant::CalculateInferredType() {
return constant_type_; return constant_type_;
} }
HType HCompare::CalculateInferredType() const { HType HCompare::CalculateInferredType() {
return HType::Boolean(); return HType::Boolean();
} }
HType HCompareJSObjectEq::CalculateInferredType() const { HType HCompareJSObjectEq::CalculateInferredType() {
return HType::Boolean(); return HType::Boolean();
} }
HType HUnaryPredicate::CalculateInferredType() const { HType HUnaryPredicate::CalculateInferredType() {
return HType::Boolean(); return HType::Boolean();
} }
HType HBitwiseBinaryOperation::CalculateInferredType() const { HType HBitwiseBinaryOperation::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HArithmeticBinaryOperation::CalculateInferredType() const { HType HArithmeticBinaryOperation::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HAdd::CalculateInferredType() const { HType HAdd::CalculateInferredType() {
return HType::Tagged(); return HType::Tagged();
} }
HType HBitAnd::CalculateInferredType() const { HType HBitAnd::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HBitXor::CalculateInferredType() const { HType HBitXor::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HBitOr::CalculateInferredType() const { HType HBitOr::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HBitNot::CalculateInferredType() const { HType HBitNot::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HUnaryMathOperation::CalculateInferredType() const { HType HUnaryMathOperation::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HShl::CalculateInferredType() const { HType HShl::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HShr::CalculateInferredType() const { HType HShr::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }
HType HSar::CalculateInferredType() const { HType HSar::CalculateInferredType() {
return HType::TaggedNumber(); return HType::TaggedNumber();
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

195
deps/v8/src/hydrogen.h vendored
View File

@ -196,94 +196,54 @@ class HSubgraph: public ZoneObject {
explicit HSubgraph(HGraph* graph) explicit HSubgraph(HGraph* graph)
: graph_(graph), : graph_(graph),
entry_block_(NULL), entry_block_(NULL),
exit_block_(NULL), exit_block_(NULL) {
break_continue_info_(4) {
} }
HGraph* graph() const { return graph_; } HGraph* graph() const { return graph_; }
HEnvironment* environment() const {
ASSERT(HasExit());
return exit_block_->last_environment();
}
bool HasExit() const { return exit_block_ != NULL; }
void PreProcessOsrEntry(IterationStatement* statement);
void AppendOptional(HSubgraph* graph,
bool on_true_branch,
HValue* boolean_value);
void AppendJoin(HSubgraph* then_graph, HSubgraph* else_graph, AstNode* node);
void AppendWhile(HSubgraph* condition,
HSubgraph* body,
IterationStatement* statement,
HSubgraph* continue_subgraph,
HSubgraph* exit);
void AppendDoWhile(HSubgraph* body,
IterationStatement* statement,
HSubgraph* go_back,
HSubgraph* exit);
void AppendEndless(HSubgraph* body, IterationStatement* statement);
void Append(HSubgraph* next, BreakableStatement* statement);
void ResolveContinue(IterationStatement* statement);
HBasicBlock* BundleBreak(BreakableStatement* statement);
HBasicBlock* BundleContinue(IterationStatement* statement);
HBasicBlock* BundleBreakContinue(BreakableStatement* statement,
bool is_continue,
int join_id);
HBasicBlock* JoinBlocks(HBasicBlock* a, HBasicBlock* b, int id);
void FinishExit(HControlInstruction* instruction);
void FinishBreakContinue(BreakableStatement* target, bool is_continue);
void Initialize(HBasicBlock* block) {
ASSERT(entry_block_ == NULL);
entry_block_ = block;
exit_block_ = block;
}
HBasicBlock* entry_block() const { return entry_block_; } HBasicBlock* entry_block() const { return entry_block_; }
HBasicBlock* exit_block() const { return exit_block_; } HBasicBlock* exit_block() const { return exit_block_; }
void set_exit_block(HBasicBlock* block) { void set_exit_block(HBasicBlock* block) {
exit_block_ = block; exit_block_ = block;
} }
void ConnectExitTo(HBasicBlock* other, bool include_stack_check = false) { void PreProcessOsrEntry(IterationStatement* statement);
if (HasExit()) {
exit_block()->Goto(other, include_stack_check);
}
}
void AddBreakContinueInfo(HSubgraph* other) { void AppendJoin(HBasicBlock* first, HBasicBlock* second, int join_id);
break_continue_info_.AddAll(other->break_continue_info_); void AppendWhile(IterationStatement* statement,
HBasicBlock* condition_entry,
HBasicBlock* exit_block,
HBasicBlock* body_exit,
HBasicBlock* break_block,
HBasicBlock* loop_entry,
HBasicBlock* loop_exit);
void AppendDoWhile(IterationStatement* statement,
HBasicBlock* body_entry,
HBasicBlock* go_back,
HBasicBlock* exit_block,
HBasicBlock* break_block);
void AppendEndless(IterationStatement* statement,
HBasicBlock* body_entry,
HBasicBlock* body_exit,
HBasicBlock* break_block);
void Append(BreakableStatement* stmt,
HBasicBlock* entry_block,
HBasicBlock* exit_block,
HBasicBlock* break_block);
void ResolveContinue(IterationStatement* statement,
HBasicBlock* continue_block);
HBasicBlock* JoinBlocks(HBasicBlock* a, HBasicBlock* b, int id);
void FinishExit(HControlInstruction* instruction);
void Initialize(HBasicBlock* block) {
ASSERT(entry_block_ == NULL);
entry_block_ = block;
exit_block_ = block;
} }
protected: protected:
class BreakContinueInfo: public ZoneObject {
public:
BreakContinueInfo(BreakableStatement* target, HBasicBlock* block,
bool is_continue)
: target_(target), block_(block), continue_(is_continue) {}
BreakableStatement* target() const { return target_; }
HBasicBlock* block() const { return block_; }
bool is_continue() const { return continue_; }
bool IsResolved() const { return block_ == NULL; }
void Resolve() { block_ = NULL; }
private:
BreakableStatement* target_;
HBasicBlock* block_;
bool continue_;
};
const ZoneList<BreakContinueInfo*>* break_continue_info() const {
return &break_continue_info_;
}
HGraph* graph_; // The graph this is a subgraph of. HGraph* graph_; // The graph this is a subgraph of.
HBasicBlock* entry_block_; HBasicBlock* entry_block_;
HBasicBlock* exit_block_; HBasicBlock* exit_block_;
private:
ZoneList<BreakContinueInfo*> break_continue_info_;
}; };
@ -621,6 +581,53 @@ class TestContext: public AstContext {
class HGraphBuilder: public AstVisitor { class HGraphBuilder: public AstVisitor {
public: public:
enum BreakType { BREAK, CONTINUE };
// A class encapsulating (lazily-allocated) break and continue blocks for
// a breakable statement. Separated from BreakAndContinueScope so that it
// can have a separate lifetime.
class BreakAndContinueInfo BASE_EMBEDDED {
public:
explicit BreakAndContinueInfo(BreakableStatement* target)
: target_(target), break_block_(NULL), continue_block_(NULL) {
}
BreakableStatement* target() { return target_; }
HBasicBlock* break_block() { return break_block_; }
void set_break_block(HBasicBlock* block) { break_block_ = block; }
HBasicBlock* continue_block() { return continue_block_; }
void set_continue_block(HBasicBlock* block) { continue_block_ = block; }
private:
BreakableStatement* target_;
HBasicBlock* break_block_;
HBasicBlock* continue_block_;
};
// A helper class to maintain a stack of current BreakAndContinueInfo
// structures mirroring BreakableStatement nesting.
class BreakAndContinueScope BASE_EMBEDDED {
public:
BreakAndContinueScope(BreakAndContinueInfo* info, HGraphBuilder* owner)
: info_(info), owner_(owner), next_(owner->break_scope()) {
owner->set_break_scope(this);
}
~BreakAndContinueScope() { owner_->set_break_scope(next_); }
BreakAndContinueInfo* info() { return info_; }
HGraphBuilder* owner() { return owner_; }
BreakAndContinueScope* next() { return next_; }
// Search the break stack for a break or continue target.
HBasicBlock* Get(BreakableStatement* stmt, BreakType type);
private:
BreakAndContinueInfo* info_;
HGraphBuilder* owner_;
BreakAndContinueScope* next_;
};
explicit HGraphBuilder(TypeFeedbackOracle* oracle) explicit HGraphBuilder(TypeFeedbackOracle* oracle)
: oracle_(oracle), : oracle_(oracle),
graph_(NULL), graph_(NULL),
@ -629,16 +636,25 @@ class HGraphBuilder: public AstVisitor {
ast_context_(NULL), ast_context_(NULL),
call_context_(NULL), call_context_(NULL),
function_return_(NULL), function_return_(NULL),
inlined_count_(0) { } inlined_count_(0),
break_scope_(NULL) {
}
HGraph* CreateGraph(CompilationInfo* info); HGraph* CreateGraph(CompilationInfo* info);
// Simple accessors. // Simple accessors.
HGraph* graph() const { return graph_; } HGraph* graph() const { return graph_; }
HSubgraph* subgraph() const { return current_subgraph_; } HSubgraph* subgraph() const { return current_subgraph_; }
BreakAndContinueScope* break_scope() const { return break_scope_; }
void set_break_scope(BreakAndContinueScope* head) { break_scope_ = head; }
HEnvironment* environment() const { return subgraph()->environment(); } HBasicBlock* current_block() const { return subgraph()->exit_block(); }
HBasicBlock* CurrentBlock() const { return subgraph()->exit_block(); } void set_current_block(HBasicBlock* block) {
subgraph()->set_exit_block(block);
}
HEnvironment* environment() const {
return current_block()->last_environment();
}
// Adding instructions. // Adding instructions.
HInstruction* AddInstruction(HInstruction* instr); HInstruction* AddInstruction(HInstruction* instr);
@ -650,8 +666,7 @@ class HGraphBuilder: public AstVisitor {
private: private:
// Type of a member function that generates inline code for a native function. // Type of a member function that generates inline code for a native function.
typedef void (HGraphBuilder::*InlineFunctionGenerator)(int argument_count, typedef void (HGraphBuilder::*InlineFunctionGenerator)(CallRuntime* call);
int ast_id);
// Forward declarations for inner scope classes. // Forward declarations for inner scope classes.
class SubgraphScope; class SubgraphScope;
@ -675,7 +690,7 @@ class HGraphBuilder: public AstVisitor {
// Generators for inline runtime functions. // Generators for inline runtime functions.
#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \ #define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \
void Generate##Name(int argument_count, int ast_id); void Generate##Name(CallRuntime* call);
INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
@ -684,9 +699,10 @@ class HGraphBuilder: public AstVisitor {
void Bailout(const char* reason); void Bailout(const char* reason);
void AppendPeeledWhile(IterationStatement* stmt, void AppendPeeledWhile(IterationStatement* stmt,
HSubgraph* cond_graph, HBasicBlock* condition_entry,
HSubgraph* body_graph, HBasicBlock* exit_block,
HSubgraph* exit_graph); HBasicBlock* body_exit,
HBasicBlock* break_block);
void AddToSubgraph(HSubgraph* graph, ZoneList<Statement*>* stmts); void AddToSubgraph(HSubgraph* graph, ZoneList<Statement*>* stmts);
void AddToSubgraph(HSubgraph* graph, Statement* stmt); void AddToSubgraph(HSubgraph* graph, Statement* stmt);
@ -702,17 +718,21 @@ class HGraphBuilder: public AstVisitor {
HBasicBlock* true_block, HBasicBlock* true_block,
HBasicBlock* false_block); HBasicBlock* false_block);
// Visit an argument subexpression. // Visit an argument subexpression and emit a push to the outgoing
// arguments.
void VisitArgument(Expression* expr); void VisitArgument(Expression* expr);
void VisitArgumentList(ZoneList<Expression*>* arguments); void VisitArgumentList(ZoneList<Expression*>* arguments);
// Visit a list of expressions from left to right, each in a value context.
void VisitExpressions(ZoneList<Expression*>* exprs);
void AddPhi(HPhi* phi); void AddPhi(HPhi* phi);
void PushAndAdd(HInstruction* instr); void PushAndAdd(HInstruction* instr);
// Remove the arguments from the bailout environment and emit instructions // Remove the arguments from the bailout environment and emit instructions
// to push them as outgoing parameters. // to push them as outgoing parameters.
void PreProcessCall(HCall* call); template <int V> HInstruction* PreProcessCall(HCall<V>* call);
void AssumeRepresentation(HValue* value, Representation r); void AssumeRepresentation(HValue* value, Representation r);
static Representation ToRepresentation(TypeInfo info); static Representation ToRepresentation(TypeInfo info);
@ -724,8 +744,6 @@ class HGraphBuilder: public AstVisitor {
AST_NODE_LIST(DECLARE_VISIT) AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT #undef DECLARE_VISIT
bool ShouldPeel(HSubgraph* cond, HSubgraph* body);
HBasicBlock* CreateBasicBlock(HEnvironment* env); HBasicBlock* CreateBasicBlock(HEnvironment* env);
HSubgraph* CreateEmptySubgraph(); HSubgraph* CreateEmptySubgraph();
HSubgraph* CreateGotoSubgraph(HEnvironment* env); HSubgraph* CreateGotoSubgraph(HEnvironment* env);
@ -816,6 +834,11 @@ class HGraphBuilder: public AstVisitor {
HValue* val, HValue* val,
Expression* expr); Expression* expr);
HInstruction* BuildStoreKeyedPixelArrayElement(HValue* object,
HValue* key,
HValue* val,
Expression* expr);
HCompare* BuildSwitchCompare(HSubgraph* subgraph, HCompare* BuildSwitchCompare(HSubgraph* subgraph,
HValue* switch_value, HValue* switch_value,
CaseClause* clause); CaseClause* clause);
@ -853,6 +876,8 @@ class HGraphBuilder: public AstVisitor {
int inlined_count_; int inlined_count_;
BreakAndContinueScope* break_scope_;
friend class AstContext; // Pushes and pops the AST context stack. friend class AstContext; // Pushes and pops the AST context stack.
DISALLOW_COPY_AND_ASSIGN(HGraphBuilder); DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);

View File

@ -183,13 +183,6 @@ const XMMRegister xmm7 = { 7 };
typedef XMMRegister DoubleRegister; typedef XMMRegister DoubleRegister;
// Index of register used in pusha/popa.
// Order of pushed registers: EAX, ECX, EDX, EBX, ESP, EBP, ESI, and EDI
inline int EspIndexForPushAll(Register reg) {
return Register::kNumRegisters - 1 - reg.code();
}
enum Condition { enum Condition {
// any value < 0 is considered no_condition // any value < 0 is considered no_condition
no_condition = -1, no_condition = -1,
@ -980,6 +973,10 @@ class Assembler : public Malloced {
PositionsRecorder* positions_recorder() { return &positions_recorder_; } PositionsRecorder* positions_recorder() { return &positions_recorder_; }
int relocation_writer_size() {
return (buffer_ + buffer_size_) - reloc_info_writer.pos();
}
// Avoid overflows for displacements etc. // Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512*MB; static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB; static const int kMinimalBufferSize = 4*KB;

View File

@ -2385,14 +2385,14 @@ void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) { void TypeRecordingBinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
NearLabel call_runtime; ASSERT(op_ == Token::ADD);
NearLabel left_not_string, call_runtime;
// Registers containing left and right operands respectively. // Registers containing left and right operands respectively.
Register left = edx; Register left = edx;
Register right = eax; Register right = eax;
// Test if left operand is a string. // Test if left operand is a string.
NearLabel left_not_string;
__ test(left, Immediate(kSmiTagMask)); __ test(left, Immediate(kSmiTagMask));
__ j(zero, &left_not_string); __ j(zero, &left_not_string);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx); __ CmpObjectType(left, FIRST_NONSTRING_TYPE, ecx);

View File

@ -8234,8 +8234,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
Variable* variable = node->expression()->AsVariableProxy()->AsVariable(); Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
if (variable != NULL) { if (variable != NULL) {
// Delete of an unqualified identifier is disallowed in strict mode // Delete of an unqualified identifier is disallowed in strict mode
// so this code can only be reached in non-strict mode. // but "delete this" is.
ASSERT(strict_mode_flag() == kNonStrictMode); ASSERT(strict_mode_flag() == kNonStrictMode || variable->is_this());
Slot* slot = variable->AsSlot(); Slot* slot = variable->AsSlot();
if (variable->is_global()) { if (variable->is_global()) {
LoadGlobal(); LoadGlobal();
@ -8244,7 +8244,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
Result answer = frame_->InvokeBuiltin(Builtins::DELETE, Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
CALL_FUNCTION, 3); CALL_FUNCTION, 3);
frame_->Push(&answer); frame_->Push(&answer);
return;
} else if (slot != NULL && slot->type() == Slot::LOOKUP) { } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
// Call the runtime to delete from the context holding the named // Call the runtime to delete from the context holding the named
@ -8255,13 +8254,11 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
frame_->EmitPush(Immediate(variable->name())); frame_->EmitPush(Immediate(variable->name()));
Result answer = frame_->CallRuntime(Runtime::kDeleteContextSlot, 2); Result answer = frame_->CallRuntime(Runtime::kDeleteContextSlot, 2);
frame_->Push(&answer); frame_->Push(&answer);
return; } else {
// Default: Result of deleting non-global, not dynamically
// introduced variables is false.
frame_->Push(Factory::false_value());
} }
// Default: Result of deleting non-global, not dynamically
// introduced variables is false.
frame_->Push(Factory::false_value());
} else { } else {
// Default: Result of deleting expressions is true. // Default: Result of deleting expressions is true.
Load(node->expression()); // may have side-effects Load(node->expression()); // may have side-effects

View File

@ -431,14 +431,16 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
fp_value, output_offset, value); fp_value, output_offset, value);
} }
// The context can be gotten from the function so long as we don't // For the bottommost output frame the context can be gotten from the input
// optimize functions that need local contexts. // frame. For all subsequent output frames it can be gotten from the function
// so long as we don't inline functions that need local contexts.
output_offset -= kPointerSize; output_offset -= kPointerSize;
input_offset -= kPointerSize; input_offset -= kPointerSize;
value = reinterpret_cast<uint32_t>(function->context()); if (is_bottommost) {
// The context for the bottommost output frame should also agree with the value = input_->GetFrameSlot(input_offset);
// input frame. } else {
ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value); value = reinterpret_cast<uint32_t>(function->context());
}
output_frame->SetFrameSlot(output_offset, value); output_frame->SetFrameSlot(output_offset, value);
if (is_topmost) output_frame->SetRegister(esi.code(), value); if (is_topmost) output_frame->SetRegister(esi.code(), value);
if (FLAG_trace_deopt) { if (FLAG_trace_deopt) {

View File

@ -3743,8 +3743,8 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} }
} else if (var != NULL) { } else if (var != NULL) {
// Delete of an unqualified identifier is disallowed in strict mode // Delete of an unqualified identifier is disallowed in strict mode
// so this code can only be reached in non-strict mode. // but "delete this" is.
ASSERT(strict_mode_flag() == kNonStrictMode); ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this());
if (var->is_global()) { if (var->is_global()) {
__ push(GlobalObjectOperand()); __ push(GlobalObjectOperand());
__ push(Immediate(var->name())); __ push(Immediate(var->name()));
@ -3782,17 +3782,22 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::NOT: { case Token::NOT: {
Comment cmnt(masm_, "[ UnaryOperation (NOT)"); Comment cmnt(masm_, "[ UnaryOperation (NOT)");
if (context()->IsEffect()) {
Label materialize_true, materialize_false; // Unary NOT has no side effects so it's only necessary to visit the
Label* if_true = NULL; // subexpression. Match the optimizing compiler by not branching.
Label* if_false = NULL; VisitForEffect(expr->expression());
Label* fall_through = NULL; } else {
// Notice that the labels are swapped. Label materialize_true, materialize_false;
context()->PrepareTest(&materialize_true, &materialize_false, Label* if_true = NULL;
&if_false, &if_true, &fall_through); Label* if_false = NULL;
if (context()->IsTest()) ForwardBailoutToChild(expr); Label* fall_through = NULL;
VisitForControl(expr->expression(), if_true, if_false, fall_through); // Notice that the labels are swapped.
context()->Plug(if_false, if_true); // Labels swapped. context()->PrepareTest(&materialize_true, &materialize_false,
&if_false, &if_true, &fall_through);
if (context()->IsTest()) ForwardBailoutToChild(expr);
VisitForControl(expr->expression(), if_true, if_false, fall_through);
context()->Plug(if_false, if_true); // Labels swapped.
}
break; break;
} }

View File

@ -55,7 +55,7 @@ class SafepointGenerator : public PostCallGenerator {
// Ensure that we have enough space in the reloc info to patch // Ensure that we have enough space in the reloc info to patch
// this with calls when doing deoptimization. // this with calls when doing deoptimization.
if (ensure_reloc_space_) { if (ensure_reloc_space_) {
codegen_->masm()->RecordComment(RelocInfo::kFillerCommentString, true); codegen_->EnsureRelocSpaceForDeoptimization();
} }
codegen_->RecordSafepoint(pointers_, deoptimization_index_); codegen_->RecordSafepoint(pointers_, deoptimization_index_);
} }
@ -78,6 +78,7 @@ bool LCodeGen::GenerateCode() {
return GeneratePrologue() && return GeneratePrologue() &&
GenerateBody() && GenerateBody() &&
GenerateDeferredCode() && GenerateDeferredCode() &&
GenerateRelocPadding() &&
GenerateSafepointTable(); GenerateSafepointTable();
} }
@ -122,6 +123,16 @@ void LCodeGen::Comment(const char* format, ...) {
} }
bool LCodeGen::GenerateRelocPadding() {
int reloc_size = masm()->relocation_writer_size();
while (reloc_size < deoptimization_reloc_size.min_size) {
__ RecordComment(RelocInfo::kFillerCommentString, true);
reloc_size += RelocInfo::kRelocCommentSize;
}
return !is_aborted();
}
bool LCodeGen::GeneratePrologue() { bool LCodeGen::GeneratePrologue() {
ASSERT(is_generating()); ASSERT(is_generating());
@ -163,6 +174,45 @@ bool LCodeGen::GeneratePrologue() {
} }
} }
// Possibly allocate a local context.
int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
if (heap_slots > 0) {
Comment(";;; Allocate local context");
// Argument to NewContext is the function, which is still in edi.
__ push(edi);
if (heap_slots <= FastNewContextStub::kMaximumSlots) {
FastNewContextStub stub(heap_slots);
__ CallStub(&stub);
} else {
__ CallRuntime(Runtime::kNewContext, 1);
}
RecordSafepoint(Safepoint::kNoDeoptimizationIndex);
// Context is returned in both eax and esi. It replaces the context
// passed to us. It's saved in the stack and kept live in esi.
__ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
// Copy parameters into context if necessary.
int num_parameters = scope()->num_parameters();
for (int i = 0; i < num_parameters; i++) {
Slot* slot = scope()->parameter(i)->AsSlot();
if (slot != NULL && slot->type() == Slot::CONTEXT) {
int parameter_offset = StandardFrameConstants::kCallerSPOffset +
(num_parameters - 1 - i) * kPointerSize;
// Load parameter from stack.
__ mov(eax, Operand(ebp, parameter_offset));
// Store it in the context.
int context_offset = Context::SlotOffset(slot->index());
__ mov(Operand(esi, context_offset), eax);
// Update the write barrier. This clobbers all involved
// registers, so we have to use a third register to avoid
// clobbering esi.
__ mov(ecx, esi);
__ RecordWrite(ecx, context_offset, eax, ebx);
}
}
Comment(";;; End allocate local context");
}
// Trace the call. // Trace the call.
if (FLAG_trace) { if (FLAG_trace) {
// We have not executed any compiled code yet, so esi still holds the // We have not executed any compiled code yet, so esi still holds the
@ -335,6 +385,22 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
} }
void LCodeGen::EnsureRelocSpaceForDeoptimization() {
// Since we patch the reloc info with RUNTIME_ENTRY calls every patch
// site will take up 2 bytes + any pc-jumps.
// We are conservative and always reserver 6 bytes in case where a
// simple pc-jump is not enough.
uint32_t pc_delta =
masm()->pc_offset() - deoptimization_reloc_size.last_pc_offset;
if (is_uintn(pc_delta, 6)) {
deoptimization_reloc_size.min_size += 2;
} else {
deoptimization_reloc_size.min_size += 6;
}
deoptimization_reloc_size.last_pc_offset = masm()->pc_offset();
}
void LCodeGen::AddToTranslation(Translation* translation, void LCodeGen::AddToTranslation(Translation* translation,
LOperand* op, LOperand* op,
bool is_tagged) { bool is_tagged) {
@ -382,10 +448,13 @@ void LCodeGen::CallCode(Handle<Code> code,
ASSERT(instr != NULL); ASSERT(instr != NULL);
LPointerMap* pointers = instr->pointer_map(); LPointerMap* pointers = instr->pointer_map();
RecordPosition(pointers->position()); RecordPosition(pointers->position());
if (!adjusted) { if (!adjusted) {
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
} }
__ call(code, mode); __ call(code, mode);
EnsureRelocSpaceForDeoptimization();
RegisterLazyDeoptimization(instr); RegisterLazyDeoptimization(instr);
// Signal that we don't inline smi code before these stubs in the // Signal that we don't inline smi code before these stubs in the
@ -595,6 +664,12 @@ void LCodeGen::RecordSafepoint(LPointerMap* pointers,
} }
void LCodeGen::RecordSafepoint(int deoptimization_index) {
LPointerMap empty_pointers(RelocInfo::kNoPosition);
RecordSafepoint(&empty_pointers, deoptimization_index);
}
void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
int arguments, int arguments,
int deoptimization_index) { int deoptimization_index) {
@ -1836,7 +1911,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
Label before_push_delta; Label before_push_delta;
__ bind(&before_push_delta); __ bind(&before_push_delta);
__ mov(temp, Immediate(delta)); __ mov(temp, Immediate(delta));
__ mov(Operand(esp, EspIndexForPushAll(temp) * kPointerSize), temp); __ StoreToSafepointRegisterSlot(temp, temp);
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
__ call(stub.GetCode(), RelocInfo::CODE_TARGET); __ call(stub.GetCode(), RelocInfo::CODE_TARGET);
ASSERT_EQ(kAdditionalDelta, ASSERT_EQ(kAdditionalDelta,
@ -1844,8 +1919,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
RecordSafepointWithRegisters( RecordSafepointWithRegisters(
instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
// Put the result value into the eax slot and restore all registers. // Put the result value into the eax slot and restore all registers.
__ mov(Operand(esp, EspIndexForPushAll(eax) * kPointerSize), eax); __ StoreToSafepointRegisterSlot(eax, eax);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }
@ -2100,13 +2174,13 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
void LCodeGen::DoLoadPixelArrayElement(LLoadPixelArrayElement* instr) { void LCodeGen::DoLoadPixelArrayElement(LLoadPixelArrayElement* instr) {
Register external_elements = ToRegister(instr->external_pointer()); Register external_pointer = ToRegister(instr->external_pointer());
Register key = ToRegister(instr->key()); Register key = ToRegister(instr->key());
Register result = ToRegister(instr->result()); Register result = ToRegister(instr->result());
ASSERT(result.is(external_elements)); ASSERT(result.is(external_pointer));
// Load the result. // Load the result.
__ movzx_b(result, Operand(external_elements, key, times_1, 0)); __ movzx_b(result, Operand(external_pointer, key, times_1, 0));
} }
@ -2301,11 +2375,8 @@ void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
if (*function == *graph()->info()->closure()) { if (*function == *graph()->info()->closure()) {
__ CallSelf(); __ CallSelf();
} else { } else {
// This is an indirect call and will not be recorded in the reloc info.
// Add a comment to the reloc info in case we need to patch this during
// deoptimization.
__ RecordComment(RelocInfo::kFillerCommentString, true);
__ call(FieldOperand(edi, JSFunction::kCodeEntryOffset)); __ call(FieldOperand(edi, JSFunction::kCodeEntryOffset));
EnsureRelocSpaceForDeoptimization();
} }
// Setup deoptimization. // Setup deoptimization.
@ -2360,7 +2431,7 @@ void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
if (!tmp.is(eax)) __ mov(tmp, eax); if (!tmp.is(eax)) __ mov(tmp, eax);
// Restore input_reg after call to runtime. // Restore input_reg after call to runtime.
__ mov(input_reg, Operand(esp, EspIndexForPushAll(input_reg) * kPointerSize)); __ LoadFromSafepointRegisterSlot(input_reg, input_reg);
__ bind(&allocated); __ bind(&allocated);
__ mov(tmp2, FieldOperand(input_reg, HeapNumber::kExponentOffset)); __ mov(tmp2, FieldOperand(input_reg, HeapNumber::kExponentOffset));
@ -2368,7 +2439,7 @@ void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
__ mov(FieldOperand(tmp, HeapNumber::kExponentOffset), tmp2); __ mov(FieldOperand(tmp, HeapNumber::kExponentOffset), tmp2);
__ mov(tmp2, FieldOperand(input_reg, HeapNumber::kMantissaOffset)); __ mov(tmp2, FieldOperand(input_reg, HeapNumber::kMantissaOffset));
__ mov(FieldOperand(tmp, HeapNumber::kMantissaOffset), tmp2); __ mov(FieldOperand(tmp, HeapNumber::kMantissaOffset), tmp2);
__ mov(Operand(esp, EspIndexForPushAll(input_reg) * kPointerSize), tmp); __ StoreToSafepointRegisterSlot(input_reg, tmp);
__ bind(&done); __ bind(&done);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
@ -2493,11 +2564,6 @@ void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
XMMRegister xmm_scratch = xmm0; XMMRegister xmm_scratch = xmm0;
XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0)); XMMRegister input_reg = ToDoubleRegister(instr->InputAt(0));
ASSERT(ToDoubleRegister(instr->result()).is(input_reg)); ASSERT(ToDoubleRegister(instr->result()).is(input_reg));
ExternalReference negative_infinity =
ExternalReference::address_of_negative_infinity();
__ movdbl(xmm_scratch, Operand::StaticVariable(negative_infinity));
__ ucomisd(xmm_scratch, input_reg);
DeoptimizeIf(equal, instr->environment());
__ xorpd(xmm_scratch, xmm_scratch); __ xorpd(xmm_scratch, xmm_scratch);
__ addsd(input_reg, xmm_scratch); // Convert -0 to +0. __ addsd(input_reg, xmm_scratch); // Convert -0 to +0.
__ sqrtsd(input_reg, input_reg); __ sqrtsd(input_reg, input_reg);
@ -2731,6 +2797,25 @@ void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
} }
void LCodeGen::DoStorePixelArrayElement(LStorePixelArrayElement* instr) {
Register external_pointer = ToRegister(instr->external_pointer());
Register key = ToRegister(instr->key());
Register value = ToRegister(instr->value());
ASSERT(ToRegister(instr->TempAt(0)).is(eax));
__ mov(eax, value);
{ // Clamp the value to [0..255].
NearLabel done;
__ test(eax, Immediate(0xFFFFFF00));
__ j(zero, &done);
__ setcc(negative, eax); // 1 if negative, 0 if positive.
__ dec_b(eax); // 0 if negative, 255 if positive.
__ bind(&done);
}
__ mov_b(Operand(external_pointer, key, times_1, 0), eax);
}
void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
Register value = ToRegister(instr->value()); Register value = ToRegister(instr->value());
Register elements = ToRegister(instr->object()); Register elements = ToRegister(instr->object());
@ -2840,19 +2925,20 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
__ test(result, Immediate(kStringRepresentationMask)); __ test(result, Immediate(kStringRepresentationMask));
__ j(not_zero, deferred->entry()); __ j(not_zero, deferred->entry());
// Check for 1-byte or 2-byte string. // Check for ASCII or two-byte string.
__ bind(&flat_string); __ bind(&flat_string);
STATIC_ASSERT(kAsciiStringTag != 0); STATIC_ASSERT(kAsciiStringTag != 0);
__ test(result, Immediate(kStringEncodingMask)); __ test(result, Immediate(kStringEncodingMask));
__ j(not_zero, &ascii_string); __ j(not_zero, &ascii_string);
// 2-byte string. // Two-byte string.
// Load the 2-byte character code into the result register. // Load the two-byte character code into the result register.
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
if (instr->index()->IsConstantOperand()) { if (instr->index()->IsConstantOperand()) {
__ movzx_w(result, __ movzx_w(result,
FieldOperand(string, FieldOperand(string,
SeqTwoByteString::kHeaderSize + 2 * const_index)); SeqTwoByteString::kHeaderSize +
(kUC16Size * const_index)));
} else { } else {
__ movzx_w(result, FieldOperand(string, __ movzx_w(result, FieldOperand(string,
index, index,
@ -2908,7 +2994,7 @@ void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
__ AbortIfNotSmi(eax); __ AbortIfNotSmi(eax);
} }
__ SmiUntag(eax); __ SmiUntag(eax);
__ mov(Operand(esp, EspIndexForPushAll(result) * kPointerSize), eax); __ StoreToSafepointRegisterSlot(result, eax);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }
@ -2976,7 +3062,7 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
// TODO(3095996): Put a valid pointer value in the stack slot where the result // TODO(3095996): Put a valid pointer value in the stack slot where the result
// register is stored, as this register is in the pointer map, but contains an // register is stored, as this register is in the pointer map, but contains an
// integer value. // integer value.
__ mov(Operand(esp, EspIndexForPushAll(reg) * kPointerSize), Immediate(0)); __ StoreToSafepointRegisterSlot(reg, Immediate(0));
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
__ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
@ -2988,7 +3074,7 @@ void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
// number. // number.
__ bind(&done); __ bind(&done);
__ movdbl(FieldOperand(reg, HeapNumber::kValueOffset), xmm0); __ movdbl(FieldOperand(reg, HeapNumber::kValueOffset), xmm0);
__ mov(Operand(esp, EspIndexForPushAll(reg) * kPointerSize), reg); __ StoreToSafepointRegisterSlot(reg, reg);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }
@ -3030,7 +3116,7 @@ void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) {
__ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber);
RecordSafepointWithRegisters( RecordSafepointWithRegisters(
instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex);
__ mov(Operand(esp, EspIndexForPushAll(reg) * kPointerSize), eax); __ StoreToSafepointRegisterSlot(reg, eax);
__ PopSafepointRegisters(); __ PopSafepointRegisters();
} }

View File

@ -60,6 +60,7 @@ class LCodeGen BASE_EMBEDDED {
status_(UNUSED), status_(UNUSED),
deferred_(8), deferred_(8),
osr_pc_offset_(-1), osr_pc_offset_(-1),
deoptimization_reloc_size(),
resolver_(this) { resolver_(this) {
PopulateDeoptimizationLiteralsWithInlinedFunctions(); PopulateDeoptimizationLiteralsWithInlinedFunctions();
} }
@ -102,6 +103,8 @@ class LCodeGen BASE_EMBEDDED {
// Emit frame translation commands for an environment. // Emit frame translation commands for an environment.
void WriteTranslation(LEnvironment* environment, Translation* translation); void WriteTranslation(LEnvironment* environment, Translation* translation);
void EnsureRelocSpaceForDeoptimization();
// Declare methods that deal with the individual node types. // Declare methods that deal with the individual node types.
#define DECLARE_DO(type) void Do##type(L##type* node); #define DECLARE_DO(type) void Do##type(L##type* node);
LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO) LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
@ -151,6 +154,9 @@ class LCodeGen BASE_EMBEDDED {
bool GeneratePrologue(); bool GeneratePrologue();
bool GenerateBody(); bool GenerateBody();
bool GenerateDeferredCode(); bool GenerateDeferredCode();
// Pad the reloc info to ensure that we have enough space to patch during
// deoptimization.
bool GenerateRelocPadding();
bool GenerateSafepointTable(); bool GenerateSafepointTable();
void CallCode(Handle<Code> code, RelocInfo::Mode mode, LInstruction* instr, void CallCode(Handle<Code> code, RelocInfo::Mode mode, LInstruction* instr,
@ -204,6 +210,7 @@ class LCodeGen BASE_EMBEDDED {
int arguments, int arguments,
int deoptimization_index); int deoptimization_index);
void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); void RecordSafepoint(LPointerMap* pointers, int deoptimization_index);
void RecordSafepoint(int deoptimization_index);
void RecordSafepointWithRegisters(LPointerMap* pointers, void RecordSafepointWithRegisters(LPointerMap* pointers,
int arguments, int arguments,
int deoptimization_index); int deoptimization_index);
@ -251,6 +258,13 @@ class LCodeGen BASE_EMBEDDED {
ZoneList<LDeferredCode*> deferred_; ZoneList<LDeferredCode*> deferred_;
int osr_pc_offset_; int osr_pc_offset_;
struct DeoptimizationRelocSize {
int min_size;
int last_pc_offset;
};
DeoptimizationRelocSize deoptimization_reloc_size;
// Builder that keeps track of safepoints in the code. The table // Builder that keeps track of safepoints in the code. The table
// itself is emitted at the end of the generated code. // itself is emitted at the end of the generated code.
SafepointTableBuilder safepoints_; SafepointTableBuilder safepoints_;

View File

@ -404,7 +404,7 @@ void LChunk::MarkEmptyBlocks() {
} }
void LStoreNamed::PrintDataTo(StringStream* stream) { void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream); object()->PrintTo(stream);
stream->Add("."); stream->Add(".");
stream->Add(*String::cast(*name())->ToCString()); stream->Add(*String::cast(*name())->ToCString());
@ -413,7 +413,25 @@ void LStoreNamed::PrintDataTo(StringStream* stream) {
} }
void LStoreKeyed::PrintDataTo(StringStream* stream) { void LStoreNamedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
stream->Add(" <- ");
value()->PrintTo(stream);
}
void LStoreKeyedFastElement::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add("[");
key()->PrintTo(stream);
stream->Add("] <- ");
value()->PrintTo(stream);
}
void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream); object()->PrintTo(stream);
stream->Add("["); stream->Add("[");
key()->PrintTo(stream); key()->PrintTo(stream);
@ -1223,7 +1241,7 @@ LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
case kMathSqrt: case kMathSqrt:
return DefineSameAsFirst(result); return DefineSameAsFirst(result);
case kMathPowHalf: case kMathPowHalf:
return AssignEnvironment(DefineSameAsFirst(result)); return DefineSameAsFirst(result);
default: default:
UNREACHABLE(); UNREACHABLE();
return NULL; return NULL;
@ -1840,6 +1858,23 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement(
} }
LInstruction* LChunkBuilder::DoStorePixelArrayElement(
HStorePixelArrayElement* instr) {
ASSERT(instr->value()->representation().IsInteger32());
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
LOperand* val = UseRegister(instr->value());
LOperand* key = UseRegister(instr->key());
// The generated code requires that the clamped value is in a byte
// register. eax is an arbitrary choice to satisfy this requirement.
LOperand* clamped = FixedTemp(eax);
return new LStorePixelArrayElement(external_pointer, key, val, clamped);
}
LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
LOperand* context = UseFixed(instr->context(), esi); LOperand* context = UseFixed(instr->context(), esi);
LOperand* object = UseFixed(instr->object(), edx); LOperand* object = UseFixed(instr->object(), edx);
@ -1923,8 +1958,8 @@ LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) { LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
LDeleteProperty* result = new LDeleteProperty(Use(instr->object()), LDeleteProperty* result =
UseOrConstant(instr->key())); new LDeleteProperty(Use(instr->object()), UseOrConstant(instr->key()));
return MarkAsCall(DefineFixed(result, eax), instr); return MarkAsCall(DefineFixed(result, eax), instr);
} }
@ -1957,8 +1992,10 @@ LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) {
// There are no real uses of the arguments object (we bail out in all other // There are no real uses of the arguments object.
// cases). // arguments.length and element access are supported directly on
// stack arguments, and any real arguments object use causes a bailout.
// So this value is never used.
return NULL; return NULL;
} }

View File

@ -42,8 +42,6 @@ class LCodeGen;
#define LITHIUM_ALL_INSTRUCTION_LIST(V) \ #define LITHIUM_ALL_INSTRUCTION_LIST(V) \
V(ControlInstruction) \ V(ControlInstruction) \
V(Call) \ V(Call) \
V(StoreKeyed) \
V(StoreNamed) \
LITHIUM_CONCRETE_INSTRUCTION_LIST(V) LITHIUM_CONCRETE_INSTRUCTION_LIST(V)
@ -150,6 +148,7 @@ class LCodeGen;
V(StoreKeyedGeneric) \ V(StoreKeyedGeneric) \
V(StoreNamedField) \ V(StoreNamedField) \
V(StoreNamedGeneric) \ V(StoreNamedGeneric) \
V(StorePixelArrayElement) \
V(StringCharCodeAt) \ V(StringCharCodeAt) \
V(StringLength) \ V(StringLength) \
V(SubI) \ V(SubI) \
@ -1580,34 +1579,23 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
}; };
class LStoreNamed: public LTemplateInstruction<0, 2, 1> { class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
public: public:
LStoreNamed(LOperand* obj, LOperand* val) { LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = val; inputs_[1] = val;
}
DECLARE_INSTRUCTION(StoreNamed)
DECLARE_HYDROGEN_ACCESSOR(StoreNamed)
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
};
class LStoreNamedField: public LStoreNamed {
public:
LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp)
: LStoreNamed(obj, val) {
temps_[0] = temp; temps_[0] = temp;
} }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedField) DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
bool is_in_object() { return hydrogen()->is_in_object(); } bool is_in_object() { return hydrogen()->is_in_object(); }
int offset() { return hydrogen()->offset(); } int offset() { return hydrogen()->offset(); }
bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); } bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
@ -1626,6 +1614,8 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 3, 0> {
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic") DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric) DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
virtual void PrintDataTo(StringStream* stream);
LOperand* context() { return inputs_[0]; } LOperand* context() { return inputs_[0]; }
LOperand* object() { return inputs_[1]; } LOperand* object() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; } LOperand* value() { return inputs_[2]; }
@ -1633,15 +1623,17 @@ class LStoreNamedGeneric: public LTemplateInstruction<0, 3, 0> {
}; };
class LStoreKeyed: public LTemplateInstruction<0, 3, 0> { class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> {
public: public:
LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val) { LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = key; inputs_[1] = key;
inputs_[2] = val; inputs_[2] = val;
} }
DECLARE_INSTRUCTION(StoreKeyed) DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
"store-keyed-fast-element")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement)
virtual void PrintDataTo(StringStream* stream); virtual void PrintDataTo(StringStream* stream);
@ -1651,14 +1643,25 @@ class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
}; };
class LStoreKeyedFastElement: public LStoreKeyed { class LStorePixelArrayElement: public LTemplateInstruction<0, 3, 1> {
public: public:
LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) LStorePixelArrayElement(LOperand* external_pointer,
: LStoreKeyed(obj, key, val) {} LOperand* key,
LOperand* val,
LOperand* clamped) {
inputs_[0] = external_pointer;
inputs_[1] = key;
inputs_[2] = val;
temps_[0] = clamped;
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement, DECLARE_CONCRETE_INSTRUCTION(StorePixelArrayElement,
"store-keyed-fast-element") "store-pixel-array-element")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement) DECLARE_HYDROGEN_ACCESSOR(StorePixelArrayElement)
LOperand* external_pointer() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
}; };
@ -1676,6 +1679,8 @@ class LStoreKeyedGeneric: public LTemplateInstruction<0, 4, 0> {
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic") DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
virtual void PrintDataTo(StringStream* stream);
LOperand* context() { return inputs_[0]; } LOperand* context() { return inputs_[0]; }
LOperand* object() { return inputs_[1]; } LOperand* object() { return inputs_[1]; }
LOperand* key() { return inputs_[2]; } LOperand* key() { return inputs_[2]; }

View File

@ -1654,6 +1654,28 @@ void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
} }
// Store the value in register src in the safepoint register stack
// slot for register dst.
void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
mov(SafepointRegisterSlot(dst), src);
}
void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Immediate src) {
mov(SafepointRegisterSlot(dst), src);
}
void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
mov(dst, SafepointRegisterSlot(src));
}
Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
return Operand(esp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
}
int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
// The registers are pushed starting with the lowest encoding, // The registers are pushed starting with the lowest encoding,
// which means that lowest encodings are furthest away from // which means that lowest encodings are furthest away from

View File

@ -143,7 +143,11 @@ class MacroAssembler: public Assembler {
// Push and pop the registers that can hold pointers. // Push and pop the registers that can hold pointers.
void PushSafepointRegisters() { pushad(); } void PushSafepointRegisters() { pushad(); }
void PopSafepointRegisters() { popad(); } void PopSafepointRegisters() { popad(); }
static int SafepointRegisterStackIndex(int reg_code); // Store the value in register/immediate src in the safepoint
// register stack slot for register dst.
void StoreToSafepointRegisterSlot(Register dst, Register src);
void StoreToSafepointRegisterSlot(Register dst, Immediate src);
void LoadFromSafepointRegisterSlot(Register dst, Register src);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// JavaScript invokes // JavaScript invokes
@ -667,6 +671,15 @@ class MacroAssembler: public Assembler {
MUST_USE_RESULT MaybeObject* PopHandleScopeHelper(Register saved, MUST_USE_RESULT MaybeObject* PopHandleScopeHelper(Register saved,
Register scratch, Register scratch,
bool gc_allowed); bool gc_allowed);
// Compute memory operands for safepoint stack slots.
Operand SafepointRegisterSlot(Register reg);
static int SafepointRegisterStackIndex(int reg_code);
// Needs access to SafepointRegisterStackIndex for optimized frame
// traversal.
friend class OptimizedFrame;
}; };

View File

@ -2204,8 +2204,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
break; break;
case STRING_CHECK: case STRING_CHECK:
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
// Check that the object is a string or a symbol. // Check that the object is a string or a symbol.
@ -2220,8 +2221,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
break; break;
case NUMBER_CHECK: { case NUMBER_CHECK: {
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
Label fast; Label fast;
@ -2241,8 +2243,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
} }
case BOOLEAN_CHECK: { case BOOLEAN_CHECK: {
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
Label fast; Label fast;
@ -2586,8 +2589,8 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
// Compute the cell operand to use. // Compute the cell operand to use.
Operand cell_operand = Operand::Cell(Handle<JSGlobalPropertyCell>(cell)); Operand cell_operand = Operand::Cell(Handle<JSGlobalPropertyCell>(cell));
if (Serializer::enabled()) { if (Serializer::enabled()) {
__ mov(ecx, Immediate(Handle<JSGlobalPropertyCell>(cell))); __ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell)));
cell_operand = FieldOperand(ecx, JSGlobalPropertyCell::kValueOffset); cell_operand = FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset);
} }
// Check that the value in the cell is not the hole. If it is, this // Check that the value in the cell is not the hole. If it is, this

93
deps/v8/src/ic.cc vendored
View File

@ -435,16 +435,25 @@ Object* CallICBase::TryCallAsFunction(Object* object) {
} }
void CallICBase::ReceiverToObject(Handle<Object> object) { void CallICBase::ReceiverToObjectIfRequired(Handle<Object> callee,
HandleScope scope; Handle<Object> object) {
Handle<Object> receiver(object); if (callee->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(callee);
if (function->shared()->strict_mode() || function->IsBuiltin()) {
// Do not wrap receiver for strict mode functions or for builtins.
return;
}
}
// Change the receiver to the result of calling ToObject on it. // And only wrap string, number or boolean.
const int argc = this->target()->arguments_count(); if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
StackFrameLocator locator; // Change the receiver to the result of calling ToObject on it.
JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); const int argc = this->target()->arguments_count();
int index = frame->ComputeExpressionsCount() - (argc + 1); StackFrameLocator locator;
frame->SetExpression(index, *Factory::ToObject(object)); JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
int index = frame->ComputeExpressionsCount() - (argc + 1);
frame->SetExpression(index, *Factory::ToObject(object));
}
} }
@ -458,10 +467,6 @@ MaybeObject* CallICBase::LoadFunction(State state,
return TypeError("non_object_property_call", object, name); return TypeError("non_object_property_call", object, name);
} }
if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
ReceiverToObject(object);
}
// Check if the name is trivially convertible to an index and get // Check if the name is trivially convertible to an index and get
// the element if so. // the element if so.
uint32_t index; uint32_t index;
@ -505,6 +510,7 @@ MaybeObject* CallICBase::LoadFunction(State state,
object->GetProperty(*object, &lookup, *name, &attr); object->GetProperty(*object, &lookup, *name, &attr);
if (!maybe_result->ToObject(&result)) return maybe_result; if (!maybe_result->ToObject(&result)) return maybe_result;
} }
if (lookup.type() == INTERCEPTOR) { if (lookup.type() == INTERCEPTOR) {
// If the object does not have the requested property, check which // If the object does not have the requested property, check which
// exception we need to throw. // exception we need to throw.
@ -516,31 +522,37 @@ MaybeObject* CallICBase::LoadFunction(State state,
} }
} }
ASSERT(result != Heap::the_hole_value()); ASSERT(!result->IsTheHole());
if (result->IsJSFunction()) { HandleScope scope;
// Wrap result in a handle because ReceiverToObjectIfRequired may allocate
// new object and cause GC.
Handle<Object> result_handle(result);
// Make receiver an object if the callee requires it. Strict mode or builtin
// functions do not wrap the receiver, non-strict functions and objects
// called as functions do.
ReceiverToObjectIfRequired(result_handle, object);
if (result_handle->IsJSFunction()) {
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
// Handle stepping into a function if step into is active. // Handle stepping into a function if step into is active.
if (Debug::StepInActive()) { if (Debug::StepInActive()) {
// Protect the result in a handle as the debugger can allocate and might // Protect the result in a handle as the debugger can allocate and might
// cause GC. // cause GC.
HandleScope scope; Handle<JSFunction> function(JSFunction::cast(*result_handle));
Handle<JSFunction> function(JSFunction::cast(result));
Debug::HandleStepIn(function, object, fp(), false); Debug::HandleStepIn(function, object, fp(), false);
return *function; return *function;
} }
#endif #endif
return result; return *result_handle;
} }
// Try to find a suitable function delegate for the object at hand. // Try to find a suitable function delegate for the object at hand.
result = TryCallAsFunction(result); result_handle = Handle<Object>(TryCallAsFunction(*result_handle));
MaybeObject* answer = result; if (result_handle->IsJSFunction()) return *result_handle;
if (!result->IsJSFunction()) {
answer = TypeError("property_not_function", object, name); return TypeError("property_not_function", object, name);
}
return answer;
} }
@ -565,8 +577,8 @@ bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
case kStringCharAt: case kStringCharAt:
if (object->IsString()) { if (object->IsString()) {
String* string = String::cast(*object); String* string = String::cast(*object);
// Check that there's the right wrapper in the receiver slot. // Check there's the right string value or wrapper in the receiver slot.
ASSERT(string == JSValue::cast(args[0])->value()); ASSERT(string == args[0] || string == JSValue::cast(args[0])->value());
// If we're in the default (fastest) state and the index is // If we're in the default (fastest) state and the index is
// out of bounds, update the state to record this fact. // out of bounds, update the state to record this fact.
if (*extra_ic_state == DEFAULT_STRING_STUB && if (*extra_ic_state == DEFAULT_STRING_STUB &&
@ -775,10 +787,6 @@ MaybeObject* KeyedCallIC::LoadFunction(State state,
return TypeError("non_object_property_call", object, key); return TypeError("non_object_property_call", object, key);
} }
if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
ReceiverToObject(object);
}
if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) { if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
int argc = target()->arguments_count(); int argc = target()->arguments_count();
InLoopFlag in_loop = target()->ic_in_loop(); InLoopFlag in_loop = target()->ic_in_loop();
@ -793,17 +801,20 @@ MaybeObject* KeyedCallIC::LoadFunction(State state,
#endif #endif
} }
} }
Object* result;
{ MaybeObject* maybe_result = Runtime::GetObjectProperty(object, key); HandleScope scope;
if (!maybe_result->ToObject(&result)) return maybe_result; Handle<Object> result = GetProperty(object, key);
}
if (result->IsJSFunction()) return result; // Make receiver an object if the callee requires it. Strict mode or builtin
result = TryCallAsFunction(result); // functions do not wrap the receiver, non-strict functions and objects
MaybeObject* answer = result; // called as functions do.
if (!result->IsJSFunction()) { ReceiverToObjectIfRequired(result, object);
answer = TypeError("property_not_function", object, key);
} if (result->IsJSFunction()) return *result;
return answer; result = Handle<Object>(TryCallAsFunction(*result));
if (result->IsJSFunction()) return *result;
return TypeError("property_not_function", object, key);
} }

2
deps/v8/src/ic.h vendored
View File

@ -224,7 +224,7 @@ class CallICBase: public IC {
// Otherwise, it returns the undefined value. // Otherwise, it returns the undefined value.
Object* TryCallAsFunction(Object* object); Object* TryCallAsFunction(Object* object);
void ReceiverToObject(Handle<Object> object); void ReceiverToObjectIfRequired(Handle<Object> callee, Handle<Object> object);
static void Clear(Address address, Code* target); static void Clear(Address address, Code* target);
friend class IC; friend class IC;

View File

@ -478,11 +478,6 @@ void LiveRange::ConvertOperands() {
} }
UsePosition* LiveRange::AddUsePosition(LifetimePosition pos) {
return AddUsePosition(pos, CreateAssignedOperand());
}
bool LiveRange::CanCover(LifetimePosition position) const { bool LiveRange::CanCover(LifetimePosition position) const {
if (IsEmpty()) return false; if (IsEmpty()) return false;
return Start().Value() <= position.Value() && return Start().Value() <= position.Value() &&
@ -1098,6 +1093,21 @@ void LAllocator::ResolveControlFlow(LiveRange* range,
} else { } else {
ASSERT(pred->end()->SecondSuccessor() == NULL); ASSERT(pred->end()->SecondSuccessor() == NULL);
gap = GetLastGap(pred); gap = GetLastGap(pred);
// We are going to insert a move before the branch instruction.
// Some branch instructions (e.g. loops' back edges)
// can potentially cause a GC so they have a pointer map.
// By insterting a move we essentially create a copy of a
// value which is invisible to PopulatePointerMaps(), because we store
// it into a location different from the operand of a live range
// covering a branch instruction.
// Thus we need to manually record a pointer.
if (HasTaggedValue(range->id())) {
LInstruction* branch = InstructionAt(pred->last_instruction_index());
if (branch->HasPointerMap()) {
branch->pointer_map()->RecordPointer(cur_op);
}
}
} }
gap->GetOrCreateParallelMove(LGap::START)->AddMove(pred_op, cur_op); gap->GetOrCreateParallelMove(LGap::START)->AddMove(pred_op, cur_op);
} }

View File

@ -286,7 +286,6 @@ class LiveRange: public ZoneObject {
LiveRange* TopLevel() { return (parent_ == NULL) ? this : parent_; } LiveRange* TopLevel() { return (parent_ == NULL) ? this : parent_; }
LiveRange* next() const { return next_; } LiveRange* next() const { return next_; }
bool IsChild() const { return parent() != NULL; } bool IsChild() const { return parent() != NULL; }
bool IsParent() const { return parent() == NULL; }
int id() const { return id_; } int id() const { return id_; }
bool IsFixed() const { return id_ < 0; } bool IsFixed() const { return id_ < 0; }
bool IsEmpty() const { return first_interval() == NULL; } bool IsEmpty() const { return first_interval() == NULL; }
@ -360,7 +359,6 @@ class LiveRange: public ZoneObject {
void EnsureInterval(LifetimePosition start, LifetimePosition end); void EnsureInterval(LifetimePosition start, LifetimePosition end);
void AddUseInterval(LifetimePosition start, LifetimePosition end); void AddUseInterval(LifetimePosition start, LifetimePosition end);
UsePosition* AddUsePosition(LifetimePosition pos, LOperand* operand); UsePosition* AddUsePosition(LifetimePosition pos, LOperand* operand);
UsePosition* AddUsePosition(LifetimePosition pos);
// Shorten the most recently added interval by setting a new start. // Shorten the most recently added interval by setting a new start.
void ShortenTo(LifetimePosition start); void ShortenTo(LifetimePosition start);

View File

@ -536,10 +536,12 @@ class ShallowIterator BASE_EMBEDDED {
inline LEnvironment* env() { return env_; } inline LEnvironment* env() { return env_; }
private: private:
inline bool ShouldSkip(LOperand* op) {
return op == NULL || op->IsConstantOperand() || op->IsArgument();
}
inline int AdvanceToNext(int start) { inline int AdvanceToNext(int start) {
while (start < limit_ && while (start < limit_ && ShouldSkip(env_->values()->at(start))) {
(env_->values()->at(start) == NULL ||
env_->values()->at(start)->IsConstantOperand())) {
start++; start++;
} }
return start; return start;

View File

@ -300,6 +300,8 @@ void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
Append("\\,"); Append("\\,");
} else if (c == '\\') { } else if (c == '\\') {
Append("\\\\"); Append("\\\\");
} else if (c == '\"') {
Append("\"\"");
} else { } else {
Append("%lc", c); Append("%lc", c);
} }

151
deps/v8/src/log.cc vendored
View File

@ -147,7 +147,7 @@ bool Profiler::paused_ = false;
// StackTracer implementation // StackTracer implementation
// //
void StackTracer::Trace(TickSample* sample) { void StackTracer::Trace(TickSample* sample) {
sample->function = NULL; sample->tos = NULL;
sample->frames_count = 0; sample->frames_count = 0;
// Avoid collecting traces while doing GC. // Avoid collecting traces while doing GC.
@ -159,15 +159,9 @@ void StackTracer::Trace(TickSample* sample) {
return; return;
} }
const Address function_address = // Sample potential return address value for frameless invocation of
sample->fp + JavaScriptFrameConstants::kFunctionOffset; // stubs (we'll figure out later, if this value makes sense).
if (SafeStackFrameIterator::IsWithinBounds(sample->sp, js_entry_sp, sample->tos = Memory::Address_at(sample->sp);
function_address)) {
Object* object = Memory::Object_at(function_address);
if (object->IsHeapObject()) {
sample->function = HeapObject::cast(object)->address();
}
}
int i = 0; int i = 0;
const Address callback = Top::external_callback(); const Address callback = Top::external_callback();
@ -181,10 +175,7 @@ void StackTracer::Trace(TickSample* sample) {
SafeStackTraceFrameIterator it(sample->fp, sample->sp, SafeStackTraceFrameIterator it(sample->fp, sample->sp,
sample->sp, js_entry_sp); sample->sp, js_entry_sp);
while (!it.done() && i < TickSample::kMaxFramesCount) { while (!it.done() && i < TickSample::kMaxFramesCount) {
Object* object = it.frame()->function_slot_object(); sample->stack[i++] = it.frame()->pc();
if (object->IsHeapObject()) {
sample->stack[i++] = HeapObject::cast(object)->address();
}
it.Advance(); it.Advance();
} }
sample->frames_count = i; sample->frames_count = i;
@ -710,17 +701,6 @@ void Logger::SetterCallbackEvent(String* name, Address entry_point) {
} }
#ifdef ENABLE_LOGGING_AND_PROFILING
static const char* ComputeMarker(Code* code) {
switch (code->kind()) {
case Code::FUNCTION: return code->optimizable() ? "~" : "";
case Code::OPTIMIZED_FUNCTION: return "*";
default: return "";
}
}
#endif
void Logger::CodeCreateEvent(LogEventsAndTags tag, void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code, Code* code,
const char* comment) { const char* comment) {
@ -731,7 +711,7 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
kLogEventsNames[CODE_CREATION_EVENT], kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]); kLogEventsNames[tag]);
msg.AppendAddress(code->address()); msg.AppendAddress(code->address());
msg.Append(",%d,\"%s", code->ExecutableSize(), ComputeMarker(code)); msg.Append(",%d,\"", code->ExecutableSize());
for (const char* p = comment; *p != '\0'; p++) { for (const char* p = comment; *p != '\0'; p++) {
if (*p == '"') { if (*p == '"') {
msg.Append('\\'); msg.Append('\\');
@ -746,9 +726,40 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
} }
void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) { void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (name != NULL) {
SmartPointer<char> str =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
CodeCreateEvent(tag, code, *str);
} else {
CodeCreateEvent(tag, code, "");
}
#endif
}
#ifdef ENABLE_LOGGING_AND_PROFILING
// ComputeMarker must only be used when SharedFunctionInfo is known.
static const char* ComputeMarker(Code* code) {
switch (code->kind()) {
case Code::FUNCTION: return code->optimizable() ? "~" : "";
case Code::OPTIMIZED_FUNCTION: return "*";
default: return "";
}
}
#endif
void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log_code) return; if (!Log::IsEnabled() || !FLAG_log_code) return;
if (code == Builtins::builtin(Builtins::LazyCompile)) return;
LogMessageBuilder msg; LogMessageBuilder msg;
SmartPointer<char> str = SmartPointer<char> str =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
@ -756,7 +767,9 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) {
kLogEventsNames[CODE_CREATION_EVENT], kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]); kLogEventsNames[tag]);
msg.AppendAddress(code->address()); msg.AppendAddress(code->address());
msg.Append(",%d,\"%s%s\"", code->ExecutableSize(), ComputeMarker(code), *str); msg.Append(",%d,\"%s\",", code->ExecutableSize(), *str);
msg.AppendAddress(shared->address());
msg.Append(",%s", ComputeMarker(code));
LowLevelCodeCreateEvent(code, &msg); LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n'); msg.Append('\n');
msg.WriteToLogFile(); msg.WriteToLogFile();
@ -764,26 +777,31 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) {
} }
// Although, it is possible to extract source and line from
// the SharedFunctionInfo object, we left it to caller
// to leave logging functions free from heap allocations.
void Logger::CodeCreateEvent(LogEventsAndTags tag, void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code, String* name, Code* code,
SharedFunctionInfo* shared,
String* source, int line) { String* source, int line) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log_code) return; if (!Log::IsEnabled() || !FLAG_log_code) return;
LogMessageBuilder msg; LogMessageBuilder msg;
SmartPointer<char> str = SmartPointer<char> name =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
SmartPointer<char> sourcestr = SmartPointer<char> sourcestr =
source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
msg.Append("%s,%s,", msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT], kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]); kLogEventsNames[tag]);
msg.AppendAddress(code->address()); msg.AppendAddress(code->address());
msg.Append(",%d,\"%s%s %s:%d\"", msg.Append(",%d,\"%s %s:%d\",",
code->ExecutableSize(), code->ExecutableSize(),
ComputeMarker(code), *name,
*str,
*sourcestr, *sourcestr,
line); line);
msg.AppendAddress(shared->address());
msg.Append(",%s", ComputeMarker(code));
LowLevelCodeCreateEvent(code, &msg); LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n'); msg.Append('\n');
msg.WriteToLogFile(); msg.WriteToLogFile();
@ -863,42 +881,9 @@ void Logger::SnapshotPositionEvent(Address addr, int pos) {
} }
void Logger::FunctionCreateEvent(JSFunction* function) { void Logger::SFIMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
// This function can be called from GC iterators (during Scavenge, MoveEventInternal(SFI_MOVE_EVENT, from, to);
// MC, and MS), so marking bits can be set on objects. That's
// why unchecked accessors are used here.
if (!Log::IsEnabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("%s,", kLogEventsNames[FUNCTION_CREATION_EVENT]);
msg.AppendAddress(function->address());
msg.Append(',');
msg.AppendAddress(function->unchecked_code()->address());
msg.Append('\n');
msg.WriteToLogFile();
#endif
}
void Logger::FunctionCreateEventFromMove(JSFunction* function) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (function->unchecked_code() != Builtins::builtin(Builtins::LazyCompile)) {
FunctionCreateEvent(function);
}
#endif
}
void Logger::FunctionMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING
MoveEventInternal(FUNCTION_MOVE_EVENT, from, to);
#endif
}
void Logger::FunctionDeleteEvent(Address from) {
#ifdef ENABLE_LOGGING_AND_PROFILING
DeleteEventInternal(FUNCTION_DELETE_EVENT, from);
#endif #endif
} }
@ -1118,7 +1103,7 @@ void Logger::TickEvent(TickSample* sample, bool overflow) {
msg.Append(','); msg.Append(',');
msg.AppendAddress(sample->sp); msg.AppendAddress(sample->sp);
msg.Append(','); msg.Append(',');
msg.AppendAddress(sample->function); msg.AppendAddress(sample->tos);
msg.Append(",%d", static_cast<int>(sample->state)); msg.Append(",%d", static_cast<int>(sample->state));
if (overflow) { if (overflow) {
msg.Append(",overflow"); msg.Append(",overflow");
@ -1187,7 +1172,6 @@ void Logger::ResumeProfiler(int flags, int tag) {
LOG(UncheckedStringEvent("profiler", "resume")); LOG(UncheckedStringEvent("profiler", "resume"));
FLAG_log_code = true; FLAG_log_code = true;
LogCompiledFunctions(); LogCompiledFunctions();
LogFunctionObjects();
LogAccessorCallbacks(); LogAccessorCallbacks();
if (!FLAG_sliding_state_window && !ticker_->IsActive()) { if (!FLAG_sliding_state_window && !ticker_->IsActive()) {
ticker_->Start(); ticker_->Start();
@ -1388,10 +1372,9 @@ void Logger::LogCompiledFunctions() {
// During iteration, there can be heap allocation due to // During iteration, there can be heap allocation due to
// GetScriptLineNumber call. // GetScriptLineNumber call.
for (int i = 0; i < compiled_funcs_count; ++i) { for (int i = 0; i < compiled_funcs_count; ++i) {
if (*code_objects[i] == Builtins::builtin(Builtins::LazyCompile)) continue;
Handle<SharedFunctionInfo> shared = sfis[i]; Handle<SharedFunctionInfo> shared = sfis[i];
Handle<String> name(String::cast(shared->name())); Handle<String> func_name(shared->DebugName());
Handle<String> func_name(name->length() > 0 ?
*name : shared->inferred_name());
if (shared->script()->IsScript()) { if (shared->script()->IsScript()) {
Handle<Script> script(Script::cast(shared->script())); Handle<Script> script(Script::cast(shared->script()));
if (script->name()->IsString()) { if (script->name()->IsString()) {
@ -1400,18 +1383,18 @@ void Logger::LogCompiledFunctions() {
if (line_num > 0) { if (line_num > 0) {
PROFILE(CodeCreateEvent( PROFILE(CodeCreateEvent(
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script), Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
*code_objects[i], *func_name, *code_objects[i], *shared,
*script_name, line_num + 1)); *script_name, line_num + 1));
} else { } else {
// Can't distinguish eval and script here, so always use Script. // Can't distinguish eval and script here, so always use Script.
PROFILE(CodeCreateEvent( PROFILE(CodeCreateEvent(
Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script), Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
*code_objects[i], *script_name)); *code_objects[i], *shared, *script_name));
} }
} else { } else {
PROFILE(CodeCreateEvent( PROFILE(CodeCreateEvent(
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script), Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
*code_objects[i], *func_name)); *code_objects[i], *shared, *func_name));
} }
} else if (shared->IsApiFunction()) { } else if (shared->IsApiFunction()) {
// API function. // API function.
@ -1425,24 +1408,12 @@ void Logger::LogCompiledFunctions() {
} }
} else { } else {
PROFILE(CodeCreateEvent( PROFILE(CodeCreateEvent(
Logger::LAZY_COMPILE_TAG, *code_objects[i], *func_name)); Logger::LAZY_COMPILE_TAG, *code_objects[i], *shared, *func_name));
} }
} }
} }
void Logger::LogFunctionObjects() {
AssertNoAllocation no_alloc;
HeapIterator iterator;
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
if (!obj->IsJSFunction()) continue;
JSFunction* jsf = JSFunction::cast(obj);
if (!jsf->is_compiled()) continue;
PROFILE(FunctionCreateEvent(jsf));
}
}
void Logger::LogAccessorCallbacks() { void Logger::LogAccessorCallbacks() {
AssertNoAllocation no_alloc; AssertNoAllocation no_alloc;
HeapIterator iterator; HeapIterator iterator;

26
deps/v8/src/log.h vendored
View File

@ -91,9 +91,7 @@ class LogMessageBuilder;
V(CODE_MOVE_EVENT, "code-move") \ V(CODE_MOVE_EVENT, "code-move") \
V(CODE_DELETE_EVENT, "code-delete") \ V(CODE_DELETE_EVENT, "code-delete") \
V(CODE_MOVING_GC, "code-moving-gc") \ V(CODE_MOVING_GC, "code-moving-gc") \
V(FUNCTION_CREATION_EVENT, "function-creation") \ V(SFI_MOVE_EVENT, "sfi-move") \
V(FUNCTION_MOVE_EVENT, "function-move") \
V(FUNCTION_DELETE_EVENT, "function-delete") \
V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \ V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
V(TICK_EVENT, "tick") \ V(TICK_EVENT, "tick") \
V(REPEAT_META_EVENT, "repeat") \ V(REPEAT_META_EVENT, "repeat") \
@ -205,8 +203,15 @@ class Logger {
// Emits a code create event. // Emits a code create event.
static void CodeCreateEvent(LogEventsAndTags tag, static void CodeCreateEvent(LogEventsAndTags tag,
Code* code, const char* source); Code* code, const char* source);
static void CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name); static void CodeCreateEvent(LogEventsAndTags tag,
static void CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name, Code* code, String* name);
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* name);
static void CodeCreateEvent(LogEventsAndTags tag,
Code* code,
SharedFunctionInfo* shared,
String* source, int line); String* source, int line);
static void CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count); static void CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count);
static void CodeMovingGCEvent(); static void CodeMovingGCEvent();
@ -216,13 +221,8 @@ class Logger {
static void CodeMoveEvent(Address from, Address to); static void CodeMoveEvent(Address from, Address to);
// Emits a code delete event. // Emits a code delete event.
static void CodeDeleteEvent(Address from); static void CodeDeleteEvent(Address from);
// Emits a function object create event.
static void FunctionCreateEvent(JSFunction* function); static void SFIMoveEvent(Address from, Address to);
static void FunctionCreateEventFromMove(JSFunction* function);
// Emits a function move event.
static void FunctionMoveEvent(Address from, Address to);
// Emits a function delete event.
static void FunctionDeleteEvent(Address from);
static void SnapshotPositionEvent(Address addr, int pos); static void SnapshotPositionEvent(Address addr, int pos);
@ -273,8 +273,6 @@ class Logger {
// Logs all compiled functions found in the heap. // Logs all compiled functions found in the heap.
static void LogCompiledFunctions(); static void LogCompiledFunctions();
// Logs all compiled JSFunction objects found in the heap.
static void LogFunctionObjects();
// Logs all accessor callbacks found in the heap. // Logs all accessor callbacks found in the heap.
static void LogAccessorCallbacks(); static void LogAccessorCallbacks();
// Used for logging stubs found in the snapshot. // Used for logging stubs found in the snapshot.

View File

@ -2819,9 +2819,8 @@ int MarkCompactCollector::RelocateOldNonCodeObject(HeapObject* obj,
ASSERT(!HeapObject::FromAddress(new_addr)->IsCode()); ASSERT(!HeapObject::FromAddress(new_addr)->IsCode());
HeapObject* copied_to = HeapObject::FromAddress(new_addr); HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsJSFunction()) { if (copied_to->IsSharedFunctionInfo()) {
PROFILE(FunctionMoveEvent(old_addr, new_addr)); PROFILE(SFIMoveEvent(old_addr, new_addr));
PROFILE(FunctionCreateEventFromMove(JSFunction::cast(copied_to)));
} }
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr)); HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
@ -2912,9 +2911,8 @@ int MarkCompactCollector::RelocateNewObject(HeapObject* obj) {
#endif #endif
HeapObject* copied_to = HeapObject::FromAddress(new_addr); HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsJSFunction()) { if (copied_to->IsSharedFunctionInfo()) {
PROFILE(FunctionMoveEvent(old_addr, new_addr)); PROFILE(SFIMoveEvent(old_addr, new_addr));
PROFILE(FunctionCreateEventFromMove(JSFunction::cast(copied_to)));
} }
HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr)); HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
@ -2931,8 +2929,6 @@ void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
if (obj->IsCode()) { if (obj->IsCode()) {
PROFILE(CodeDeleteEvent(obj->address())); PROFILE(CodeDeleteEvent(obj->address()));
} else if (obj->IsJSFunction()) {
PROFILE(FunctionDeleteEvent(obj->address()));
} }
#endif #endif
} }

View File

@ -2813,6 +2813,12 @@ bool JSObject::ReferencesObject(Object* obj) {
MaybeObject* JSObject::PreventExtensions() { MaybeObject* JSObject::PreventExtensions() {
if (IsAccessCheckNeeded() &&
!Top::MayNamedAccess(this, Heap::undefined_value(), v8::ACCESS_KEYS)) {
Top::ReportFailedAccessCheck(this, v8::ACCESS_KEYS);
return Heap::false_value();
}
if (IsJSGlobalProxy()) { if (IsJSGlobalProxy()) {
Object* proto = GetPrototype(); Object* proto = GetPrototype();
if (proto->IsNull()) return this; if (proto->IsNull()) return this;
@ -6660,7 +6666,6 @@ bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
break; break;
} }
case PIXEL_ELEMENTS: { case PIXEL_ELEMENTS: {
// TODO(iposva): Add testcase.
PixelArray* pixels = PixelArray::cast(elements()); PixelArray* pixels = PixelArray::cast(elements());
if (index < static_cast<uint32_t>(pixels->length())) { if (index < static_cast<uint32_t>(pixels->length())) {
return true; return true;
@ -6674,7 +6679,6 @@ bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
case EXTERNAL_INT_ELEMENTS: case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS: { case EXTERNAL_FLOAT_ELEMENTS: {
// TODO(kbr): Add testcase.
ExternalArray* array = ExternalArray::cast(elements()); ExternalArray* array = ExternalArray::cast(elements());
if (index < static_cast<uint32_t>(array->length())) { if (index < static_cast<uint32_t>(array->length())) {
return true; return true;
@ -7265,11 +7269,7 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
} }
break; break;
} }
case PIXEL_ELEMENTS: { case PIXEL_ELEMENTS:
// TODO(iposva): Add testcase and implement.
UNIMPLEMENTED();
break;
}
case EXTERNAL_BYTE_ELEMENTS: case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
case EXTERNAL_SHORT_ELEMENTS: case EXTERNAL_SHORT_ELEMENTS:
@ -7277,8 +7277,8 @@ MaybeObject* JSObject::GetElementPostInterceptor(Object* receiver,
case EXTERNAL_INT_ELEMENTS: case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS: case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS: { case EXTERNAL_FLOAT_ELEMENTS: {
// TODO(kbr): Add testcase and implement. MaybeObject* value = GetExternalElement(index);
UNIMPLEMENTED(); if (!value->ToObjectUnchecked()->IsUndefined()) return value;
break; break;
} }
case DICTIONARY_ELEMENTS: { case DICTIONARY_ELEMENTS: {
@ -7366,6 +7366,46 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
} }
break; break;
} }
case PIXEL_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
case EXTERNAL_SHORT_ELEMENTS:
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS: {
MaybeObject* value = GetExternalElement(index);
if (!value->ToObjectUnchecked()->IsUndefined()) return value;
break;
}
case DICTIONARY_ELEMENTS: {
NumberDictionary* dictionary = element_dictionary();
int entry = dictionary->FindEntry(index);
if (entry != NumberDictionary::kNotFound) {
Object* element = dictionary->ValueAt(entry);
PropertyDetails details = dictionary->DetailsAt(entry);
if (details.type() == CALLBACKS) {
return GetElementWithCallback(receiver,
element,
index,
this);
}
return element;
}
break;
}
}
Object* pt = GetPrototype();
if (pt == Heap::null_value()) return Heap::undefined_value();
return pt->GetElementWithReceiver(receiver, index);
}
MaybeObject* JSObject::GetExternalElement(uint32_t index) {
// Get element works for both JSObject and JSArray since
// JSArray::length cannot change.
switch (GetElementsKind()) {
case PIXEL_ELEMENTS: { case PIXEL_ELEMENTS: {
PixelArray* pixels = PixelArray::cast(elements()); PixelArray* pixels = PixelArray::cast(elements());
if (index < static_cast<uint32_t>(pixels->length())) { if (index < static_cast<uint32_t>(pixels->length())) {
@ -7433,27 +7473,12 @@ MaybeObject* JSObject::GetElementWithReceiver(Object* receiver,
} }
break; break;
} }
case DICTIONARY_ELEMENTS: { case FAST_ELEMENTS:
NumberDictionary* dictionary = element_dictionary(); case DICTIONARY_ELEMENTS:
int entry = dictionary->FindEntry(index); UNREACHABLE();
if (entry != NumberDictionary::kNotFound) {
Object* element = dictionary->ValueAt(entry);
PropertyDetails details = dictionary->DetailsAt(entry);
if (details.type() == CALLBACKS) {
return GetElementWithCallback(receiver,
element,
index,
this);
}
return element;
}
break; break;
}
} }
return Heap::undefined_value();
Object* pt = GetPrototype();
if (pt == Heap::null_value()) return Heap::undefined_value();
return pt->GetElementWithReceiver(receiver, index);
} }

View File

@ -1549,6 +1549,11 @@ class JSObject: public HeapObject {
MaybeObject* GetElementWithReceiver(Object* receiver, uint32_t index); MaybeObject* GetElementWithReceiver(Object* receiver, uint32_t index);
MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index); MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index);
// Get external element value at index if there is one and undefined
// otherwise. Can return a failure if allocation of a heap number
// failed.
MaybeObject* GetExternalElement(uint32_t index);
MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity, MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity,
int length); int length);
MUST_USE_RESULT MaybeObject* SetSlowElements(Object* length); MUST_USE_RESULT MaybeObject* SetSlowElements(Object* length);

View File

@ -1,4 +1,4 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved. // Copyright 2006-2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -71,7 +71,7 @@ void OS::Setup() {
uint64_t OS::CpuFeaturesImpliedByPlatform() { uint64_t OS::CpuFeaturesImpliedByPlatform() {
return 0; // Nothing special about cygwin return 0; // Nothing special about Cygwin.
} }
@ -209,7 +209,7 @@ class PosixMemoryMappedFile : public OS::MemoryMappedFile {
OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
FILE* file = fopen(name, "w+"); FILE* file = fopen(name, "r+");
if (file == NULL) return NULL; if (file == NULL) return NULL;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
@ -304,12 +304,12 @@ void OS::LogSharedLibraryAddresses() {
void OS::SignalCodeMovingGC() { void OS::SignalCodeMovingGC() {
// Nothing to do on Cygwin // Nothing to do on Cygwin.
} }
int OS::StackWalk(Vector<OS::StackFrame> frames) { int OS::StackWalk(Vector<OS::StackFrame> frames) {
// Not supported on Cygwin // Not supported on Cygwin.
return 0; return 0;
} }
@ -443,17 +443,36 @@ void Thread::Join() {
} }
static inline Thread::LocalStorageKey PthreadKeyToLocalKey(
pthread_key_t pthread_key) {
// We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
// because pthread_key_t is a pointer type on Cygwin. This will probably not
// work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
return static_cast<Thread::LocalStorageKey>(ptr_key);
}
static inline pthread_key_t LocalKeyToPthreadKey(
Thread::LocalStorageKey local_key) {
STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
intptr_t ptr_key = static_cast<intptr_t>(local_key);
return reinterpret_cast<pthread_key_t>(ptr_key);
}
Thread::LocalStorageKey Thread::CreateThreadLocalKey() { Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
pthread_key_t key; pthread_key_t key;
int result = pthread_key_create(&key, NULL); int result = pthread_key_create(&key, NULL);
USE(result); USE(result);
ASSERT(result == 0); ASSERT(result == 0);
return static_cast<LocalStorageKey>(key); return PthreadKeyToLocalKey(key);
} }
void Thread::DeleteThreadLocalKey(LocalStorageKey key) { void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
pthread_key_t pthread_key = static_cast<pthread_key_t>(key); pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
int result = pthread_key_delete(pthread_key); int result = pthread_key_delete(pthread_key);
USE(result); USE(result);
ASSERT(result == 0); ASSERT(result == 0);
@ -461,13 +480,13 @@ void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
void* Thread::GetThreadLocal(LocalStorageKey key) { void* Thread::GetThreadLocal(LocalStorageKey key) {
pthread_key_t pthread_key = static_cast<pthread_key_t>(key); pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
return pthread_getspecific(pthread_key); return pthread_getspecific(pthread_key);
} }
void Thread::SetThreadLocal(LocalStorageKey key, void* value) { void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
pthread_key_t pthread_key = static_cast<pthread_key_t>(key); pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
pthread_setspecific(pthread_key, value); pthread_setspecific(pthread_key, value);
} }
@ -594,7 +613,7 @@ Semaphore* OS::CreateSemaphore(int count) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Cygwin profiler support. // Cygwin profiler support.
// //
// On cygwin we use the same sampler implementation as on win32 // On Cygwin we use the same sampler implementation as on win32.
class Sampler::PlatformData : public Malloced { class Sampler::PlatformData : public Malloced {
public: public:
@ -698,8 +717,7 @@ void Sampler::Start() {
// Start sampler thread. // Start sampler thread.
DWORD tid; DWORD tid;
SetActive(true); SetActive(true);
data_->sampler_thread_ = CreateThread(NULL, 0, SamplerEntry, data_, 0, data_->sampler_thread_ = CreateThread(NULL, 0, SamplerEntry, data_, 0, &tid);
&tid);
// Set thread to high priority to increase sampling accuracy. // Set thread to high priority to increase sampling accuracy.
SetThreadPriority(data_->sampler_thread_, THREAD_PRIORITY_TIME_CRITICAL); SetThreadPriority(data_->sampler_thread_, THREAD_PRIORITY_TIME_CRITICAL);
} }

View File

@ -224,7 +224,7 @@ class PosixMemoryMappedFile : public OS::MemoryMappedFile {
OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
FILE* file = fopen(name, "w+"); FILE* file = fopen(name, "r+");
if (file == NULL) return NULL; if (file == NULL) return NULL;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);

View File

@ -327,7 +327,7 @@ class PosixMemoryMappedFile : public OS::MemoryMappedFile {
OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
FILE* file = fopen(name, "w+"); FILE* file = fopen(name, "r+");
if (file == NULL) return NULL; if (file == NULL) return NULL;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);

View File

@ -205,7 +205,7 @@ class PosixMemoryMappedFile : public OS::MemoryMappedFile {
OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
FILE* file = fopen(name, "w+"); FILE* file = fopen(name, "r+");
if (file == NULL) return NULL; if (file == NULL) return NULL;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);

View File

@ -222,7 +222,7 @@ class PosixMemoryMappedFile : public OS::MemoryMappedFile {
OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
FILE* file = fopen(name, "w+"); FILE* file = fopen(name, "r+");
if (file == NULL) return NULL; if (file == NULL) return NULL;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);

View File

@ -235,7 +235,7 @@ class PosixMemoryMappedFile : public OS::MemoryMappedFile {
OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
FILE* file = fopen(name, "w+"); FILE* file = fopen(name, "r+");
if (file == NULL) return NULL; if (file == NULL) return NULL;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);

View File

@ -939,7 +939,7 @@ OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
// Open a physical file // Open a physical file
HANDLE file = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, HANDLE file = CreateFileA(name, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (file == NULL) return NULL; if (file == INVALID_HANDLE_VALUE) return NULL;
int size = static_cast<int>(GetFileSize(file, NULL)); int size = static_cast<int>(GetFileSize(file, NULL));

View File

@ -383,14 +383,10 @@ class Thread: public ThreadHandle {
// LOCAL_STORAGE_KEY_MIN_VALUE and LOCAL_STORAGE_KEY_MAX_VALUE are specified // LOCAL_STORAGE_KEY_MIN_VALUE and LOCAL_STORAGE_KEY_MAX_VALUE are specified
// to ensure that enumeration type has correct value range (see Issue 830 for // to ensure that enumeration type has correct value range (see Issue 830 for
// more details). // more details).
#ifdef __CYGWIN__
typedef void* LocalStorageKey;
#else
enum LocalStorageKey { enum LocalStorageKey {
LOCAL_STORAGE_KEY_MIN_VALUE = kMinInt, LOCAL_STORAGE_KEY_MIN_VALUE = kMinInt,
LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt LOCAL_STORAGE_KEY_MAX_VALUE = kMaxInt
}; };
#endif
// Create new thread. // Create new thread.
Thread(); Thread();
@ -571,13 +567,13 @@ class TickSample {
pc(NULL), pc(NULL),
sp(NULL), sp(NULL),
fp(NULL), fp(NULL),
function(NULL), tos(NULL),
frames_count(0) {} frames_count(0) {}
StateTag state; // The state of the VM. StateTag state; // The state of the VM.
Address pc; // Instruction pointer. Address pc; // Instruction pointer.
Address sp; // Stack pointer. Address sp; // Stack pointer.
Address fp; // Frame pointer. Address fp; // Frame pointer.
Address function; // The last called JS function. Address tos; // Top stack value (*sp).
static const int kMaxFramesCount = 64; static const int kMaxFramesCount = 64;
Address stack[kMaxFramesCount]; // Call stack. Address stack[kMaxFramesCount]; // Call stack.
int frames_count; // Number of captured frames. int frames_count; // Number of captured frames.

View File

@ -45,16 +45,6 @@ const char* StringsStorage::GetFunctionName(const char* name) {
} }
CodeEntry::CodeEntry(int security_token_id)
: tag_(Logger::FUNCTION_TAG),
name_prefix_(kEmptyNamePrefix),
name_(""),
resource_name_(""),
line_number_(0),
security_token_id_(security_token_id) {
}
CodeEntry::CodeEntry(Logger::LogEventsAndTags tag, CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
const char* name_prefix, const char* name_prefix,
const char* name, const char* name,
@ -66,6 +56,7 @@ CodeEntry::CodeEntry(Logger::LogEventsAndTags tag,
name_(name), name_(name),
resource_name_(resource_name), resource_name_(resource_name),
line_number_(line_number), line_number_(line_number),
shared_id_(0),
security_token_id_(security_token_id) { security_token_id_(security_token_id) {
} }

View File

@ -156,13 +156,18 @@ void CodeEntry::CopyData(const CodeEntry& source) {
uint32_t CodeEntry::GetCallUid() const { uint32_t CodeEntry::GetCallUid() const {
uint32_t hash = ComputeIntegerHash(tag_); uint32_t hash = ComputeIntegerHash(tag_);
hash ^= ComputeIntegerHash( if (shared_id_ != 0) {
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_))); hash ^= ComputeIntegerHash(
hash ^= ComputeIntegerHash( static_cast<uint32_t>(shared_id_));
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_))); } else {
hash ^= ComputeIntegerHash( hash ^= ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_))); static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)));
hash ^= ComputeIntegerHash(line_number_); hash ^= ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)));
hash ^= ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)));
hash ^= ComputeIntegerHash(line_number_);
}
return hash; return hash;
} }
@ -170,10 +175,12 @@ uint32_t CodeEntry::GetCallUid() const {
bool CodeEntry::IsSameAs(CodeEntry* entry) const { bool CodeEntry::IsSameAs(CodeEntry* entry) const {
return this == entry return this == entry
|| (tag_ == entry->tag_ || (tag_ == entry->tag_
&& name_prefix_ == entry->name_prefix_ && shared_id_ == entry->shared_id_
&& name_ == entry->name_ && (shared_id_ != 0
&& resource_name_ == entry->resource_name_ || (name_prefix_ == entry->name_prefix_
&& line_number_ == entry->line_number_); && name_ == entry->name_
&& resource_name_ == entry->resource_name_
&& line_number_ == entry->line_number_)));
} }
@ -458,23 +465,12 @@ void CpuProfile::Print() {
} }
CodeEntry* const CodeMap::kSfiCodeEntry = NULL;
const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL; const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue = const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue =
CodeMap::CodeEntryInfo(NULL, 0); CodeMap::CodeEntryInfo(NULL, 0);
void CodeMap::AddAlias(Address start, CodeEntry* entry, Address code_start) {
CodeTree::Locator locator;
if (tree_.Find(code_start, &locator)) {
const CodeEntryInfo& code_info = locator.value();
if (tree_.Insert(start, &locator)) {
entry->CopyData(*code_info.entry);
locator.set_value(CodeEntryInfo(entry, code_info.size));
}
}
}
CodeEntry* CodeMap::FindEntry(Address addr) { CodeEntry* CodeMap::FindEntry(Address addr) {
CodeTree::Locator locator; CodeTree::Locator locator;
if (tree_.FindGreatestLessThan(addr, &locator)) { if (tree_.FindGreatestLessThan(addr, &locator)) {
@ -487,6 +483,22 @@ CodeEntry* CodeMap::FindEntry(Address addr) {
} }
int CodeMap::GetSFITag(Address addr) {
CodeTree::Locator locator;
// For SFI entries, 'size' field is used to store their IDs.
if (tree_.Find(addr, &locator)) {
const CodeEntryInfo& entry = locator.value();
ASSERT(entry.entry == kSfiCodeEntry);
return entry.size;
} else {
tree_.Insert(addr, &locator);
int tag = next_sfi_tag_++;
locator.set_value(CodeEntryInfo(kSfiCodeEntry, tag));
return tag;
}
}
void CodeMap::CodeTreePrinter::Call( void CodeMap::CodeTreePrinter::Call(
const Address& key, const CodeMap::CodeEntryInfo& value) { const Address& key, const CodeMap::CodeEntryInfo& value) {
OS::Print("%p %5d %s\n", key, value.size, value.entry->name()); OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
@ -715,13 +727,6 @@ CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
} }
CodeEntry* CpuProfilesCollection::NewCodeEntry(int security_token_id) {
CodeEntry* entry = new CodeEntry(security_token_id);
code_entries_.Add(entry);
return entry;
}
void CpuProfilesCollection::AddPathToCurrentProfiles( void CpuProfilesCollection::AddPathToCurrentProfiles(
const Vector<CodeEntry*>& path) { const Vector<CodeEntry*>& path) {
// As starting / stopping profiles is rare relatively to this // As starting / stopping profiles is rare relatively to this
@ -784,19 +789,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) {
if (sample.pc != NULL) { if (sample.pc != NULL) {
*entry++ = code_map_.FindEntry(sample.pc); *entry++ = code_map_.FindEntry(sample.pc);
if (sample.function != NULL) { if (sample.tos != NULL) {
*entry = code_map_.FindEntry(sample.function); *entry = code_map_.FindEntry(sample.tos);
if (*entry != NULL && !(*entry)->is_js_function()) { if (*entry != NULL && !(*entry)->is_js_function()) {
*entry = NULL; *entry = NULL;
} else {
CodeEntry* pc_entry = *entries.start();
if (pc_entry == NULL) {
*entry = NULL;
} else if (pc_entry->is_js_function()) {
// Use function entry in favor of pc entry, as function
// entry has security token.
*entries.start() = NULL;
}
} }
entry++; entry++;
} }

View File

@ -88,7 +88,6 @@ class StringsStorage {
class CodeEntry { class CodeEntry {
public: public:
explicit INLINE(CodeEntry(int security_token_id));
// CodeEntry doesn't own name strings, just references them. // CodeEntry doesn't own name strings, just references them.
INLINE(CodeEntry(Logger::LogEventsAndTags tag, INLINE(CodeEntry(Logger::LogEventsAndTags tag,
const char* name_prefix, const char* name_prefix,
@ -103,6 +102,8 @@ class CodeEntry {
INLINE(const char* name() const) { return name_; } INLINE(const char* name() const) { return name_; }
INLINE(const char* resource_name() const) { return resource_name_; } INLINE(const char* resource_name() const) { return resource_name_; }
INLINE(int line_number() const) { return line_number_; } INLINE(int line_number() const) { return line_number_; }
INLINE(int shared_id() const) { return shared_id_; }
INLINE(void set_shared_id(int shared_id)) { shared_id_ = shared_id; }
INLINE(int security_token_id() const) { return security_token_id_; } INLINE(int security_token_id() const) { return security_token_id_; }
INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag)); INLINE(static bool is_js_function_tag(Logger::LogEventsAndTags tag));
@ -119,6 +120,7 @@ class CodeEntry {
const char* name_; const char* name_;
const char* resource_name_; const char* resource_name_;
int line_number_; int line_number_;
int shared_id_;
int security_token_id_; int security_token_id_;
DISALLOW_COPY_AND_ASSIGN(CodeEntry); DISALLOW_COPY_AND_ASSIGN(CodeEntry);
@ -234,12 +236,12 @@ class CpuProfile {
class CodeMap { class CodeMap {
public: public:
CodeMap() { } CodeMap() : next_sfi_tag_(1) { }
INLINE(void AddCode(Address addr, CodeEntry* entry, unsigned size)); INLINE(void AddCode(Address addr, CodeEntry* entry, unsigned size));
INLINE(void MoveCode(Address from, Address to)); INLINE(void MoveCode(Address from, Address to));
INLINE(void DeleteCode(Address addr)); INLINE(void DeleteCode(Address addr));
void AddAlias(Address start, CodeEntry* entry, Address code_start);
CodeEntry* FindEntry(Address addr); CodeEntry* FindEntry(Address addr);
int GetSFITag(Address addr);
void Print(); void Print();
@ -267,7 +269,11 @@ class CodeMap {
void Call(const Address& key, const CodeEntryInfo& value); void Call(const Address& key, const CodeEntryInfo& value);
}; };
// Fake CodeEntry pointer to distinguish SFI entries.
static CodeEntry* const kSfiCodeEntry;
CodeTree tree_; CodeTree tree_;
int next_sfi_tag_;
DISALLOW_COPY_AND_ASSIGN(CodeMap); DISALLOW_COPY_AND_ASSIGN(CodeMap);
}; };

View File

@ -1051,6 +1051,12 @@ static MaybeObject* Runtime_DeclareGlobals(Arguments args) {
// Fall-through and introduce the absent property by using // Fall-through and introduce the absent property by using
// SetProperty. // SetProperty.
} else { } else {
// For const properties, we treat a callback with this name
// even in the prototype as a conflicting declaration.
if (is_const_property && (lookup.type() == CALLBACKS)) {
return ThrowRedeclarationError("const", name);
}
// Otherwise, we check for locally conflicting declarations.
if (is_local && (is_read_only || is_const_property)) { if (is_local && (is_read_only || is_const_property)) {
const char* type = (is_read_only) ? "const" : "var"; const char* type = (is_read_only) ? "const" : "var";
return ThrowRedeclarationError(type, name); return ThrowRedeclarationError(type, name);
@ -1076,29 +1082,34 @@ static MaybeObject* Runtime_DeclareGlobals(Arguments args) {
? static_cast<PropertyAttributes>(base | READ_ONLY) ? static_cast<PropertyAttributes>(base | READ_ONLY)
: base; : base;
if (lookup.IsProperty()) { // There's a local property that we need to overwrite because
// There's a local property that we need to overwrite because // we're either declaring a function or there's an interceptor
// we're either declaring a function or there's an interceptor // that claims the property is absent.
// that claims the property is absent. //
// Check for conflicting re-declarations. We cannot have
// conflicting types in case of intercepted properties because
// they are absent.
if (lookup.IsProperty() &&
(lookup.type() != INTERCEPTOR) &&
(lookup.IsReadOnly() || is_const_property)) {
const char* type = (lookup.IsReadOnly()) ? "const" : "var";
return ThrowRedeclarationError(type, name);
}
// Check for conflicting re-declarations. We cannot have // Safari does not allow the invocation of callback setters for
// conflicting types in case of intercepted properties because // function declarations. To mimic this behavior, we do not allow
// they are absent. // the invocation of setters for function values. This makes a
if (lookup.type() != INTERCEPTOR && // difference for global functions with the same names as event
(lookup.IsReadOnly() || is_const_property)) { // handlers such as "function onload() {}". Firefox does call the
const char* type = (lookup.IsReadOnly()) ? "const" : "var"; // onload setter in those case and Safari does not. We follow
return ThrowRedeclarationError(type, name); // Safari for compatibility.
} if (value->IsJSFunction()) {
RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes)); RETURN_IF_EMPTY_HANDLE(SetLocalPropertyIgnoreAttributes(global,
name,
value,
attributes));
} else { } else {
// If a property with this name does not already exist on the RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes));
// global object add the property locally. We take special
// precautions to always add it as a local property even in case
// of callbacks in the prototype chain (this rules out using
// SetProperty). Also, we must use the handle-based version to
// avoid GC issues.
RETURN_IF_EMPTY_HANDLE(
SetLocalPropertyIgnoreAttributes(global, name, value, attributes));
} }
} }
@ -1186,6 +1197,20 @@ static MaybeObject* Runtime_DeclareContextSlot(Arguments args) {
ASSERT(!context_ext->HasLocalProperty(*name)); ASSERT(!context_ext->HasLocalProperty(*name));
Handle<Object> value(Heap::undefined_value()); Handle<Object> value(Heap::undefined_value());
if (*initial_value != NULL) value = initial_value; if (*initial_value != NULL) value = initial_value;
// Declaring a const context slot is a conflicting declaration if
// there is a callback with that name in a prototype. It is
// allowed to introduce const variables in
// JSContextExtensionObjects. They are treated specially in
// SetProperty and no setters are invoked for those since they are
// not real JSObjects.
if (initial_value->IsTheHole() &&
!context_ext->IsJSContextExtensionObject()) {
LookupResult lookup;
context_ext->Lookup(*name, &lookup);
if (lookup.IsProperty() && (lookup.type() == CALLBACKS)) {
return ThrowRedeclarationError("const", name);
}
}
RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode)); RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode));
} }
@ -1212,11 +1237,7 @@ static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) {
// there, there is a property with this name in the prototype chain. // there, there is a property with this name in the prototype chain.
// We follow Safari and Firefox behavior and only set the property // We follow Safari and Firefox behavior and only set the property
// locally if there is an explicit initialization value that we have // locally if there is an explicit initialization value that we have
// to assign to the property. When adding the property we take // to assign to the property.
// special precautions to always add it as a local property even in
// case of callbacks in the prototype chain (this rules out using
// SetProperty). We have SetLocalPropertyIgnoreAttributes for
// this.
// Note that objects can have hidden prototypes, so we need to traverse // Note that objects can have hidden prototypes, so we need to traverse
// the whole chain of hidden prototypes to do a 'local' lookup. // the whole chain of hidden prototypes to do a 'local' lookup.
JSObject* real_holder = global; JSObject* real_holder = global;
@ -1277,11 +1298,7 @@ static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) {
} }
global = Top::context()->global(); global = Top::context()->global();
if (assign) { if (assign) return global->SetProperty(*name, args[1], attributes);
return global->SetLocalPropertyIgnoreAttributes(*name,
args[1],
attributes);
}
return Heap::undefined_value(); return Heap::undefined_value();
} }
@ -3673,6 +3690,8 @@ static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) {
is_element) { is_element) {
// Normalize the elements to enable attributes on the property. // Normalize the elements to enable attributes on the property.
if (js_object->IsJSGlobalProxy()) { if (js_object->IsJSGlobalProxy()) {
// We do not need to do access checks here since these has already
// been performed by the call to GetOwnProperty.
Handle<Object> proto(js_object->GetPrototype()); Handle<Object> proto(js_object->GetPrototype());
// If proxy is detached, ignore the assignment. Alternatively, // If proxy is detached, ignore the assignment. Alternatively,
// we could throw an exception. // we could throw an exception.
@ -6927,6 +6946,7 @@ static MaybeObject* Runtime_NewObject(Arguments args) {
bool first_allocation = !shared->live_objects_may_exist(); bool first_allocation = !shared->live_objects_may_exist();
Handle<JSObject> result = Factory::NewJSObject(function); Handle<JSObject> result = Factory::NewJSObject(function);
RETURN_IF_EMPTY_HANDLE(result);
// Delay setting the stub if inobject slack tracking is in progress. // Delay setting the stub if inobject slack tracking is in progress.
if (first_allocation && !shared->IsInobjectSlackTrackingInProgress()) { if (first_allocation && !shared->IsInobjectSlackTrackingInProgress()) {
TrySettingInlineConstructStub(function); TrySettingInlineConstructStub(function);

12
deps/v8/src/top.cc vendored
View File

@ -735,9 +735,8 @@ Failure* Top::ReThrow(MaybeObject* exception, MessageLocation* location) {
bool can_be_caught_externally = false; bool can_be_caught_externally = false;
ShouldReportException(&can_be_caught_externally, ShouldReportException(&can_be_caught_externally,
is_catchable_by_javascript(exception)); is_catchable_by_javascript(exception));
if (can_be_caught_externally) { thread_local_.catcher_ = can_be_caught_externally ?
thread_local_.catcher_ = try_catch_handler(); try_catch_handler() : NULL;
}
// Set the exception being re-thrown. // Set the exception being re-thrown.
set_pending_exception(exception); set_pending_exception(exception);
@ -913,9 +912,10 @@ void Top::DoThrow(MaybeObject* exception,
} }
} }
if (can_be_caught_externally) { // Do not forget to clean catcher_ if currently thrown exception cannot
thread_local_.catcher_ = try_catch_handler(); // be caught. If necessary, ReThrow will update the catcher.
} thread_local_.catcher_ = can_be_caught_externally ?
try_catch_handler() : NULL;
// NOTE: Notifying the debugger or generating the message // NOTE: Notifying the debugger or generating the message
// may have caused new exceptions. For now, we just ignore // may have caused new exceptions. For now, we just ignore

5
deps/v8/src/v8.cc vendored
View File

@ -54,7 +54,12 @@ bool V8::Initialize(Deserializer* des) {
if (has_been_disposed_ || has_fatal_error_) return false; if (has_been_disposed_ || has_fatal_error_) return false;
if (IsRunning()) return true; if (IsRunning()) return true;
#if defined(V8_TARGET_ARCH_ARM) && !defined(USE_ARM_EABI)
use_crankshaft_ = false;
#else
use_crankshaft_ = FLAG_crankshaft; use_crankshaft_ = FLAG_crankshaft;
#endif
// Peephole optimization might interfere with deoptimization. // Peephole optimization might interfere with deoptimization.
FLAG_peephole_optimization = !use_crankshaft_; FLAG_peephole_optimization = !use_crankshaft_;
is_running_ = true; is_running_ = true;

View File

@ -92,7 +92,7 @@ function GlobalIsFinite(number) {
// ECMA-262 - 15.1.2.2 // ECMA-262 - 15.1.2.2
function GlobalParseInt(string, radix) { function GlobalParseInt(string, radix) {
if (IS_UNDEFINED(radix)) { if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
// Some people use parseInt instead of Math.floor. This // Some people use parseInt instead of Math.floor. This
// optimization makes parseInt on a Smi 12 times faster (60ns // optimization makes parseInt on a Smi 12 times faster (60ns
// vs 800ns). The following optimization makes parseInt on a // vs 800ns). The following optimization makes parseInt on a
@ -105,7 +105,7 @@ function GlobalParseInt(string, radix) {
// Truncate number. // Truncate number.
return string | 0; return string | 0;
} }
radix = 0; if (IS_UNDEFINED(radix)) radix = 0;
} else { } else {
radix = TO_INT32(radix); radix = TO_INT32(radix);
if (!(radix == 0 || (2 <= radix && radix <= 36))) if (!(radix == 0 || (2 <= radix && radix <= 36)))

View File

@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script. // cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3 #define MAJOR_VERSION 3
#define MINOR_VERSION 1 #define MINOR_VERSION 1
#define BUILD_NUMBER 5 #define BUILD_NUMBER 6
#define PATCH_LEVEL 0 #define PATCH_LEVEL 0
#define CANDIDATE_VERSION false #define CANDIDATE_VERSION false

View File

@ -190,13 +190,13 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Register constants. // Register constants.
const int Register::registerCodeByAllocationIndex[kNumAllocatableRegisters] = { const int Register::kRegisterCodeByAllocationIndex[kNumAllocatableRegisters] = {
// rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12 // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12
0, 3, 2, 1, 7, 8, 9, 11, 14, 12 0, 3, 2, 1, 7, 8, 9, 11, 14, 12
}; };
const int Register::allocationIndexByRegisterCode[kNumRegisters] = { const int Register::kAllocationIndexByRegisterCode[kNumRegisters] = {
0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1
}; };
@ -3114,8 +3114,8 @@ void Assembler::RecordDebugBreakSlot() {
} }
void Assembler::RecordComment(const char* msg) { void Assembler::RecordComment(const char* msg, bool force) {
if (FLAG_code_comments) { if (FLAG_code_comments || force) {
EnsureSpace ensure_space(this); EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg)); RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
} }

View File

@ -99,12 +99,12 @@ struct Register {
static const int kNumAllocatableRegisters = 10; static const int kNumAllocatableRegisters = 10;
static int ToAllocationIndex(Register reg) { static int ToAllocationIndex(Register reg) {
return allocationIndexByRegisterCode[reg.code()]; return kAllocationIndexByRegisterCode[reg.code()];
} }
static Register FromAllocationIndex(int index) { static Register FromAllocationIndex(int index) {
ASSERT(index >= 0 && index < kNumAllocatableRegisters); ASSERT(index >= 0 && index < kNumAllocatableRegisters);
Register result = { registerCodeByAllocationIndex[index] }; Register result = { kRegisterCodeByAllocationIndex[index] };
return result; return result;
} }
@ -155,8 +155,8 @@ struct Register {
int code_; int code_;
private: private:
static const int registerCodeByAllocationIndex[kNumAllocatableRegisters]; static const int kRegisterCodeByAllocationIndex[kNumAllocatableRegisters];
static const int allocationIndexByRegisterCode[kNumRegisters]; static const int kAllocationIndexByRegisterCode[kNumRegisters];
}; };
const Register rax = { 0 }; const Register rax = { 0 };
@ -1312,7 +1312,7 @@ class Assembler : public Malloced {
// Record a comment relocation entry that can be used by a disassembler. // Record a comment relocation entry that can be used by a disassembler.
// Use --code-comments to enable. // Use --code-comments to enable.
void RecordComment(const char* msg); void RecordComment(const char* msg, bool force = false);
// Writes a single word of data in the code stream. // Writes a single word of data in the code stream.
// Used for inline tables, e.g., jump-tables. // Used for inline tables, e.g., jump-tables.

View File

@ -1336,54 +1336,33 @@ void TypeRecordingBinaryOpStub::GenerateFloatingPointCode(
void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) { void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
GenerateRegisterArgsPush(masm); ASSERT(op_ == Token::ADD);
NearLabel left_not_string, call_runtime;
// Registers containing left and right operands respectively. // Registers containing left and right operands respectively.
Register lhs = rdx; Register left = rdx;
Register rhs = rax; Register right = rax;
// Test for string arguments before calling runtime. // Test if left operand is a string.
Label not_strings, both_strings, not_string1, string1, string1_smi2; __ JumpIfSmi(left, &left_not_string);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
__ j(above_equal, &left_not_string);
StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_left_stub);
__ JumpIfNotString(lhs, r8, &not_string1); // Left operand is not a string, test right.
__ bind(&left_not_string);
__ JumpIfSmi(right, &call_runtime);
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
__ j(above_equal, &call_runtime);
// First argument is a a string, test second. StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
__ JumpIfSmi(rhs, &string1_smi2); GenerateRegisterArgsPush(masm);
__ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9); __ TailCallStub(&string_add_right_stub);
__ j(above_equal, &string1);
// First and second argument are strings.
StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
__ TailCallStub(&string_add_stub);
__ bind(&string1_smi2);
// First argument is a string, second is a smi. Try to lookup the number
// string for the smi in the number string cache.
NumberToStringStub::GenerateLookupNumberStringCache(
masm, rhs, rbx, rcx, r8, true, &string1);
// Replace second argument on stack and tailcall string add stub to make
// the result.
__ movq(Operand(rsp, 1 * kPointerSize), rbx);
__ TailCallStub(&string_add_stub);
// Only first argument is a string.
__ bind(&string1);
__ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
// First argument was not a string, test second.
__ bind(&not_string1);
__ JumpIfNotString(rhs, rhs, &not_strings);
// Only second argument is a string.
__ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
__ bind(&not_strings);
// Neither argument is a string. // Neither argument is a string.
// Pop arguments, because CallRuntimeCode wants to push them again. __ bind(&call_runtime);
__ pop(rcx);
__ pop(rax);
__ pop(rdx);
__ push(rcx);
} }
@ -1440,9 +1419,11 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) { void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
ASSERT(operands_type_ == TRBinaryOpIC::STRING);
ASSERT(op_ == Token::ADD); ASSERT(op_ == Token::ADD);
GenerateStringAddCode(masm); GenerateStringAddCode(masm);
// Try to add arguments as strings, otherwise, transition to the generic
// TRBinaryOpIC type.
GenerateTypeTransition(masm); GenerateTypeTransition(masm);
} }
@ -3461,6 +3442,9 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
// is and instance of the function and anything else to // is and instance of the function and anything else to
// indicate that the value is not an instance. // indicate that the value is not an instance.
// None of the flags are supported on X64.
ASSERT(flags_ == kNoFlags);
// Get the object - go slow case if it's a smi. // Get the object - go slow case if it's a smi.
Label slow; Label slow;
__ movq(rax, Operand(rsp, 2 * kPointerSize)); __ movq(rax, Operand(rsp, 2 * kPointerSize));
@ -3536,10 +3520,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
} }
Register InstanceofStub::left() { return rax; } // Passing arguments in registers is not supported.
Register InstanceofStub::left() { return no_reg; }
Register InstanceofStub::right() { return rdx; } Register InstanceofStub::right() { return no_reg; }
int CompareStub::MinorKey() { int CompareStub::MinorKey() {
@ -3798,14 +3783,15 @@ void StringCharAtGenerator::GenerateSlow(
void StringAddStub::Generate(MacroAssembler* masm) { void StringAddStub::Generate(MacroAssembler* masm) {
Label string_add_runtime; Label string_add_runtime, call_builtin;
Builtins::JavaScript builtin_id = Builtins::ADD;
// Load the two arguments. // Load the two arguments.
__ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument. __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument (left).
__ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument. __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument (right).
// Make sure that both arguments are strings if not known in advance. // Make sure that both arguments are strings if not known in advance.
if (string_check_) { if (flags_ == NO_STRING_ADD_FLAGS) {
Condition is_smi; Condition is_smi;
is_smi = masm->CheckSmi(rax); is_smi = masm->CheckSmi(rax);
__ j(is_smi, &string_add_runtime); __ j(is_smi, &string_add_runtime);
@ -3817,6 +3803,20 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ j(is_smi, &string_add_runtime); __ j(is_smi, &string_add_runtime);
__ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9); __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
__ j(above_equal, &string_add_runtime); __ j(above_equal, &string_add_runtime);
} else {
// Here at least one of the arguments is definitely a string.
// We convert the one that is not known to be a string.
if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
GenerateConvertArgument(masm, 2 * kPointerSize, rax, rbx, rcx, rdi,
&call_builtin);
builtin_id = Builtins::STRING_ADD_RIGHT;
} else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
GenerateConvertArgument(masm, 1 * kPointerSize, rdx, rbx, rcx, rdi,
&call_builtin);
builtin_id = Builtins::STRING_ADD_LEFT;
}
} }
// Both arguments are strings. // Both arguments are strings.
@ -3844,14 +3844,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// rbx: length of first string // rbx: length of first string
// rcx: length of second string // rcx: length of second string
// rdx: second string // rdx: second string
// r8: map of first string if string check was performed above // r8: map of first string (if flags_ == NO_STRING_ADD_FLAGS)
// r9: map of second string if string check was performed above // r9: map of second string (if flags_ == NO_STRING_ADD_FLAGS)
Label string_add_flat_result, longer_than_two; Label string_add_flat_result, longer_than_two;
__ bind(&both_not_zero_length); __ bind(&both_not_zero_length);
// If arguments where known to be strings, maps are not loaded to r8 and r9 // If arguments where known to be strings, maps are not loaded to r8 and r9
// by the code above. // by the code above.
if (!string_check_) { if (flags_ != NO_STRING_ADD_FLAGS) {
__ movq(r8, FieldOperand(rax, HeapObject::kMapOffset)); __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
__ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset)); __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
} }
@ -4037,6 +4037,54 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// Just jump to runtime to add the two strings. // Just jump to runtime to add the two strings.
__ bind(&string_add_runtime); __ bind(&string_add_runtime);
__ TailCallRuntime(Runtime::kStringAdd, 2, 1); __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
if (call_builtin.is_linked()) {
__ bind(&call_builtin);
__ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
}
}
void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
int stack_offset,
Register arg,
Register scratch1,
Register scratch2,
Register scratch3,
Label* slow) {
// First check if the argument is already a string.
Label not_string, done;
__ JumpIfSmi(arg, &not_string);
__ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1);
__ j(below, &done);
// Check the number to string cache.
Label not_cached;
__ bind(&not_string);
// Puts the cached result into scratch1.
NumberToStringStub::GenerateLookupNumberStringCache(masm,
arg,
scratch1,
scratch2,
scratch3,
false,
&not_cached);
__ movq(arg, scratch1);
__ movq(Operand(rsp, stack_offset), arg);
__ jmp(&done);
// Check if the argument is a safe string wrapper.
__ bind(&not_cached);
__ JumpIfSmi(arg, slow);
__ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1.
__ j(not_equal, slow);
__ testb(FieldOperand(scratch1, Map::kBitField2Offset),
Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf));
__ j(zero, slow);
__ movq(arg, FieldOperand(arg, JSValue::kValueOffset));
__ movq(Operand(rsp, stack_offset), arg);
__ bind(&done);
} }

View File

@ -360,24 +360,35 @@ class StringHelper : public AllStatic {
// Flag that indicates how to generate code for the stub StringAddStub. // Flag that indicates how to generate code for the stub StringAddStub.
enum StringAddFlags { enum StringAddFlags {
NO_STRING_ADD_FLAGS = 0, NO_STRING_ADD_FLAGS = 0,
NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. // Omit left string check in stub (left is definitely a string).
NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0,
// Omit right string check in stub (right is definitely a string).
NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1,
// Omit both string checks in stub.
NO_STRING_CHECK_IN_STUB =
NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB
}; };
class StringAddStub: public CodeStub { class StringAddStub: public CodeStub {
public: public:
explicit StringAddStub(StringAddFlags flags) { explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
}
private: private:
Major MajorKey() { return StringAdd; } Major MajorKey() { return StringAdd; }
int MinorKey() { return string_check_ ? 0 : 1; } int MinorKey() { return flags_; }
void Generate(MacroAssembler* masm); void Generate(MacroAssembler* masm);
// Should the stub check whether arguments are strings? void GenerateConvertArgument(MacroAssembler* masm,
bool string_check_; int stack_offset,
Register arg,
Register scratch1,
Register scratch2,
Register scratch3,
Label* slow);
const StringAddFlags flags_;
}; };

View File

@ -7239,8 +7239,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
Variable* variable = node->expression()->AsVariableProxy()->AsVariable(); Variable* variable = node->expression()->AsVariableProxy()->AsVariable();
if (variable != NULL) { if (variable != NULL) {
// Delete of an unqualified identifier is disallowed in strict mode // Delete of an unqualified identifier is disallowed in strict mode
// so this code can only be reached in non-strict mode. // but "delete this" is.
ASSERT(strict_mode_flag() == kNonStrictMode); ASSERT(strict_mode_flag() == kNonStrictMode || variable->is_this());
Slot* slot = variable->AsSlot(); Slot* slot = variable->AsSlot();
if (variable->is_global()) { if (variable->is_global()) {
LoadGlobal(); LoadGlobal();
@ -7249,7 +7249,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
Result answer = frame_->InvokeBuiltin(Builtins::DELETE, Result answer = frame_->InvokeBuiltin(Builtins::DELETE,
CALL_FUNCTION, 3); CALL_FUNCTION, 3);
frame_->Push(&answer); frame_->Push(&answer);
return;
} else if (slot != NULL && slot->type() == Slot::LOOKUP) { } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
// Call the runtime to delete from the context holding the named // Call the runtime to delete from the context holding the named
@ -7260,13 +7259,11 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
frame_->EmitPush(variable->name()); frame_->EmitPush(variable->name());
Result answer = frame_->CallRuntime(Runtime::kDeleteContextSlot, 2); Result answer = frame_->CallRuntime(Runtime::kDeleteContextSlot, 2);
frame_->Push(&answer); frame_->Push(&answer);
return; } else {
// Default: Result of deleting non-global, not dynamically
// introduced variables is false.
frame_->Push(Factory::false_value());
} }
// Default: Result of deleting non-global, not dynamically
// introduced variables is false.
frame_->Push(Factory::false_value());
} else { } else {
// Default: Result of deleting expressions is true. // Default: Result of deleting expressions is true.
Load(node->expression()); // may have side-effects Load(node->expression()); // may have side-effects

View File

@ -358,14 +358,16 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
fp_value, output_offset, value); fp_value, output_offset, value);
} }
// The context can be gotten from the function so long as we don't // For the bottommost output frame the context can be gotten from the input
// optimize functions that need local contexts. // frame. For all subsequent output frames it can be gotten from the function
// so long as we don't inline functions that need local contexts.
output_offset -= kPointerSize; output_offset -= kPointerSize;
input_offset -= kPointerSize; input_offset -= kPointerSize;
value = reinterpret_cast<intptr_t>(function->context()); if (is_bottommost) {
// The context for the bottommost output frame should also agree with the value = input_->GetFrameSlot(input_offset);
// input frame. } else {
ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value); value = reinterpret_cast<intptr_t>(function->context());
}
output_frame->SetFrameSlot(output_offset, value); output_frame->SetFrameSlot(output_offset, value);
if (is_topmost) output_frame->SetRegister(rsi.code(), value); if (is_topmost) output_frame->SetRegister(rsi.code(), value);
if (FLAG_trace_deopt) { if (FLAG_trace_deopt) {

View File

@ -3075,8 +3075,8 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
} }
} else if (var != NULL) { } else if (var != NULL) {
// Delete of an unqualified identifier is disallowed in strict mode // Delete of an unqualified identifier is disallowed in strict mode
// so this code can only be reached in non-strict mode. // but "delete this" is.
ASSERT(strict_mode_flag() == kNonStrictMode); ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this());
if (var->is_global()) { if (var->is_global()) {
__ push(GlobalObjectOperand()); __ push(GlobalObjectOperand());
__ Push(var->name()); __ Push(var->name());
@ -3114,16 +3114,22 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::NOT: { case Token::NOT: {
Comment cmnt(masm_, "[ UnaryOperation (NOT)"); Comment cmnt(masm_, "[ UnaryOperation (NOT)");
Label materialize_true, materialize_false; if (context()->IsEffect()) {
Label* if_true = NULL; // Unary NOT has no side effects so it's only necessary to visit the
Label* if_false = NULL; // subexpression. Match the optimizing compiler by not branching.
Label* fall_through = NULL; VisitForEffect(expr->expression());
// Notice that the labels are swapped. } else {
context()->PrepareTest(&materialize_true, &materialize_false, Label materialize_true, materialize_false;
&if_false, &if_true, &fall_through); Label* if_true = NULL;
if (context()->IsTest()) ForwardBailoutToChild(expr); Label* if_false = NULL;
VisitForControl(expr->expression(), if_true, if_false, fall_through); Label* fall_through = NULL;
context()->Plug(if_false, if_true); // Labels swapped. // Notice that the labels are swapped.
context()->PrepareTest(&materialize_true, &materialize_false,
&if_false, &if_true, &fall_through);
if (context()->IsTest()) ForwardBailoutToChild(expr);
VisitForControl(expr->expression(), if_true, if_false, fall_through);
context()->Plug(if_false, if_true); // Labels swapped.
}
break; break;
} }

File diff suppressed because it is too large Load Diff

View File

@ -90,8 +90,8 @@ class LCodeGen BASE_EMBEDDED {
void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredTaggedToI(LTaggedToI* instr);
void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr); void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
void DoDeferredStackCheck(LGoto* instr); void DoDeferredStackCheck(LGoto* instr);
void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
Label* map_check); void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr);
// Parallel move support. // Parallel move support.
void DoParallelMove(LParallelMove* move); void DoParallelMove(LParallelMove* move);
@ -117,6 +117,10 @@ class LCodeGen BASE_EMBEDDED {
bool is_done() const { return status_ == DONE; } bool is_done() const { return status_ == DONE; }
bool is_aborted() const { return status_ == ABORTED; } bool is_aborted() const { return status_ == ABORTED; }
int strict_mode_flag() const {
return info_->is_strict() ? kStrictMode : kNonStrictMode;
}
LChunk* chunk() const { return chunk_; } LChunk* chunk() const { return chunk_; }
Scope* scope() const { return scope_; } Scope* scope() const { return scope_; }
HGraph* graph() const { return chunk_->graph(); } HGraph* graph() const { return chunk_->graph(); }
@ -197,6 +201,7 @@ class LCodeGen BASE_EMBEDDED {
int arguments, int arguments,
int deoptimization_index); int deoptimization_index);
void RecordSafepoint(LPointerMap* pointers, int deoptimization_index); void RecordSafepoint(LPointerMap* pointers, int deoptimization_index);
void RecordSafepoint(int deoptimization_index);
void RecordSafepointWithRegisters(LPointerMap* pointers, void RecordSafepointWithRegisters(LPointerMap* pointers,
int arguments, int arguments,
int deoptimization_index); int deoptimization_index);
@ -225,6 +230,9 @@ class LCodeGen BASE_EMBEDDED {
// Caller should branch on equal condition. // Caller should branch on equal condition.
void EmitIsConstructCall(Register temp); void EmitIsConstructCall(Register temp);
// Emits code for pushing a constant operand.
void EmitPushConstantOperand(LOperand* operand);
LChunk* const chunk_; LChunk* const chunk_;
MacroAssembler* const masm_; MacroAssembler* const masm_;
CompilationInfo* const info_; CompilationInfo* const info_;

View File

@ -296,8 +296,15 @@ void LLoadContextSlot::PrintDataTo(StringStream* stream) {
} }
void LStoreContextSlot::PrintDataTo(StringStream* stream) {
InputAt(0)->PrintTo(stream);
stream->Add("[%d] <- ", slot_index());
InputAt(1)->PrintTo(stream);
}
void LCallKeyed::PrintDataTo(StringStream* stream) { void LCallKeyed::PrintDataTo(StringStream* stream) {
stream->Add("[ecx] #%d / ", arity()); stream->Add("[rcx] #%d / ", arity());
} }
@ -398,7 +405,7 @@ void LChunk::MarkEmptyBlocks() {
} }
void LStoreNamed::PrintDataTo(StringStream* stream) { void LStoreNamedField::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream); object()->PrintTo(stream);
stream->Add("."); stream->Add(".");
stream->Add(*String::cast(*name())->ToCString()); stream->Add(*String::cast(*name())->ToCString());
@ -407,7 +414,25 @@ void LStoreNamed::PrintDataTo(StringStream* stream) {
} }
void LStoreKeyed::PrintDataTo(StringStream* stream) { void LStoreNamedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add(".");
stream->Add(*String::cast(*name())->ToCString());
stream->Add(" <- ");
value()->PrintTo(stream);
}
void LStoreKeyedFastElement::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream);
stream->Add("[");
key()->PrintTo(stream);
stream->Add("] <- ");
value()->PrintTo(stream);
}
void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
object()->PrintTo(stream); object()->PrintTo(stream);
stream->Add("["); stream->Add("[");
key()->PrintTo(stream); key()->PrintTo(stream);
@ -1081,9 +1106,8 @@ LInstruction* LChunkBuilder::DoTest(HTest* instr) {
} else if (v->IsInstanceOf()) { } else if (v->IsInstanceOf()) {
HInstanceOf* instance_of = HInstanceOf::cast(v); HInstanceOf* instance_of = HInstanceOf::cast(v);
LInstanceOfAndBranch* result = LInstanceOfAndBranch* result =
new LInstanceOfAndBranch( new LInstanceOfAndBranch(UseFixed(instance_of->left(), rax),
UseFixed(instance_of->left(), InstanceofStub::left()), UseFixed(instance_of->right(), rdx));
UseFixed(instance_of->right(), InstanceofStub::right()));
return MarkAsCall(result, instr); return MarkAsCall(result, instr);
} else if (v->IsTypeofIs()) { } else if (v->IsTypeofIs()) {
HTypeofIs* typeof_is = HTypeofIs::cast(v); HTypeofIs* typeof_is = HTypeofIs::cast(v);
@ -1124,21 +1148,32 @@ LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
Abort("Unimplemented: %s", "DoInstanceOf"); LOperand* left = UseFixed(instr->left(), rax);
return NULL; LOperand* right = UseFixed(instr->right(), rdx);
LInstanceOf* result = new LInstanceOf(left, right);
return MarkAsCall(DefineFixed(result, rax), instr);
} }
LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
HInstanceOfKnownGlobal* instr) { HInstanceOfKnownGlobal* instr) {
Abort("Unimplemented: %s", "DoInstanceOfKnownGlobal"); LInstanceOfKnownGlobal* result =
return NULL; new LInstanceOfKnownGlobal(UseRegisterAtStart(instr->value()));
MarkAsSaveDoubles(result);
return AssignEnvironment(AssignPointerMap(DefineFixed(result, rax)));
} }
LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
Abort("Unimplemented: %s", "DoApplyArguments"); LOperand* function = UseFixed(instr->function(), rdi);
return NULL; LOperand* receiver = UseFixed(instr->receiver(), rax);
LOperand* length = UseFixed(instr->length(), rbx);
LOperand* elements = UseFixed(instr->elements(), rcx);
LApplyArguments* result = new LApplyArguments(function,
receiver,
length,
elements);
return MarkAsCall(DefineFixed(result, rax), instr, CAN_DEOPTIMIZE_EAGERLY);
} }
@ -1155,8 +1190,8 @@ LInstruction* LChunkBuilder::DoContext(HContext* instr) {
LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) { LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) {
Abort("Unimplemented: DoOuterContext"); LOperand* context = UseRegisterAtStart(instr->value());
return NULL; return DefineAsRegister(new LOuterContext(context));
} }
@ -1178,14 +1213,39 @@ LInstruction* LChunkBuilder::DoCallConstantFunction(
LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
Abort("Unimplemented: %s", "DoUnaryMathOperation"); BuiltinFunctionId op = instr->op();
return NULL; if (op == kMathLog || op == kMathSin || op == kMathCos) {
LOperand* input = UseFixedDouble(instr->value(), xmm1);
LUnaryMathOperation* result = new LUnaryMathOperation(input);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
} else {
LOperand* input = UseRegisterAtStart(instr->value());
LUnaryMathOperation* result = new LUnaryMathOperation(input);
switch (op) {
case kMathAbs:
return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
case kMathFloor:
return AssignEnvironment(DefineAsRegister(result));
case kMathRound:
return AssignEnvironment(DefineAsRegister(result));
case kMathSqrt:
return DefineSameAsFirst(result);
case kMathPowHalf:
return DefineSameAsFirst(result);
default:
UNREACHABLE();
return NULL;
}
}
} }
LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) { LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) {
Abort("Unimplemented: %s", "DoCallKeyed"); ASSERT(instr->key()->representation().IsTagged());
return NULL; LOperand* key = UseFixed(instr->key(), rcx);
argument_count_ -= instr->argument_count();
LCallKeyed* result = new LCallKeyed(key);
return MarkAsCall(DefineFixed(result, rax), instr);
} }
@ -1216,8 +1276,9 @@ LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
Abort("Unimplemented: %s", "DoCallFunction"); argument_count_ -= instr->argument_count();
return NULL; LCallFunction* result = new LCallFunction();
return MarkAsCall(DefineFixed(result, rax), instr);
} }
@ -1285,8 +1346,32 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
LInstruction* LChunkBuilder::DoMod(HMod* instr) { LInstruction* LChunkBuilder::DoMod(HMod* instr) {
Abort("Unimplemented: %s", "DoMod"); if (instr->representation().IsInteger32()) {
return NULL; ASSERT(instr->left()->representation().IsInteger32());
ASSERT(instr->right()->representation().IsInteger32());
// The temporary operand is necessary to ensure that right is not allocated
// into edx.
LOperand* temp = FixedTemp(rdx);
LOperand* value = UseFixed(instr->left(), rax);
LOperand* divisor = UseRegister(instr->right());
LModI* mod = new LModI(value, divisor, temp);
LInstruction* result = DefineFixed(mod, rdx);
return (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
instr->CheckFlag(HValue::kCanBeDivByZero))
? AssignEnvironment(result)
: result;
} else if (instr->representation().IsTagged()) {
return DoArithmeticT(Token::MOD, instr);
} else {
ASSERT(instr->representation().IsDouble());
// We call a C function for double modulo. It can't trigger a GC.
// We need to use fixed result register for the call.
// TODO(fschneider): Allow any register as input registers.
LOperand* left = UseFixedDouble(instr->left(), xmm1);
LOperand* right = UseFixedDouble(instr->right(), xmm2);
LArithmeticD* result = new LArithmeticD(Token::MOD, left, right);
return MarkAsCall(DefineFixedDouble(result, xmm1), instr);
}
} }
@ -1461,8 +1546,9 @@ LInstruction* LChunkBuilder::DoPixelArrayLength(HPixelArrayLength* instr) {
LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) { LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
Abort("Unimplemented: %s", "DoValueOf"); LOperand* object = UseRegister(instr->value());
return NULL; LValueOf* result = new LValueOf(object);
return AssignEnvironment(DefineSameAsFirst(result));
} }
@ -1519,12 +1605,8 @@ LInstruction* LChunkBuilder::DoChange(HChange* instr) {
return AssignPointerMap(Define(result, result_temp)); return AssignPointerMap(Define(result, result_temp));
} else { } else {
ASSERT(to.IsInteger32()); ASSERT(to.IsInteger32());
bool needs_temp = instr->CanTruncateToInt32() && LOperand* value = UseRegister(instr->value());
!CpuFeatures::IsSupported(SSE3); return AssignEnvironment(DefineAsRegister(new LDoubleToI(value)));
LOperand* value = needs_temp ?
UseTempRegister(instr->value()) : UseRegister(instr->value());
LOperand* temp = needs_temp ? TempRegister() : NULL;
return AssignEnvironment(DefineAsRegister(new LDoubleToI(value, temp)));
} }
} else if (from.IsInteger32()) { } else if (from.IsInteger32()) {
if (to.IsTagged()) { if (to.IsTagged()) {
@ -1622,14 +1704,23 @@ LInstruction* LChunkBuilder::DoStoreGlobal(HStoreGlobal* instr) {
LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) { LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
Abort("Unimplemented: %s", "DoLoadContextSlot"); LOperand* context = UseRegisterAtStart(instr->value());
return NULL; return DefineAsRegister(new LLoadContextSlot(context));
} }
LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
Abort("Unimplemented: DoStoreContextSlot"); Abort("Unimplemented: DoStoreContextSlot"); // Temporarily disabled (whesse).
return NULL; LOperand* context;
LOperand* value;
if (instr->NeedsWriteBarrier()) {
context = UseTempRegister(instr->context());
value = UseTempRegister(instr->value());
} else {
context = UseRegister(instr->context());
value = UseRegister(instr->value());
}
return new LStoreContextSlot(context, value);
} }
@ -1692,8 +1783,11 @@ LInstruction* LChunkBuilder::DoLoadPixelArrayElement(
LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
Abort("Unimplemented: %s", "DoLoadKeyedGeneric"); LOperand* object = UseFixed(instr->object(), rdx);
return NULL; LOperand* key = UseFixed(instr->key(), rax);
LLoadKeyedGeneric* result = new LLoadKeyedGeneric(object, key);
return MarkAsCall(DefineFixed(result, rax), instr);
} }
@ -1716,9 +1810,31 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastElement(
} }
LInstruction* LChunkBuilder::DoStorePixelArrayElement(
HStorePixelArrayElement* instr) {
ASSERT(instr->value()->representation().IsInteger32());
ASSERT(instr->external_pointer()->representation().IsExternal());
ASSERT(instr->key()->representation().IsInteger32());
LOperand* external_pointer = UseRegister(instr->external_pointer());
LOperand* val = UseTempRegister(instr->value());
LOperand* key = UseRegister(instr->key());
return new LStorePixelArrayElement(external_pointer, key, val);
}
LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
Abort("Unimplemented: %s", "DoStoreKeyedGeneric"); LOperand* object = UseFixed(instr->object(), rdx);
return NULL; LOperand* key = UseFixed(instr->key(), rcx);
LOperand* value = UseFixed(instr->value(), rax);
ASSERT(instr->object()->representation().IsTagged());
ASSERT(instr->key()->representation().IsTagged());
ASSERT(instr->value()->representation().IsTagged());
LStoreKeyedGeneric* result = new LStoreKeyedGeneric(object, key, value);
return MarkAsCall(result, instr);
} }
@ -1743,14 +1859,19 @@ LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
Abort("Unimplemented: %s", "DoStoreNamedGeneric"); LOperand* object = UseFixed(instr->object(), rdx);
return NULL; LOperand* value = UseFixed(instr->value(), rax);
LStoreNamedGeneric* result = new LStoreNamedGeneric(object, value);
return MarkAsCall(result, instr);
} }
LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
Abort("Unimplemented: %s", "DoStringCharCodeAt"); LOperand* string = UseRegister(instr->string());
return NULL; LOperand* index = UseRegisterOrConstant(instr->index());
LStringCharCodeAt* result = new LStringCharCodeAt(string, index);
return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
} }
@ -1771,8 +1892,7 @@ LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) {
LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) { LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
Abort("Unimplemented: %s", "DoRegExpLiteral"); return MarkAsCall(DefineFixed(new LRegExpLiteral, rax), instr);
return NULL;
} }
@ -1782,14 +1902,16 @@ LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) { LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
Abort("Unimplemented: %s", "DoDeleteProperty"); LDeleteProperty* result =
return NULL; new LDeleteProperty(Use(instr->object()), UseOrConstant(instr->key()));
return MarkAsCall(DefineFixed(result, rax), instr);
} }
LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) { LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
Abort("Unimplemented: %s", "DoOsrEntry"); allocator_->MarkAsOsrEntry();
return NULL; current_block_->last_environment()->set_ast_id(instr->ast_id());
return AssignEnvironment(new LOsrEntry);
} }
@ -1800,8 +1922,8 @@ LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
Abort("Unimplemented: %s", "DoUnknownOSRValue"); int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
return NULL; return DefineAsSpilled(new LUnknownOSRValue, spill_index);
} }
@ -1812,7 +1934,10 @@ LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) {
Abort("Unimplemented: %s", "DoArgumentsObject"); // There are no real uses of the arguments object.
// arguments.length and element access are supported directly on
// stack arguments, and any real arguments object use causes a bailout.
// So this value is never used.
return NULL; return NULL;
} }
@ -1827,14 +1952,13 @@ LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
Abort("Unimplemented: %s", "DoTypeof"); LTypeof* result = new LTypeof(UseAtStart(instr->value()));
return NULL; return MarkAsCall(DefineFixed(result, rax), instr);
} }
LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) { LInstruction* LChunkBuilder::DoTypeofIs(HTypeofIs* instr) {
Abort("Unimplemented: %s", "DoTypeofIs"); return DefineSameAsFirst(new LTypeofIs(UseRegister(instr->value())));
return NULL;
} }

View File

@ -42,8 +42,6 @@ class LCodeGen;
#define LITHIUM_ALL_INSTRUCTION_LIST(V) \ #define LITHIUM_ALL_INSTRUCTION_LIST(V) \
V(ControlInstruction) \ V(ControlInstruction) \
V(Call) \ V(Call) \
V(StoreKeyed) \
V(StoreNamed) \
LITHIUM_CONCRETE_INSTRUCTION_LIST(V) LITHIUM_CONCRETE_INSTRUCTION_LIST(V)
@ -131,6 +129,7 @@ class LCodeGen;
V(NumberUntagD) \ V(NumberUntagD) \
V(ObjectLiteral) \ V(ObjectLiteral) \
V(OsrEntry) \ V(OsrEntry) \
V(OuterContext) \
V(Parameter) \ V(Parameter) \
V(PixelArrayLength) \ V(PixelArrayLength) \
V(Power) \ V(Power) \
@ -141,11 +140,14 @@ class LCodeGen;
V(SmiTag) \ V(SmiTag) \
V(SmiUntag) \ V(SmiUntag) \
V(StackCheck) \ V(StackCheck) \
V(StoreContextSlot) \
V(StoreGlobal) \ V(StoreGlobal) \
V(StoreKeyedFastElement) \ V(StoreKeyedFastElement) \
V(StoreKeyedGeneric) \ V(StoreKeyedGeneric) \
V(StoreNamedField) \ V(StoreNamedField) \
V(StoreNamedGeneric) \ V(StoreNamedGeneric) \
V(StorePixelArrayElement) \
V(StringCharCodeAt) \
V(StringLength) \ V(StringLength) \
V(SubI) \ V(SubI) \
V(TaggedToI) \ V(TaggedToI) \
@ -830,11 +832,10 @@ class LInstanceOfAndBranch: public LControlInstruction<2, 0> {
}; };
class LInstanceOfKnownGlobal: public LTemplateInstruction<1, 1, 1> { class LInstanceOfKnownGlobal: public LTemplateInstruction<1, 1, 0> {
public: public:
LInstanceOfKnownGlobal(LOperand* value, LOperand* temp) { explicit LInstanceOfKnownGlobal(LOperand* value) {
inputs_[0] = value; inputs_[0] = value;
temps_[0] = temp;
} }
DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal, DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
@ -1005,11 +1006,10 @@ class LFixedArrayLength: public LTemplateInstruction<1, 1, 0> {
}; };
class LValueOf: public LTemplateInstruction<1, 1, 1> { class LValueOf: public LTemplateInstruction<1, 1, 0> {
public: public:
LValueOf(LOperand* value, LOperand* temp) { explicit LValueOf(LOperand* value) {
inputs_[0] = value; inputs_[0] = value;
temps_[0] = temp;
} }
DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of") DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
@ -1246,6 +1246,25 @@ class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> {
}; };
class LStoreContextSlot: public LTemplateInstruction<0, 2, 0> {
public:
LStoreContextSlot(LOperand* context, LOperand* value) {
inputs_[0] = context;
inputs_[1] = value;
}
DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot")
DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot)
LOperand* context() { return InputAt(0); }
LOperand* value() { return InputAt(1); }
int slot_index() { return hydrogen()->slot_index(); }
int needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
virtual void PrintDataTo(StringStream* stream);
};
class LPushArgument: public LTemplateInstruction<0, 1, 0> { class LPushArgument: public LTemplateInstruction<0, 1, 0> {
public: public:
explicit LPushArgument(LOperand* value) { explicit LPushArgument(LOperand* value) {
@ -1262,6 +1281,18 @@ class LContext: public LTemplateInstruction<1, 0, 0> {
}; };
class LOuterContext: public LTemplateInstruction<1, 1, 0> {
public:
explicit LOuterContext(LOperand* context) {
inputs_[0] = context;
}
DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context")
LOperand* context() { return InputAt(0); }
};
class LGlobalObject: public LTemplateInstruction<1, 0, 0> { class LGlobalObject: public LTemplateInstruction<1, 0, 0> {
public: public:
DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object") DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
@ -1295,6 +1326,8 @@ class LCallKeyed: public LTemplateInstruction<1, 1, 0> {
DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call-keyed") DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call-keyed")
DECLARE_HYDROGEN_ACCESSOR(CallKeyed) DECLARE_HYDROGEN_ACCESSOR(CallKeyed)
LOperand* key() { return inputs_[0]; }
virtual void PrintDataTo(StringStream* stream); virtual void PrintDataTo(StringStream* stream);
int arity() const { return hydrogen()->argument_count() - 1; } int arity() const { return hydrogen()->argument_count() - 1; }
@ -1315,6 +1348,8 @@ class LCallNamed: public LTemplateInstruction<1, 0, 0> {
class LCallFunction: public LTemplateInstruction<1, 0, 0> { class LCallFunction: public LTemplateInstruction<1, 0, 0> {
public: public:
LCallFunction() {}
DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function") DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
DECLARE_HYDROGEN_ACCESSOR(CallFunction) DECLARE_HYDROGEN_ACCESSOR(CallFunction)
@ -1403,11 +1438,10 @@ class LNumberTagD: public LTemplateInstruction<1, 1, 1> {
// Sometimes truncating conversion from a tagged value to an int32. // Sometimes truncating conversion from a tagged value to an int32.
class LDoubleToI: public LTemplateInstruction<1, 1, 1> { class LDoubleToI: public LTemplateInstruction<1, 1, 0> {
public: public:
LDoubleToI(LOperand* value, LOperand* temp) { explicit LDoubleToI(LOperand* value) {
inputs_[0] = value; inputs_[0] = value;
temps_[0] = temp;
} }
DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i") DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
@ -1468,15 +1502,39 @@ class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
}; };
class LStoreNamed: public LTemplateInstruction<0, 2, 1> { class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
public: public:
LStoreNamed(LOperand* obj, LOperand* val) { LStoreNamedField(LOperand* object, LOperand* value, LOperand* temp) {
inputs_[0] = obj; inputs_[0] = object;
inputs_[1] = val; inputs_[1] = value;
temps_[0] = temp;
} }
DECLARE_INSTRUCTION(StoreNamed) DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
DECLARE_HYDROGEN_ACCESSOR(StoreNamed) DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* value() { return inputs_[1]; }
Handle<Object> name() const { return hydrogen()->name(); }
bool is_in_object() { return hydrogen()->is_in_object(); }
int offset() { return hydrogen()->offset(); }
bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
Handle<Map> transition() const { return hydrogen()->transition(); }
};
class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
public:
LStoreNamedGeneric(LOperand* object, LOperand* value) {
inputs_[0] = object;
inputs_[1] = value;
}
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
virtual void PrintDataTo(StringStream* stream); virtual void PrintDataTo(StringStream* stream);
@ -1486,42 +1544,17 @@ class LStoreNamed: public LTemplateInstruction<0, 2, 1> {
}; };
class LStoreNamedField: public LStoreNamed { class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> {
public: public:
LStoreNamedField(LOperand* obj, LOperand* val, LOperand* temp) LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) {
: LStoreNamed(obj, val) {
temps_[0] = temp;
}
DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
bool is_in_object() { return hydrogen()->is_in_object(); }
int offset() { return hydrogen()->offset(); }
bool needs_write_barrier() { return hydrogen()->NeedsWriteBarrier(); }
Handle<Map> transition() const { return hydrogen()->transition(); }
};
class LStoreNamedGeneric: public LStoreNamed {
public:
LStoreNamedGeneric(LOperand* obj, LOperand* val)
: LStoreNamed(obj, val) { }
DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
};
class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
public:
LStoreKeyed(LOperand* obj, LOperand* key, LOperand* val) {
inputs_[0] = obj; inputs_[0] = obj;
inputs_[1] = key; inputs_[1] = key;
inputs_[2] = val; inputs_[2] = val;
} }
DECLARE_INSTRUCTION(StoreKeyed) DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
"store-keyed-fast-element")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement)
virtual void PrintDataTo(StringStream* stream); virtual void PrintDataTo(StringStream* stream);
@ -1531,23 +1564,56 @@ class LStoreKeyed: public LTemplateInstruction<0, 3, 0> {
}; };
class LStoreKeyedFastElement: public LStoreKeyed { class LStorePixelArrayElement: public LTemplateInstruction<0, 3, 0> {
public: public:
LStoreKeyedFastElement(LOperand* obj, LOperand* key, LOperand* val) LStorePixelArrayElement(LOperand* external_pointer,
: LStoreKeyed(obj, key, val) {} LOperand* key,
LOperand* val) {
inputs_[0] = external_pointer;
inputs_[1] = key;
inputs_[2] = val;
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement, DECLARE_CONCRETE_INSTRUCTION(StorePixelArrayElement,
"store-keyed-fast-element") "store-pixel-array-element")
DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement) DECLARE_HYDROGEN_ACCESSOR(StorePixelArrayElement)
LOperand* external_pointer() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
}; };
class LStoreKeyedGeneric: public LStoreKeyed { class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
public: public:
LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* val) LStoreKeyedGeneric(LOperand* object, LOperand* key, LOperand* value) {
: LStoreKeyed(obj, key, val) { } inputs_[0] = object;
inputs_[1] = key;
inputs_[2] = value;
}
DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic") DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
virtual void PrintDataTo(StringStream* stream);
LOperand* object() { return inputs_[0]; }
LOperand* key() { return inputs_[1]; }
LOperand* value() { return inputs_[2]; }
};
class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
public:
LStringCharCodeAt(LOperand* string, LOperand* index) {
inputs_[0] = string;
inputs_[1] = index;
}
DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
LOperand* string() { return inputs_[0]; }
LOperand* index() { return inputs_[1]; }
}; };

View File

@ -623,7 +623,9 @@ MaybeObject* MacroAssembler::TryJumpToExternalReference(
} }
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
PostCallGenerator* post_call_generator) {
// Calls are not allowed in some stubs. // Calls are not allowed in some stubs.
ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
@ -632,7 +634,7 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
// parameter count to avoid emitting code to do the check. // parameter count to avoid emitting code to do the check.
ParameterCount expected(0); ParameterCount expected(0);
GetBuiltinEntry(rdx, id); GetBuiltinEntry(rdx, id);
InvokeCode(rdx, expected, expected, flag); InvokeCode(rdx, expected, expected, flag, post_call_generator);
} }
@ -1444,15 +1446,17 @@ void MacroAssembler::Pushad() {
// r15 is kSmiConstantRegister // r15 is kSmiConstantRegister
STATIC_ASSERT(11 == kNumSafepointSavedRegisters); STATIC_ASSERT(11 == kNumSafepointSavedRegisters);
// Use lea for symmetry with Popad. // Use lea for symmetry with Popad.
lea(rsp, Operand(rsp, int sp_delta =
-(kNumSafepointRegisters-kNumSafepointSavedRegisters) * kPointerSize)); (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
lea(rsp, Operand(rsp, -sp_delta));
} }
void MacroAssembler::Popad() { void MacroAssembler::Popad() {
// Popad must not change the flags, so use lea instead of addq. // Popad must not change the flags, so use lea instead of addq.
lea(rsp, Operand(rsp, int sp_delta =
(kNumSafepointRegisters-kNumSafepointSavedRegisters) * kPointerSize)); (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
lea(rsp, Operand(rsp, sp_delta));
pop(r14); pop(r14);
pop(r12); pop(r12);
pop(r11); pop(r11);
@ -1494,6 +1498,16 @@ int MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = {
}; };
void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
movq(SafepointRegisterSlot(dst), src);
}
Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
return Operand(rsp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
}
void MacroAssembler::PushTryHandler(CodeLocation try_location, void MacroAssembler::PushTryHandler(CodeLocation try_location,
HandlerType type) { HandlerType type) {
// Adjust this code if not the case. // Adjust this code if not the case.
@ -1835,11 +1849,19 @@ void MacroAssembler::DebugBreak() {
void MacroAssembler::InvokeCode(Register code, void MacroAssembler::InvokeCode(Register code,
const ParameterCount& expected, const ParameterCount& expected,
const ParameterCount& actual, const ParameterCount& actual,
InvokeFlag flag) { InvokeFlag flag,
PostCallGenerator* post_call_generator) {
NearLabel done; NearLabel done;
InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag); InvokePrologue(expected,
actual,
Handle<Code>::null(),
code,
&done,
flag,
post_call_generator);
if (flag == CALL_FUNCTION) { if (flag == CALL_FUNCTION) {
call(code); call(code);
if (post_call_generator != NULL) post_call_generator->Generate();
} else { } else {
ASSERT(flag == JUMP_FUNCTION); ASSERT(flag == JUMP_FUNCTION);
jmp(code); jmp(code);
@ -1852,12 +1874,20 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
const ParameterCount& expected, const ParameterCount& expected,
const ParameterCount& actual, const ParameterCount& actual,
RelocInfo::Mode rmode, RelocInfo::Mode rmode,
InvokeFlag flag) { InvokeFlag flag,
PostCallGenerator* post_call_generator) {
NearLabel done; NearLabel done;
Register dummy = rax; Register dummy = rax;
InvokePrologue(expected, actual, code, dummy, &done, flag); InvokePrologue(expected,
actual,
code,
dummy,
&done,
flag,
post_call_generator);
if (flag == CALL_FUNCTION) { if (flag == CALL_FUNCTION) {
Call(code, rmode); Call(code, rmode);
if (post_call_generator != NULL) post_call_generator->Generate();
} else { } else {
ASSERT(flag == JUMP_FUNCTION); ASSERT(flag == JUMP_FUNCTION);
Jump(code, rmode); Jump(code, rmode);
@ -1868,7 +1898,8 @@ void MacroAssembler::InvokeCode(Handle<Code> code,
void MacroAssembler::InvokeFunction(Register function, void MacroAssembler::InvokeFunction(Register function,
const ParameterCount& actual, const ParameterCount& actual,
InvokeFlag flag) { InvokeFlag flag,
PostCallGenerator* post_call_generator) {
ASSERT(function.is(rdi)); ASSERT(function.is(rdi));
movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset)); movq(rdx, FieldOperand(function, JSFunction::kSharedFunctionInfoOffset));
movq(rsi, FieldOperand(function, JSFunction::kContextOffset)); movq(rsi, FieldOperand(function, JSFunction::kContextOffset));
@ -1879,13 +1910,14 @@ void MacroAssembler::InvokeFunction(Register function,
movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
ParameterCount expected(rbx); ParameterCount expected(rbx);
InvokeCode(rdx, expected, actual, flag); InvokeCode(rdx, expected, actual, flag, post_call_generator);
} }
void MacroAssembler::InvokeFunction(JSFunction* function, void MacroAssembler::InvokeFunction(JSFunction* function,
const ParameterCount& actual, const ParameterCount& actual,
InvokeFlag flag) { InvokeFlag flag,
PostCallGenerator* post_call_generator) {
ASSERT(function->is_compiled()); ASSERT(function->is_compiled());
// Get the function and setup the context. // Get the function and setup the context.
Move(rdi, Handle<JSFunction>(function)); Move(rdi, Handle<JSFunction>(function));
@ -1896,12 +1928,17 @@ void MacroAssembler::InvokeFunction(JSFunction* function,
// the Code object every time we call the function. // the Code object every time we call the function.
movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
ParameterCount expected(function->shared()->formal_parameter_count()); ParameterCount expected(function->shared()->formal_parameter_count());
InvokeCode(rdx, expected, actual, flag); InvokeCode(rdx, expected, actual, flag, post_call_generator);
} else { } else {
// Invoke the cached code. // Invoke the cached code.
Handle<Code> code(function->code()); Handle<Code> code(function->code());
ParameterCount expected(function->shared()->formal_parameter_count()); ParameterCount expected(function->shared()->formal_parameter_count());
InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag); InvokeCode(code,
expected,
actual,
RelocInfo::CODE_TARGET,
flag,
post_call_generator);
} }
} }

View File

@ -58,6 +58,7 @@ typedef Operand MemOperand;
// Forward declaration. // Forward declaration.
class JumpTarget; class JumpTarget;
class PostCallGenerator;
struct SmiIndex { struct SmiIndex {
SmiIndex(Register index_register, ScaleFactor scale) SmiIndex(Register index_register, ScaleFactor scale)
@ -170,10 +171,9 @@ class MacroAssembler: public Assembler {
// Push and pop the registers that can hold pointers. // Push and pop the registers that can hold pointers.
void PushSafepointRegisters() { Pushad(); } void PushSafepointRegisters() { Pushad(); }
void PopSafepointRegisters() { Popad(); } void PopSafepointRegisters() { Popad(); }
static int SafepointRegisterStackIndex(int reg_code) { // Store the value in register src in the safepoint register stack
return kNumSafepointRegisters - 1 - // slot for register dst.
kSafepointPushRegisterIndices[reg_code]; void StoreToSafepointRegisterSlot(Register dst, Register src);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -183,27 +183,33 @@ class MacroAssembler: public Assembler {
void InvokeCode(Register code, void InvokeCode(Register code,
const ParameterCount& expected, const ParameterCount& expected,
const ParameterCount& actual, const ParameterCount& actual,
InvokeFlag flag); InvokeFlag flag,
PostCallGenerator* post_call_generator = NULL);
void InvokeCode(Handle<Code> code, void InvokeCode(Handle<Code> code,
const ParameterCount& expected, const ParameterCount& expected,
const ParameterCount& actual, const ParameterCount& actual,
RelocInfo::Mode rmode, RelocInfo::Mode rmode,
InvokeFlag flag); InvokeFlag flag,
PostCallGenerator* post_call_generator = NULL);
// Invoke the JavaScript function in the given register. Changes the // Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking. // current context to the context in the function before invoking.
void InvokeFunction(Register function, void InvokeFunction(Register function,
const ParameterCount& actual, const ParameterCount& actual,
InvokeFlag flag); InvokeFlag flag,
PostCallGenerator* post_call_generator = NULL);
void InvokeFunction(JSFunction* function, void InvokeFunction(JSFunction* function,
const ParameterCount& actual, const ParameterCount& actual,
InvokeFlag flag); InvokeFlag flag,
PostCallGenerator* post_call_generator = NULL);
// Invoke specified builtin JavaScript function. Adds an entry to // Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve. // the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag); void InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
PostCallGenerator* post_call_generator = NULL);
// Store the function for the given builtin in the target register. // Store the function for the given builtin in the target register.
void GetBuiltinFunction(Register target, Builtins::JavaScript id); void GetBuiltinFunction(Register target, Builtins::JavaScript id);
@ -996,7 +1002,8 @@ class MacroAssembler: public Assembler {
Handle<Code> code_constant, Handle<Code> code_constant,
Register code_register, Register code_register,
LabelType* done, LabelType* done,
InvokeFlag flag); InvokeFlag flag,
PostCallGenerator* post_call_generator);
// Activation support. // Activation support.
void EnterFrame(StackFrame::Type type); void EnterFrame(StackFrame::Type type);
@ -1027,6 +1034,17 @@ class MacroAssembler: public Assembler {
Object* PopHandleScopeHelper(Register saved, Object* PopHandleScopeHelper(Register saved,
Register scratch, Register scratch,
bool gc_allowed); bool gc_allowed);
// Compute memory operands for safepoint stack slots.
Operand SafepointRegisterSlot(Register reg);
static int SafepointRegisterStackIndex(int reg_code) {
return kNumSafepointRegisters - kSafepointPushRegisterIndices[reg_code] - 1;
}
// Needs access to SafepointRegisterStackIndex for optimized frame
// traversal.
friend class OptimizedFrame;
}; };
@ -1050,6 +1068,17 @@ class CodePatcher {
}; };
// Helper class for generating code or data associated with the code
// right after a call instruction. As an example this can be used to
// generate safepoint data after calls for crankshaft.
class PostCallGenerator {
public:
PostCallGenerator() { }
virtual ~PostCallGenerator() { }
virtual void Generate() = 0;
};
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Static helper functions. // Static helper functions.
@ -1756,7 +1785,8 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
Handle<Code> code_constant, Handle<Code> code_constant,
Register code_register, Register code_register,
LabelType* done, LabelType* done,
InvokeFlag flag) { InvokeFlag flag,
PostCallGenerator* post_call_generator) {
bool definitely_matches = false; bool definitely_matches = false;
NearLabel invoke; NearLabel invoke;
if (expected.is_immediate()) { if (expected.is_immediate()) {
@ -1807,6 +1837,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
if (flag == CALL_FUNCTION) { if (flag == CALL_FUNCTION) {
Call(adaptor, RelocInfo::CODE_TARGET); Call(adaptor, RelocInfo::CODE_TARGET);
if (post_call_generator != NULL) post_call_generator->Generate();
jmp(done); jmp(done);
} else { } else {
Jump(adaptor, RelocInfo::CODE_TARGET); Jump(adaptor, RelocInfo::CODE_TARGET);

View File

@ -2060,8 +2060,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
break; break;
case STRING_CHECK: case STRING_CHECK:
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
// Check that the object is a two-byte string or a symbol. // Check that the object is a two-byte string or a symbol.
@ -2076,8 +2077,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
break; break;
case NUMBER_CHECK: { case NUMBER_CHECK: {
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
Label fast; Label fast;
@ -2096,8 +2098,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object,
} }
case BOOLEAN_CHECK: { case BOOLEAN_CHECK: {
if (!function->IsBuiltin()) { if (!function->IsBuiltin() && !function_info->strict_mode()) {
// Calling non-builtins with a value as receiver requires boxing. // Calling non-strict non-builtins with a value as the receiver
// requires boxing.
__ jmp(&miss); __ jmp(&miss);
} else { } else {
Label fast; Label fast;

View File

@ -46,30 +46,9 @@ test-heap-profiler/HeapSnapshotsDiff: PASS || FAIL
test-serialize/TestThatAlwaysFails: FAIL test-serialize/TestThatAlwaysFails: FAIL
test-serialize/DependentTestThatAlwaysFails: FAIL test-serialize/DependentTestThatAlwaysFails: FAIL
##############################################################################
[ $arch == x64 ]
# Optimization is currently not working on crankshaft x64 and ARM.
test-heap/TestInternalWeakLists: PASS || FAIL
test-heap/TestInternalWeakListsTraverseWithGC: PASS || FAIL
##############################################################################
[ $arch == x64 && $crankshaft ]
# Tests that fail with crankshaft.
test-deoptimization/DeoptimizeBinaryOperationMOD: FAIL
test-deoptimization/DeoptimizeLoadICStoreIC: FAIL
test-deoptimization/DeoptimizeLoadICStoreICNested: FAIL
test-deoptimization/DeoptimizeCompare: PASS || FAIL
############################################################################## ##############################################################################
[ $arch == arm ] [ $arch == arm ]
# Optimization is currently not working on crankshaft x64 and ARM.
test-heap/TestInternalWeakLists: PASS || FAIL
test-heap/TestInternalWeakListsTraverseWithGC: PASS || FAIL
# We cannot assume that we can throw OutOfMemory exceptions in all situations. # We cannot assume that we can throw OutOfMemory exceptions in all situations.
# Apparently our ARM box is in such a state. Skip the test as it also runs for # Apparently our ARM box is in such a state. Skip the test as it also runs for
# a long time. # a long time.

View File

@ -5652,8 +5652,7 @@ TEST(AccessControl) {
} }
// This is a regression test for issue 1154. TEST(AccessControlES5) {
TEST(AccessControlObjectKeys) {
v8::HandleScope handle_scope; v8::HandleScope handle_scope;
v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
@ -5677,7 +5676,29 @@ TEST(AccessControlObjectKeys) {
v8::Handle<v8::Object> global1 = context1->Global(); v8::Handle<v8::Object> global1 = context1->Global();
global1->Set(v8_str("other"), global0); global1->Set(v8_str("other"), global0);
// Regression test for issue 1154.
ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1"); ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1");
ExpectUndefined("other.blocked_prop");
// Regression test for issue 1027.
CompileRun("Object.defineProperty(\n"
" other, 'blocked_prop', {configurable: false})");
ExpectUndefined("other.blocked_prop");
ExpectUndefined(
"Object.getOwnPropertyDescriptor(other, 'blocked_prop')");
// Regression test for issue 1171.
ExpectTrue("Object.isExtensible(other)");
CompileRun("Object.preventExtensions(other)");
ExpectTrue("Object.isExtensible(other)");
// Object.seal and Object.freeze.
CompileRun("Object.freeze(other)");
ExpectTrue("Object.isExtensible(other)");
CompileRun("Object.seal(other)");
ExpectTrue("Object.isExtensible(other)");
} }
@ -10825,6 +10846,24 @@ THREADED_TEST(PixelArray) {
"result"); "result");
CHECK_EQ(32640, result->Int32Value()); CHECK_EQ(32640, result->Int32Value());
// Make sure that pixel array stores are optimized by crankshaft.
result = CompileRun("function pa_init(p) {"
"for (var i = 0; i < 256; ++i) { p[i] = i; }"
"}"
"function pa_load(p) {"
" var sum = 0;"
" for (var i=0; i<256; ++i) {"
" sum += p[i];"
" }"
" return sum; "
"}"
"for (var i = 0; i < 100000; ++i) {"
" pa_init(pixels);"
"}"
"result = pa_load(pixels);"
"result");
CHECK_EQ(32640, result->Int32Value());
free(pixel_data); free(pixel_data);
} }
@ -10844,6 +10883,53 @@ THREADED_TEST(PixelArrayInfo) {
} }
static v8::Handle<Value> NotHandledIndexedPropertyGetter(
uint32_t index,
const AccessorInfo& info) {
ApiTestFuzzer::Fuzz();
return v8::Handle<Value>();
}
static v8::Handle<Value> NotHandledIndexedPropertySetter(
uint32_t index,
Local<Value> value,
const AccessorInfo& info) {
ApiTestFuzzer::Fuzz();
return v8::Handle<Value>();
}
THREADED_TEST(PixelArrayWithInterceptor) {
v8::HandleScope scope;
LocalContext context;
const int kElementCount = 260;
uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount));
i::Handle<i::PixelArray> pixels =
i::Factory::NewPixelArray(kElementCount, pixel_data);
for (int i = 0; i < kElementCount; i++) {
pixels->set(i, i % 256);
}
v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter,
NotHandledIndexedPropertySetter);
v8::Handle<v8::Object> obj = templ->NewInstance();
obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount);
context->Global()->Set(v8_str("pixels"), obj);
v8::Handle<v8::Value> result = CompileRun("pixels[1]");
CHECK_EQ(1, result->Int32Value());
result = CompileRun("var sum = 0;"
"for (var i = 0; i < 8; i++) {"
" sum += pixels[i] = pixels[i] = -i;"
"}"
"sum;");
CHECK_EQ(-28, result->Int32Value());
result = CompileRun("pixels.hasOwnProperty('1')");
CHECK(result->BooleanValue());
free(pixel_data);
}
static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
switch (array_type) { switch (array_type) {
case v8::kExternalByteArray: case v8::kExternalByteArray:

View File

@ -50,7 +50,7 @@ static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
i::Address frame3 = NULL) { i::Address frame3 = NULL) {
i::TickSample* sample = proc->TickSampleEvent(); i::TickSample* sample = proc->TickSampleEvent();
sample->pc = frame1; sample->pc = frame1;
sample->function = frame1; sample->tos = frame1;
sample->frames_count = 0; sample->frames_count = 0;
if (frame2 != NULL) { if (frame2 != NULL) {
sample->stack[0] = frame2; sample->stack[0] = frame2;
@ -103,7 +103,8 @@ TEST(CodeEvents) {
i::Heap::empty_string(), i::Heap::empty_string(),
0, 0,
ToAddress(0x1000), ToAddress(0x1000),
0x100); 0x100,
ToAddress(0x10000));
processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, processor.CodeCreateEvent(i::Logger::BUILTIN_TAG,
"bbb", "bbb",
ToAddress(0x1200), ToAddress(0x1200),
@ -116,8 +117,6 @@ TEST(CodeEvents) {
processor.CodeMoveEvent(ToAddress(0x1400), ToAddress(0x1500)); processor.CodeMoveEvent(ToAddress(0x1400), ToAddress(0x1500));
processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10); processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10);
processor.CodeDeleteEvent(ToAddress(0x1600)); processor.CodeDeleteEvent(ToAddress(0x1600));
processor.FunctionCreateEvent(ToAddress(0x1700), ToAddress(0x1000),
TokenEnumerator::kNoSecurityToken);
// Enqueue a tick event to enable code events processing. // Enqueue a tick event to enable code events processing.
EnqueueTickSampleEvent(&processor, ToAddress(0x1000)); EnqueueTickSampleEvent(&processor, ToAddress(0x1000));
@ -139,9 +138,6 @@ TEST(CodeEvents) {
CHECK_NE(NULL, entry4); CHECK_NE(NULL, entry4);
CHECK_EQ("ddd", entry4->name()); CHECK_EQ("ddd", entry4->name());
CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1600))); CHECK_EQ(NULL, generator.code_map()->FindEntry(ToAddress(0x1600)));
CodeEntry* entry5 = generator.code_map()->FindEntry(ToAddress(0x1700));
CHECK_NE(NULL, entry5);
CHECK_EQ(aaa_str, entry5->name());
} }

View File

@ -223,7 +223,7 @@ TEST(Unknown) {
{ DeclarationContext context; { DeclarationContext context;
context.Check("function x() { }; x", context.Check("function x() { }; x",
1, // access 1, // access
1, // declaration 0,
0, 0,
EXPECT_RESULT); EXPECT_RESULT);
} }
@ -278,7 +278,7 @@ TEST(Present) {
{ PresentPropertyContext context; { PresentPropertyContext context;
context.Check("function x() { }; x", context.Check("function x() { }; x",
1, // access 1, // access
1, // declaration 0,
0, 0,
EXPECT_RESULT); EXPECT_RESULT);
} }
@ -332,7 +332,7 @@ TEST(Absent) {
{ AbsentPropertyContext context; { AbsentPropertyContext context;
context.Check("function x() { }; x", context.Check("function x() { }; x",
1, // access 1, // access
1, // declaration 0,
0, 0,
EXPECT_RESULT); EXPECT_RESULT);
} }
@ -422,7 +422,7 @@ TEST(Appearing) {
{ AppearingPropertyContext context; { AppearingPropertyContext context;
context.Check("function x() { }; x", context.Check("function x() { }; x",
1, // access 1, // access
1, // declaration 0,
0, 0,
EXPECT_RESULT); EXPECT_RESULT);
} }

View File

@ -33,6 +33,7 @@
#include "v8.h" #include "v8.h"
#include "api.h"
#include "codegen.h" #include "codegen.h"
#include "log.h" #include "log.h"
#include "top.h" #include "top.h"
@ -200,16 +201,16 @@ static void InitializeVM() {
} }
static void CheckJSFunctionAtAddress(const char* func_name, Address addr) { static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
CHECK(i::Heap::Contains(addr)); i::Code* code = function->code();
i::Object* obj = i::HeapObject::FromAddress(addr); return code->contains(addr);
CHECK(obj->IsJSFunction()); }
CHECK(JSFunction::cast(obj)->shared()->name()->IsString());
i::SmartPointer<char> found_name = static bool IsAddressWithinFuncCode(const char* func_name, Address addr) {
i::String::cast( v8::Local<v8::Value> func = env->Global()->Get(v8_str(func_name));
JSFunction::cast( CHECK(func->IsFunction());
obj)->shared()->name())->ToCString(); JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
CHECK_EQ(func_name, *found_name); return IsAddressWithinFuncCode(js_func, addr);
} }
@ -309,8 +310,8 @@ TEST(CFromJSStackTrace) {
// Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
CHECK_GT(sample.frames_count, base + 1); CHECK_GT(sample.frames_count, base + 1);
CheckJSFunctionAtAddress("JSFuncDoTrace", sample.stack[base + 0]); CHECK(IsAddressWithinFuncCode("JSFuncDoTrace", sample.stack[base + 0]));
CheckJSFunctionAtAddress("JSTrace", sample.stack[base + 1]); CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 1]));
} }
@ -351,9 +352,6 @@ TEST(PureJSStackTrace) {
// DoTraceHideCEntryFPAddress(EBP) [native] // DoTraceHideCEntryFPAddress(EBP) [native]
// StackTracer::Trace // StackTracer::Trace
// //
// The last JS function called. It is only visible through
// sample.function, as its return address is above captured EBP value.
CheckJSFunctionAtAddress("JSFuncDoTrace", sample.function);
// The VM state tracking keeps track of external callbacks and puts // The VM state tracking keeps track of external callbacks and puts
// them at the top of the sample stack. // them at the top of the sample stack.
@ -363,8 +361,8 @@ TEST(PureJSStackTrace) {
// Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
CHECK_GT(sample.frames_count, base + 1); CHECK_GT(sample.frames_count, base + 1);
CheckJSFunctionAtAddress("JSTrace", sample.stack[base + 0]); CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 0]));
CheckJSFunctionAtAddress("OuterJSTrace", sample.stack[base + 1]); CHECK(IsAddressWithinFuncCode("OuterJSTrace", sample.stack[base + 1]));
} }

View File

@ -1053,10 +1053,10 @@ static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
// Skip size. // Skip size.
ref_s = strchr(ref_s, ',') + 1; ref_s = strchr(ref_s, ',') + 1;
new_s = strchr(new_s, ',') + 1; new_s = strchr(new_s, ',') + 1;
int ref_len = StrChrLen(ref_s, '\n'); CHECK_EQ('"', ref_s[0]);
int new_len = StrChrLen(new_s, '\n'); CHECK_EQ('"', new_s[0]);
// If reference is anonymous (""), it's OK to have anything in new. int ref_len = StrChrLen(ref_s + 1, '\"');
if (ref_len == 2) return true; int new_len = StrChrLen(new_s + 1, '\"');
// A special case for ErrorPrototype. Haven't yet figured out why they // A special case for ErrorPrototype. Haven't yet figured out why they
// are different. // are different.
const char* error_prototype = "\"ErrorPrototype"; const char* error_prototype = "\"ErrorPrototype";
@ -1074,21 +1074,6 @@ static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
return true; return true;
} }
} }
// Code objects can change their optimizability: code object may start
// as optimizable, but later be discovered to be actually not optimizable.
// Alas, we don't record this info as of now, so we allow cases when
// ref is thought to be optimizable while traverse finds it to be
// not optimizable.
if (ref_s[1] == '~') { // Code object used to be optimizable
if (new_s[1] == ' ') { // ...but later was set unoptimizable.
CHECK_EQ('"', ref_s[0]);
CHECK_EQ('"', new_s[0]);
ref_s += 2; // Cut the leading quote and the marker
ref_len -= 2;
new_s += 1; // Cut the leading quote only.
new_len -= 1;
}
}
return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
} }

View File

@ -321,14 +321,17 @@ TEST(Regress928) {
data->Initialize(); data->Initialize();
int first_function = strstr(program, "function") - program; int first_function =
int first_lbrace = first_function + strlen("function () "); static_cast<int>(strstr(program, "function") - program);
int first_lbrace = first_function + static_cast<int>(strlen("function () "));
CHECK_EQ('{', program[first_lbrace]); CHECK_EQ('{', program[first_lbrace]);
i::FunctionEntry entry1 = data->GetFunctionEntry(first_lbrace); i::FunctionEntry entry1 = data->GetFunctionEntry(first_lbrace);
CHECK(!entry1.is_valid()); CHECK(!entry1.is_valid());
int second_function = strstr(program + first_lbrace, "function") - program; int second_function =
int second_lbrace = second_function + strlen("function () "); static_cast<int>(strstr(program + first_lbrace, "function") - program);
int second_lbrace =
second_function + static_cast<int>(strlen("function () "));
CHECK_EQ('{', program[second_lbrace]); CHECK_EQ('{', program[second_lbrace]);
i::FunctionEntry entry2 = data->GetFunctionEntry(second_lbrace); i::FunctionEntry entry2 = data->GetFunctionEntry(second_lbrace);
CHECK(entry2.is_valid()); CHECK(entry2.is_valid());

View File

@ -600,13 +600,13 @@ TEST(RecordTickSample) {
// -> ccc -> aaa - sample3 // -> ccc -> aaa - sample3
TickSample sample1; TickSample sample1;
sample1.pc = ToAddress(0x1600); sample1.pc = ToAddress(0x1600);
sample1.function = ToAddress(0x1500); sample1.tos = ToAddress(0x1500);
sample1.stack[0] = ToAddress(0x1510); sample1.stack[0] = ToAddress(0x1510);
sample1.frames_count = 1; sample1.frames_count = 1;
generator.RecordTickSample(sample1); generator.RecordTickSample(sample1);
TickSample sample2; TickSample sample2;
sample2.pc = ToAddress(0x1925); sample2.pc = ToAddress(0x1925);
sample2.function = ToAddress(0x1900); sample2.tos = ToAddress(0x1900);
sample2.stack[0] = ToAddress(0x1780); sample2.stack[0] = ToAddress(0x1780);
sample2.stack[1] = ToAddress(0x10000); // non-existent. sample2.stack[1] = ToAddress(0x10000); // non-existent.
sample2.stack[2] = ToAddress(0x1620); sample2.stack[2] = ToAddress(0x1620);
@ -614,7 +614,7 @@ TEST(RecordTickSample) {
generator.RecordTickSample(sample2); generator.RecordTickSample(sample2);
TickSample sample3; TickSample sample3;
sample3.pc = ToAddress(0x1510); sample3.pc = ToAddress(0x1510);
sample3.function = ToAddress(0x1500); sample3.tos = ToAddress(0x1500);
sample3.stack[0] = ToAddress(0x1910); sample3.stack[0] = ToAddress(0x1910);
sample3.stack[1] = ToAddress(0x1610); sample3.stack[1] = ToAddress(0x1610);
sample3.frames_count = 2; sample3.frames_count = 2;

Some files were not shown because too many files have changed in this diff Show More